########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/SCore/RepositoryImp.py,v 1.28 2005/03/22 17:36:58 mbrown Exp $
"""
Repository class

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

import ContainerImp, ObjectCache

from Ft.Lib import Time
from Ft.Rdf import Statement
from Ft.Server.Common import ResourceTypes, AclConstants, Schema
from Ft.Server.Server import FtServerServerException, Error
from Ft.Server.Server.Lib import SwishHelper

class RepositoryImp(ContainerImp.ContainerImp):
    """
    Implementation for a repository instance
    """

    def __init__(self, path, driver, sessionId):
        self._completed = 0
        cache = ObjectCache.ObjectCache()

        ContainerImp.ContainerImp.__init__(self, path, driver, cache)

        #Session state information
        self._sessionId = sessionId

        return

    ##############################################
    # Overridden interfaces
    ##############################################
    def getParent(self):
        """
        Overridden to return None, as the Repository has no parent
        """
        return None

    def reloadModule(self, module):
        print "Reloading", module
        mod = __import__(module)
        reload(mod)
        return

    #####################################
    # XSLT Strobe
    #####################################
    def addXsltStrobe(self, path):
        """
        Register an XSLT file to be run on the strobe signal
        """
        m = self._driver.getSystemModel()
        s = Statement.Statement('/', Schema.RUN_ON_STROBE, path, '/')
        m.add(s)
        return

    def runXsltStrobe(self):
        m = self._driver.getSystemModel()
        stmts = m.complete('/', Schema.RUN_ON_STROBE, None)
        paths = [ s.object for s in stmts ]
        for path in paths:
            doc = self.fetchResource('/')
            sty = doc.fetchResource(path)
            res, imt = doc.applyXslt([sty],
                                     #params=top_level_params,
                                     )
            print "Strobe XSLT execution results for %s: imt=%s" % (path, imt)
            print res
        return


    #####################################
    #Text Indexing and Searching interfaces
    #####################################
    def purgeTemporaryResources(self):
        """
        Remove from the repository all temporary resources that have
        outlived their lifetimes
        """
        m = self._driver.getSystemModel()
        self._driver.setTempFileDelete(1)
        stmts = m.complete(None, Schema.TIME_TO_LIVE, None)
        cur = Time.FromPythonTime().asISO8601DateTime()
        try:
            for s in stmts:
                if s.object <= cur:
                    self._driver._logger.info("Purging Temporary Resource: %s" % s.subject)
                    self.deleteResource(s.subject)
        finally:
            self._driver.setTempFileDelete(0)

    def searchDocuments(self, searchString):
        """
        Returns the paths to all metadocuments (whose document
        definitions specify that they are to be text indexed) that
        match the specified Swish query string
        """
        self._verifyTx()
        return SwishHelper.SearchDocuments(searchString,self)

    def reindex(self):
        """
        Performs a text-reindex on all metadocuments whose document
        definiton specifies a full text index
        """
        self._verifyTx()
        return SwishHelper.ReIndex(self)

    ##############################################
    #Session Interfaces
    ##############################################
    def createSession(self, key='', ttl=0, overriddenLogin=None):
        """
        Create a new session from this connection.
        If key is specified, then the session ID is generated from
        this key, and whenever the session is accessed, the key must be
        given or the session will be invalidated.

        ttl - session time to live, in seconds.
        If ttl is 0, the driver default is used (usually 15 minutes).
        """
        self._verifyTx()

        if self._sessionId is not None:
            raise FtServerServerException(Error.SESSION_EXISTS)

        if overriddenLogin:
            u, p = overriddenLogin
        elif self._driver.getAclIdent() == AclConstants.WORLD_GROUP_NAME:
            #An anon login
            u, p = (None, None)
        else:
            u = self._driver.getAclIdent()
            model =self._driver.getSystemModel()
            userUri = model.complete(None, Schema.USER_NAME, u)[0].subject
            user = self.fetchResource(userUri)
            p = user.getPassword()

        self._sessionId = self._driver.createSession(key, u, p, ttl)
        return self._sessionId

    def checkLogin(self, username, password):
        """
        Check whether the given credentials are correct without actually
        logging in.  Returns 1 if successful, else 0
        """
        self._verifyTx()
        try:
            self._driver.checkLogin(username, password)
            return 1
        except FtServerServerException, e:
            if e.errorCode == Error.INVALID_LOGIN:
                return 0
            else:
                raise

    def hasSession(self):
        """
        See if this connection is in a session
        """
        self._verifyTx()
        return self._sessionId != None

    def getSessionData(self, key):
        """
        Get Session Data
        """
        self._verifyTx()
        if self._sessionId is None:
            raise FtServerServerException(Error.INVALID_SESSION)
        return self._driver.getSessionData(self._sessionId, key)

    def setSessionData(self, key, value):
        """
        Set Session Data
        """
        self._verifyTx()
        if self._sessionId is None:
            raise FtServerServerException(Error.INVALID_SESSION)
        self._driver.setSessionData(self._sessionId, key, value)

    def deleteSessionData(self, key):
        """
        Delete Session Data
        """
        self._verifyTx()
        if self._sessionId is None:
            raise FtServerServerException(Error.INVALID_SESSION)
        self._driver.deleteSessionData(self._sessionId, key)

    def getSessionExpiration(self):
        """
        Get The expiration time of this session
        """
        self._verifyTx()
        if self._sessionId is None:
            return 0
        try:
            return self._driver.getSessionExpiration(self._sessionId)
        except FtServerServerException:
            return 0

    def invalidateSession(self):
        """
        Invalidate this session.
        """
        self._verifyTx()
        if self._sessionId is None:
            raise FtServerServerException(Error.INVALID_SESSION)
        self._driver.invalidateSession(self._sessionId)
        self._sessionId = None

    ##############################################
    #Transaction Interfaces
    ##############################################
    def getUserName(self):
        self._verifyTx()
        return self._driver.getAclIdent()

    def getCurrentUser(self):
        self._verifyTx()
        return self.fetchUserOrGroupByName(self._driver.getAclIdent())

    def txCommit(self):
        """
        Commit the transaction associated w/ this repository instance
        """
        self._driver._verifyTx()
        self._driver.commit()
        self._completed = 1
        self._cache = None
        return

    def txRollback(self):
        """
        Rollback the transaction associated w/ this repository instance
        """
        self._driver._verifyTx()
        self._driver.rollback()
        self._completed = 1
        self._cache = None
        return

    #######################################
    # Interfaces that span the repo
    #######################################
    def getAllGroupPaths(self):
        """
        Get a list of Paths for all of the groups in the system
        """
        self._verifyTx()
        return map(lambda x: x.subject,
                   self._driver.getSystemModel().complete(
                       None,
                       Schema.TYPE,
                       Schema.g_rdfResourceTypes[ResourceTypes.ResourceType.GROUP])
                   )

    def getAllUserPaths(self):
        """
        Get a list of Paths for all of the users in the system
        """
        self._verifyTx()
        return map(lambda x: x.subject,
                   self._driver.getSystemModel().complete(
                       None,
                       Schema.TYPE,
                       Schema.g_rdfResourceTypes[ResourceTypes.ResourceType.USER])
                   )

    def getAllUserNames(self):
        """
        Get a list of user names for all of the users in the system
        """
        self._verifyTx()
        stmts = self._driver.getSystemModel().complete(None, Schema.USER_NAME, None)
        return map(lambda x: x.object, stmts)

    def getAllGroupNames(self):
        """
        Get a list of group names for all of the users in the system
        """
        self._verifyTx()
        stmts = self._driver.getSystemModel().complete(None, Schema.GROUP_NAME, None)
        return map(lambda x: x.object, stmts)


    def fetchUserOrGroupByName(self, name):
        """
        Fetch a user or a group by name (not full path)
        """
        user = self._driver.getSystemModel().complete(None,
                                                      Schema.USER_NAME,
                                                      name)
        if user:
            return self.fetchResource(user[0].subject)

        group = self._driver.getSystemModel().complete(None,
                                                       Schema.GROUP_NAME,
                                                       name)
        if group:
            return self.fetchResource(group[0].subject)

        return None

    def getAllDocumentDefinitionPaths(self):
        """
        Get a list of Paths for all of the document definitions in the system
        """
        self._verifyTx()
        stmts = self._driver.getSystemModel().complete(
            None,
            Schema.TYPE,
            Schema.g_rdfResourceTypes[ResourceTypes.ResourceType.XPATH_DOCUMENT_DEFINITION])
        stmts = stmts + self._driver.getSystemModel().complete(
            None,
            Schema.TYPE,
            Schema.g_rdfResourceTypes[ResourceTypes.ResourceType.XSLT_DOCUMENT_DEFINITION])
        return map(lambda x:x.subject,stmts)

    def getCommand(self, commandName):
        """
        Get a command object by its name.
        The name should be dot-separated, like 4ss_manager.init
        """
        stmts = self._driver.getSystemModel().complete(None,Schema.COMMAND_FULL_NAME,commandName)
        if len(stmts) != 1:
            return None
        return self.fetchResource(stmts[0].subject)
