# -*- test-case-name: xmantissa.test.test_sharing -*-

"""
This module provides web-based access to objects shared with the
xmantissa.sharing module.

Users' publicly shared objects are exposed at the url::

    http://your-server/by/<user>@<hostname>/<share-id>

"""

from zope.interface import implements

from axiom import userbase

from nevow import inevow, url, rend

from xmantissa import ixmantissa
from xmantissa import sharing


def linkTo(sharedProxy, store):
    """
    Generate the path part of a URL to link to a proxy for a shared item.

    @return: an absolute path URL string, which looks like
    '/by/user@host/shareID'

    @rtype: str
    """
    username = '@'.join(userbase.getAccountNames(store).next())
    return '/by/' + username + '/' + sharedProxy.shareID


class UserIndexPage(object):
    """
    This is the resource accessible at "/by"

    See L{xmantissa.publicweb.PublicFrontPage.child_by} for the integration
    point with the rest of the system.
    """
    implements(inevow.IResource)

    def __init__(self, loginSystem):
        """
        Create a UserIndexPage which draws users from a given
        L{userbase.LoginSystem}.

        @param loginSystem: the login system to look up users in.
        @type loginSystem: L{userbase.LoginSystem}
        """
        self.loginSystem = loginSystem


    def locateChild(self, ctx, segments):
        """
        Retrieve a L{SharingIndex} for a particular user, or rend.NotFound.
        """
        user = segments[0]
        host = inevow.IRequest(ctx).getHeader('host')
        parts = unicode(user).split(u'@')
        if len(parts) == 1:
            parts.append(host.decode('ascii'))
        result = self.loginSystem.accountByAddress(*parts)
        if result is not None:
            return SharingIndex(result.avatars.open()), segments[1:]
        return rend.NotFound


    def renderHTTP(self, ctx):
        """
        Return a sarcastic string to the user when they try to list the index of
        users by hitting '/by' by itself.

        (This should probably do something more helpful.  There might be a very
        large number of users so returning a simple listing is infeasible, but
        one might at least present a search page or something.)
        """
        return 'Keep trying.  You are almost there.'



class SharingIndex(object):
    """
    A SharingIndex is an http resource which provides a view onto a user's
    store, for another user.
    """
    implements(inevow.IResource, ixmantissa.ICustomizable)

    def __init__(self, userStore, avatarName=None):
        """
        Create a SharingIndex.

        @param userStore: an L{axiom.store.Store} to be viewed.

        @param avatarName: the external identifier of the viewer.
        """
        self.userStore = userStore
        self.avatarName = avatarName


    def customizeFor(self, avatarName):
        """
        @param avatarName: the external identifier of the new viewer.

        @return: a version of this sharing index as viewed by a different role.
        """
        return SharingIndex(self.userStore, avatarName)


    def renderHTTP(self, ctx):
        """
        The sharing index is located at '/by/username@host' - when rendered, it
        will redirect to '/by/username@host/', i.e. the default shared item or
        the item with the shareID of the empty string.
        """
        return url.URL.fromContext(ctx).child('')


    def locateChild(self, ctx, segments):
        """
        Look up a shared item for the role viewing this SharingIndex and return a
        L{PublicAthenaLivePage} containing that shared item's fragment to the
        user.

        These semantics are UNSTABLE.  This method is adequate for simple uses,
        but it should be expanded in the future to be more consistent with
        other resource lookups.  In particular, it should allow share
        implementors to adapt their shares to L{IResource} directly rather than
        L{INavigableFragment}, to allow for simpler child dispatch.

        @param segments: a list of strings, the first of which should be the
        shareID of the desired item.

        @param ctx: unused.

        @return: a L{PublicAthenaLivePage} wrapping a customized fragment.
        """
        shareID = segments[0].decode('ascii')

        role = sharing.getPrimaryRole(self.userStore, self.avatarName)

        try:
            sharedItem = sharing.getShare(self.userStore, role, shareID)
        except sharing.NoSuchShare:
            return rend.NotFound
        fragment = ixmantissa.INavigableFragment(sharedItem)
        # If you're shared, you MUST implement customizeFor (maybe this should
        # be a different interface? ugh.
        fragment = fragment.customizeFor(self.avatarName)
        if fragment.fragmentName is not None:
            fragDocFactory = ixmantissa.IWebTranslator(
                self.userStore).getDocFactory(fragment.fragmentName, None)
            if fragDocFactory is not None:
                fragment.docFactory = fragDocFactory
        from xmantissa.publicweb import PublicAthenaLivePage
        result = PublicAthenaLivePage(
            self.userStore.parent, fragment, forUser=self.avatarName)
        return result, segments[1:]


syntax highlighted by Code2HTML, v. 0.9.1