turbogears

Patched TG2 bootstrap script for python 2.6

Wednesday, July 28th, 2010 | Uncategorized | Comments

As actually I end up installing Tg2 on various systems having python2.6 quite often I have patched the bootstrap script to work on it.

If you had problems installing TG2 on python2.6 failing on Extremes or Zope dependencies you can try to use this script, it should work for you:

https://bitbucket.org/_amol_/tg-bootstrap-py2.6/src/tip/tg2-bootstrap.py

Tags:

ACR got support for user permissions

Wednesday, April 7th, 2010 | Opensource | Comments

ACR opensource Turbogears2 CMS got support for user permissions to allow users to edit only some pages and create children only in some sections. This should permit to separate work between multiple people in ACR based sites.

ACR also got support for blog/news slice template. This permits to create blogs in ACR with just two clicks instead of having to declare the ACR slicegroup youself.

As usual you can download ACR from http://repo.axant.it/hg/acr by using mercurial

Tags: ,

Remember me in Turbogears2

Wednesday, March 10th, 2010 | Software Development | Comments

One of the problems with TG2 is that the current version doesn’t support a “standard” way to remember the user after he closes the browser. We have been able to find a quick and dirty solution that we like to share here. Keep in mind that this solution only works with recent versions of repoze.who, this works with TG2.0.3, but might not work with previous releases of TG2.

Inside the login.html we set a cookie for the remember_me option to pass it to the controller and then inside the post_login we change the cookie.

Supposing you have a #remember_me checkbox inside your login.html you can add this to set the cookie:

    function set_remember_cookie() {
        is_checked = jQuery('#remember_me:checked').length;
        if (is_checked)
            document.cookie = 'remember_me=1';
        else
            document.cookie = 'remember_me=0';
    }
 
    jQuery(document).ready(function() {
        set_remember_cookie();
        jQuery('#remember_me').click(set_remember_cookie);
    }

Then inside your post_login method in the root controller you can place:

        remember_me = request.cookies.get('remember_me', 0)
        try:
            remember_me = int(remember_me)
        except:
            remember_me = 0
 
        if remember_me:
            request.identity['max_age'] = 2252000 # 30 days
            request.identity['userdata']= "max_age" # force cookie refresh

This would remember the user for 30 days even if he closes the browser.

Tags: ,

Turbogears authentication over mongodb users database

Wednesday, March 3rd, 2010 | Software Development | Comments

As we saw that there isn’t a lot of documentation around about how to perform authentication in turbogears over mongodb we decided to create a simple code snippet and public it here to help people trying to obtain the same thing.

This is mainly a proof of concept and is quick and dirty way to obtain it. You will probably have something like ming as your model, instead of directly accessing mongo.

This code also validates password over the clear text one, you will probably have hashed passwords in your database, so remember to change validate_password method as required

To make it work you will have to place this code inside your config.app_cfg, it also expects you to have you database exposed as db inside your model

from my_app.model import db
from zope.interface import implements
from repoze.who.interfaces import IAuthenticator, IMetadataProvider
from repoze.who.plugins.friendlyform import FriendlyFormPlugin
from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
from repoze.who.middleware import PluggableAuthenticationMiddleware
 
def validate_password(user, password):
   return user['password'] == password
 
class MongoAuthenticatorPlugin(object):
    implements(IAuthenticator)
 
    # IAuthenticator
    def authenticate(self, environ, identity):
        if not ('login' in identity and 'password' in identity):
            return None
 
        login = identity.get('login')
        user = db.users.find_one({'user_name':login})
        if user and validate_password(user, identity.get('password')):
            return identity['login']
 
class MongoUserMDPlugin(object):
    implements(IMetadataProvider)
 
    def add_metadata(self, environ, identity):
        user_data = {'user_name':identity['repoze.who.userid']}
        identity['user'] = db.users.find_one(user_data)
 
class MyAppConfig(AppConfig):
    auth_backend = 'sqlalchemy' #this is a fake, but it's needed to enable
                                #auth middleware at least on TG2.0
 
    login_url = '/login'
    login_handler = '/login_handler'
    post_login_url = None
    logout_handler = '/logout_handler'
    post_logout_url = None
    login_counter_name = None
 
    def add_auth_middleware(self, app, skip_authentication):
        cookie_secret = pylons_config.get('auth_cookie_secret', 
                                          'myapp_adsfsdfh3423')
        cookie_name = pylons_config.get('auth_cookie_name', 
                                        'myapp_auth')
 
        who_args = {}
 
        form_plugin = FriendlyFormPlugin(self.login_url,
                              self.login_handler,
                              self.post_login_url,
                              self.logout_handler,
                              self.post_logout_url,
                              login_counter_name=self.login_counter_name,
                              rememberer_name='cookie')
        challengers = [('form', form_plugin)]
 
        auth = MongoAuthenticatorPlugin()
        authenticators = [('mongoauth', auth)]
 
        cookie = AuthTktCookiePlugin(cookie_secret, cookie_name)
 
        identifiers = [('cookie', cookie), ('form', form_plugin)]
 
        provider = MongoUserMDPlugin()
        mdproviders = [('mongoprovider', provider)]
 
        from repoze.who.classifiers import default_request_classifier
        from repoze.who.classifiers import default_challenge_decider
        log_stream = None
 
        app = PluggableAuthenticationMiddleware(app,
                                          identifiers,
                                          authenticators,
                                          challengers,
                                          mdproviders,
                                          default_request_classifier,
                                          default_challenge_decider)
 
        return app
 
base_config = MyAppConfig()
base_config.renderers = []
 
base_config.package = my_app
 
#Set the default renderer
base_config.default_renderer = 'genshi'
base_config.renderers.append('genshi')
base_config.renderers.append('json')
 
#Configure the base SQLALchemy Setup
base_config.use_sqlalchemy = False
base_config.model = my_app.model

Tags: , ,

ACR gets Slice Templates

Monday, March 1st, 2010 | Web | Comments

ACR is a flexible and quite powerfull CMS library, but users have to learn Slices and Slicegroups to be able to insert more advanced content like photo galleries, videos and a news section. To simplify this process we created “Slice Templates”.

Slice Templates are actually a set of common ways to use slices to insert more advanced content. The first two slice templates implemented are:

  • Youtube Videos which make easy to insert a youtube video inside an ACR Page.
  • Photo Gallery which makes easy to insert a photo gallery with cool effects inside an ACR Page.

Next template to come will be a news section, for now you can upgrade your libACR and start using the templates feature or take a look at the screenshots of the currently implemented templates

Tags: , ,

Turbogears Glossom finally live

Sunday, January 31st, 2010 | Web | Comments

We started to collaborate with the Glossom project more than an year ago, the first version was a Ruby on Rails prototype and has served well thousand of users for about an year. When the Glossom team decided to rewrite the software to move from a prototype to a more complete software we studied which frameworks were available and finally decided for Turbogears2.

Turbogears is a quite complete and really flexible framework, we used it since version 1.0 for about 3 years and the more we used it the more we loved it. It might have its problems but differently from other solutions when the framework has a limit it is really easy to bypass the framework and implement your own solution without any hack and with a clean way. Turbogears developers really focused on letting your tune each component in any way, and even totally replace it with something else if you don’t like it.

After six months of development the application has finally gone live and is now serving ~10000 registered users with its servers based on an apache load balancer, mod_wsgi turbogears application servers and mysql database servers.

So welcome to Glossom and thank you to Turbogears team for all the help!

We also have to say thank you to FFmpeg for now converting our videos and FlyPDF for generating our PDFs! :D (you can download FlyPDF from sourceforge if you want to give it a try)

Tags:

Rehearsing new ACR look and feel

Tuesday, November 24th, 2009 | Opensource, Web | Comments

As some turbogears projects are starting to use ACR as their CMS library we received the first few requests by real users and the most prominent one is to have a better administrative section. Currently administration section is implemented by using the great tgext.admin and sprox, even if those are really good to quickly implement a CRUD section they might not couple very well when a more interactive and advanced user experience is required.

So a transition phase that will end with a totally new administration section for ACR has been started, currently the system implements a new user interface still using the same backend as before to handle the operations, but on the long time the backend itself will be rewritten to handle easier contents creation and management. In the mean time also support for multi-language, versioning and authors has been added.

Tags: , ,

Injecting static content in TurboGears

Saturday, November 21st, 2009 | Web | Comments

Something that you usually need to do when providing a library or a reusable wsgi application is installing static data with the library itself.

This can be quickly performed by adding something like

package_data = {”:['*.html', '*.js', '*.css', '*.png']}

to your setup.py

But then how can we let our turbogears application serve that?

  • For html files (genshi templates) the solution is quite simple, you can just expose them by using @expose(’librarypackage.templatesdir.template’). For example supposing we are installing libcool with its templates in libcool/templates you can do @expose(’libcool.templates.index’)
  • For js and css files you can add them to your pages by creating a tw.api.JSLink or tw.api.CSSLink object. Just create inside your library something like: cool_js = tw.api.JSLink(modname = __name__, filename = ’static/cool.js’) and then place in the controller exposing the view where you want to use that js file cool_js.inject()
  • Exposing images can be more complex, you have to declare a widget which will render the img tag by using ToscaWidgets resources exposure.
    class IconLink(tw.api.Link):
        """
        A link to an icon.
        """
        template = """<img src="$link" alt="$alt" />"""
    
        params = dict(alt="Alternative text when not displaying the image")
    
        def __init__(self, *args, **kw):
            super(IconLink, self).__init__(*args, **kw)
            self.alt = kw.get('alt')

    then you can create one IconLink for each icon in your library with something like: parent = IconLink(modname=__name__, filename=’static/icons/parent.png’, alt=’Up’) and inside your views you can place the icon by doing ${parent.display()} . This will add the image by exposing it from inside the static directory of your library package. Remember that you need to add an __init__.py inside the static directory and its subdirectory if you want setuptools to correctly install the static files.

Tags: , ,

Using SwfUpload with TurboGears 2

Saturday, October 10th, 2009 | Web | Comments

SwfUpload doesn’t permit to upload things through authenticated methods, this is because it doesn’t pass the cookies needed to identify your users.

Partly this problem can be solved by using swfupload.cookies.js plugin. This plugins fetches all your cookies and passes them as POST arguments. This way you can get your authtkt cookie and use it to identify your user.

from webob.exc import *
from paste.auth import auth_tkt

if kw.has_key('authtkt'):
    #by default it is usually configured to do not use the remote address
    #otherwise you can fetch it from request.environ['REMOTE_ADDR']
    remote_addr = '0.0.0.0'

    #cookie secret is usually defined in your config/app_cfg.py
    #as base_config.sa_auth.cookie_secret or in your development.ini
    cookie_secret = "some_random_string_like_BQQP+BeyrTzTHClBCEdW"

    try:
        data = auth_tkt.parse_ticket(cookie_secret,
                                      kw.get('authtkt'),
                                      remote_addr)
        username = data[1]
        user = DBSession.query(User).filter_by(username=username).one()
    except:
        raise HTTPBadRequest

filename = kw['Filename']
file = kw['Filedata'].file

By using this code you can fetch the user that is uploading the file. This requires the method to do not use @require decorator to check for user permissions, as you will know the user only after entering the method. But you can create your own predicate if you really want to use @require.

Tags: , ,

Caching in TurboGears 2

Thursday, August 20th, 2009 | Web | Comments

TurboGears 2 has a quite good and complete caching support inherited from Pylons, as it is a pylons feature it is not really available by itself, but you can import it.

All you need is those three little things:

from pylons.decorators.cache import beaker_cache from pylons.controllers.util import etag_cache from pylons import cache

The first imports a decorator which makes possible to cache entire controller methods, the second imports a function to use client side caching and the third makes available a caching repository where to store whatever data you might want.

The easiest caching mechanism is etag_cache, this tells to the browser to use its own cached version of the page if it has any available instead of requesting it again to the server. etag_cache requires only one parameter: the caching key. By placing as the first instruction inside your controller action etag_cache(’mykey’) you will tell to the browser to use its own cached version if it has any. You can use the same key inside each action as the browser will check both for key and url, so different urls won’t collide. Keep in mind that this will let the browser keep using the cached version of the page until the browser won’t be restarted and this is usually something that you don’t want. To avoid this behaviour I suggest to keep changing the key argument constantly each time you want the cache to decay, using a timestamp as key might be a good idea.

For example you can add to your lib.base.BaseController.__call__ method something like

app_globals = tg.config['pylons.app_globals'] if app_globals.caching_key+datetime.timedelta(0,10) < datetime.datetime.now(): app_globals.caching_key = datetime.datetime.now() self.caching_key = str(app_globals.caching_key)

and then use etag_cache(self.caching_key) inside the controller action, this will let your cache expire every 10 seconds.

This might be enough in some situations where you want to completely cache your page, but often you might want to cache only your controller and render your view again. This can be achieved by using the @beaker_cache decorator. This will use Beaker to perform caching of the values returned by your controller, if it finds any available data for your controller it will return it without calling the controller method.

@expose()
@beaker_cache(expire=10)
def index(self):
    #Long and slow operation here
    return 'OK'

This way you will keep your action cached for 10 seconds and will cache different versions if the action parameters change.

For more complex things you might want to cache only parts of an action, this can be achieved by directly using the cache object.

c = cache.get_cache(’my_function’)
result = c.get_value(key=function_args, createfunc=slow_function, type=”memory”, expiretime=10)

This will get the caching namespace for the current function and will retrieve the available value with the given key if available (you might see that I have called the key “function_args”, this is because it is usually a good idea to build the key by using function arguments that have any effect on the result). If it isn’t found any value (or the value has expired) slow_function will be called to calculate the new value.

New versions of beaker have a nice decorator @cache.cache which prevent you from having to get the cache namespace and the cache value by yourself, by applying @cache.cache to slow_function each call to slow_function will return the available cached value by itself. More information can be found on the relative beaker documentation section. Keep in mind that @cache.cache decorator can only be used by passing arguments as a list, it won’t work for keyword arguments.

Tags: , ,

Search