"""
$RCSfile: XMLMethod.py,v $

ZopeXMLMethods provides methods to apply to Zope objects for XML/XSLT
processing.  XMLMethod is the base class for all ZopeXMLMethods.

Author: Craeg Strong <cstrong@arielpartners.com>
Release: 1.0

$Id: XMLMethod.py,v 1.1 2003/06/16 05:47:53 arielpartners Exp $
"""

__cvstag__  = '$Name:  $'[6:-2]
__date__    = '$Date: 2003/06/16 05:47:53 $'[6:-2]
__version__ = '$Revision: 1.1 $'[10:-2]

# base classes
from OFS.SimpleItem import SimpleItem
from Products.ZCatalog.CatalogAwareness import CatalogAware
from OFS.PropertyManager import PropertyManager

# peer classes/modules
from interfaces import IXMLMethod
from GeneratorRegistry import GeneratorRegistry

# Zope builtins
import OFS
import Globals
from Acquisition import aq_base, aq_parent
from AccessControl import ClassSecurityInfo
from ZPublisher.mapply import mapply
from zope.interface import implements

################################################################
# Defaults
################################################################


################################################################
# Permissions
################################################################
# By defining these constants, no new permissions will be created when
# misspelling them in the declareProtected() call
PERM_VIEW    = "View"
PERM_EDIT    = "Edit"
PERM_FTP     = "FTP access"
PERM_CONTENT = "Access contents information"
PERM_MANAGE  = "Manage XML Methods"

################################################################
# Utilties
################################################################

def getPublishedResult(name, obj, REQUEST):
    """
    Get the result of an object as if it were published
    """
    #
    # index_html is generally used for human-readable HTML, whereas
    # __call__ would normally be used to obtain access to the
    # underlying XML source.  However, some Zope products do not adhere
    # to this convention, so we call index_html if __call__ is not
    # supported.
    #
    base = aq_base(obj)
    if (hasattr(base, 'data') and hasattr(obj.data, '__class__')
        and obj.data.__class__ is OFS.Image.Pdata):
        # yuck! we have a File instance with chunked data, let's stream it!
        result = ""
        data = obj.data
        while data is not None:
            result += data.data
            data = data.next
            return result

    if hasattr(base, "__call__") and obj.__call__ is not None:
        return obj(obj, REQUEST)
    elif hasattr(base, "index_html") and obj.index_html is not None:
        return mapply(obj.index_html, (), REQUEST)
    elif callable(obj):
        return apply(obj)
    else:
        message = "Error, unable to obtain source from object %s" % (name)
        raise Exception(message)

def behaveLikeList(self):
    """
    Return list of standard zope objects this Method can behave like
    """
    return GeneratorRegistry.supportedMetaTypes()




################################################################
# Main class
################################################################

class XMLMethod(CatalogAware,    # ZCatalog support
                PropertyManager, # Property support
                SimpleItem):

    """
    Base class for all Zope XMLMethods
    """

    implements(IXMLMethod)

    _security = ClassSecurityInfo()

    _properties = (
        {'id':'title',              'type':'string',    'mode': 'w' },
        {'id':'description',        'type':'text',      'mode': 'w' },
        {'id':'content_type',       'type':'string',    'mode': 'w' },        
        {'id':'selected_processor', 'type':'selection', 'mode': 'w',
         'select_variable':'availableProcessors' },
        {'id':'behave_like', 'type':'selection', 'mode': 'w',
         'select_variable':'behaveLikeList' },
        {'id':'debugLevel',         'type':'int',       'mode': 'w' },
        )
    
    manage_options = (
        OFS.PropertyManager.PropertyManager.manage_options + \
        OFS.SimpleItem.SimpleItem.manage_options
        )

    def __init__(self, id, title, description,
                 selected_processor,
                 content_type, behave_like,
                 debugLevel=0):
        # string attributes
        self.id                 = id
        self.title              = title
        self.description        = description
        self.selected_processor = selected_processor
        self.content_type       = content_type
        self.behave_like        = behave_like        
        self.debugLevel         = debugLevel

    ################################################################
    # Methods implementing the IXMLMethod interface below
    ################################################################

    _security.declarePublic('availableProcessors')
    def availableProcessors(self):
        """
        Return names of currently available processor libraries
        Derived classes must override this method.
        """
        raise NotImplementedError()

    _security.declarePublic('processor')
    def processor(self):
        """
        Obtain the object encapsulating the selected processor.
        Derived classes must override this method.
        """
        raise NotImplementedError()        

    _security.declareProtected(PERM_VIEW, 'getXmlSourceObject')
    def getXmlSourceObject(self):
        """
        Retrieve the source object by using acquisition on the ID
        """
        # Our immediate parent might be a folderish object.  Keep
        # going up until we get to the first non folderish object

        ob = aq_parent(self)
        while ob.isPrincipiaFolderish:
            ob = aq_parent(ob)
        if self.isDebugging():
            print "Requesting contents of", ob.getId()
        return ob

    # innocuous methods should be declared public
    _security.declarePublic('setDebugLevel')
    def setDebugLevel(self, value):
        """
        Set debug level for ourselves and (eventually) our underlying
        processor.
        """
        self.debugLevel = value

    # innocuous methods should be declared public
    _security.declarePublic('isDebugging')
    def isDebugging(self):
        """
        Return true if and only if debugging is on.
        """
        return self.debugLevel > 0

    _security.declarePublic('behaveLikeList')
    def behaveLikeList(self):
        """
        Return list of standard zope objects this Method can behave like
        """
        return GeneratorRegistry.supportedMetaTypes()

    ################################################################
    # Utilities
    ################################################################

    _security.declarePublic('getSelf')
    def getSelf(self):
        """
        Return this object. For use in DTML scripts
        """
        return self.aq_chain[0]

    ################################################################
    # Standard Zope stuff
    ################################################################

    # next line is not strictly necessary, Access contents info is Anonymous by default
    # as opposed to all other protections that get Manager Role by default
    _security.setPermissionDefault(PERM_CONTENT, ('Anonymous')) 

    _security.declareProtected(PERM_VIEW, 'index_html')
    # next line is not strictly necessary, Access contents info is Anonymous by default
    # as opposed to all other protections that get Manager Role by default
    _security.setPermissionDefault(PERM_VIEW, ('Anonymous')) 
    def index_html(self, REQUEST = None, RESPONSE = None):
        """
        Default view of rendered version of the results of XML
        process.  This is called when some one types the
        transformation path (e.g. "aSource/aMethod") directly into the
        browser, rather than via DTML or a page template.  We *must*
        use this method to pass the REQUEST parameter on, otherwise we
        wouldn't get it because _render_with_namespace_ is *not*
        called in this case.
        """
        return self(self, REQUEST, RESPONSE)
    
    #
    # isDocTemp tells Zope that we are like a Document Template, which
    # means that __call__ will get called with a REQUEST parameter
    # (which is what we want, since we grab the URL from REQUEST)
    #
    # CKS 3/22/2003 isDocTemp is no longer needed as of Zope 2.6.1,
    # but it might be needed by older releases, so might as well
    # keep it around...
    #
    isDocTemp = 1

    _security.declareProtected(PERM_VIEW, '__render_with_namespace__')
    def __render_with_namespace__(self, namespace):
        """
        Render with namespace namespace will be given to us by the ZPT
        that calls us, for example if a ZPT were to include something
        like the below:

        <div tal:replace="here/aSource/aXPathMethod">replaceme</div>
        """
        REQUEST  = namespace["REQUEST"]
        RESPONSE = namespace["RESPONSE"]
        return self.__call__(REQUEST=REQUEST, RESPONSE=RESPONSE)
        
    ################################################################
    # Support for Schema Migration
    ################################################################

    def repair(self):
        """
        Repair this object.  This method is used for schema migration,
        when the class definition changes and existing instances of
        previous versions must be updated.
        """
        pass
        # nothing to repair at the moment

# register security information
Globals.InitializeClass(XMLMethod)
