mirror of http://localhost:9400/cloud/cms
				
				
				
			
							parent
							
								
									8bfa773195
								
							
						
					
					
						commit
						d450779557
					
				@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Reads from nextcloud
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import nextcloud_client as nc
 | 
				
			||||||
 | 
					import copy
 | 
				
			||||||
 | 
					from mistune import markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_CLOUDHANDLER = None
 | 
				
			||||||
 | 
					def _format_root_folder (_root):
 | 
				
			||||||
 | 
					    if _root[0] == '/' :
 | 
				
			||||||
 | 
					        _root = _root[1:]
 | 
				
			||||||
 | 
					    if _root[-1] == '/' :
 | 
				
			||||||
 | 
					        _root = _root[:-1]
 | 
				
			||||||
 | 
					    return _root
 | 
				
			||||||
 | 
					def content(_args):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    :url
 | 
				
			||||||
 | 
					    :uid
 | 
				
			||||||
 | 
					    :token
 | 
				
			||||||
 | 
					    :folder
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    global _CLOUDHANDLER
 | 
				
			||||||
 | 
					    _handler = nc.Client(_args['url'])
 | 
				
			||||||
 | 
					    _handler.login(_args['uid'],_args['token'])
 | 
				
			||||||
 | 
					    _CLOUDHANDLER = _handler
 | 
				
			||||||
 | 
					    _files = _handler.list(_args['folder'],10)
 | 
				
			||||||
 | 
					    _root = _args['folder']
 | 
				
			||||||
 | 
					    if _root.startswith('/') :
 | 
				
			||||||
 | 
					        _root = _root[1:]
 | 
				
			||||||
 | 
					    if _root.endswith('/') :
 | 
				
			||||||
 | 
					        _root = _root[:-1]
 | 
				
			||||||
 | 
					    _menu = {} #[_args['folder']] + [_item for _item in _files if _item.file_type == 'dir' and _item.name[0] not in ['.','_']]
 | 
				
			||||||
 | 
					    _menu = {} #dict.fromkeys(_menu,[])
 | 
				
			||||||
 | 
					    for _item in _files :
 | 
				
			||||||
 | 
					        _folder = _item.path.split(_item.name)[0].strip()
 | 
				
			||||||
 | 
					        _folder = _folder.replace(_root,'').replace('/','')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if _item.name[0] in ['.','_'] or _folder == '':
 | 
				
			||||||
 | 
					            continue ;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if _item.file_type == 'file' and _item.get_content_type() in ['text/markdown','text/html'] :
 | 
				
			||||||
 | 
					            # _folder = _item.path.split(_item.name)[0].strip()
 | 
				
			||||||
 | 
					            # _folder = _folder.replace(_root,'').replace('//','')
 | 
				
			||||||
 | 
					            if _folder == '' :
 | 
				
			||||||
 | 
					                _folder = str(_root)
 | 
				
			||||||
 | 
					            _folder = _folder.replace('/' ,' ').strip()
 | 
				
			||||||
 | 
					            if _folder not in _menu :
 | 
				
			||||||
 | 
					                _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:])
 | 
				
			||||||
 | 
					            _menu[_folder].append({'text':_item.name.split('.')[0],'uri':uri})
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # clean up the content ...
 | 
				
			||||||
 | 
					    _keys = [_name for _name in _menu.keys() if _name.startswith('_')]
 | 
				
			||||||
 | 
					    [_menu.pop(_name) for _name in _keys]
 | 
				
			||||||
 | 
					    return _menu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def build(_config):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    The function will build a menu based on a folder structure in nextcloud
 | 
				
			||||||
 | 
					    :_args  authentication arguments for nextcloud
 | 
				
			||||||
 | 
					    :_config    configuration for the cms
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    _args = copy.deepcopy(_config['system']['source']['auth'])
 | 
				
			||||||
 | 
					    _args['folder'] = _config['layout']['root']
 | 
				
			||||||
 | 
					    # update(_config)
 | 
				
			||||||
 | 
					    return content(_args)
 | 
				
			||||||
 | 
					def html (uri,_config) :
 | 
				
			||||||
 | 
					    global _CLOUDHANDLER
 | 
				
			||||||
 | 
					    _handler = _CLOUDHANDLER
 | 
				
			||||||
 | 
					    _root = _format_root_folder(_config['layout']['root'])
 | 
				
			||||||
 | 
					    uri = _format_root_folder (uri)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    _prefix = '/'.join (uri.split('/')[:-1])
 | 
				
			||||||
 | 
					    _link = '/'.join(['{{context}}api/cloud/download?doc='+_prefix,'.attachments.'])
 | 
				
			||||||
 | 
					    # _link = '/'.join(['api/cloud/download?doc='+_prefix,'_images'])
 | 
				
			||||||
 | 
					    _html = _handler.get_file_contents(uri).decode('utf-8').replace('.attachments.',_link)
 | 
				
			||||||
 | 
					    # print ([uri,uri[-2:] ,uri[-2:] in ['md','MD','markdown']])
 | 
				
			||||||
 | 
					    return markdown(_html) if uri[-2:] in ['md','MD','Md','mD'] else _html.replace(_root,('{{context}}api/cloud/download?doc='+_root))
 | 
				
			||||||
 | 
					# def update (_config):
 | 
				
			||||||
 | 
					#     """
 | 
				
			||||||
 | 
					#     This function updates the configuration provided by loading default plugins
 | 
				
			||||||
 | 
					#     """
 | 
				
			||||||
 | 
					#     if 'plugins' not in _config :
 | 
				
			||||||
 | 
					#         _config['plugins'] = {}
 | 
				
			||||||
 | 
					#     _config['plugins'] = plugins ()
 | 
				
			||||||
 | 
					#     return _config
 | 
				
			||||||
 | 
					def download(**_args):
 | 
				
			||||||
 | 
					    _handler = _CLOUDHANDLER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if _args['doc'][-2:] in ['md','ht']:
 | 
				
			||||||
 | 
					        _stream = html(_args['doc'],_args['config'])
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        _stream = _handler.get_file_contents(_args['doc'])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return _stream
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def plugins ():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This function publishes the plugins associated with this module
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    return {'api/cloud/download':download}
 | 
				
			||||||
@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					This file pulls the content from the disk 
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					def folders (_path):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This function reads the content of a folder (no depth, it must be simple)
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    _content = os.listdir(_path)        
 | 
				
			||||||
 | 
					    return [_name for _name in _content if os.path.isdir(os.sep.join([_path,_name])) if not _name.startswith('_')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def content(_folder):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    :content of the folder
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if os.path.exists(_folder) :
 | 
				
			||||||
 | 
					        _menuItems = 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]))]
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					def build (_config): #(_path,_content):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    building the menu for the site given the content is on disk
 | 
				
			||||||
 | 
					    :path  path of the files on disk
 | 
				
			||||||
 | 
					    :config configuration associated with the 
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    _path = _config['layout']['root']
 | 
				
			||||||
 | 
					    _items = folders(_path)
 | 
				
			||||||
 | 
					    _subItems = [ content (os.sep.join([_path,_name]))for _name in _items ]
 | 
				
			||||||
 | 
					    # return dict(zip())
 | 
				
			||||||
 | 
					    return dict.fromkeys(_items,_subItems)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def html(uri) :
 | 
				
			||||||
 | 
					    _html = (open(uri)).read()    
 | 
				
			||||||
 | 
					    return _html
 | 
				
			||||||
 | 
					def plugins ():
 | 
				
			||||||
 | 
					    return {}
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					These are a few default plugins that will be exported and made available to the calling code
 | 
				
			||||||
 | 
					The purpose of plugins is to perform data processing
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def copyright (_args) :
 | 
				
			||||||
 | 
					    return {"author":"Steve L. Nyemba","email":"steve@the-phi.com","organization":"The Phi Technology","license":"MIT", "site":"https://dev.the-phi.com/git/cloud/qcms"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def log (_args):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    perform logging against duckdb  
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					* This is the default window and we will have to hide the pane (side)
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body .pane { display:none}
 | 
				
			||||||
@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					.main {
 | 
				
			||||||
 | 
					    margin:10px;
 | 
				
			||||||
 | 
					    padding:4px;
 | 
				
			||||||
 | 
					    display:grid;
 | 
				
			||||||
 | 
					    grid-template-columns: 50% 50% ; gap:4px;
 | 
				
			||||||
 | 
					    grid-template-rows: 48px 48px auto 32px;
 | 
				
			||||||
 | 
					    font-family: helvetica;
 | 
				
			||||||
 | 
					    font-weight: lighter;
 | 
				
			||||||
 | 
					    font-size:18px;
 | 
				
			||||||
 | 
					    line-height: 1.5;
 | 
				
			||||||
 | 
					    justify-items: normal;
 | 
				
			||||||
 | 
					    ;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.main .header {
 | 
				
			||||||
 | 
					    height:48px;
 | 
				
			||||||
 | 
					    grid-row:1;
 | 
				
			||||||
 | 
					    grid-column: 1  / span 2;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main .header img { width:40px; margin:4px;}
 | 
				
			||||||
 | 
					.main .menu { grid-row:2; grid-column: 1 / span 2; background-color: #f3f3f3; }
 | 
				
			||||||
 | 
					.main .content {
 | 
				
			||||||
 | 
					    grid-row:3;
 | 
				
			||||||
 | 
					    grid-column: 1 ;
 | 
				
			||||||
 | 
					    text-wrap: wrap;
 | 
				
			||||||
 | 
					    height:100%;
 | 
				
			||||||
 | 
					    display:grid;
 | 
				
			||||||
 | 
					    align-content: start;
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.main .content #index {
 | 
				
			||||||
 | 
					    text-align: left;
 | 
				
			||||||
 | 
					    text-wrap: wrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/* .main .content #index{
 | 
				
			||||||
 | 
					    text-align:left;
 | 
				
			||||||
 | 
					    align-content:normal;
 | 
				
			||||||
 | 
					    display:grid;
 | 
				
			||||||
 | 
					    background-color: green;
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main .pane {
 | 
				
			||||||
 | 
					    border-left:3px dotted gray;
 | 
				
			||||||
 | 
					    grid-column: 2;
 | 
				
			||||||
 | 
					    font-family: sans-serif;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.pane iframe {
 | 
				
			||||||
 | 
					    border:1px solid transparent;
 | 
				
			||||||
 | 
					    width:99%;
 | 
				
			||||||
 | 
					    height:100%;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.main .footer {grid-row:4; grid-column: 1 / span 2; font-size:13px; font-weight: lighter;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* styling tables here
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					p {
 | 
				
			||||||
 | 
					    margin-top:22px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					table {
 | 
				
			||||||
 | 
					    width:99%;
 | 
				
			||||||
 | 
					    border: 1px solid #CAD5E0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					table td {padding:4px; margin:4px;}
 | 
				
			||||||
 | 
					table thead {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    font-weight:bold;
 | 
				
			||||||
 | 
					    background-color:#f3f3f3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/* table tbody tr:hover {
 | 
				
			||||||
 | 
					    background-color: #4682B4;
 | 
				
			||||||
 | 
					    color:white ;
 | 
				
			||||||
 | 
					    cursor:pointer;
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#gallery img {
 | 
				
			||||||
 | 
					    justify-content: left;
 | 
				
			||||||
 | 
					    width:70px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#gallery table {width:100%;}
 | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue