"""
This plugin is archieve some xml-rpc api which are not standard, but may be useful.

Implemented methods:
  - newedit.editCategory you can edit the existing category, if the category is not existed, it'll create a new one
  - newedit.putFile put file to blog
  - newedit.getFileList get file list which you'v uploaded
  - newedit.getSysFile get system file which you can access
  - newedit.getSysFileList get system file list which you can access
  - newedit.putSysFile put system file which you can access

If you want to use putFile function, you should add parameter as blow:
    
    newedit_filepath - the path that files will be saved, it's a relative path according to datadir
    
getSysFile is used to modify some important files just like links.dat, flavour template files, etc. If you want to use
this method, you shoud add parameter in config.py as blow:
    
    newedit_filelist - is a filename, the content of it is a list of filename that enable accessed via xmlrpc.
                       so if you invoke the method, it'll check the filename in the list first. If existed, you can
                       get the content of the file, and put the new content back via putSysFile. It should be a relative 
                       path according to datadir.

For example:
    
config.py

py['newedit_filepath'] = 'files'
py['newedit_filelist'] = 'filelist.dat'

the structure of directory:
    
      files/
      entries/   #datadir
         |
         +------ filelist.dat
"""
__version__ = '0.1'
__author__ = 'limodou chatme at 263 dot com'

import os
import os.path
import xmlrpclib
from Pyblosxom import tools, plugin_utils

class NError(Exception): pass

def getFilePath(request):
    """
    get newedit_filepath value from request
    
    @param request: the pyblosxom Request instance
    @type  request: Request
    
    @returns the path string of filepath or raise a exception
    """
    config = request.getConfiguration()
    datadir = config['rootdir']
    path = os.path.join(datadir, config['newedit_filepath'])
    if path.startswith(datadir):
        return path
    else:
        raise NError, 'The filepath is not correct.'

def verify_installation(request):
    config = request.getConfiguration()
    
    retval = 1
    
    if not config.has_key('newedit_filepath'):
        print 'The "newedit_filepath" is not existing'
        retval = 0
    
    try:
        dir = getFilePath(request)
    except:
        print 'The "newedit_filepath" is not correct, it should be a relavite path according to datadir.'
        retval = 0
        
    if not os.path.isdir(dir):
        print 'The "newedit_filepath" property in the config file must refer to a directory'
        retval = 0
        
    if os.access(dir, 0777):
        print 'The rights of the "newedit_filepath" is should be 777'
        retval = 0

    return retval

def cb_xmlrpc_register(args):
    """
    Binds the methods that we handle with the function instances.
    Part of the pyblosxom callback api

    @param args: the callback arguments
    @type args: dict
    """
    args["methods"].update( 
            {'newedit.editCategory':newedit_editCategory,
             'newedit.putFile':newedit_putFile,
             'newedit.getFileList':newedit_getFileList,
             'newedit.getSysFile':newedit_getSysFile,
             'newedit.getSysFileList':newedit_getSysFileList,
             'newedit.putSysFile':newedit_putSysFile,
            })
    
    return args

def authenticate(request, username, password):
    """
    Handles authentication.  This works by getting the xmlrpc dispatcher
    plugin Python module and then calling authenticate on that.

    @param request: the pyblosxom Request instance
    @type  request: Request

    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string
    """
    xmlrpc_plugin = plugin_utils.get_plugin_by_name("xmlrpc")
    xmlrpc_plugin.authenticate(request, username, password)

def newedit_editCategory(request, username, password, path, description):
    """
    Edit an existing category, but if the category is not existed,
    it'll create a new one. And the description is must not be existed.

    @param request: the pyblosxom Request instance
    @type  request: Request

    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string

    @param path: category's path
    @type  path: string

    @param description: category's description
    @type  description: string

    @returns an xmlrpclib boolean -- true if the edit was successful, false otherwise
    """
    #tools.log("edit category %s %s" % (path, description))
    authenticate(request, username, password)
    config = request.getConfiguration()
    
    path = path.encode('utf-8')
    description = description.encode('utf-8')
    
    #check the path, if not existed, make it
    datadir = config['datadir']
    if path and path[0] == '/':
        path = path[1:]
    p = os.path.join(datadir, path)
    if not os.path.exists(p):
        os.makedirs(p)
        os.chmod(p, 0777)
    
    #get the category file, if not existed, create it
    categoryfile = config.get('categoryfile', os.path.join(config['datadir'], 'categoryfile'))
    if not os.path.exists(categoryfile):
        f = file(categoryfile, 'w')
        f.close()
        
    #get all categories
    categories = {}
    lines = file(categoryfile).readlines()
    for line in lines:
        line = line.strip()
        p, des = line.split('=')
        categories[p] = des
        
    #add new description
    categories[path] = description
    
    #check if description is existed, if existed, then raise exception
    if categories.values().count(description) > 1:
        raise NError, "The description has existed!"

    #save category file
    file(categoryfile, 'w').write('\n'.join(['%s=%s' % (p, d) for p, d in categories.items()]))
    
    return xmlrpclib.Boolean(True)

def newedit_putFile(request, username, password, filename, data):
    """
    Upload a file to a specified directory

    @param request: the pyblosxom Request instance
    @type  request: Request

    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string

    @param filename: filename
    @type  filename: string

    @param data: content of the file
    @type  data: binary string

    @returns an xmlrpclib boolean -- true if the edit was successful, false otherwise
    """
    tools.log("putfile %s" % filename)
    authenticate(request, username, password)
    config = request.getConfiguration()
    
    filename = os.path.basename(filename.encode('utf-8'))

    dir = getFilePath(request)
    if dir:
        f = file(os.path.join(dir, filename), 'wb')
        f.write(str(data))
        f.close()
        
        return xmlrpclib.Boolean(True)

def newedit_getFileList(request, username, password):
    """
    get the file list in filepath directory

    @param request: the pyblosxom Request instance
    @type  request: Request

    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string

    @returns an xmlrpclib struct list -- each item is a dict ('filename':filename, 'url':url)
    """
    #tools.log("getFileList")
    authenticate(request, username, password)
    config = request.getConfiguration()
    
    dir = getFilePath(request)
    rootdir = config['rootdir']
    
    #relative path of filepath
    rdir = dir[len(rootdir)+1:]
    
    files = os.listdir(dir)
    
    url = config['start_url']
    result = []
    for f in files:
        path = url + '/' + rdir + '/' + f
        result.append({'filename':f, 'url':path})

    return result

def newedit_getSysFile(request, username, password, filename):
    """
    get the content of the system file defined in filelist.dat

    @param request: the pyblosxom Request instance
    @type  request: Request

    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string

    @param filename: the file which you want to get
    @type  filename: string
    
    @returns an xmlrpclib struct list -- each item is a tuple (filename, url)
    """
    #tools.log("getSysFile %s" % filename)
    authenticate(request, username, password)
    config = request.getConfiguration()
    
    if checkSysFile(request, filename):
        data = xmlrpclib.Binary(file(os.path.join(config['datadir'], filename), 'rb').read())
        return data
    return xmlrpclib.Boolean(False)
    
def newedit_putSysFile(request, username, password, filename, data):
    """
    change the content of the filename in filelist.dat

    @param request: the pyblosxom Request instance
    @type  request: Request

    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string

    @param filename: the file which you want to get
    @type  filename: string
    
    @param data: the content of the file
    @type  data: binary
    
    @returns True or False
    """
    #tools.log("putSysFile %s" % filename)
    authenticate(request, username, password)
    config = request.getConfiguration()
    
    if checkSysFile(request, filename):
        f = file(os.path.join(config['datadir'], filename), 'wb')
        f.write(str(data))
        f.close()
        
        return xmlrpclib.Boolean(True)
    else:
        return xmlrpclib.Boolean(False)

def checkSysFile(request, filename):
    """
    Check if the filename can be accessed
    
    @param request: the pyblosxom Request instance
    @type  request: Request
    
    @param filename: the filename which you want to access
    @type  filename: string
    
    @returns True if available or False if disable
    """
    config = request.getConfiguration()
    datadir = config['datadir']
    filelist = config['newedit_filelist']

    f = os.path.join(datadir, filelist)
    lines = file(f).readlines()
    for line in lines:
        if filename == line.strip():
            return True
    return False

def newedit_getSysFileList(request, username, password):
    """
    Get the file list which can be accessed
    
    @param request: the pyblosxom Request instance
    @type  request: Request
    
    @param username: the username
    @type  username: string

    @param password: the password
    @type  password: string

    @returns True if available or False if disable
    """
    authenticate(request, username, password)
    config = request.getConfiguration()
    datadir = config['datadir']
    filelist = config['newedit_filelist']

    f = os.path.join(datadir, filelist)
    lines = file(f).readlines()
    return [line.strip() for line in lines]
