bug fixes: enabling sharing features that would allow smaller footprint

v2.0
Steve Nyemba 7 months ago
parent 541ae469f6
commit 06754b9a57

@ -26,6 +26,24 @@ def _format_root_folder (_root):
_root = _root[:-1] _root = _root[:-1]
return _root.replace('//','/') return _root.replace('//','/')
def list_files(folder,_config) :
"""
List the content of a folder (html/md) for now
"""
_authfile = _config['system']['source']['auth']
_handler = login(_authfile)
_files = _handler.list(folder,50)
_content = []
for _item in _files :
if _item.file_type == 'file' and _item.get_content_type() in ['text/markdown','text/html'] :
_uri = '/'.join(_item.path.split('/')[2:])
_uri = _item.path
# _content.append({'text':_item.name.split('.')[0],'uri':_uri})
_content.append(_item.name)
return _content
def content(_args): def content(_args):
""" """
:url :url
@ -49,10 +67,18 @@ def content(_args):
_menu = {} #dict.fromkeys(_menu,[]) _menu = {} #dict.fromkeys(_menu,[])
for _item in _files : for _item in _files :
_folder = _item.path.split(_item.name)[0].strip() _folder = _item.path.split(_item.name)[0].strip()
_folder = _folder.replace(_root,'').replace('/','') _folder = _folder.replace(_root,'').replace('//','')
#
# The following lines are intended to prevent an irradict recursive read of a folder content
# We want to keep things simple as we build the menu
#
if len (_folder.split('/')) > 2:
continue
else:
_folder = _folder.replace('/','')
if _item.name[0] in ['.','_'] or _folder == '': if _item.name[0] in ['.','_'] or _folder == '':
continue ; continue ;
if _item.file_type == 'file' and _item.get_content_type() in ['text/markdown','text/html'] : if _item.file_type == 'file' and _item.get_content_type() in ['text/markdown','text/html'] :
# _folder = _item.path.split(_item.name)[0].strip() # _folder = _item.path.split(_item.name)[0].strip()
# _folder = _folder.replace(_root,'').replace('//','') # _folder = _folder.replace(_root,'').replace('//','')
@ -61,12 +87,6 @@ def content(_args):
_folder = _folder.replace('/' ,' ').strip() _folder = _folder.replace('/' ,' ').strip()
if _folder not in _menu : if _folder not in _menu :
_menu [_folder] = [] _menu [_folder] = []
# print ([_item.name,_key, _key in _menu])
# _menuItem = _ref[_key]
# uri = '/'.join([_args['url'],_item.path])
# uri = _item
# print ([_menuItem, _menuItem in _menu])
uri = '/'.join(_item.path.split('/')[2:]) uri = '/'.join(_item.path.split('/')[2:])
uri = _item.path uri = _item.path
_menu[_folder].append({'text':_item.name.split('.')[0],'uri':uri}) _menu[_folder].append({'text':_item.name.split('.')[0],'uri':uri})
@ -130,9 +150,15 @@ def download(**_args):
else: else:
_stream = _handler.get_file_contents(_request.args['doc']) _stream = _handler.get_file_contents(_request.args['doc'])
_handler.logout() _handler.logout()
return _stream return _stream
pass pass
def _format (uri,_config) :
"""
This function does nothing but is used to satisfy the demands of a design pattern
@TODO: revisit the design pattern
"""
return uri
def plugins (): def plugins ():
""" """
This function publishes the plugins associated with this module This function publishes the plugins associated with this module

@ -2,24 +2,39 @@
This file pulls the content from the disk This file pulls the content from the disk
""" """
import os import os
def folders (_path): import importlib
import importlib.util
import copy
from mistune import markdown
def folders (_path,_config):
""" """
This function reads the content of a folder (no depth, it must be simple) This function reads the content of a folder (no depth, it must be simple)
""" """
_content = os.listdir(_path) _content = os.listdir(_path)
return [_name for _name in _content if os.path.isdir(os.sep.join([_path,_name])) if not _name.startswith('_')] return [_name for _name in _content if os.path.isdir(os.sep.join([_path,_name])) if not _name.startswith('_')]
def content(_folder,_config):
def content(_folder):
""" """
:content of the folder :content of the folder
""" """
_layout = _config['layout']
if 'location' in _layout :
_uri = os.sep.join([_layout['root'] ,_folder.split(os.sep)[-1]])
_path = os.sep.join([_layout['root'],_folder.split(os.sep)[-1]])
else:
_path = _folder
if os.path.exists(_folder) : if os.path.exists(_folder) :
_menuItems = os.listdir(_folder) _menuItems = list_files(_folder,_config) #os.listdir(_folder)
# return [{'text':_name.split('.')[0].replace('_', ' ').replace('-',' ').strip(),'uri': os.sep.join([_folder,_name])} for _name in os.listdir(_folder) if not _name.startswith('_') and os.path.isfile( os.sep.join([_folder,_name]))] # return [{'text':_name.split('.')[0].replace('_', ' ').replace('-',' ').strip(),'uri': os.sep.join([_folder,_name])} for _name in os.listdir(_folder) if not _name.startswith('_') and os.path.isfile( os.sep.join([_folder,_name]))]
return [{'text':_name.split('.')[0].replace('_', ' ').replace('-',' ').strip(),'uri': os.sep.join([_folder,_name])} for _name in os.listdir(_folder) if not _name.startswith('_') and os.path.isfile( os.sep.join([_folder,_name]))]
return [{'text':_name.split('.')[0].replace('_', ' ').replace('-',' ').strip(),'uri': os.sep.join([_path,_name])} for _name in os.listdir(_folder) if not _name[0] in ['.','_'] and os.path.isfile( os.sep.join([_folder,_name]))]
else: else:
return [] return []
def list_files(_folder,_config):
return [name for name in os.listdir(_folder) if name[0] not in ['.','_']]
def build (_config): #(_path,_content): def build (_config): #(_path,_content):
""" """
building the menu for the site given the content is on disk building the menu for the site given the content is on disk
@ -27,20 +42,104 @@ def build (_config): #(_path,_content):
:config configuration associated with the :config configuration associated with the
""" """
_path = _config['layout']['root'] _path = _config['layout']['root']
_items = folders(_path) # if 'location' in _config['layout'] :
_subItems = [ content (os.sep.join([_path,_name]))for _name in _items ] # _path = _config['layout']['location']
_path = _realpath(_path,_config)
# print (_path)
_items = folders(_path,_config)
_subItems = [ content (os.sep.join([_path,_name]),_config)for _name in _items ]
_r = {} _r = {}
for _name in _items : for _name in _items :
_index = _items.index(_name) _index = _items.index(_name)
if _name.startswith('_') or len(_subItems[_index]) == 0:
continue
# print ([_name,_subItems[_index]])
if _name not in _r : if _name not in _r :
_r[_name] = [] _r[_name] = []
_r[_name] += _subItems[_index] _r[_name] += _subItems[_index]
# _r = [_r[_key] for _key in _r if len(_r[_key]) > 0]
return _r return _r
# return dict.fromkeys(_items,_subItems) # return dict.fromkeys(_items,_subItems)
def html(uri) : def _realpath (uri,_config) :
_html = (open(uri)).read() _layout = _config['layout']
_uri = copy.copy(uri)
if 'location' in _layout :
_uri = os.sep.join([_layout['location'],_uri])
return _uri
def _format (uri,_config):
_layout = _config['layout']
if 'location' in _layout :
return 'api/disk/read?uri='+uri
return uri
def read (**_args):
"""
This will read binary files from disk, and allow the location or not to be read
@TODO: add permissions otherwise there can be disk-wide reads
"""
request = _args['request']
_layout = _args['config']['layout']
_uri = request.args['uri'] # if 'location' in _layout :
# _uri = os.sep.join([_layout['location'],_uri])
_uri = _realpath(_uri, _args['config'])
if os.path.exists(_uri):
f = open(_uri,mode='rb')
_stream = f.read()
f.close()
return _stream
return None
def exists(**_args):
_path = _realpath(_args['uri'],_args['config'])
# _layout = _args['config']['layout']
# if 'location' in _layout :
# _path = os.sep.join([_layout['location'],_path])
return os.path.exists(_path)
def html(_uri,_config) :
# _html = (open(uri)).read()
_path = _realpath(_uri,_config)
_html = ( open(_path)).read()
_layout = _config['layout']
if 'location' in _layout :
_api = os.sep.join(['api/disk/read?uri=',_layout['root']])
_html = _html.replace(_layout['root'],_api)
_html = markdown(_html) if _uri[-2:] in ['md','MD','Md','mD'] else _html
return _html return _html
def plugins (): def plugins (**_args):
return {} """
This function will load plugins from disk given where they come from
:path path of the files
:name name of the module
"""
if 'path' not in _args :
return {'api/disk/read':read}
_path = _args['path'] #os.sep.join([_args['root'],'plugin'])
if os.path.isdir(_path):
files = os.listdir(_path)
if files :
files = [name for name in files if name.endswith('.py')]
if files:
_path = os.sep.join([_path,files[0]])
else:
return None
else:
#
# LOG: not a file
return None
#-- We have a file ...
_name = _args['name']
spec = importlib.util.spec_from_file_location(_name, _path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
#
# LOG This plugin ....
return getattr(module,_name) if hasattr(module,_name) else None

@ -9,9 +9,14 @@ from jinja2 import Environment, BaseLoader, FileSystemLoader
import importlib import importlib
import importlib.util import importlib.util
from cms import disk, cloud from cms import disk, cloud
from . import basic
class Loader : class Loader :
"""
This class is designed to exclusively load configuration from disk into an object
:path path to the configuraiton file
:location original location (caller)
"""
def __init__(self,**_args): def __init__(self,**_args):
self._path = _args['path'] self._path = _args['path']
self._original_location = None if 'location' not in _args else _args['location'] self._original_location = None if 'location' not in _args else _args['location']
@ -21,6 +26,7 @@ class Loader :
self._plugins={} self._plugins={}
self.load() self.load()
def load(self): def load(self):
""" """
This function will load menu (overwrite) and plugins This function will load menu (overwrite) and plugins
@ -35,16 +41,31 @@ class Loader :
f = open (self._path) f = open (self._path)
self._config = json.loads(f.read()) self._config = json.loads(f.read())
if self._caller : if self._caller :
self._location = self._original_location.split(os.sep) # needed for plugin loading self._location = self._original_location.split(os.sep) # needed for plugin loading
self._location = os.sep.join(self._location[:-1]) self._location = os.sep.join(self._location[:-1])
self._config['system']['portal'] = self._caller != None self._config['system']['portal'] = self._caller != None
# #
# let's see if we have a location for a key in the configuration # let's see if we have a location for a key (i.e security key) in the configuration
# #
self.update_config()
# _system = self._config['system']
# if 'source' in _system and 'key' in _system['source'] :
# _path = _system['source']['key']
# if os.path.exists(_path):
# f = open(_path)
# _system['source']['key'] = f.read()
# f.close()
# self._system = _system
# self._config['system'] = _system
def update_config(self):
"""
We are going to update the configuration (source.key, layout.root)
"""
_system = self._config['system'] _system = self._config['system']
#
# updating security-key that allows the application to update on-demand
if 'source' in _system and 'key' in _system['source'] : if 'source' in _system and 'key' in _system['source'] :
_path = _system['source']['key'] _path = _system['source']['key']
if os.path.exists(_path): if os.path.exists(_path):
@ -53,6 +74,22 @@ class Loader :
f.close() f.close()
self._system = _system self._system = _system
self._config['system'] = _system self._config['system'] = _system
_layout = self._config['layout']
#
# update root so that the app can be launched from anywhere
# This would help reduce the footprint of the app/framework
_path = os.sep.join(self._path.split(os.sep)[:-1])
_p = 'source' not in _system
_q = 'source' in _system and _system['source']['id'] != 'cloud'
_r = os.path.exists(_layout['root'])
if not _r and (_p or _q) :
#
# If we did running this app from installed framework (this should not apply to dependent apps)
#
_root = os.sep.join([_path,_layout['root']])
self._config['layout']['root'] = _root
self._config['layout']['root_prefix'] = _root
def init_menu(self): def init_menu(self):
""" """
This function will read menu and sub-menu items from disk structure, This function will read menu and sub-menu items from disk structure,
@ -252,7 +289,8 @@ class Getter (Loader):
_html = cloud.html(uri,dict(_args,**{'system':_system})) _html = cloud.html(uri,dict(_args,**{'system':_system}))
else: else:
_html = disk.html(uri)
_html = disk.html(uri,self.layout())
# _html = (open(uri)).read() # _html = (open(uri)).read()
@ -313,14 +351,19 @@ class Getter (Loader):
else: else:
_system= _data _system= _data
return _system return _system
def layout(self):
return self._config['layout']
def get_app(self): def get_app(self):
return self._config['system']['app'] return self._config['system']['app']
class Router : class Router :
def __init__(self,**_args) : def __init__(self,**_args) :
path = _args['path']
_app = Getter (path = path) # _app = Getter (path = path)
_app = Getter (**_args)
self._id = 'main' self._id = 'main'
# _app.load() # _app.load()
self._apps = {} self._apps = {}
@ -330,8 +373,8 @@ class Router :
for _name in _system : for _name in _system :
_path = _system[_name]['path'] _path = _system[_name]['path']
self._apps[_name] = Getter(path=_path,caller=_app,location=_path) self._apps[_name] = Getter(path=_path,caller=_app,location=_path)
print ([_name, self._apps[_name].plugins().keys()])
self._apps['main'] = _app self._apps['main'] = _app
def set(self,_id): def set(self,_id):
self._id = _id self._id = _id
def get(self): def get(self):

@ -0,0 +1,435 @@
import json
import os
import io
import copy
from cms import disk, cloud
from jinja2 import Environment, BaseLoader, FileSystemLoader
import importlib
import importlib.util
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']
#
# actual initialization of the CMS components
# self._iconfig(**_args)
# self._uconfig(**_args)
# self._isource()
# self._imenu()
# self._iplugins()
self._args = _args
self.reload()
def reload(self):
self._iconfig(**self._args)
self._uconfig(**self._args)
self._isource()
self._imenu()
self._iplugins()
# 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 _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) :
# #
# # we need to determin if there's an existing
# print ([' **** ',PATH])
# PATH = os.sep.join(self._path.split(os.sep)[:-1]+ [PATH] )
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'])
_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)
if _pointer :
_uri = "/".join(["api",_key,_name])
_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()
else:
_plugins = disk.plugins()
#
# 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 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 {}
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 _label not in _menu :
_menu [_label] = []
_menu[_label].append ({"text":_text,'uri':uri,'type':'open'})
#
# update the menu items and the configuration
#
_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
if self._caller :
#
self._config['system']['caller'] = {'icon':'/caller/main/' + self._caller.system()['icon']}
# self._config['system']['caller'] = {'icon': self._caller.icon()}
if os.path.exists(_newpath) and not self._ISCLOUD:
#
# LOG: rewrite due to the mode in which the site is being run
#
_api = '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'/api/cloud/download?doc=/{_logo}'
else:
_icon = f'api/disk/read?uri={_logo}'
if disk.exists(uri=_logo,config=self._config):
_icon = _logo
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 Accessor (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']['id'] == 'cloud'
#
#
# 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 (Accessor):
"""
This is a CMS MicroService class that is capable of initializing a site and exposing accessor 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:
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):
_handler = self.get()
_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']

@ -15,28 +15,28 @@ import io
import base64 import base64
from jinja2 import Environment, BaseLoader from jinja2 import Environment, BaseLoader
import typer import typer
import pandas as pd
_app = Flask(__name__) _app = Flask(__name__)
cli = typer.Typer() cli = typer.Typer()
@_app.route('/favicon.ico') # @_app.route('/favicon.ico')
def favicon(): # def favicon():
global _route # global _route
_system = _route.get ().system() # _system = _route.get ().system()
_handler = _route.get() # _handler = _route.get()
_logo =_system['icon'] if 'icon' in _system else 'static/img/logo.svg' # _logo =_system['icon'] if 'icon' in _system else 'static/img/logo.svg'
return _handler.get(_logo) # return _handler.get(_logo)
# # _root = _route.get().config()['layout']['root'] # # # _root = _route.get().config()['layout']['root']
# # print ([_system]) # # # print ([_system])
# # if 'source' in _system and 'id' in _system['source'] and (_system['source']['id'] == 'cloud'): # # # if 'source' in _system and 'id' in _system['source'] and (_system['source']['id'] == 'cloud'):
# # uri = f'/api/cloud/downloads?doc=/{_logo}' # # # uri = f'/api/cloud/downloads?doc=/{_logo}'
# # print (['****' , uri]) # # # print (['****' , uri])
# # return redirect(uri,200) #,{'content-type':'application/image'} # # # return redirect(uri,200) #,{'content-type':'application/image'}
# # else: # # # else:
# # return send_from_directory(_root, #_app.root_path, 'static/img'), # # # return send_from_directory(_root, #_app.root_path, 'static/img'),
# _logo, mimetype='image/vnd.microsoft.icon') # # _logo, mimetype='image/vnd.microsoft.icon')
@_app.route("/robots.txt") @_app.route("/robots.txt")
def robots_txt(): def robots_txt():
""" """
@ -66,29 +66,32 @@ def _index ():
global _config global _config
global _route global _route
_handler = _route.get() _handler = _route.get()
_config = _handler.config() _config = _route.config()
_system = _handler.system() # _config = _handler.config()
_plugins= _handler.plugins() # _system = _handler.system()
_args = {} # _plugins= _handler.plugins()
# if 'plugins' in _config : # _args = {}
# _args['routes']=_config['plugins'] # # if 'plugins' in _config :
# _system = cms.components.get_system(_config) #copy.deepcopy(_config['system']) # # _args['routes']=_config['plugins']
_html = "" # # _system = cms.components.get_system(_config) #copy.deepcopy(_config['system'])
# _html = ""
_args={}
try: try:
uri = os.sep.join([_config['layout']['root'], _config['layout']['index']]) uri = os.sep.join([_config['layout']['root'], _config['layout']['index']])
_html = _route.get().html(uri,'index',_config,_system) # # _html = _route.get().html(uri,'index',_config,_system)
# _html = _handler.html(uri,'index')
_index_page = "index.html" _index_page = "index.html"
_args = _route.render(uri,'index')
except Exception as e: except Exception as e:
print () # print ()
print (e) print (e)
_index_page = "404.html" _index_page = "404.html"
_args['uri'] = request.base_url # _args['uri'] = request.base_url
pass # pass
# if 'source' in _system : # # if 'source' in _system :
# del _system['source'] # # del _system['source']
_args = {'layout':_config['layout'],'index':_html} # _args = {'layout':_config['layout'],'index':_html}
_args['system'] = _handler.system(skip=['source','app','route']) # _args['system'] = _handler.system(skip=['source','app','route'])
return render_template(_index_page,**_args) return render_template(_index_page,**_args)
@ -105,36 +108,19 @@ def _dialog ():
# global _config # global _config
global _route global _route
_handler = _route.get() _handler = _route.get()
_system = _handler.system() _uri = request.headers['uri']
_config = _handler.config()
_uri = os.sep.join([_config['layout']['root'],request.headers['uri']])
# _uri = request.headers['uri']
_id = request.headers['dom'] _id = request.headers['dom']
# _data = cms.components.data(_config) # _html = ''.join(["<div style='padding:1%'>",str( e.render(**_args)),'</div>'])
_args = {} #{'system':_config['system']} _args = _route.render(_uri,'html')
_args['title'] = _id _args['title'] = _id
# if 'plugins' in _config :
# _args['routes'] = _config['plugins']
# _system = copy.deepcopy(_config['system'])
# _html = cms.components.html(_uri,_id,_config,_system)
_html = _handler.html(_uri,_id,_config,_system)
e = Environment(loader=BaseLoader()).from_string(_html)
# if 'source' in _system :
# del _system['source']
_args['system'] = _handler.system(skip=['source','routes','app'])
_args['html'] = _html
_html = ''.join(["<div style='padding:1%'>",str( e.render(**_args)),'</div>'])
return render_template('dialog.html',**_args) #title=_id,html=_html) return render_template('dialog.html',**_args) #title=_id,html=_html)
# return _html @_app.route("/caller/<app>/api/<module>/<name>")
# e = Environment(loader=BaseLoader()).from_string(_html) def _delegate_call(app,module,name):
# _data = cms.components.data(_config) global _route
# _args = {'system':_config['system'],'data':_data} _handler = _route._apps[app]
return _delegate(_handler,module,name)
# _html = ( e.render(**_args))
@_app.route('/api/<module>/<name>') @_app.route('/api/<module>/<name>')
def _getproxy(module,name) : def _getproxy(module,name) :
""" """
@ -145,12 +131,14 @@ def _getproxy(module,name) :
# global _config # global _config
global _route global _route
_handler = _route.get() _handler = _route.get()
return _delegate(_handler,module,name)
def _delegate(_handler,module,name):
uri = '/'.join(['api',module,name]) uri = '/'.join(['api',module,name])
# _args = dict(request.args,**{}) # _args = dict(request.args,**{})
# _args['config'] = _handler.config() # _args['config'] = _handler.config()
_plugins = _handler.plugins() _plugins = _handler.plugins()
if uri not in _plugins : if uri not in _plugins :
_data = {} _data = {}
_code = 404 _code = 404
@ -160,40 +148,46 @@ def _getproxy(module,name) :
# _data = pointer (**_args) # _data = pointer (**_args)
# else: # else:
# _data = pointer() # _data = pointer()
_data = pointer(request=request,config=_handler.config()) _data = pointer(request=request,config=_handler.config())
if type(_data) == pd.DataFrame :
_data = _data.to_dict(orient='records')
if type(_data) == list:
_data = json.dumps(_data)
_code = 200 if _data else 500 _code = 200 if _data else 500
return _data,_code return _data,_code
@_app.route("/api/<module>/<name>" , methods=['POST']) @_app.route("/api/<module>/<name>" , methods=['POST'])
def _post (module,name): def _post (module,name):
# global _config # global _config
global _route global _route
_handler = _route.get() _handler = _route.get()
_config = _handler.config() return _delegate(_handler,module,name)
_plugins = _handler.plugins() # _config = _handler.config()
uri = '/'.join(['api',module,name]) # _plugins = _handler.plugins()
# uri = '/'.join(['api',module,name])
# # _args = request.json
# # _args['config'] = _config
# code = 404
# _data = ""
# if uri in _plugins :
# _pointer = _plugins[uri]
# # _info = _pointer(**_args)
# _data = _pointer(request=request,config=_handler.config() )
# if type(_data) == pd.DataFrame :
# _data = _data.to_dict(orient='records')
# if type(_data) == list:
# _data = json.dumps(_data)
# _args = request.json # _code = 200 if _data else 500
# _args['config'] = _config
code = 404
_info = ""
if uri in _plugins :
_pointer = _plugins[uri]
# _info = _pointer(**_args)
_info = _pointer(request=request,config=_handler.config() )
if _info:
code = 200
else:
# _info = ""
code = 500
# _info =io.BytesIO(_info) # # _info =io.BytesIO(_info)
# _info = base64.encodebytes(_info.getvalue()).decode('ascii') # # _info = base64.encodebytes(_info.getvalue()).decode('ascii')
return _info,code # return _data,code
@_app.route('/version') @_app.route('/version')
def _version (): def _version ():
global _route global _route
@ -213,7 +207,7 @@ def reload():
_systemKey = _system['source']['key'] _systemKey = _system['source']['key']
print ([_key,_systemKey,_systemKey == _key]) print ([_key,_systemKey,_systemKey == _key])
if _key and _systemKey and _systemKey == _key : if _key and _systemKey and _systemKey == _key :
_handler.load() _handler.reload()
return "",200 return "",200
pass pass
return "",403 return "",403
@ -233,36 +227,42 @@ def cms_page():
_id = _uri.split('/')[-1].split('.')[0] _id = _uri.split('/')[-1].split('.')[0]
else: else:
_id = request.headers['dom'] _id = request.headers['dom']
_args = {'layout':_config['layout']} # _args = {'layout':_config['layout']}
if 'plugins' in _config: # if 'plugins' in _config:
_args['routes'] = _config['plugins'] # _args['routes'] = _config['plugins']
# _system = _handler.system() #cms.components.get_system(_config)
# # _html = _handler.html(_uri,_id,_args,_system) #cms.components.html(_uri,_id,_args,_system)
_system = _handler.system() #cms.components.get_system(_config) # _html = _handler.html(_uri,_id)
_html = _handler.html(_uri,_id,_args,_system) #cms.components.html(_uri,_id,_args,_system) # # _system = cms.components.get_system(_config)
e = Environment(loader=BaseLoader()).from_string(_html) # _args['system'] = _handler.system(skip=['source','app'])
# _data = {} #cms.components.data(_config) # e = Environment(loader=BaseLoader()).from_string(_html)
_system = cms.components.get_system(_config) # _html = e.render(**_args)
_args['system'] = _handler.system(skip=['source','app']) if 'read?uri=' in _uri or 'download?doc=' in _uri :
_uri = _uri.split('=')[1]
_html = e.render(**_args) _args = _route.render(_uri,_id)
return _html,200 return _args[_id],200
# return _html,200
@_app.route('/page') @_app.route('/page')
def _cms_page (): def _cms_page ():
# global _config # global _config
global _route global _route
_handler = _route.get() # _handler = _route.get()
_config = _handler.config() # _config = _handler.config()
_uri = request.args['uri'] _uri = request.args['uri']
# _uri = os.sep.join([_config['layout']['root'],_uri]) # _uri = os.sep.join([_config['layout']['root'],_uri])
_title = request.args['title'] if 'title' in request.args else '' _title = request.args['title'] if 'title' in request.args else ''
_args = {'system':_handler.system()} #cms.components.get_system(_config) } # _args = {'system':_handler.system()} #cms.components.get_system(_config) }
# if 'plugins' in _config: # if 'plugins' in _config:
# _args['routes'] = _config['plugins'] # _args['routes'] = _config['plugins']
_html = _handler.html(_uri,_title,_args) # cms.components.html(_uri,_title,_args) # _html = _handler.html(_uri,_config) # cmsc.components.html(_uri,_title,_args)
e = Environment(loader=BaseLoader()).from_string(_html) # e = Environment(loader=BaseLoader()).from_string(_html)
_args['system'] = _handler.system(skip=['app','source']) # _args['system'] = _handler.system(skip=['app','source'])
return e.render(**_args),200 # return e.render(**_args),200
_args = _route.render(_uri,_title)
return _args[_title],200
@_app.route('/set/<id>') @_app.route('/set/<id>')
def set(id): def set(id):
@ -272,8 +272,14 @@ def set(id):
@_app.route('/<id>') @_app.route('/<id>')
def _open(id): def _open(id):
global _route global _route
_route.set(id) _handler = _route.get()
return _index() if id not in _route._apps :
_args = {'config':_handler.config(), 'layout':_handler.layout(),'system':_handler.system(skip=['source','app'])}
return render_template("404.html",**_args)
else:
_route.set(id)
return _index()
# #
# Let us bootup the application # Let us bootup the application
SYS_ARGS = {} SYS_ARGS = {}
@ -297,15 +303,25 @@ if len(sys.argv) > 1:
@cli.command() @cli.command()
def start (path:str='config.json') : def start (path:str='config.json',shared:bool=False) :
""" """
This function is designed to start the application with its associated manifest (configuration) location This function is designed to start the application with its associated manifest (configuration) location
:path path to the the manifest
:shared run in shared mode i.e
""" """
global _route global _route
if os.path.exists(path) and os.path.isfile(path): if os.path.exists(path) and os.path.isfile(path):
_route = cms.engine.Router(path=path) _args = {'path':path}
_args = _route.get().get_app() if shared :
_args['location'] = path
_args['shared'] = shared
# _route = cms.engine.Router(**_args) #path=path,shared=shared)
_route = cms.engine.basic.CMS(**_args)
# dir(_route)
# _args = _route.get().get_app()
_args = _route.get().app()
_app.run(**_args) _app.run(**_args)
_status = 'found' _status = 'found'
else: else:
@ -319,49 +335,3 @@ def _help() :
pass pass
if __name__ == '__main__' : if __name__ == '__main__' :
cli() cli()
# pass
# _path = SYS_ARGS['config'] if 'config' in SYS_ARGS else 'config.json'
# if os.path.exists(_path):
# _route = cms.engine.Router(path=_path)
# _args = _route.get().get_app()
# _app.run(**_args)
# _config = json.loads((open (_path)).read())
# if 'theme' not in _config['system'] :
# _config['system']['theme'] = 'magazine.css'
# #
# # root can be either on disk or in the cloud ...
# # root: "<path>" reading from disk
# # root: {uid,token,folder}
# #
# _root = _config['layout']['root']
# _menu = cms.components.menu(_config)
# if 'order' in _config['layout'] and 'menu' in _config['layout']['order']:
# _sortedmenu = {}
# for _name in _config['layout']['order']['menu'] :
# if _name in _menu :
# _sortedmenu[_name] = _menu[_name]
# _menu = _sortedmenu if _sortedmenu else _menu
# _config['layout']['menu'] = _menu #cms.components.menu(_config)
# # if 'data' in _config :
# # _config['data'] = cms.components.data(_config['data'])
# #
# _map = cms.components.plugins(_config)
# _config['plugins'] = _map
# # Let us load the plugins if any are available
# # if 'plugins' in _config :
# # _map = cms.components.plugins(_config)
# # if _map :
# # _config['plugins'] = _map
# #
# # register the functions with Jinja2
# # cms.components.context(_config)
# _args = _config['system']['app']
# _app.run(**_args)
# else:
# print (__doc__)
# print ()

@ -11,13 +11,17 @@ bootup.CMSObserver = function(_sysId,_domId,_fileURI){
this.apply = function (_caller){ this.apply = function (_caller){
var http = HttpClient.instance() var http = HttpClient.instance()
http.setHeader('uri',_fileURI) http.setHeader('uri',_fileURI)
var uri = sessionStorage[_sysId]+'/page'
if (sessionStorage[_sysId] != null){
var uri = sessionStorage[_sysId]+'/page'
}else{
var uri = '/page'
}
try{ try{
var _domElement = jx.dom.get.instance('div') var _domElement = jx.dom.get.instance('div')
_domElement.className = 'busy-loading' _domElement.className = 'busy-loading'
jx.dom.append(_domId, _domElement) jx.dom.append(_domId, _domElement)
http.post(uri,function(x){ http.post(uri,function(x){
// console.log(jx.dom.exists(_domId)) // console.log(jx.dom.exists(_domId))
// var _domElement = jx.dom.get.instance('div') // var _domElement = jx.dom.get.instance('div')
@ -63,6 +67,7 @@ bootup.init = function(sys_id,_layout){
var observers = var observers =
jx.utils.patterns.visitor(_layout.on.load[_domId], function(_uri){ jx.utils.patterns.visitor(_layout.on.load[_domId], function(_uri){
// _uri = _layout.root_prefix != null? (_layout.root_prefix+_uri) : _uri
return new bootup.CMSObserver(sys_id,_domId,_uri) return new bootup.CMSObserver(sys_id,_domId,_uri)
}) })
observers.push(new bootup.finalize(_domId)) observers.push(new bootup.finalize(_domId))

@ -1,3 +1,4 @@
{% if layout.header.logo == True %} {% if layout.header.logo == True %}
<div class="icon"> <div class="icon">
<img src="{{system.icon}}"> <img src="{{system.icon}}">

@ -35,6 +35,7 @@ Vanderbilt University Medical Center
<script src="{{system.context}}/static/js/jx/utils.js"></script> <script src="{{system.context}}/static/js/jx/utils.js"></script>
<script src="{{system.context}}/static/js/jx/rpc.js"></script> <script src="{{system.context}}/static/js/jx/rpc.js"></script>
<script src="{{system.context}}/static/js/jx/ext/modal.js"></script> <script src="{{system.context}}/static/js/jx/ext/modal.js"></script>
<script src="{{system.context}}/static/js/jx/ext/math.js"></script>
<script src="{{system.context}}/static/js/jquery/jquery.js"></script> <script src="{{system.context}}/static/js/jquery/jquery.js"></script>
<script src="{{system.context}}/static/js/menu.js"></script> <script src="{{system.context}}/static/js/menu.js"></script>
<script src="{{system.context}}/static/js/search.js"></script> <script src="{{system.context}}/static/js/search.js"></script>
@ -44,75 +45,13 @@ Vanderbilt University Medical Center
<script> <script>
sessionStorage.setItem('{{system.id}}','{{system.context|safe}}') sessionStorage.setItem('{{system.id}}','{{system.context|safe}}')
var _layout = {{layout|tojson}} var _layout = {{layout|tojson}}
//
// Rendering here requires an observer design pattern because calls are asynchronous
//
/**
* @id _domURI of the pane where the output is to be written
* @uri _fileURI of the file
*/
// var CMSObserver = function(_domId,_fileURI){
// this._domId = _domId
// this._fileURI = _fileURI
// this.apply = function (_caller){
// var http = HttpClient.instance()
// http.setHeader('uri',_fileURI)
// var uri = '{{context}}/page'
// try{
// http.post(uri,function(x){
// console.log(jx.dom.exists(_domId))
// if (x.status == 200){
// // jx.dom.set.value(_domId,x.responseText)
// var _domElement = jx.dom.get.instance('div')
// _domElement.innerHTML = x.responseText
// jx.dom.append(_domId, _domElement)
// }
// _caller.notify()
// })
// }catch(error){
// _caller.notify()
// }
// }
// }
// var finalize = function(_id){
// this.apply = function(_caller){
// menu.runScript('#'+_id)
// }
// }
// if (_layout.on != null ){
// //
// // load and error are required
// // $(document).ready(function(){
// // jx.utils.keys(_layout.on.load).forEach(function(_id){
// // var observers =
// // jx.utils.patterns.visitor(_layout.on.load[_id], function(_uri){
// // return new CMSObserver(_id,_uri)
// // })
// // observers.push(new finalize(_id))
// // //
// // // At this point we can execute the observer design pattern
// // //
// // console.log(observers)
// // jx.utils.patterns.observer(observers,'apply')
// // })
// // })
// // $(document).ready(bootup.init)
// }
sessionStorage.setItem('{{system.id}}','{{system.context|safe}}')
var _layout = {{layout|tojson}} sessionStorage.setItem('{{system.id}}','{{system.context|safe}}')
$(document).ready( function(){ $(document).ready( function(){
bootup.init('{{system.id}}',_layout) bootup.init('{{system.id}}',_layout)
}) })
</script> </script>
<body> <body>

@ -1,4 +1,5 @@
{%if system.portal %} {%if system.portal %}
<div class="icon active"> <div class="icon active">
<div align="center" class="button" onclick="window.open('{{system.context}}/set/main','_self')" style="display:grid; grid-template-columns:auto auto; gap:4px; align-items:center "> <div align="center" class="button" onclick="window.open('{{system.context}}/set/main','_self')" style="display:grid; grid-template-columns:auto auto; gap:4px; align-items:center ">
<i class="fa-solid fa-chevron-left" style="color:darkgray; display:block"></i> <i class="fa-solid fa-chevron-left" style="color:darkgray; display:block"></i>

Loading…
Cancel
Save