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