mirror of http://localhost:9400/cloud/cms
Merge pull request 'integration of branch - v2.2' (#25) from v2.2 into master
Reviewed-on: cloud/cms#25master
commit
415e3401a5
@ -1,473 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import io
|
||||
import copy
|
||||
from cms import disk, cloud
|
||||
from jinja2 import Environment, BaseLoader, FileSystemLoader
|
||||
# import importlib
|
||||
# import importlib.util
|
||||
"""
|
||||
There are four classes at play here:
|
||||
[ Initializer ] <|-- [ Module ] <|-- [ MicroService ] <--<>[ CMS ]
|
||||
"""
|
||||
|
||||
class Initializer :
|
||||
"""
|
||||
This class handles initialization of all sorts associated with "cms engine"
|
||||
:path
|
||||
:location
|
||||
:shared
|
||||
"""
|
||||
def __init__(self,**_args):
|
||||
self._config = {'system':{},'layout':{},'plugins':{}}
|
||||
# self._shared = False if not 'shared' in _args else _args['shared']
|
||||
self._location= _args['location'] if 'location' in _args else None
|
||||
|
||||
self._menu = {}
|
||||
# _source = self._config ['system']['source'] if 'source' in self._config['system'] else {}
|
||||
# self._ISCLOUD = 'source' in self._config['system'] and self._config['system']['source']['id'] == 'cloud'
|
||||
# print ([self._ISCLOUD,self._config['system'].keys()])
|
||||
self._ISCLOUD = False
|
||||
self._caller = None if 'caller' not in _args else _args['caller']
|
||||
self._args = _args
|
||||
# if 'context' in _args :
|
||||
# self._config
|
||||
self.reload() #-- this is an initial load of various components
|
||||
#
|
||||
# @TODO:
|
||||
# Each module should submit it's routers to the parent, and adjust the references given the callers
|
||||
#
|
||||
|
||||
def reload(self):
|
||||
|
||||
self._iconfig(**self._args)
|
||||
self._uconfig(**self._args)
|
||||
self._isource()
|
||||
self._imenu()
|
||||
self._iplugins()
|
||||
self._iroutes ()
|
||||
|
||||
# self._ISCLOUD = 'source' in self._config['system'] and self._config['system']['source']['id'] == 'cloud'
|
||||
|
||||
def _handler (self):
|
||||
"""
|
||||
This function returns the appropriate handler to the calling code, The handler enables read/write from a location
|
||||
"""
|
||||
|
||||
if self._ISCLOUD: #'source' in self._config['system'] and self._config['system']['source']['id'] == 'cloud' :
|
||||
return cloud
|
||||
else:
|
||||
return disk
|
||||
def _iroutes (self):
|
||||
"""
|
||||
Initializing routes to be submitted to the CMS Handler
|
||||
The routes must be able to :
|
||||
1. submit an object (dependency to the cms)
|
||||
2. submit with the object a route associated
|
||||
The CMS handler will resolve dependencies/redundancies
|
||||
"""
|
||||
pass
|
||||
def _imenu(self,**_args) :
|
||||
pass
|
||||
def _iplugins(self,**_args) :
|
||||
"""
|
||||
Initialize plugins from disk (always)
|
||||
:plugins
|
||||
"""
|
||||
_config = self._config
|
||||
PATH= os.sep.join([_config['layout']['root'],'_plugins'])
|
||||
|
||||
if not os.path.exists(PATH) and self._location and os.path.exists(self._location) :
|
||||
#
|
||||
# overriding the location of plugins ...
|
||||
if os.path.isfile(self._location) :
|
||||
_location = os.sep.join(self._location.split(os.sep)[:-1])
|
||||
else:
|
||||
_location = self._location
|
||||
PATH = os.sep.join([_location, _config['layout']['root'],'_plugins'])
|
||||
_context = _config['system']['context']
|
||||
_map = {}
|
||||
# if not os.path.exists(PATH) :
|
||||
# return _map
|
||||
if 'plugins' not in _config :
|
||||
_config['plugins'] = {}
|
||||
_conf = _config['plugins']
|
||||
|
||||
for _key in _conf :
|
||||
|
||||
_path = os.sep.join([PATH,_key+".py"])
|
||||
if not os.path.exists(_path):
|
||||
continue
|
||||
for _name in _conf[_key] :
|
||||
_pointer = disk.plugins(path=_path,name=_name,context=_context)
|
||||
if _pointer :
|
||||
_uri = "/".join(["api",_key,_name])
|
||||
if _context :
|
||||
_uri = f'{_context}/{_uri}'
|
||||
_map[_uri] = _pointer
|
||||
#
|
||||
# We are adding some source specific plugins to the user-defined plugins
|
||||
# This is intended to have out-of the box plugins...
|
||||
#
|
||||
|
||||
if self._ISCLOUD :
|
||||
_plugins = cloud.plugins(_context)
|
||||
else:
|
||||
_plugins = disk.plugins(context=_context)
|
||||
#
|
||||
# If there are any plugins found, we should load them and use them
|
||||
|
||||
if _plugins :
|
||||
_map = dict(_map,**_plugins)
|
||||
else:
|
||||
pass
|
||||
|
||||
self._plugins = _map
|
||||
self._config['plugins'] = self._plugins
|
||||
|
||||
|
||||
def _isource (self):
|
||||
"""
|
||||
Initializing the source of the data, so we can read/write load from anywhere
|
||||
"""
|
||||
if 'source' not in self._config['system'] :
|
||||
return
|
||||
#
|
||||
#
|
||||
self._ISCLOUD = 'source' in self._config['system'] and self._config['system']['source']['id'] == 'cloud'
|
||||
_source = self._config['system']['source']
|
||||
|
||||
if 'key' in _source :
|
||||
#
|
||||
_path = _source['key']
|
||||
if 'location' in self._config['layout'] and not os.path.exists(_path):
|
||||
_path = os.sep.join([self._config['layout']['location'],_path])
|
||||
|
||||
if os.path.exists(_path) :
|
||||
f = open(_path)
|
||||
_source['key'] = f.read()
|
||||
f.close()
|
||||
self._config['system']['source'] = _source
|
||||
def _ilayout(self,**_args):
|
||||
"""
|
||||
Initialization of the layout section (should only happen if ) being called via framework
|
||||
:path path to the dependent apps
|
||||
"""
|
||||
_layout = self._config['layout']
|
||||
_path = os.sep.join(_args['path'].split(os.sep)[:-1])
|
||||
#
|
||||
# find the new root and the one associated with the dependent app
|
||||
#
|
||||
|
||||
pass
|
||||
def _imenu(self,**_args):
|
||||
_gshandler = self._handler()
|
||||
_object = _gshandler.build(self._config) #-- this will build the menu
|
||||
#
|
||||
# post-processing menu, overwrite items and behaviors
|
||||
#
|
||||
|
||||
_layout = copy.deepcopy(self._config['layout'])
|
||||
_overwrite = _layout['overwrite'] if 'overwrite' in _layout else {}
|
||||
_context = self.system()['context']
|
||||
for _name in _object :
|
||||
_submenu = _object[_name]
|
||||
_index = 0
|
||||
for _item in _submenu :
|
||||
text = _item['text'].strip()
|
||||
|
||||
if text in _overwrite :
|
||||
if 'uri' in _item and 'url' in _overwrite[text] :
|
||||
del _item['uri']
|
||||
_item = dict(_item,**_overwrite[text])
|
||||
|
||||
if 'uri' in _item and 'type' in _item and _item['type'] != 'open':
|
||||
_item['uri'] = _item['uri'] #.replace(_layout['root'],'')
|
||||
# _item['uri'] = _gshandler._format(_item['uri'],self._config)
|
||||
|
||||
_submenu[_index] = _item
|
||||
_index += 1
|
||||
#
|
||||
# updating menu _items as it relates to apps, configuration and the order in which they appear
|
||||
#
|
||||
_layout['menu'] = _object
|
||||
self._menu = _object
|
||||
self._config['layout'] = _layout
|
||||
self._iapps()
|
||||
self._iorder()
|
||||
pass
|
||||
|
||||
def _iorder (self):
|
||||
_config = self._config
|
||||
if 'order' in _config['layout'] and 'menu' in _config['layout']['order']:
|
||||
_sortedmenu = {}
|
||||
_menu = self._menu
|
||||
for _name in _config['layout']['order']['menu'] :
|
||||
if _name in _menu :
|
||||
_sortedmenu[_name] = _menu[_name]
|
||||
|
||||
_menu = _sortedmenu if _sortedmenu else _menu
|
||||
#
|
||||
# If there are missing items in the sorting
|
||||
_missing = list(set(self._menu.keys()) - set(_sortedmenu))
|
||||
|
||||
if _missing :
|
||||
for _name in _missing :
|
||||
_menu[_name] = self._menu[_name]
|
||||
_config['layout']['menu'] = _menu #cms.components.menu(_config)
|
||||
self._menu = _menu
|
||||
self._config = _config
|
||||
def _iapps (self):
|
||||
"""
|
||||
Initializing dependent applications into a menu area if need be
|
||||
"""
|
||||
_layout = self._config['layout']
|
||||
_menu = _layout['menu'] if 'menu' in _layout else {}
|
||||
_system = self._config['system']
|
||||
_context= _system['context']
|
||||
if 'routes' in _system :
|
||||
# _items = []
|
||||
_overwrite = {} if 'overwrite' not in self._config['layout'] else self._config['layout']['overwrite']
|
||||
for _text in _system['routes'] :
|
||||
_item = _system['routes'][_text]
|
||||
if 'menu' not in _item :
|
||||
continue
|
||||
uri = f'{_context}/{_text}'
|
||||
# _items.append ({"text":_text,'uri':uri,'type':'open'})
|
||||
_label = _item['menu']
|
||||
if _text in _overwrite :
|
||||
_text = _overwrite[_text]['text']
|
||||
if _label not in _menu :
|
||||
_menu [_label] = []
|
||||
_menu[_label].append ({"text":_text,'uri':uri,'type':'open'})
|
||||
#
|
||||
# update the menu items and the configuration
|
||||
#
|
||||
_keys = list(_menu.keys())
|
||||
# for _key in _keys :
|
||||
# if len(_menu[_key]) == 0 :
|
||||
# del _menu[_key]
|
||||
# #
|
||||
# # doing some house-keeping work here to make sure the front-end gets clean data
|
||||
# #
|
||||
_layout['menu'] = _menu
|
||||
self._config['layout'] = _layout
|
||||
def _ilogo(self):
|
||||
_gshandler = self._handler()
|
||||
|
||||
pass
|
||||
def _iconfig(self,**_args):
|
||||
"""
|
||||
Implement this in a base class
|
||||
:path or uri
|
||||
"""
|
||||
raise Exception ("Configuration Initialization is NOT implemented")
|
||||
def _uconfig(self,**_args):
|
||||
"""
|
||||
This file will update the configuration provided the CMS is run in shared mode (framework level)
|
||||
"""
|
||||
if not self._location :
|
||||
return ;
|
||||
_path = os.sep.join(self._location.split(os.sep)[:-1])
|
||||
|
||||
_layout = self._config['layout']
|
||||
_oroot = _layout['root']
|
||||
_orw = _layout['overwrite']
|
||||
_index = _layout['index']
|
||||
_newpath = os.sep.join([_path,_oroot])
|
||||
self._config['system']['portal'] = self._caller != None
|
||||
_context = self.system()['context']
|
||||
if self._caller :
|
||||
#
|
||||
_callerContext = self._caller.system()['context']
|
||||
if not self._config['system']['context'] :
|
||||
self._config['system']['context'] = _callerContext
|
||||
self._config['system']['caller'] = {'icon': '/main'+self._caller.system()['icon'].replace(_callerContext,'')}
|
||||
_context = '/'.join([_callerContext,_context]) if _callerContext != '' else _context
|
||||
|
||||
|
||||
|
||||
if os.path.exists(_newpath) and not self._ISCLOUD:
|
||||
#
|
||||
# LOG: rewrite due to the mode in which the site is being run
|
||||
#
|
||||
_api = f'{_context}/api/disk/read?uri='+_oroot
|
||||
_stream = json.dumps(self._config)
|
||||
_stream = _stream.replace(_oroot,_api)
|
||||
# self._config = json.loads(_stream)
|
||||
self._config['layout']['root'] = _oroot
|
||||
|
||||
# self._config['layout']['overwrite'] = _orw
|
||||
#
|
||||
# We need to update the logo/icon
|
||||
_logo = self._config['system']['logo']
|
||||
if self._ISCLOUD:
|
||||
|
||||
_icon = f'{_context}/api/cloud/download?doc=/{_logo}'
|
||||
|
||||
|
||||
else:
|
||||
|
||||
_icon = f'{_context}/api/disk/read?uri={_logo}'
|
||||
# if disk.exists(uri=_logo,config=self._config):
|
||||
# _icon = _logo
|
||||
_logo = _icon
|
||||
if self._location :
|
||||
self._config['layout']['location'] = _path
|
||||
self._config['system']['icon'] = _icon
|
||||
self._config['system']['logo'] = _logo
|
||||
|
||||
# self.set('layout.root',os.sep.join([_path,_oroot]))
|
||||
pass
|
||||
class Module (Initializer):
|
||||
"""
|
||||
This is a basic structure for an application working in either portal or app mode,
|
||||
"""
|
||||
def __init__(self,**_args):
|
||||
super().__init__(**_args)
|
||||
pass
|
||||
def _iconfig(self, **_args):
|
||||
"""
|
||||
initialization of the configuration file i.e loading the files and having a baseline workable structure
|
||||
:path|stream path of the configuration file
|
||||
or stream of JSON configuration file
|
||||
"""
|
||||
if 'path' in _args :
|
||||
f = open(_args['path'])
|
||||
self._config = json.loads(f.read())
|
||||
f.close()
|
||||
elif 'stream' in _args :
|
||||
_stream = _args['stream']
|
||||
if type(_stream) == 'str' :
|
||||
self._config = json.loads(_stream)
|
||||
elif type(_stream) == io.StringIO :
|
||||
self._config = json.loads( _stream.read())
|
||||
self._ISCLOUD = 'source' in self._config['system'] and self._config['system']['source'] and self._config['system']['source']['id'] == 'cloud'
|
||||
if self._caller :
|
||||
self._config['system']['parentContext'] = self._caller.system()['context']
|
||||
else:
|
||||
self._config['system']['parentContext'] = self._config['system']['context']
|
||||
if 'context' in _args :
|
||||
self._config['system']['context'] = _args['context']
|
||||
|
||||
if '/' in self._config['system']['context'] :
|
||||
self._config['system']['context'] = self._config['system']['context'].split('/')[-1]
|
||||
#
|
||||
#
|
||||
# self._name = self._config['system']['name'] if 'name' in self._config['system'] else _args['name']
|
||||
def system (self,skip=[]):
|
||||
"""
|
||||
This function returns system attributes without specific components
|
||||
"""
|
||||
_data = copy.deepcopy(self._config['system'])
|
||||
exclude = skip
|
||||
_system = {}
|
||||
if exclude and _system:
|
||||
for key in _data.keys() :
|
||||
if key not in exclude :
|
||||
_system[key] = _data[key]
|
||||
else:
|
||||
_system= _data
|
||||
return _system
|
||||
def layout (self):
|
||||
return copy.copy(self._config['layout'])
|
||||
def plugins (self):
|
||||
return copy.copy(self._config['plugins'])
|
||||
def config (self):
|
||||
|
||||
return copy.copy(self._config)
|
||||
def app(self):
|
||||
_system = self.system()
|
||||
return _system['app']
|
||||
def set(self,key,value):
|
||||
"""
|
||||
This function will update/set an attribute with a given value
|
||||
:key
|
||||
"""
|
||||
_keys = key.split('.')
|
||||
_found = 0
|
||||
if _keys[0] in self._config :
|
||||
_object = self._config[_keys[0]]
|
||||
for _akey in _object.keys() :
|
||||
if _akey == _keys[-1] :
|
||||
_object[_akey] = value
|
||||
_found = 1
|
||||
break
|
||||
|
||||
#
|
||||
#
|
||||
return _found
|
||||
#
|
||||
class MicroService (Module):
|
||||
"""
|
||||
This is a CMS MicroService class that is capable of initializing a site and exposing Module functions
|
||||
|
||||
"""
|
||||
def __init__(self,**_args):
|
||||
super().__init__(**_args)
|
||||
def format(_content,mimetype):
|
||||
pass
|
||||
def html (self,uri, id) :
|
||||
_system = self.system()
|
||||
_gshandler = self._handler()
|
||||
#
|
||||
#@TODO:
|
||||
# The uri here must be properly formatted, We need to define the conditions for this
|
||||
#
|
||||
_html = _gshandler.html(uri,self._config)
|
||||
return " ".join([f'<div id="{id}" > ',_html, '</div>'])
|
||||
def context(self):
|
||||
return Environment(loader=BaseLoader())
|
||||
def data (self,**_args):
|
||||
request = _args['request']
|
||||
def icon (self):
|
||||
_handler = self._handler()
|
||||
_uri = self.system()['icon']
|
||||
|
||||
return _handler.read(uri=_uri,config=self._config)
|
||||
class CMS:
|
||||
"""
|
||||
This class aggregates microservices and allows the application of a given service (site/app)
|
||||
"""
|
||||
def __init__(self,**_args) :
|
||||
|
||||
# _app = Getter (path = path)
|
||||
# _void = MicroService()
|
||||
|
||||
_app = MicroService (**_args)
|
||||
self._id = 'main'
|
||||
# _app.load()
|
||||
self._apps = {}
|
||||
_system = _app.system()
|
||||
if 'routes' in _system :
|
||||
_system = _system['routes']
|
||||
for _name in _system :
|
||||
_path = _system[_name]['path']
|
||||
self._apps[_name] = MicroService(context=_name,path=_path,caller=_app,location=_path)
|
||||
|
||||
self._apps['main'] = _app
|
||||
#
|
||||
# The following are just a simple delegation pattern (it makes the calling code simpler)
|
||||
#
|
||||
def config (self):
|
||||
return self.get().config()
|
||||
|
||||
def render(self,_uri,_id,_appid):
|
||||
# _handler = self.get()
|
||||
_handler = self._apps[_appid]
|
||||
_config = _handler.config()
|
||||
_args = {'layout':_handler.layout()}
|
||||
if 'plugins' in _config:
|
||||
_args['routes'] = _config['plugins']
|
||||
|
||||
_html = _handler.html(_uri,_id)
|
||||
_args['system'] = _handler.system(skip=['source','app'])
|
||||
e = Environment(loader=BaseLoader()).from_string(_html)
|
||||
_args[_id] = str(e.render(**_args)) #,_args
|
||||
return _args
|
||||
def set(self,_id):
|
||||
self._id = _id
|
||||
def get(self):
|
||||
return self._apps['main'] if self._id not in self._apps else self._apps[self._id]
|
||||
def get_main(self):
|
||||
return self._apps['main']
|
@ -0,0 +1,352 @@
|
||||
"""
|
||||
This file will describe how a site is loaded
|
||||
"""
|
||||
from cms.engine.config.structure import Layout, System
|
||||
from cms import disk, cloud
|
||||
import copy
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
from jinja2 import Environment, BaseLoader, FileSystemLoader
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class IOConfig:
|
||||
"""
|
||||
This class encapsulates read/write for configuration
|
||||
"""
|
||||
def __init__(self,**_args):
|
||||
self._config = {'system':{},'layout':{},'plugins':{}}
|
||||
self._caller = None
|
||||
self._location= _args['location'] if 'location' in _args else None
|
||||
self._logs = []
|
||||
def get(self,_key) :
|
||||
if not _key :
|
||||
return self._config
|
||||
_keys = _key.split('.') if '.' in _key else [_key]
|
||||
_value= None
|
||||
_object = copy.deepcopy(self._config)
|
||||
for _key in _keys :
|
||||
if _key in _object :
|
||||
_object = _object[_key]
|
||||
else:
|
||||
return None
|
||||
|
||||
return _object
|
||||
def set(self,_expr,value):
|
||||
if len(_expr.split('.')) == 1 :
|
||||
self._config[_expr] = value
|
||||
else:
|
||||
_object = self._config
|
||||
for _attr in _expr.split('.')[:-1] :
|
||||
if _attr not in _object :
|
||||
_object[_attr] = {}
|
||||
_object = _object[_attr]
|
||||
_attr = _expr.split('.')[-1]
|
||||
_object[_attr] = value
|
||||
|
||||
def log(self,**_args):
|
||||
|
||||
_row = dict({'_date':datetime.now().strftime('%Y-%m-%d'),'time':datetime.now().strftime('%H:%M:%S'), 'context':self.get('system.context')},**_args)
|
||||
self._logs.append(_row)
|
||||
|
||||
def debug(self,**_args):
|
||||
return self._logs,'application/json'
|
||||
|
||||
|
||||
class Initialization (IOConfig):
|
||||
def __init__(self,**_args):
|
||||
super().__init__(**_args)
|
||||
self._config = self.read_config(**_args)
|
||||
self._args = _args
|
||||
#
|
||||
# Invoke initialization
|
||||
self.reload()
|
||||
|
||||
def reload (self):
|
||||
_args = self._args
|
||||
|
||||
self._caller = _args['caller'] if 'caller' in _args else None
|
||||
if (self._caller):
|
||||
self._config['system']['parentContext'] = ''
|
||||
if not self.get('system.icon') :
|
||||
self._config['system']['icon'] = None
|
||||
|
||||
self._config['layout']['location'] = None
|
||||
if not self.get('layout.menu') :
|
||||
self._config['layout']['menu'] = {}
|
||||
|
||||
self.context(**_args)
|
||||
|
||||
self.menu()
|
||||
self.plugins()
|
||||
def read_config(self,**_args) :
|
||||
_config = {}
|
||||
if 'path' in _args :
|
||||
f = open(_args['path'])
|
||||
_config = json.loads(f.read())
|
||||
f.close()
|
||||
elif 'stream' in _args :
|
||||
_stream = _args['stream']
|
||||
if type(_stream) == 'str' :
|
||||
_config = json.loads(_stream)
|
||||
elif type(_stream) == io.StringIO :
|
||||
_config = json.loads( _stream.read())
|
||||
_name = self._caller.__name__ if self._caller else None
|
||||
self.log(action='init.config',module='read_config',input={'caller':_name})
|
||||
return _config
|
||||
def context(self,**_args):
|
||||
"""
|
||||
Updating the context so that we can have a consistent representation
|
||||
"""
|
||||
if ('context' in _args):
|
||||
self.set('system.context',_args['context'])
|
||||
|
||||
pass
|
||||
_context = self.get('system.context')
|
||||
|
||||
if self._caller :
|
||||
#
|
||||
# There is a parent context we need to account and we are updating the current context to reflect the caller context
|
||||
_parentContext = self._caller.get('system.context')
|
||||
_context = '/'.join([self._caller.get('system.context'),_context])
|
||||
#
|
||||
# updating the configuratioin
|
||||
_iconURI = '/'.join(["",_parentContext,self._caller.get('system.icon')])
|
||||
|
||||
# print ([self._caller.get('system.context'), _parentContext,_context])
|
||||
if self._caller.get('system.context') != '':
|
||||
_parentContext = "/"+_parentContext
|
||||
_context = "/"+_context
|
||||
self.set('system.onport',0)
|
||||
self._config['system']['onport'] = 0
|
||||
else:
|
||||
_iconURI = _iconURI.replace("///","/")
|
||||
self.set('system.onport',1)
|
||||
self._config['system']['onport'] = 1
|
||||
self.set('system.caller',{'icon':_iconURI,'context':self._caller.get('system.context')})
|
||||
|
||||
# _context = _context.replace('/','')
|
||||
else:
|
||||
#
|
||||
# If we have no parent site, there should be no parent context (or empty string)
|
||||
_parentContext = _context #@TODO: should be none ?
|
||||
|
||||
|
||||
#
|
||||
# Updating context so it is usable throughout the code base
|
||||
if self._caller:
|
||||
self.set('system.parentContext', _parentContext.strip())
|
||||
self.set('system.context',_context.strip())
|
||||
# self._config['system']['context'] = _context.strip()
|
||||
self._config['system']['parentContext'] = _parentContext
|
||||
p = {'has_caller':self._caller != None,'context':_context}
|
||||
self.log(action='init.context',module='context',input=p)
|
||||
#
|
||||
# loosly context to a certain extent involves locations of icons and document root
|
||||
#
|
||||
|
||||
self.locations(**_args)
|
||||
|
||||
#
|
||||
# submit log of status
|
||||
|
||||
def locations(self,**_args):
|
||||
"""
|
||||
We are updating the icons, project location file and security key (if any), we need to make sure that context is updated first
|
||||
"""
|
||||
#
|
||||
# updating location & path of the application
|
||||
# if self.get('system.caller') :
|
||||
# return
|
||||
_context = self.get('system.context')
|
||||
_logo = self.get('system.logo')
|
||||
_root = self.get('layout.root')
|
||||
if self.get('system.source.id') == 'cloud' :
|
||||
_icon = f'{_context}/api/cloud/download?doc={_logo}'
|
||||
else:
|
||||
_icon = f'{_context}/api/disk/read?uri={_logo}'
|
||||
# _root = f'{_context}/api/disk/read?uri={_root}'
|
||||
|
||||
# self.set('layout.root',_root)
|
||||
self.set('system.icon',_icon)
|
||||
self.set('system.logo',_icon)
|
||||
#
|
||||
# from the path provided we can determine the location of the project
|
||||
_homefolder = os.sep.join(_args['path'].split(os.sep)[:-1])
|
||||
if _homefolder and os.path.exists(os.sep.join([_homefolder,_root])) :
|
||||
self.set('layout.location',_homefolder)
|
||||
|
||||
#
|
||||
# updating the security key and trying to determine the location
|
||||
if self.get('system.source.key') :
|
||||
_path = self.get('system.source.key')
|
||||
f = None
|
||||
if os.path.exists(_path) :
|
||||
f = open(_path)
|
||||
elif os.path.exists(os.sep.join([self.get('layout.location'),_path])) :
|
||||
f = open(os.sep.join([self.get('layout.location'),_path]))
|
||||
if f :
|
||||
self.set('system.source.key',f.read())
|
||||
f.close()
|
||||
|
||||
def menu (self,**_args):
|
||||
"""
|
||||
This function will build the menu of the site (if need be)
|
||||
"""
|
||||
_handler = cloud if self.get('system.source.id') == 'cloud' else disk
|
||||
_object = _handler.build(self._config)
|
||||
|
||||
_overwrite = self.get('layout.overwrite')
|
||||
for _name in _object :
|
||||
_submenu = _object[_name]
|
||||
_index = 0
|
||||
for _item in _submenu :
|
||||
_text = _item['text'].strip()
|
||||
if _text in _overwrite :
|
||||
if 'uri' in _item and 'url' in _overwrite :
|
||||
del _item['uri']
|
||||
_item = dict(_item,**_overwrite[_text])
|
||||
_submenu[_index] = _item
|
||||
_index += 1
|
||||
self.set('layout.menu',_object)
|
||||
# self._config['layout']['menu'] = _object
|
||||
self.routes() #-- other apps/sites integrated in case we are operating in portal mode
|
||||
#
|
||||
# At this point the entire menu is build and we need to have it sorted
|
||||
self.order()
|
||||
# _labels = list(self.get('layout.menu').keys())
|
||||
# self.log(action='init.menu',module='menu',input=_labels)
|
||||
# print (self.get('layout.menu'))
|
||||
# print (_object)
|
||||
def order(self,**_args):
|
||||
if self.get('layout.order.menu') :
|
||||
_sorted = {}
|
||||
_menu = self.get('layout.menu')
|
||||
_ordered = self.get('layout.order.menu')
|
||||
for _name in _ordered :
|
||||
if _name in _menu :
|
||||
_sorted[_name] = _menu[_name]
|
||||
if _sorted :
|
||||
_menu = _sorted
|
||||
_missing = list(set(_menu.keys()) - set(_sorted))
|
||||
if _missing :
|
||||
for _name in _missing :
|
||||
_menu[_name] = _menu[_name]
|
||||
self.set('layout.menu',_menu)
|
||||
def routes (self,**_args):
|
||||
"""
|
||||
This method will update menu with dependent sites/apps and add them to the menu
|
||||
"""
|
||||
_overwrite = self.get('layout.overwrite')
|
||||
_routes = self.get('system.routes')
|
||||
_context = self.get('system.context')
|
||||
_menu = self.get('layout.menu')
|
||||
|
||||
if _routes :
|
||||
for _text in _routes :
|
||||
_item = _routes[_text]
|
||||
if 'menu' in _item :
|
||||
_uri = f'{_context}/{_text}'
|
||||
_label = _item['menu']
|
||||
if _text in _overwrite :
|
||||
_text = _overwrite[_text]['text']
|
||||
if _label not in _menu :
|
||||
_menu[_label] = []
|
||||
_menu[_label].append({"text":_text,"uri":_uri,"type":"open"})
|
||||
#
|
||||
# updating menu ...
|
||||
self.set('layout.menu',_menu)
|
||||
# self._config['layout']['menu'] = _menu
|
||||
else:
|
||||
pass
|
||||
def plugins (self,**_args):
|
||||
"""
|
||||
This function will load the plugins from disk (only)
|
||||
"""
|
||||
_folder = os.sep.join([self.get('layout.location'),self.get('layout.root'),'_plugins'])
|
||||
_context = self.get('system.context')
|
||||
_parentContext = self.get('system.parentContext')
|
||||
_map = {}
|
||||
_plugins = {}
|
||||
if self.get('system.source.id') == 'cloud' :
|
||||
_plugins = cloud.plugins(_context)
|
||||
else:
|
||||
_plugins = disk.plugins(context=_context)
|
||||
_uri = 'api/system/debug'
|
||||
_uri = _uri if not _context else f'{_context}/{_uri}'
|
||||
_plugins[_uri] = self.debug
|
||||
if os.path.exists(_folder) and self.get('plugins'):
|
||||
|
||||
_items = self.get('plugins')
|
||||
|
||||
for _filename in _items:
|
||||
_path = os.sep.join([_folder,_filename+'.py'])
|
||||
|
||||
if os.path.exists(_path) :
|
||||
for _module in _items[_filename] :
|
||||
_pointer = disk.plugins(path=_path,name=_module,context=_context)
|
||||
if _pointer :
|
||||
_uri = f"api/{_filename}/{_module}"
|
||||
_uri = f"{_context}/{_uri}" if _context else _uri
|
||||
_map[_uri] = _pointer
|
||||
if _parentContext :
|
||||
# _uri = f"{_parentContext}/{_context}"
|
||||
_map[_uri] = _pointer
|
||||
|
||||
self.log(action='load.plugin',module='plugins',input={'uri':_uri,'path':_path,'name':_module})
|
||||
else:
|
||||
self.log(action='missing.function',module='plugins',input={'name':_module,'path':_path})
|
||||
|
||||
else:
|
||||
#
|
||||
# log the error ...
|
||||
self.log(action='missing.plugins',module='plugins',input=_path)
|
||||
#
|
||||
# Updating plugins from disk/cloud
|
||||
_plugins = _map if not _plugins else dict(_plugins,**_map)
|
||||
#
|
||||
#
|
||||
self.set('plugins',_plugins)
|
||||
self.log(action='init.plugins',module='plugins',input=list(_plugins.keys()))
|
||||
|
||||
class Site(Initialization) :
|
||||
def __init__(self,**_args):
|
||||
super().__init__(**_args)
|
||||
self._config['system']['portal'] = (self.get('system.routes')) == None
|
||||
def html(self,_uri,_id) :
|
||||
_handler = cloud if self.get('system.source.id') == 'cloud' else disk
|
||||
|
||||
_html = _handler.html(_uri, self.get(None))
|
||||
|
||||
return " ".join([f'<div id="{_id}"> ',_html,"</div>"])
|
||||
|
||||
class QCMS:
|
||||
def __init__(self,**_args):
|
||||
_app = Site(**_args)
|
||||
self._id = _app.get('system.context') #if _app.get('system.context') else 'main'
|
||||
self._sites = {self._id:_app}
|
||||
if _app.get('system.routes') :
|
||||
_routes = _app.get('system.routes')
|
||||
|
||||
for _name in _routes :
|
||||
_path = _routes[_name]['path']
|
||||
self._sites[_name] = Site(context=_name,path=_path,caller=_app)
|
||||
def render(self,_uri,_id,_appid=None):
|
||||
_site = self._sites[_appid] if _appid else self._sites[self._id]
|
||||
_args = {'layout':_site.get('layout')}
|
||||
_system = _site.get('system')
|
||||
for k in ['source','app'] :
|
||||
if k in _system :
|
||||
del _system[k]
|
||||
_args['system'] = _system
|
||||
_html = _site.html(_uri,_id)
|
||||
|
||||
_env = Environment(loader=BaseLoader()).from_string(_html)
|
||||
_args[_id] = str(_env.render(**_args))
|
||||
return _args
|
||||
def set(self,_id):
|
||||
self._id = _id
|
||||
def get(self,_id=None):
|
||||
|
||||
return self._sites[self._id] if not _id else self._sites[_id]
|
@ -0,0 +1,60 @@
|
||||
/***
|
||||
*
|
||||
* This file build forms from a given set of specifications (in JSON) format
|
||||
*/
|
||||
|
||||
var _form =function(_id,_data) {
|
||||
this._data = _data // {input,defaults,choices}
|
||||
this._id = _id
|
||||
//
|
||||
// let's clean up the data :
|
||||
this._data.defaults = (this._data.defaults == null)? {}:this._data.defaults
|
||||
this._data.choices = (this._data.choices == null)?{}: this._data.choices
|
||||
this.render = function (){
|
||||
_title = $('<div></div>').html(this._id).addClass(this._id)
|
||||
var _input = this._data.input
|
||||
var _defaults = this._data.defaults
|
||||
var _choices = this._data.choices
|
||||
|
||||
_input.forEach (_label=>{
|
||||
_value = _defaults[_label]
|
||||
if (_choices[_label]){
|
||||
|
||||
}else{
|
||||
//
|
||||
// This
|
||||
|
||||
_input = this.get.input(_label,_value)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
this.get = {}
|
||||
this.get.input = function (label,value){
|
||||
_frame = $('<div></div>')
|
||||
_label = $('<div></div>').html(label)
|
||||
_input = $('<input type="text"/>').attr('placeholder',label.0.replace(/_/,' ').trim()).addClass("input-"+label)
|
||||
$(_input)[0].data = label
|
||||
if (value){
|
||||
$(_input).val(value)
|
||||
}
|
||||
_frame.append(_label,_input)
|
||||
return _frame
|
||||
}
|
||||
this.get.choice= function (label,_default,choices) {
|
||||
var _input = this.get.input(label,_default)
|
||||
$(input).addClass("hidden-input")
|
||||
$(_input).on('click',function (){
|
||||
|
||||
})
|
||||
_pane = $('<div class="choices"></div>')
|
||||
choices.forEach(_value=>{
|
||||
_option = $('<div><div>').addClass("option").addClass("option-"+_value)
|
||||
$(_pane).append(_option)
|
||||
})
|
||||
_pane.append(_input,choices)
|
||||
_frame = $('<div></div>').append(_pane)
|
||||
return _frame
|
||||
}
|
||||
}
|
Loading…
Reference in new issue