mirror of http://localhost:9400/cloud/cms
				
				
				
			
							parent
							
								
									cbf63d3f56
								
							
						
					
					
						commit
						d9bf641a24
					
				@ -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.locations(**_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]
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue