text: # -*- coding: utf-8 -*-
  from cone.app import browser
  from cone.app import security
  from cone.app.interfaces import IApplicationNode
  from cone.app.model import AppRoot
  from cone.app.model import AppSettings
  from cone.app.model import LayoutConfig
  from cone.app.model import Properties
  from cone.app.ugm import ugm_backend
  from cone.app.utils import format_traceback
  from cone.app.utils import node_path
  from node.interfaces import INode
  from pyramid.authentication import AuthTktAuthenticationPolicy
  from pyramid.authorization import ACLAuthorizationPolicy
  from pyramid.config import Configurator
  from pyramid.static import static_view
  from pyramid.traversal import ResourceTreeTraverser
  from yafowil.resources import YafowilResources as YafowilResourcesBase
  from zope.component import adapter
  from zope.component import getGlobalSiteManager
  import importlib
  import logging
  import pyramid_chameleon
  import pyramid_zcml
  import sys
  import threading
  
  
  logger = logging.getLogger('cone.app')
  
  # configuration
  cfg = Properties()
  
  # available languages
  cfg.available_languages = []
  
  # used main template
  cfg.main_template = 'cone.app.browser:templates/main.pt'
  
  # default node icon
  cfg.default_node_icon = 'glyphicon glyphicon-asterisk'
  
  # XXX: move resource registration to browser package
  # XXX: support developmenet and production mode
  
  # JS resources
  cfg.js = Properties()
  cfg.js.public = [
      'static/public.js'
  ]
  cfg.js.protected = [
      'static/protected.js'
  ]
  
  # CSS Resources
  cfg.css = Properties()
  
  # development
  cfg.css.public = [
      'static/jqueryui/jquery-ui-1.10.3.custom.css',
      'static/bootstrap/css/bootstrap.css',
      'static/bootstrap/css/bootstrap-theme.css',
      'static/ionicons/css/ionicons.css',
      'static/typeahead/typeahead.css',
      '++resource++bdajax/bdajax_bs3.css',
      'static/styles.css'
  ]
  
  # production
  # cfg.css.public = [
  #     'static/jqueryui/jquery-ui-1.10.3.custom.css',
  #     'static/bootstrap/css/bootstrap.min.css',
  #     'static/bootstrap/css/bootstrap-theme.min.css',
  #     'static/ionicons/css/ionicons.css',
  #     'static/typeahead/typeahead.css',
  #     '++resource++bdajax/bdajax_bs3.css',
  #     'static/styles.css'
  # ]
  
  cfg.css.protected = list()
  
  # JS and CSS Assets to publish merged
  cfg.merged = Properties()
  cfg.merged.js = Properties()
  
  # development
  cfg.merged.js.public = [
      (browser.static_resources, 'jquery-1.9.1.js'),
      (browser.static_resources, 'jquery.migrate-1.2.1.js'),
      (browser.static_resources, 'jqueryui/jquery-ui-1.10.3.custom.js'),
      (browser.static_resources, 'bootstrap/js/bootstrap.js'),
      (browser.static_resources, 'typeahead/typeahead.bundle.js'),
      (browser.static_resources, 'cookie_functions.js')
  ]
  
  # production
  # cfg.merged.js.public = [
  #     (browser.static_resources, 'jquery-1.9.1.min.js'),
  #     (browser.static_resources, 'jquery.migrate-1.2.1.min.js'),
  #     (browser.static_resources, 'jqueryui/jquery-ui-1.10.3.custom.min.js'),
  #     (browser.static_resources, 'bootstrap/js/bootstrap.min.js'),
  #     (browser.static_resources, 'typeahead/typeahead.bundle.js'),
  #     (browser.static_resources, 'cookie_functions.js')
  # ]
  
  cfg.merged.js.protected = list()
  
  cfg.merged.css = Properties()
  cfg.merged.css.public = list()
  cfg.merged.css.protected = list()
  
  cfg.merged.print_css = Properties()
  cfg.merged.print_css.public = [
      (browser.static_resources, 'print.css')
  ]
  cfg.merged.print_css.protected = list()
  
  
  class layout_config(object):
      _registry = dict()
  
      def __init__(self, *for_):
          self.for_ = for_
  
      def __call__(self, factory):
          for context in self.for_:
              self._registry[context] = factory
          return factory
  
      @classmethod
      def lookup(cls, model=None, request=None):
          for cls_ in model.__class__.mro():
              factory = cls._registry.get(cls_)
              if factory:
                  return factory(model=model, request=request)
  
  
  @layout_config(object)
  class DefaultLayoutConfig(LayoutConfig):
  
      def __init__(self, model=None, request=None):
          super(DefaultLayoutConfig, self).__init__(model=model, request=request)
          self.mainmenu = True
          self.mainmenu_fluid = False
          self.livesearch = True
          self.personaltools = True
          self.columns_fluid = False
          self.pathbar = True
          self.sidebar_left = ['navtree']
          self.sidebar_left_grid_width = 3
          self.content_grid_width = 9
  
  
  def import_from_string(path):
      mod, ob = path.rsplit('.', 1)
      return getattr(importlib.import_module(mod), ob)
  
  
  root = None
  
  
  def configure_root(root, settings):
      root.metadata.title = settings.get('cone.root.title', 'CONE')
      root.properties.default_child = settings.get('cone.root.default_child')
      mainmenu_empty_title = settings.get('cone.root.mainmenu_empty_title')
      mainmenu_empty_title = mainmenu_empty_title in ['True', 'true', '1']
      root.properties.mainmenu_empty_title = mainmenu_empty_title
      default_content_tile = settings.get('cone.root.default_content_tile')
      if default_content_tile:
          root.properties.default_content_tile = default_content_tile
      root.properties.in_navtree = False
  
  
  def default_root_node_factory(settings):
      root = AppRoot()
      root.factories['settings'] = AppSettings
      configure_root(root, settings)
      return root
  
  
  def register_config(key, factory):
      factories = root['settings'].factories
      if key in factories:
          raise ValueError(u"Config with name '%s' already registered." % key)
      factories[key] = factory
  
  
  # B/C
  register_plugin_config = register_config
  
  
  def register_entry(key, factory):
      factories = root.factories
      if key in factories:
          raise ValueError(u"Entry with name '%s' already registered." % key)
      root.factories[key] = factory
  
  
  # B/C
  register_plugin = register_entry
  
  
  main_hooks = list()
  
  
  def main_hook(func):
      """decorator to register main hook.
  
      Decorated function gets called on application startup.
      """
      main_hooks.append(func)
      return func
  
  
  # B/C
  def register_main_hook(callback):
      """Register function to get called on application startup.
      """
      main_hooks.append(callback)
  
  
  thread_shutdown_hooks = list()
  
  
  def thread_shutdown_hook(func):  # pragma: no cover
      """decorator to register thread shutdown hook.
  
      Decorated function gets called when main thread joins. Thread shutdown
      hooks are used for graceful joining of non daemon threads.
      """
      thread_shutdown_hooks.append(func)
      return func
  
  
  def get_root(environ=None):
      return root
  
  
  def auth_tkt_factory(**kwargs):
      kwargs.setdefault('callback', security.groups_callback)
      return AuthTktAuthenticationPolicy(**kwargs)
  
  
  def acl_factory(**kwargs):
      return ACLAuthorizationPolicy()
  
  
  cfg.yafowil = Properties()
  cfg.yafowil.js_skip = set()
  cfg.yafowil.css_skip = set()
  
  # ignore bootstrap dependencies delivered by yafowil.bootstrap
  cfg.yafowil.js_skip.add('bootstrap.dependencies')
  cfg.yafowil.css_skip.add('bootstrap.dependencies')
  
  
  class YafowilResources(YafowilResourcesBase):
  
      def __init__(self, js_skip=[], css_skip=[], config=None):
          self.config = config
          super(YafowilResources, self).__init__(
              js_skip=js_skip,
              css_skip=css_skip
          )
  
      def configure_resource_directory(self, plugin_name, resourc_edir):
          app = sys.modules[__name__]
          resources_view = static_view(resourc_edir, use_subpath=True)
          view_name = '%s_resources' % plugin_name.replace('.', '_')
          setattr(app, view_name, resources_view)
          view_path = 'cone.app.%s' % view_name
          resource_base = '++resource++%s' % plugin_name
          self.config.add_view(view_path, name=resource_base)
          return resource_base
  
  
  def configure_yafowil_addon_resources(config, public):
      resources = YafowilResources(
          js_skip=cfg.yafowil.js_skip,
          css_skip=cfg.yafowil.css_skip,
          config=config
      )
      js_resources = cfg.js.public if public else cfg.js.protected
      css_resources = cfg.css.public if public else cfg.css.protected
      for js in reversed(resources.js_resources):
          js_resources.insert(0, js)
      for css in resources.css_resources:
          css_resources.insert(0, css)
  
  
  def configure_bdajax_resources():
      # bdajax needs to be loaded before resources depending on it order to
      # avoid double binding on document ready
      cfg.js.public.insert(0, '++resource++bdajax/bdajax_bs3.js')
      cfg.js.public.insert(0, '++resource++bdajax/bdajax.js')
      cfg.js.public.insert(0, '++resource++bdajax/overlay.js')
  
  
  @adapter(IApplicationNode)
  class ApplicationNodeTraverser(ResourceTreeTraverser):
  
      def __call__(self, request):
          result = super(ApplicationNodeTraverser, self).__call__(request)
          context = result['context']
          if not IApplicationNode.providedBy(context):
              if INode.providedBy(context):
                  result['context'] = context.acquire(IApplicationNode)
                  result['view_name'] = context.name
                  result['traversed'] = tuple(node_path(result['context']))
              else:
                  result['context'] = get_root()
                  result['view_name'] = ''
                  result['traversed'] = tuple()
          return result
  
  
  def start_thread_monitor():  # pragma: no cover
      if not thread_shutdown_hooks:
          return
  
      def _monitor():
          main_thread = threading.main_thread()
          main_thread.join()
          for hook in thread_shutdown_hooks:
              hook()
  
      monitor = threading.Thread(target=_monitor)
      monitor.daemon = True
      monitor.start()
  
  
  def main(global_config, **settings):
      """Returns WSGI application.
      """
      # set authentication related application properties
      security.ADMIN_USER = settings.get('cone.admin_user')
      security.ADMIN_PASSWORD = settings.get('cone.admin_password')
      security.AUTHENTICATOR = settings.get('cone.authenticator')
  
      auth_secret = settings.pop('cone.auth_secret', 'secret')
      auth_cookie_name = settings.pop('cone.auth_cookie_name', 'auth_tkt')
      auth_secure = settings.pop('cone.auth_secure', False)
      auth_include_ip = settings.pop('cone.auth_include_ip', False)
      auth_timeout = settings.pop('cone.auth_timeout', None)
      auth_reissue_time = settings.pop('cone.auth_reissue_time', None)
      if auth_reissue_time is not None:
          auth_reissue_time = int(auth_reissue_time)
      auth_max_age = settings.pop('cone.auth_max_age', None)
      if auth_max_age is not None:
          auth_max_age = int(auth_max_age)
      auth_http_only = settings.pop('cone.auth_http_only', False)
      auth_path = settings.pop('cone.auth_path', "/")
      auth_wild_domain = settings.pop('cone.auth_wild_domain', True)
  
      auth_policy = auth_tkt_factory(
          secret=auth_secret,
          cookie_name=auth_cookie_name,
          secure=auth_secure,
          include_ip=auth_include_ip,
          timeout=auth_timeout,
          reissue_time=auth_reissue_time,
          max_age=auth_max_age,
          http_only=auth_http_only,
          path=auth_path,
          wild_domain=auth_wild_domain,
      )
  
      # create root node
      global root
      root_node_factory = settings.pop('cone.root.node_factory', None)
      if root_node_factory:
          root = import_from_string(root_node_factory)(settings)
      else:
          root = default_root_node_factory(settings)
  
      if settings.get('testing.hook_global_registry'):
          globalreg = getGlobalSiteManager()
          config = Configurator(registry=globalreg)
          config.setup_registry(root_factory=get_root, settings=settings)
      else:
          config = Configurator(root_factory=get_root, settings=settings)
  
      # set authentication and authorization policies
      config.set_authentication_policy(auth_policy)
      config.set_authorization_policy(acl_factory())
      config.commit()
  
      # begin configuration
      config.begin()
  
      # include general dependencies
      config.include(pyramid_chameleon)
      config.include(pyramid_zcml)
  
      # add custom traverser
      config.registry.registerAdapter(ApplicationNodeTraverser)
  
      # available languages
      available_languages = settings.get('cone.available_languages', '')
      cfg.available_languages = [
          lang.strip() for lang in available_languages.split(',') if lang
      ]
  
      # main template
      main_template = settings.get('cone.main_template')
      if main_template:
          cfg.main_template = main_template
  
      # add translation
      config.add_translation_dirs('cone.app:locale/')
  
      # XXX: register yafowil and all yafowil addon widget locales.
      #      provide locales either in yafowil resources or as entry points in
      #      all yafowil packages providing translations
  
      # static routes
      config.add_route("favicon", "/favicon.ico")
      # XXX: robots.txt
      # XXX: humans.txt
  
      # register static resources
      config.add_view(browser.static_resources, name='static')
  
      # scan browser package
      config.scan(browser)
  
      # load zcml
      config.load_zcml('configure.zcml')
  
      # read plugin configurator
      plugins = settings.get('cone.plugins', '')
      plugins = plugins.split('\n')
      plugins = [pl for pl in plugins if pl and not pl.startswith('#')]
      for plugin in plugins:
          try:
              importlib.import_module(plugin)
          except ImportError:
              msg = 'Cannot import plugin {}\n{}'.format(
                  plugin,
                  format_traceback()
              )
              logger.error(msg)
              continue
          try:
              config.load_zcml('{}:configure.zcml'.format(plugin))
          except IOError:  # pragma: no cover
              msg = 'No configure.zcml in {}'.format(plugin)
              logger.info(msg)
  
      # execute main hooks
      filtered_hooks = list()
      for plugin in plugins:
          for hook in main_hooks:
              if hook.__module__.startswith(plugin):
                  filtered_hooks.append(hook)
      for hook in filtered_hooks:
          hook(config, global_config, settings)
  
      # load and initialize UGM
      backend_name = settings.get('ugm.backend')
      # B/C
      if not backend_name:
          backend_name = settings.get('cone.auth_impl')
      if backend_name:
          try:
              ugm_backend.load(backend_name, settings)
              ugm_backend.initialize()
          except Exception:  # pragma: no cover
              msg = 'Failed to create UGM backend:\n{}'.format(format_traceback())
              logger.error(msg)
  
      user_display_attr = settings.get('ugm.user_display_attr')
      if user_display_attr:
          ugm_backend.user_display_attr = user_display_attr
  
      group_display_attr = settings.get('ugm.group_display_attr')
      if group_display_attr:
          ugm_backend.group_display_attr = group_display_attr
  
      # register yafowil static resources
      # done after addon config - addon code may disable yafowil resource groups
      # XXX: ``yafowil.resources_public`` is a temporary hack and stays
      # undocumented. In 1.1. ``webresource`` will be used for resource
      # registration and resource delivery configuration.
      yafowil_resources_public = settings.get('yafowil.resources_public')
      yafowil_resources_public = yafowil_resources_public in ['1', 'True', 'true']
      configure_yafowil_addon_resources(config, yafowil_resources_public)
  
      # ensure bdajax resources gets loaded before resources depending on it
      configure_bdajax_resources()
  
      # end configuration
      config.end()
  
      # start thread monitor if thread shutdown hooks registered
      start_thread_monitor()
  
      # return wsgi app
      return config.make_wsgi_app()
  
  
  def make_remote_addr_middleware(app, global_conf):
      return RemoteAddrFilter(app)
  
  
  class RemoteAddrFilter(object):
      """Use this middleware if nginx is used as proxy and IP address should be
      included in auth cookie. make sure nginx passes the right header:
  
      proxy_set_header X-Real-IP $remote_addr;
      """
  
      def __init__(self, app):
          self.app = app
  
      def __call__(self, environ, start_response):
          if 'HTTP_X_REAL_IP' in environ:
              environ['REMOTE_ADDR'] = environ['HTTP_X_REAL_IP']
          return self.app(environ, start_response)
  