2015-02-03

Kotti CMS - how to create a new content type with an image

If you want to create a new content type based on an existing one with Kotti you need to write few lines of code and zero html for the add and edit views: it is very simple (browse Kotti's resources.py and views code).


Basically you have to extend the existing content type shipped with Kotti and add your custom fields.

But let's suppose you need a new content type named ImageWithLink with the following fields:
  • title
  • description
  • image
  • link
In this case the implementation is more verbose compared to extend another content type (like the Document, but it is still an easy job).

resources.py
from zope.interface import implements
from kotti.resources import Image
from kotti.interfaces import IImage
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import Unicode


class ImageWithLink(Image):
    implements(IImage)

    id = Column(Integer, ForeignKey('images.id'), primary_key=True)
    link = Column(Unicode(1000))

    type_info = Image.type_info.copy(
        name=u'ImageWithLink',
        title=u'ImageWithLink',
        add_view=u'add_image_link',
        addable_to=['Document'],
        )

    def __init__(self, link=u"", **kwargs):
        super(ImageWithLink, self).__init__(**kwargs)
        self.link = link 
The code is quite self-explaining: you create a new ImageWithLink class that inherits from Image. You only need to add your custom field named link and you initialize the link in the __init__ code after calling the super method.

views/content.py
import colander
from deform import FileData
from deform.widget import FileUploadWidget
from kotti.views.edit import ContentSchema
from kotti.views.edit.content import ImageEditForm
from kotti.views.edit.content import ImageAddForm
from kotti.views.form import validate_file_size_limit
from kotti.views.form import FileUploadTempStore
from kotti.views.form import AddFormView
from pyramid.view import view_config
from kotti_yourplugin import _
from kotti_yourplugin.resources import ImageWithLink
from kotti_yourplugin.validators import link_validator


def ImageWithLinkSchema(tmpstore):
    """ File schema with no set title missing binding """
    class ImageWithLinkSchema(ContentSchema):
        file = colander.SchemaNode(
            FileData(),
            title=_(u'File'),
            widget=FileUploadWidget(tmpstore),
            validator=validate_file_size_limit,
            )
        link = colander.SchemaNode(
            colander.String(),
            title=_('Link'),
            validator=link_validator,
            missing=u'',
            )

    def after_bind(node, kw):
        del node['tags']

    return ImageWithLinkSchema(after_bind=after_bind)


@view_config(name='edit', permission='edit',
             renderer='kotti:templates/edit/node.pt')
class ImageWithLinkEditForm(ImageEditForm):
    def schema_factory(self):
        tmpstore = FileUploadTempStore(self.request)
        return ImageWithLinkSchema(tmpstore)


@view_config(name=ImageWithLink.type_info.add_view, permission='add',
             renderer='kotti:templates/edit/node.pt')
class ImageWithLinkAddForm(ImageAddForm):
    item_type = _(u"Banner Box")
    item_class = ImageWithLink

    def schema_factory(self):
        tmpstore = FileUploadTempStore(self.request)
        return ImageWithLinkSchema(tmpstore)

    def save_success(self, appstruct):
        # override this method (no filename as title
        # like images)
        return AddFormView.save_success(self, appstruct)

    def add(self, **appstruct):
        # override (no tags in our form)
        buf = appstruct['file']['fp'].read()
        filename = appstruct['file']['filename']
        return self.item_class(
            title=appstruct['title'] or filename,
            description=appstruct['description'],
            data=buf,
            filename=filename,
            mimetype=appstruct['file']['mimetype'],
            size=len(buf),
            )
Here the code is more complex. There is a dynamic schema definition with the Kotti's temp store implementation. Both the add and the edit form refer to this schema, with some overrides because our object does not behave like files or images.

validators.py
UPDATE 20150211: no need to write this validator. Use the url validator provided by colander instead (colander.url). Anyway you can use all the builtin colander validators or write your own validators.

import re
import colander
from kotti_yourplugin import _


VALID_PROTOCOLS = ('http',)
URL_REGEXP = r'(%s)s?://[^\s\r\n]+' % '|'.join(VALID_PROTOCOLS)


def link_validator(node, value):
    """ Raise a colander.Invalid exception if the provided url
        is not valid
    """
    def raise_invalid_url(node, value):
        raise colander.Invalid(
            node, _(u"You must provide a valid url."))
    if value:
        if not re.match(URL_REGEXP, value):
            raise_invalid_url(node, value)
Here you can see an example of link validator based on a regular expression. This validator decorates our link field of the ImageWithLink schema.

Obviously you need to add in your kotti_configure method your ImageWithLink in the kotti.available_types settings.

__init__.py
def kotti_configure(settings):
    settings['pyramid.includes'] += ' kotti_yourplugin'
    settings['kotti.available_types'] += ' kotti_yourplugin.resources.ImageWithLink'

and enable your configurator in your .ini file:
kotti.configurators =     mip_course.kotti_configure

And what about the default view of your content types? If you visit an ImageWithLink box it will behave like an image: it inherits the default view of the image (you should customize it adding the link on the image, very simple: not showed in this blog post), no need to deal with the image resize machinery, etc.

As you can see, Kotti is a flexible solution if you need a simple but powerful CMS solution based on Python, Pyramid and SQLAlchemy. You may consider it as a simple framework (but easy to understand, don't be scared by the word framework. It is really developer friendly). If you are curious about how to manage contents with Kotti you may play with the demo online: http://kottidemo.danielnouri.org/ (admin - qwerty).

All posts about Kotti

All Kotti posts published by @davidemoro:


No comments:

Post a Comment

Note: only a member of this blog may post a comment.