Exemple #1
0
    def __before__(self, action, **params):
        # Let templates get at the total render time
        c.timer = ResponseTimer()

        c.links = config["spline.plugins.links"]
        c.javascripts = [
            ("spline", "lib/jquery-1.7.1.min"),
            ("spline", "lib/jquery.cookies-2.2.0.min"),
            ("spline", "lib/jquery.ui-1.8.4.min"),
            ("spline", "core"),
        ]
        run_hooks("before_controller", action, **params)
Exemple #2
0
    def __before__(self, action, **params):
        # Let templates get at the total render time
        c.timer = ResponseTimer()

        c.links = config['spline.plugins.links']
        c.javascripts = [
            ('spline', 'lib/jquery-1.3.2.min'),
            ('spline', 'lib/jquery.cookies-2.2.0.min'),
            ('spline', 'lib/jquery.ui-1.8.4.min'),
            ('spline', 'core'),
        ]
        run_hooks('before_controller', action, **params)
Exemple #3
0
    def cron(self):
        """Runs interested cron-jobs."""
        cron_cache = cache.get_cache('spline:cron')

        # XXX Tiny race condition here; checking for a value and then setting
        # it is not atomic
        if 'LOCK' in cron_cache:
            return 'already running'
        cron_cache['LOCK'] = 1
        try:
            now = datetime.datetime.now().time()
            tic = now.hour * 60 + now.minute
            run_hooks('cron', tic=tic)

        finally:
            # Always unlock when done
            del cron_cache['LOCK']

        return 'ok'
Exemple #4
0
def make_map(config, content_dirs=[]):
    """Create, configure and return the routes Mapper"""
    map = I18nMapper(
        controller_scan=lambda directory: controller_scan(config, directory),
        directory=config['pylons.paths']['controllers'],
        always_scan=config['debug'])
    map.minimization = False

    # Content files get explicitly mapped so we don't have to pull any cheap
    # tricks like looking for them in a 404 handler.  We do them first so
    # controllers can't be clobbered by bad choices of filenames
    for content_dir in content_dirs:
        for root, dirs, files in os.walk(content_dir):
            for name in files:
                localpath = os.path.join(root, name)
                webpath, ext = os.path.splitext(localpath)

                # Skip over hidden files.
                # For now, also require a .html extension.
                if webpath[0] == '.' or ext != '.html':
                    continue

                # Use the full path as a route name so url() can easily route
                # to a static page
                map.connect('/' + os.path.relpath(webpath, content_dir),
                            '/' + os.path.relpath(webpath, content_dir),
                            controller='main', action='content', path=localpath)

    # The ErrorController route (handles 404/500 error pages); it should
    # likely stay at the top, ensuring it can always be resolved
    map.connect('/error/{action}', controller='error')
    map.connect('/error/{action}/{id}', controller='error')

    map.connect('/css', controller='main', action='css')
    map.connect('/cron', controller='main', action='cron')

    # Allow plugins to map routes without the below defaults clobbering them
    run_hooks('routes_mapping', config=config, map=map)

    map.connect('/', controller='main', action='index')

    return map
Exemple #5
0
def load_sources_hook(config, *args, **kwargs):
    """Hook to load all the known sources and stuff them in config.  Run once,
    on server startup.

    Frontpage hooks are also passed the `config` hash, as it's not available
    during setup.
    """
    # Extract source definitions from config and store as source_name => config
    update_config = defaultdict(dict)
    key_rx = re.compile(
        '(?x) ^ spline-frontpage [.] sources [.] (\w+) (?: [.] (\w+) )? $')
    for key, val in config.iteritems():
        # Match against spline-frontpage.source.(source).(key)
        match = key_rx.match(key)
        if not match:
            continue

        source_name, subkey = match.groups()
        if not subkey:
            # This is the type declaration; use a special key
            subkey = '__type__'

        update_config[source_name][subkey] = val

    # Figure out the global limit and expiration time, with reasonable
    # defaults.  Make sure they're integers.
    global_limit = int(config.get('spline-frontpage.limit', 10))
    # max_age is optional and can be None
    try:
        global_max_age = int(config['spline-frontpage.max_age'])
    except KeyError:
        global_max_age = None

    config['spline-frontpage.limit'] = global_limit
    config['spline-frontpage.max_age'] = global_max_age

    # Ask plugins to turn configuration into source objects
    sources = []
    for source, source_config in update_config.iteritems():
        hook_name = 'frontpage_updates_' + source_config['__type__']
        del source_config['__type__']  # don't feed this to constructor!

        # Default to global limit and max age.  Source takes care of making
        # integers and whatnot
        source_config.setdefault('limit', global_limit)
        source_config.setdefault('max_age', global_max_age)

        # Hooks return a list of sources; combine with running list
        sources += run_hooks(hook_name, config=config, **source_config)

    # Save the list of sources, and done
    config['spline-frontpage.sources'] = sources
Exemple #6
0
def load_sources_hook(config, *args, **kwargs):
    """Hook to load all the known sources and stuff them in config.  Run once,
    on server startup.

    Frontpage hooks are also passed the `config` hash, as it's not available
    during setup.
    """
    # Extract source definitions from config and store as source_name => config
    update_config = defaultdict(dict)
    key_rx = re.compile(
        '(?x) ^ spline-frontpage [.] sources [.] (\w+) (?: [.] (\w+) )? $')
    for key, val in config.iteritems():
        # Match against spline-frontpage.source.(source).(key)
        match = key_rx.match(key)
        if not match:
            continue

        source_name, subkey = match.groups()
        if not subkey:
            # This is the type declaration; use a special key
            subkey = '__type__'

        update_config[source_name][subkey] = val

    # Figure out the global limit and expiration time, with reasonable
    # defaults.  Make sure they're integers.
    global_limit = int(config.get('spline-frontpage.limit', 10))
    # max_age is optional and can be None
    try:
        global_max_age = int(config['spline-frontpage.max_age'])
    except KeyError:
        global_max_age = None

    config['spline-frontpage.limit'] = global_limit
    config['spline-frontpage.max_age'] = global_max_age

    # Ask plugins to turn configuration into source objects
    sources = []
    for source, source_config in update_config.iteritems():
        hook_name = 'frontpage_updates_' + source_config['__type__']
        del source_config['__type__']  # don't feed this to constructor!

        # Default to global limit and max age.  Source takes care of making
        # integers and whatnot
        source_config.setdefault('limit', global_limit)
        source_config.setdefault('max_age', global_max_age)

        # Hooks return a list of sources; combine with running list
        sources += run_hooks(hook_name, config=config, **source_config)

    # Save the list of sources, and done
    config['spline-frontpage.sources'] = sources
Exemple #7
0
def make_app(global_conf, full_stack=True, **app_conf):
    """Create a Pylons WSGI application and return it

    ``global_conf``
        The inherited configuration for this application. Normally from
        the [DEFAULT] section of the Paste ini file.

    ``full_stack``
        Whether or not this application provides a full WSGI stack (by
        default, meaning it handles its own exceptions and errors).
        Disable full_stack when this application is "managed" by
        another WSGI middleware.

    ``app_conf``
        The application's local configuration. Normally specified in
        the [app:<name>] section of the Paste ini file (where <name>
        defaults to main).

    """
    # Configure the Pylons environment
    config = load_environment(global_conf, app_conf)

    # The Pylons WSGI app
    app = SplineApp(config=config)

    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)

    # XXX This is dumb.  Hack around a Beaker bug which ignores enabled=False
    # in config -- but not, thankfully, in kwargs
    cache_kwargs = {}
    if 'beaker.cache.enabled' in config:
        cache_kwargs['enabled'] = asbool(config['beaker.cache.enabled'])

    # Routing/Session/Cache Middleware
    app = RoutesMiddleware(app, config['routes.map'])
    app = SessionMiddleware(app, config)

    # Super ultra debug mode
    #from paste.translogger import TransLogger
    #app = TransLogger(app)

    if asbool(full_stack):
        # Handle Python exceptions
        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])

        # Display error documents for 401, 403, 404 status codes (and
        # 500 when debug is disabled)
        if asbool(config['debug']):
            app = StatusCodeRedirect(app)
        else:
            app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])

    # Establish the Registry for this application
    app = RegistryManager(app)

    # Static files (If running in production, and Apache or another web
    # server is handling this static content, remove the following 2 lines)
    static_app = SplineStaticURLParser(config['pylons.paths']['static_files'])
    app = Cascade([static_app, app])
    app.config = config

    # Let plugins do any final setup
    run_hooks('after_setup', config=config)

    return app
Exemple #8
0
 def __after__(self, **params):
     run_hooks('after_controller', **params)
Exemple #9
0
    def index(self):
        """Magicaltastic front page.

        Plugins can register a hook called 'frontpage_updates_<type>' to add
        updates to the front page.  `<type>` is an arbitrary string indicating
        the sort of update the plugin knows how to handle; for example,
        spline-forum has a `frontpage_updates_forum` hook for posting news from
        a specific forum.

        Hook handlers should return a list of FrontPageUpdate objects.

        Standard hook parameters are:
        `limit`, the maximum number of items that should ever be returned.
        `max_age`, the number of seconds after which items expire.
        `title`, a name for the source.
        `icon`, an icon to show next to its name.

        `limit` and `max_age` are also global options.

        Updates are configured in the .ini like so:

            spline-frontpage.sources.foo = updatetype
            spline-frontpage.sources.foo.opt1 = val1
            spline-frontpage.sources.foo.opt2 = val2

        Note that the 'foo' name is completely arbitrary and is only used for
        grouping options together.  This will result in a call to:

            run_hooks('frontpage_updates_updatetype', opt1=val1, opt2=val2)

        Plugins may also respond to the `frontpage_extras` hook with other
        interesting things to put on the front page.  There's no way to
        customize the order of these extras or which appear and which don't, at
        the moment.  Such hooks should return an object with at least a
        `template` attribute; the template will be called with the object
        passed in as its `obj` argument.

        Local plugins can override the fairly simple index.mako template to
        customize the front page layout.
        """

        updates = []
        global_limit = config['spline-frontpage.limit']
        global_max_age = max_age_to_datetime(
            config['spline-frontpage.max_age'])

        c.sources = config['spline-frontpage.sources']
        for source in c.sources:
            new_updates = source.poll(global_limit, global_max_age)
            updates.extend(new_updates)

            # Little optimization: once there are global_limit items, anything
            # older than the oldest cannot possibly make it onto the list.  So,
            # bump global_max_age to that oldest time if this is ever the case.
            updates.sort(key=lambda obj: obj.time, reverse=True)
            del updates[global_limit:]

            if updates and len(updates) == global_limit:
                global_max_age = updates[-1].time

        # Find the oldest unseen item, to draw a divider after it.
        # If this stays as None, the divider goes at the top
        c.last_seen_item = None
        # Could have a timestamp in the stash if this is a user, or in a cookie
        # if this session has ever been logged out...
        times = []
        for source in (c.user.stash, request.cookies):
            try:
                times.append( int(source['frontpage-last-seen-time']) )
            except (KeyError, ValueError):
                pass

        if times:
            last_seen_time = datetime.datetime.fromtimestamp(max(times))
            for update in updates:
                if update.time > last_seen_time:
                    c.last_seen_item = update
                else:
                    break

        # Save ~now~ as the last-seen time
        now = datetime.datetime.now().strftime('%s')
        if c.user:
            c.user.stash['frontpage-last-seen-time'] = now
            meta.Session.add(c.user)
        else:
            response.set_cookie('frontpage-last-seen-time', now)

        # Done!  Feed to template
        c.updates = updates

        # Hook for non-update interesting things to put on the front page.
        # This hook should return objects with a 'template' attribute, and
        # whatever else they need
        c.extras = run_hooks('frontpage_extras')

        ret = render('/index.mako')

        # Commit AFTER rendering the template!  Committing invalidates
        # everything in the session, undoing any eagerloading that may have
        # been done by sources
        meta.Session.commit()
        return ret
Exemple #10
0
    def index(self):
        """Magicaltastic front page.

        Plugins can register a hook called 'frontpage_updates_<type>' to add
        updates to the front page.  `<type>` is an arbitrary string indicating
        the sort of update the plugin knows how to handle; for example,
        spline-forum has a `frontpage_updates_forum` hook for posting news from
        a specific forum.

        Hook handlers should return a list of FrontPageUpdate objects.

        Standard hook parameters are:
        `limit`, the maximum number of items that should ever be returned.
        `max_age`, the number of seconds after which items expire.
        `title`, a name for the source.
        `icon`, an icon to show next to its name.

        `limit` and `max_age` are also global options.

        Updates are configured in the .ini like so:

            spline-frontpage.sources.foo = updatetype
            spline-frontpage.sources.foo.opt1 = val1
            spline-frontpage.sources.foo.opt2 = val2

        Note that the 'foo' name is completely arbitrary and is only used for
        grouping options together.  This will result in a call to:

            run_hooks('frontpage_updates_updatetype', opt1=val1, opt2=val2)

        Plugins may also respond to the `frontpage_extras` hook with other
        interesting things to put on the front page.  There's no way to
        customize the order of these extras or which appear and which don't, at
        the moment.  Such hooks should return an object with at least a
        `template` attribute; the template will be called with the object
        passed in as its `obj` argument.

        Local plugins can override the fairly simple index.mako template to
        customize the front page layout.
        """

        updates = []
        global_limit = config['spline-frontpage.limit']
        global_max_age = max_age_to_datetime(
            config['spline-frontpage.max_age'])

        c.sources = config['spline-frontpage.sources']
        for source in c.sources:
            new_updates = source.poll(global_limit, global_max_age)
            updates.extend(new_updates)

            # Little optimization: once there are global_limit items, anything
            # older than the oldest cannot possibly make it onto the list.  So,
            # bump global_max_age to that oldest time if this is ever the case.
            updates.sort(key=lambda obj: obj.time, reverse=True)
            del updates[global_limit:]

            if updates and len(updates) == global_limit:
                global_max_age = updates[-1].time

        # Find the oldest unseen item, to draw a divider after it.
        # If this stays as None, the divider goes at the top
        c.last_seen_item = None
        # Could have a timestamp in the stash if this is a user, or in a cookie
        # if this session has ever been logged out...
        times = []
        for source in (c.user.stash, request.cookies):
            try:
                times.append(int(source['frontpage-last-seen-time']))
            except (KeyError, ValueError):
                pass

        if times:
            last_seen_time = datetime.datetime.fromtimestamp(max(times))
            for update in updates:
                if update.time > last_seen_time:
                    c.last_seen_item = update
                else:
                    break

        # Save ~now~ as the last-seen time
        now = datetime.datetime.now().strftime('%s')
        if c.user:
            c.user.stash['frontpage-last-seen-time'] = now
            meta.Session.add(c.user)
        else:
            response.set_cookie('frontpage-last-seen-time', now)

        # Done!  Feed to template
        c.updates = updates

        # Hook for non-update interesting things to put on the front page.
        # This hook should return objects with a 'template' attribute, and
        # whatever else they need
        c.extras = run_hooks('frontpage_extras')

        ret = render('/index.mako')

        # Commit AFTER rendering the template!  Committing invalidates
        # everything in the session, undoing any eagerloading that may have
        # been done by sources
        meta.Session.commit()
        return ret
Exemple #11
0
def make_app(global_conf, full_stack=True, **app_conf):
    """Create a Pylons WSGI application and return it

    ``global_conf``
        The inherited configuration for this application. Normally from
        the [DEFAULT] section of the Paste ini file.

    ``full_stack``
        Whether or not this application provides a full WSGI stack (by
        default, meaning it handles its own exceptions and errors).
        Disable full_stack when this application is "managed" by
        another WSGI middleware.

    ``app_conf``
        The application's local configuration. Normally specified in
        the [app:<name>] section of the Paste ini file (where <name>
        defaults to main).

    """
    # Configure the Pylons environment
    config = load_environment(global_conf, app_conf)

    # The Pylons WSGI app
    app = SplineApp(config=config)

    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)

    # XXX This is dumb.  Hack around a Beaker bug which ignores enabled=False
    # in config -- but not, thankfully, in kwargs
    cache_kwargs = {}
    if 'beaker.cache.enabled' in config:
        cache_kwargs['enabled'] = asbool(config['beaker.cache.enabled'])

    # Routing/Session/Cache Middleware
    app = RoutesMiddleware(app, config['routes.map'])
    app = SessionMiddleware(app, config)

    # Super ultra debug mode
    #from paste.translogger import TransLogger
    #app = TransLogger(app)

    if asbool(full_stack):
        # Handle Python exceptions
        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])

        # Display error documents for 401, 403, 404 status codes (and
        # 500 when debug is disabled)
        if asbool(config['debug']):
            app = StatusCodeRedirect(app)
        else:
            app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])

    # Establish the Registry for this application
    app = RegistryManager(app)

    # Static files (If running in production, and Apache or another web 
    # server is handling this static content, remove the following 2 lines)
    static_app = SplineStaticURLParser(config['pylons.paths']['static_files'])
    app = Cascade([static_app, app])
    app.config = config

    # Let plugins do any final setup
    run_hooks('after_setup', config=config)

    return app
Exemple #12
0
 def __after__(self, **params):
     run_hooks("after_controller", **params)
Exemple #13
0
 def __after__(self, **params):
     run_hooks('after_controller', **params)