SilverFlask

SilverFlask aims to be the first full-featured CMS building on top of solid foundations such as Flask, SQLAlchemy, Jinja2, WTForms and a huge number of plugins built on top of these such as SQLAlchemy-continuum, Flask-User, Flask-Login and countless others.

It wants to be an environment that offers the whole slew of power experienced developers are accustomed to while provide an easy platform for.

SilverFlask is partly named after SilverStripe, one of the best and fully-featured CMS’s around in PHP land. However, for my taste it had a few shortcomings. Of course, PHP as a programming language is multitudes less pleasant than python. And unfortunately, SilverStripe is built around a framework that is more or less made only for the CMS. I personally don’t like this tight coupling as the framework has not gotten much exposure outside of the SilverStripe CMS world. On the other hand, Flask is a very mature framework, and python is a lovely programming language.

Installation

Warning

SilverFlask is currently in a pre-alpha-stealth state. Don’t use it for anything serious. That said, feel free to experiment with it as much as you’d like to!

  • Clone repo: git clone https://github.com/wolfv/SilverFlask

  • Create a virtualenv with Python 3 (the future is now):
    • Linux: export VIRTUALENV_PYTHON=/usr/bin/python3
    • OSX: export VIRTUALENV_PYTHON=/usr/local/bin/python3 on OS X with Homebrew
    • I like virtualenvwrapper, instructions for Ubuntu here: Link
    • Toggle the virtualenv with workon <yourvirtualenvname>
  • When in the virtualenv, install all necessary packages via pip install -r requirements

  • Use the manage.py script to create the database (defaults to an sqlite database.db in the app folder: python manage.py createdb

  • Start the application server by entering python manage.py runserver

  • Point your webbrowser to http://localhost:5000 to visit your first SilverFlask website

  • Login as admin:admin on http://localhost:5000/admin

The basic building blocks

SilverFlask defines a number of basic building blocks for websites.

User

The user model for SilverFlask is already defined and wired up with Flask-Login and Flask-User to provide the whole array of features that you would come to expect from a modern web-application.

Query the current user with from silverflask.core import current_user

A Permission model is also included to provide fine-grained access control if needed in silverflask.models.Role.

Tutorial

This tutorial is trying to give a quick introduction in how to create your first website with SilverFlask.

Creating a simple page type

First, we will create a basic page type for our website. Therefore we need to subclass silverflask.models.SiteTree:

from silverflask.models import SiteTree

class SimplePage(SiteTree):
    db = {
        "content": "UnicodeText"
    }

Now you should point your browser to localhost:5000/admin/dev/build to automatically generate the necessary database entries for you.

When you open admin and check the “Add Pages” menu, you will see our SimplePage as one of the available page types. You can even start editing the page, save and publish it and it should render just fine in the default template. Note that all of this functionality comes from the SiteTree

This is because we chose the name content for our db field, which is also the standard name for the main content which is used in the default template.

We can also create a little script to create a new instance of this page:

from silverflask import db

sp = SimplePage()
sp.name = "What a beautiful title"
sp.content = "Interesting Content"
db.session.add(sp)
db.session.commit(sp)

This example also shows how the variables defined in db are added to the class namespace and are accessible through their names.

All column types from sqlalchemy are available, such as

  • Integer
  • Text
  • String (text, but with a maximum length)
  • Boolean
  • Date, and DateTime
  • ... (check sqla_types. for more)

Extending the page further

Suppose we want to add a cute little header image to the page, we can do that easily by adding a has_one relation to the SimplePage. The relation will point to the silverflask.models.ImageObject.

from silverflask.models import SiteTree

class SimplePage(SiteTree):
    db = {
        "content": "UnicodeText"
    }
    has_one = {
        "header_image": "ImageObject"
    }

The necessary database columns for this relation are automatically added to the model.

Now you might wonder how to access this image – it’s easy. Relations are added to the object in the same way as the database columns. So using

s.image

Will return you the image object that is related to the SimplePage. There are actually several interesting functions defined on the silverflask.models.ImageObject which you can utilize e.g. in the template.

Reference

DataObject

class silverflask.models.DataObject.DataObject

The DataObject class is the basic building block of any CMS Element. It is a mixin that provides three basic database columns:

Attributes:

Variables:
  • id – Primary key, integer id (use for joins and relationships)
  • created_on – the datetime when the DataObject was created
  • last_modified – the datetime when the DataObject was last modified
as_dict()

Get object as dict. Very useful for json responses. :return: dict with all database columns

classmethod get_cms_form()

Build and return Form class.

If you want to define your custom CMS Object, you likely want to override the default CMS Form. E.g.:

from wtforms import fields
def get_cms_form(cls):
    form = super().get_cms_form()
    form.textfield = fields.StringField("Textfield")
    return form
Returns:Form Class (has to be instantiated!).

SiteTree

class silverflask.models.SiteTree.SiteTree

The SiteTree is the database model from which all pages have to inherit. It defines the parent/children relationships of the page tree. It also defines everything that’s needed to get nice URL slugs working.

Mixins

Silverflask defines a number of mixins that can be utilized to enhance DataObjects (and are used in the SiteTree class for example).

class silverflask.mixins.OrderableMixin.OrderableMixin

A mixin that makes a DataObject sortable by adding a sort_order database field. Classes with this mixin automatically keep the sort_order in sync.

classmethod check_duplicates()

Check the table for duplicates and if there are duplicates reindex :return: nothing

init_order()

Sort element to the end

insert_after(index, orderable_base_class=None, index_absolute=True, query=None)

Inser after index variable

Parameters:
  • index – index (this is the sort_order variable of the element that you want to insert after!)
  • orderable_base_class – baseclass (useful in certain circumstances e.g. gridfields)
Returns:

nothing

move_after(obj)

Move current DataObject after index (= sort_order) of another element

Parameters:index – obj element where to move after or sort order of other elements
Returns:nothing
classmethod reindex()

Reindexes the table.

The sort order field can have “jumps” in it (e.g. 1, 4, 5, 8, 9) and reindex brings that back to a linearly ascending order: (1,2,3,4...)

class silverflask.mixins.VersionedMixin.VersionedMixin

A mixin that adds versioning support to DataObjects. It adds a new live table that saves the object on a publish operation, a new query_live operator that queries the live versions of the object and a versions relationship that saves all versions of this object (a version is automatically created on a save operation).

can_publish()

Override this function to control who is allowed to publish pages in the CMS :return: Boolean (True if allowed to publish)

mark_as_published()

Create a copy of the current draft to the live table and publish this DataObject (make it visible to the outside world). :return: empty

User

class silverflask.models.User.User(username, password, email=None, is_enabled=True)

The base User model. Defines the following fields:

username = String, and unique, firstname, lastname = String

email = String, unique

The password is set with user.set_password(“password”), and then stored with encryption.

FileObject

The file objects reflect the file items uploaded through the CMS. ImageObject is an inherited class which implements common image functionality, such as cropping and resizing.

class silverflask.models.FileObject.FileObject(file=None, location=None, folder=None)

Contains file information

Variables:
  • location – Location of the file
  • name – Name of the file (usually filename without extension)
  • type – Contains
delete_file()

Delete this file also from disk.

url()

Return the url for this file (handled by the FileStorageBackend

class silverflask.models.FileObject.ImageObject(file=None, location=None, folder=None)

A basic ImageObject, that inherits all properties from the file object and adds some functionality related to images, such as cropping and resizing, as well as caching the cropped and/or resized images.

resize(width=None, height=None, mode='crop', background='white')

Resize image

Parameters:
  • width – define width in pixels
  • height – height in pixels. If no height is set, it will be set to width (i.e. the result will be a square)
  • mode – string, one of ‘crop’ (default), ‘pad’, ‘fit’ or ‘reshape’
  • background – background color, as string (i.e. ‘blue’ or hex ‘#F0F000’). As supported by the ImageColor module of Pillow.

FileStorageBackend

The FileStorageBackend is a class that abstracts away the underlying file storage. At the moment, the only implemented backend is the LocalFileStorageBackend which implements methods to save files to the local flask installation (defaults to the /static/uploads/ folder).

Another FileStorageBackend could, for example, be an implementation for a S3 Backend (e.g. using the boto library).