########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Xslt/Container.py,v 1.40 2005/04/06 23:05:46 jkloth Exp $
"""
XSLT and XPath extensions supporting the 4SS container API

Copyright 2003 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

import cStringIO, sha

from Ft.Server.Common import ResourceTypes, Schema, XmlLib
from Ft.Xml.XPath import Conversions
from Ft.Xml.Xslt import XsltElement
from Ft.Xml.Xslt import ContentInfo, AttributeInfo
from Ft.Xml.Xslt import OutputParameters

from Ns import SCORE_NS
import FtssXsltBase

class CreateUriReferenceElement(XsltElement):
    """
    Creates a new uri reference resource.  A URI reference resource is
    a special object that references an external URI (which can be a file)
    and loads its contents whenever the resource is requested.  It behaves
    in some ways similar to a soft link in UNIX.
    """

    content = ContentInfo.Empty

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            required=1, description='The path for the new uri-reference'),
        'imt' : AttributeInfo.StringAvt(
            description='The internet media-type (MIME content-type)'),
        'source-uri' : AttributeInfo.UriReferenceAvt(
            description='The source uri of the resource being referenced'),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base to use for relative path'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        imt = self._imt.evaluate(context)
        source_uri = self._source_uri.evaluate(context)
        basePath = self._base_path.evaluate(context)

        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
            base = base.getParent()
        base.createUriReferenceFile(path, imt, source_uri)

        return


class CreateGroupElement(XsltElement):
    """
    Creates a new group with the specified path.  A group is a set of users
    and other groups.
    """

    content = ContentInfo.Empty

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            required=1, description='The path for the new group'),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base-uri to use for relative path'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        # The 'default' will be returned if not attribute is present
        basePath = self._base_path.evaluate(context)

        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
            base = base.getParent()
        base.createGroup(path)

        return


class CreateContainerElement(XsltElement):
    """
    Creates a container, if createParents is true then all its parents along
    the path are created (if they dont exist).  createParents defaults to
    false if not set.  The base-path attribute is the base to use for the
    new containers path, if relative. If not specified it uses the path of
    the source document.
    """

    content = ContentInfo.Empty

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            required=1, description='The path for the new container'),
        'create-parents' : AttributeInfo.YesNoAvt(
            default='yes', description=('Create any parent containers of the'
                                        ' specified container that do not'
                                        ' already exist.')),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base to use for relative path'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        # YesNoAvt returns 1 for 'yes' and 0 for 'no'
        parents = self._create_parents.evaluate(context)
        basePath = self._base_path.evaluate(context)

        print "Creating a new container"
        print "     Path: %s" % path
        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
            base = base.getParent()
        container = base.createContainer(path, createParents=parents)

        return


class CreateDocumentElement(XsltElement):
    """
    Creates a document with the given path, document definition and type.
    The output of the body of the element makes up the content of the
    resulting document.  All the attributes available on the xsl:output
    instruction are also allowed on this element, in order to control the
    generation of the document.
    If the document type is not specified, and the internet media type of
    the source is XML, the type will be inferred from the content,
    if possible.
    If path is an empty string, then a UUID will be generated for
    the file name, which will be placed in the container that holds the source
    document of the transform.
    If the document definition specifies a full text index, then
    the content is indexed

    The content of the fcore:create-document element is a template; this is
    instantiated to create a sequence of nodes; a root node is created with
    this sequence of nodes as its children; the tree with this root node
    represents the subsidiary result document.
    """

    content = ContentInfo.Template

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            description='The path for the new document'),
        'docdef' : AttributeInfo.UriReferenceAvt(
            description='The document definition to use'),
        'type' : AttributeInfo.StringAvt(
            description='Specifies the resource type to create'),
        'imt' : AttributeInfo.StringAvt(
            default='text/xml', description='(deprecated: see media-type)'),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base to use for relative paths'),
        # attributes as defined by exsl:document
        'method' : AttributeInfo.QNameAvt(),
        'version' : AttributeInfo.NMTokenAvt(),
        'encoding' : AttributeInfo.StringAvt(),
        'omit-xml-declaration' : AttributeInfo.YesNoAvt(),
        'standalone' : AttributeInfo.YesNoAvt(),
        'doctype-public' : AttributeInfo.StringAvt(),
        'doctype-system' : AttributeInfo.StringAvt(),
        'cdata-section-elements' : AttributeInfo.QNamesAvt(),
        'indent' : AttributeInfo.YesNoAvt(),
        'media-type' : AttributeInfo.StringAvt(
            default='text/xml', description=('Specifies the internet media'
                                             ' type for the new resource')),
        }

    doesSetup = True

    def setup(self):
        self._output_parameters = OutputParameters.OutputParameters()
        return

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        dd = self._docdef.evaluate(context)
        forcedType = Schema.g_resourceTypeFromRdf.get(self._type.evaluate(context))
        imt = self._imt.evaluate(context)
        basePath = self._base_path.evaluate(context)

        stream = cStringIO.StringIO()
        #op = processor.writers[-1]._outputParams.clone()
        #op.method = (EMPTY_NAMESPACE, 'xml')
        #op.omitXmlDeclaration = "yes"
        self._output_parameters.avtParse(self, context)
        processor.addHandler(self._output_parameters, stream)
        self.processChildren(context, processor)
        processor.removeHandler()

        text = stream.getvalue()

        print "Creating a new %s Document" % imt
        print "     Path: %s" % path
        if len(text) > 1024:
            print "  Content: %r..." % text[:1024]
        else:
            print "  Content: %r" % text
        import sys
        sys.stdout.flush()

        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
            base = base.getParent()

        ## MOlson says don't do this because he wants the XSLT API to SCore
        ## to just be a wrapper. It is the XSLT programmer's job to check
        ## to see if the resource already exists.

        #Damn straight its their job!!  --Molson

        #if base.hasResource(uri):
        #    doc = base.fetchResource(uri)
        #    doc.setContent(text)
        #else:
        #    doc = base.createDocument(uri, text,docDef=dd,forcedType=forcedType,imt=imt)

        #print "Creating"
        base.createDocument(path, text, docDef=dd, forcedType=forcedType,
                            imt=imt)
        #print "Done"
        return


class CreateRawFileElement(XsltElement):
    """
    Creates a raw file resource, using the specified path and
    internet media type.
    The output of the body of the element makes up the content of the
    resulting document.  All the attributes available on the xsl:output
    instruction are also allowed on this element, in order to control the
    generation of the document.

    In order to set the IMT, use the media-type attribute
    The default is text/plain
    There is also a special value for the method attribute
    method="binary" is equivalent to method="text" and
    encoding="latin-1" (to simulate the binary byte stream)
    """

    content = ContentInfo.Template

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            description='The path for the new raw file'),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base to use for relative paths'),
        'content-as-string' : AttributeInfo.YesNo(
            description=('Specifies whether or not to assume the child'
                         ' argument is a string')),
        # attributes as defined by exsl:document
        'method' : AttributeInfo.QNameAvt(),
        'version' : AttributeInfo.NMTokenAvt(),
        'encoding' : AttributeInfo.StringAvt(),
        'omit-xml-declaration' : AttributeInfo.YesNoAvt(),
        'standalone' : AttributeInfo.YesNoAvt(),
        'doctype-public' : AttributeInfo.StringAvt(),
        'doctype-system' : AttributeInfo.StringAvt(),
        'cdata-section-elements' : AttributeInfo.QNamesAvt(),
        'indent' : AttributeInfo.YesNoAvt(),
        'media-type' : AttributeInfo.StringAvt(
            default='text/plain', description=('Specifies the internet media'
                                               ' type for the new resource')),
        }

    doesSetup = True

    def setup(self):
        self._output_parameters = OutputParameters.OutputParameters()
        return

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        basePath = self._base_path.evaluate(context)

        inputString = self._content_as_string

        stream = cStringIO.StringIO()
        #params = processor.writers[-1]._outputParams.clone()

        self._output_parameters.avtParse(self, context)
        #if not inputString:
        #    params.method = (None, u'xml')
        params = self._output_parameters
        if params.method == (None, u'binary'):
            params.method = (None, u'text')
            params.encoding = u'iso-8859-1'
        processor.addHandler(params, stream)
        self.processChildren(context, processor)
        processor.removeHandler()

        #Data is as a regular Python string, which is how we want it
        data = stream.getvalue()
        
        #Not sure what all this buggy crap was for
        #enc = processor.writers[-1]._outputParams.encoding or 'utf-8'
        #text = unicode(stream.getvalue(), enc).encode('utf-8')
        imt = self._output_parameters.mediaType or 'text/plain'

        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
            base = base.getParent()

        #print "*"*10
        print "Creating a Raw File: %s (%s)" % (path, imt)
        #print "*"*10

        doc = base.createRawFile(path, imt, data)

        return


class CreateUserElement(XsltElement):
    """
    Create a new user in the system
    """

    content = ContentInfo.Empty

    legalAttrs = {
        'username' : AttributeInfo.StringAvt(
            required=1, description='The name of the user to create'),
        'password' : AttributeInfo.StringAvt(
            required=1, description='The initial password for new user'),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description=('The base path to be used for creating this user.'
                         ' If not specified, uses the path of the source'
                         ' document.')),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        username = self._username.evaluate(context)
        password = sha.new(self._password.evaluate(context)).hexdigest()
        basePath = self._base_path.evaluate(context)

        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
            base = base.getParent()

        user = base.createUser(username, password)
        print "Creating User: %s" % (username)
        return


def HasResource(context, path):
    """
    Query if the system has a resource specified by the path
    """
    base = FtssXsltBase.FetchBaseObject(context.processor)
    if not base.isResourceType(ResourceTypes.ResourceType.CONTAINER):
        base = base.getParent()

    path = Conversions.StringValue(path)
    return base.hasResource(path)


ExtFunctions = {
    (SCORE_NS, 'has-resource'): HasResource,
}

ExtElements = {
    (SCORE_NS, 'create-container'): CreateContainerElement,
    (SCORE_NS, 'create-document'): CreateDocumentElement,
    (SCORE_NS, 'create-raw-file'): CreateRawFileElement,
    (SCORE_NS, 'create-group'): CreateGroupElement,
    (SCORE_NS, 'create-user'): CreateUserElement,
    (SCORE_NS, 'create-uri-reference'): CreateUriReferenceElement,
}
