"""
$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/ui/publisher.py $
$Id: publisher.py 27873 2006-01-26 17:45:06Z dbinger $
"""
from dulcinea.common import get_response
from dulcinea.common import stringify, get_site_name
from dulcinea.persistent_session import DulcineaSessionManager
from dulcinea.site_util import get_root_directory, get_config_value
from dulcinea.ui.directory import DynamicExportingDirectory
from dulcinea.ui.user.profile import dulcinea_user_profile
from dulcinea.ui.user.util import ensure_signed_in
from dulcinea.ui.user.util import format_user
from dulcinea.ui.util import format_date, format_date_time
from dulcinea.ui.util import page
from durus.error import ConflictError
from quixote.errors import PublishError
from quixote.http_response import HTTPResponse, Stream
from quixote.publish import Publisher
from socket import getfqdn
import os
import random
import tempfile
import time


TIDY_IGNORE = [
    'Warning: <table> lacks "summary" attribute',
    'Warning: trimming empty <div>',
    'Warning: trimming empty <option>',
    'Warning: <form> attribute "action" lacks value',
    ]

class RespondNow (PublishError):
   """
   Raised by Publisher.repond_now().
   """



class DulcineaPublisherBase (Publisher):

    # times to retry a request that failed due to a conflict error
    CONFLICT_RETRIES = 3

    def __init__(self, connection=None, root_directory=None, session_manager=None, config=None):
        import quixote
        quixote.DEFAULT_CHARSET = 'utf-8'
        if not root_directory:
            site = get_site_name()
            if site in (None, 'test'):
                root_directory = DynamicExportingDirectory()
            else:
                root_directory = get_root_directory()
        self.connection = connection
        assert session_manager is None
        session_manager = connection.get_root().get('session_manager')
        if session_manager is None:
            session_manager = DulcineaSessionManager()
            connection.get_root()['session_manager'] = session_manager
            connection.commit()

        Publisher.__init__(
            self, root_directory=root_directory,
            session_manager=session_manager,
            config=config)

    def respond_now(self):
        """
        Exit the publishing loop and return the current response.
        """
        raise RespondNow

    def abort(self):
        return self.get_connection().abort()

    def commit(self):
        self.get_connection().commit()

    def start_request(self):
        self.abort()
        Publisher.start_request(self)

    def format_publish_error(self, exc):
        return self.page(exc.title,
            '<p>',
            exc.description,
            '</p>',
            '<code>',
            exc.format(),
            '</code>')

    def finish_interrupted_request(self, exc):
        if isinstance(exc, RespondNow):
            output = get_response().body
            self.session_manager.finish_successful_request()
        else:
            output = Publisher.finish_interrupted_request(self, exc)
        return output

    def try_publish(self, request):
        """Try to process a single request.  Use the base class method to the
        do actual the work.  If a conflict error occurs retry a number of
        times.
        """
        for i in range(self.CONFLICT_RETRIES + 1):
            try:
                value = Publisher.try_publish(self, request)
                if value is None:
                    return ensure_signed_in()
                return value
            except ConflictError, exc:
                self.log("ConflictError (%s): retrying request" % exc)
                self.get_connection().abort()
                request.response = HTTPResponse() # reset response object
                time.sleep(random.random() * (i+1))
        else:
            raise RuntimeError("too many conflict errors")

    def filter_output(self, request, output):
        if get_config_value('tidy_check'):
            ctype = request.response.content_type
            if (output and
                (not ctype or ctype=="text/html") and
                not isinstance(output, Stream)):
                self.html_tidy(request, output)
        return Publisher.filter_output(self, request, output)

    def html_tidy(self, request, output):
        """
        Small hack to call html-tidy for every html
        page which is serverd by quixote.

        It is used for checking the html syntax only, the html
        output won't be changed.
        """
        htmlfile = tempfile.NamedTemporaryFile()
        output = stringify(output)
        if isinstance(output, unicode):
            output = output.encode(request.response.charset)
        htmlfile.write(output)
        htmlfile.flush()
        htmlfile.seek(0)
        fp = os.popen("/usr/bin/tidy -q -e '%s' 2>&1" % htmlfile.name, "r")
        errors = []
        for line in fp:
            line = line.strip()
            if line:
                for ign in TIDY_IGNORE:
                    if ign in line:
                        break
                else:
                    errors.append(line)
        if errors:
            self.log("HTML-Tidy: %s" % request.get_path())
            for line in errors[:20]:
                self.log("   " + line)
            if len(errors) > 20:
                self.log("   [...more errors...]")
        else:
            self.log("HTML-Tidy: %s okay" % request.get_path())
        fp.close()
        htmlfile.close()

    def get_session_cookie_name(self):
        return self.config.session_cookie_name

class DulcineaPublisher(DulcineaPublisherBase):

    def get_connection(self):
       return self.connection

    def get_sessions(self):
        return self.get_connection().get_root()['session_manager'].sessions

    def get_users(self):
        return self.get_connection().get_root()['user_db'].users

    def header(self, *args, **kwargs):
        return None

    def footer(self, *args, **kwargs):
        return None

    def page(self, *args, **kwargs):
        return page(*args, **kwargs)

    def format_login_agreement(self):
        return None

    def format_date_time(self, date):
        return format_date_time(date)

    def format_date(self, date):
        return format_date(date)

    def format_user(self, user, **kwargs):
        return format_user(user, **kwargs)

    def format_user_profile(self, user):
        return dulcinea_user_profile(user)

    def get_feedback_domain(self):
        return ''

    def get_feedback_addresses(self):
        return ['webmaster']

    def respond(self, title, *content, **kwargs):
        # init response?
        if 'status' in kwargs:
            status = kwargs['status']
            del kwargs['status']
        else:
            status = 400
        response = get_response()
        response.set_status(status)
        response.set_body(self.page(title, *content, **kwargs))
        self.respond_now()

    def not_found(self, body=None):
        """(body:str)
        This prepares a 404 response and uses an exception
        to break out of the path traversal and return the response
        immediately.
        """
        self.respond('Not Found', body or 'That page is not here.', status=404)

    def get_user_db(self):
        return self.get_connection().get_root()['user_db']

    def get_material_db(self):
        return self.get_connection().get_root()['material_db']

    def get_standard_units(self):
        return self.get_connection().get_root()['standard_units']

    def get_parameter_db(self):
        return self.get_connection().get_root()['parameter_db']

    def get_link_db(self):
        return self.get_connection().get_root()['link_db']

    def get_link_triple_db(self):
        return self.get_connection().get_root()['link_triple_db']

    def get_news_db(self):
        return self.get_connection().get_root()['news_db']

    def get_inquiry_db(self):
        return self.get_connection().get_root()['inquiry_db']

    def get_gallery(self):
        return self.get_connection().get_root()['gallery']

    def get_issue_db(self):
        return self.get_connection().get_root()['issue_db']

    def get_site_title(self):
        return (get_site_name() or "site").capitalize()

    def get_webmaster_address(self):
        return 'webmaster@%s' % getfqdn()
