"""
$RCSfile: ExternalFileBatch.py,v $

A Factory for creating batches of ExternalFile instances in one go.
This class can be subclassed so that other kinds of similar objects
can be created in a batch.  cf. CVSFile.CVSFileBatch

Author: <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
Release: 1.2
"""

__cvstag__  = '$Name:  $'[6:-2]
__date__    = '$Date: 2003/04/21 01:52:57 $'[6:-2]
__version__ = '$Revision: 1.7 $'[10:-2]

# python builtins
import os.path, string, sys, traceback

# Zope builtins
from Globals import MessageDialog
from OFS.ObjectManager import BadRequestException

from ExternalFile import addExternalFile as addObject

################################################################
# Defaults
################################################################

# omit CVS control files
omittedDirNames    = ['CVS',]
    
# omit directories like .ssh, .mozilla
omittedDirPrefixes = ['.',]
    
omittedDirSuffixes = []
    
omittedFileNames = []
    
# omit UNIX dot files or UNIX pipes
# and Zope doesn't like things that begin with
# underscores (underscores are ok, just not as first
# character)
omittedFilePrefixes = [ '.', '@', '_', '#' ]

# omit emacs backup files
omittedFileSuffixes = ['~',]

# Chars in the filename that are illegal for Zope URLs
# these characters are automatically elided
illegalChars = ['@','%',':','!','`','^','*','=','+','|',
                '\\','/','<','>','?','[',']','{','}']

def omittedDirNamesAsString(folder):
    return ' '.join(omittedDirNames)
def omittedDirPrefixesAsString(folder):
    return ' '.join(omittedDirPrefixes)
def omittedDirSuffixesAsString(folder):
    return ' '.join(omittedDirSuffixes)
def omittedFileNamesAsString(folder):
    return ' '.join(omittedFileNames)
def omittedFilePrefixesAsString(folder):
    return ' '.join(omittedFilePrefixes)
def omittedFileSuffixesAsString(folder):
    return ' '.join(omittedFileSuffixes)
def illegalCharsAsString(folder):
    return ' '.join(illegalChars)

messages = []

################################################################
# Constructors
################################################################

# derived classes must create their own version of this function
def manage_addBatchViaGui(folder, target_dir = '', basedir='', recursive=None,
                          illegalChars = '', dirNames = '', dirPrefixes = '',
                          dirSuffixes = '', fileNames = '',
                          filePrefixes = '', fileSuffixes = '',
                          REQUEST=None):
    """
    Factory method to create a batch of instances of ExternalFile,
    called from GUI in the ZMI
    """
    results = '<h2>External File Batch Create Results:</h2>'

    try:
        results = results + folder.manage_addBatch(target_dir, basedir, recursive,
                                                   illegalChars, dirNames, dirPrefixes,
                                                   dirSuffixes, fileNames, filePrefixes,
                                                   fileSuffixes)
    except Exception, e:
        results = results + str(e)

    return MessageDialog(title   = 'results',
                         message = results.replace('\n','<br/>'),
                         action  = 'manage_main')

def manage_addBatch(folder, target_dir='', basedir='', recursive=None,
                    illegalChars = '', dirNames = '', dirPrefixes = '',
                    dirSuffixes = '', fileNames = '',
                    filePrefixes = '', fileSuffixes = ''):
    """
    Factory method to actually create a batch of instances of
    ExternalFile.  This is designed to be called from PythonScripts.
    """
    if not target_dir:
        message = 'ERROR: You must specify a source directory.\nNo action taken'
        raise Exception(message)
            
    dirname = os.path.join(basedir,target_dir)
    if not dirname or not os.path.exists(dirname) or not os.path.isdir(dirname):
        message = """
        ERROR: directory \''%s'\' does not exist or is not readable.
        No action taken.""" % (dirname)
        raise Exception(message)

    global messages
    global omittedDirNames
    global omittedDirPrefixes
    global omittedDirSuffixes
    global omittedFileNames
    global omittedFilePrefixes
    global omittedFileSuffixes

    messages            = []    
    omittedDirNames     = string.split(dirNames)
    omittedDirPrefixes  = string.split(dirPrefixes)
    omittedDirSuffixes  = string.split(dirSuffixes)
    omittedFileNames    = string.split(fileNames)
    omittedFilePrefixes = string.split(filePrefixes)
    omittedFileSuffixes = string.split(fileSuffixes)

    try:
        if recursive:
            os.path.walk(dirname, nodeaction, folder)
        else:
            # grab a list of fully qualified names from the directory
            filelist = map( lambda f, dir=dirname: os.path.join(dir, f),
                            os.listdir(dirname) )
                
            if len(filelist) == 0:
                messages.append('No files found.')
            else:
                for filepath in filelist:
                    createObjectFromFile(folder, filepath)

    except OSError, e:
        messages.append(str(e))

    return '\n'.join(messages)

def nodeaction(folder, dirname, filelist):
    """
    This method is called at every node of the file tree to create
    objects and folders.
    """
    global messages
    global omittedDirNames
    global illegalChars

    if os.path.basename(dirname) in omittedDirNames:
        messages.append('Skipping directory ' + dirname +
                        ' ...matches omitted name')
        return

    (files, dirs) = ([],[])
    for f in filelist:
        ff = os.path.join(dirname, f)
        e =  filter(lambda x, baddies=illegalChars: x not in baddies,f)
        if os.path.isfile(ff): 
            files.append( os.path.join( dirname, e  ))
        if os.path.isdir( ff ): 
            dirs.append( os.path.join( dirname, e ))

    messages.append( 'Processing %s files %s dirs %s' %
                     (dirname, len(files),len(dirs),) )
    filelist[:]=[] 

    for filepath in files:
        createObjectFromFile(folder, filepath)
		
    for subdir in dirs:
        createFolderFromDirectory(folder, dirname, subdir)

################################################################
# Object Creation Methods
################################################################

def createObjectFromFile(folder, filepath):
    "Factory to create objects from files in the file system."

    fileid = str(os.path.basename(filepath))

    global messages
    global omittedFileNames
    global omittedFilePrefixes
    global omittedFileSuffixes

    for name in omittedFileNames:
        if fileid == name:
            messages.append( 'Skipping file: '+ fileid +
                             ' ...matches omitted name ')
            return

    for prefix in omittedFilePrefixes:
        if fileid[:len(prefix)] == prefix:
            messages.append( 'Skipping file: '+ fileid +
                             ' ...matches omitted prefix')                             
            return

    for suffix in omittedFileSuffixes:
        if fileid[ -1 * len(suffix):] == suffix:
            messages.append( 'Skipping file: ' + fileid +
                             ' ...matches omitted suffix')
            return

    if os.path.isdir(filepath):
        messages.append( 'Skipping: ' + fileid +
                         ' ...because it is a directory, not a file')
        return

    if not os.path.isfile(filepath):
        messages.append( 'Skipping file: ' + fileid +
                         ' ...not regular file')
        return
        
    try:
        addObject(folder, fileid, '', '', filepath, '', None)
        messages.append('created object instance: ' + fileid + 
                        ' pointing to file: ' + filepath)

    except Exception, e:
        messages.append('Skipping file: ' + filepath + ' ...' +
                        str(e))
    except BadRequestException:
        if sys.exc_info()[1].find('already in use') != -1:
            messages.append('Skipping file: ' + filepath +
                            ' ...duplicate Zope ID ' + fileid)
        else:
            raise BadRequestException

def createFolderFromDirectory(folder, parentdir, subdir):
    """
    Factory for creating Zope folders from directories in the file
    system
    """

    global messages
    global omittedDirNames
    global omittedDirPrefixes
    global omittedDirSuffixes

    dirname  = str(os.path.basename(subdir))
    fullpath = str(os.path.join(parentdir, subdir))

    if dirname in omittedDirNames:
        messages.append('Skipping directory ' + fullpath +
                        ' ...matches omitted name')
        return

    for prefix in omittedDirPrefixes:
        if dirname[:len(prefix)] == prefix:
            messages.append( 'Skipping directory: '+ fullpath +
                             ' ...matches omitted prefix')
            return

    for prefix in omittedDirSuffixes:
        if dirname[ -1 * len(suffix):] == suffix:
            messages.append( 'Skipping directory: '+ fullpath +
                             ' ...matches omitted suffix')
            return

    try:
        folder.manage_addProduct.manage_addFolder(id=dirname,
                                                  title=dirname)
        messages.append( 'created Zope Folder instance: ' + dirname +
                         ' from directory ' + fullpath)
        os.path.walk(fullpath, nodeaction, getattr(folder, dirname))
    except Exception, e:
        messages.append('Skipping directory: ' + fullpath + ' ...' +
                        str(e))
    except BadRequestException:
        if sys.exc_info()[1].find('already in use') != -1:
            messages.append('Skipping directory: ' + fullpath +
                            ' ...duplicate Zope ID ' + dirname)
            os.path.walk(fullpath, nodeaction, getattr(folder, dirname))
        else:
            raise BadRequestException

# EOF ExternalFileBatch.py
