summaryrefslogtreecommitdiffstats
path: root/manas
diff options
context:
space:
mode:
authorLuke Macken <lmacken@redhat.com>2008-06-27 21:43:09 -0400
committerLuke Macken <lmacken@redhat.com>2008-06-27 21:43:09 -0400
commitef6aa096bbb8f52c2bad76f3b086167db326da89 (patch)
tree022e2363dbfb8879948d89cfcc7ae1f2f53b64a0 /manas
downloadmanas-ef6aa096bbb8f52c2bad76f3b086167db326da89.tar.gz
manas-ef6aa096bbb8f52c2bad76f3b086167db326da89.tar.xz
manas-ef6aa096bbb8f52c2bad76f3b086167db326da89.zip
Initial TG2 quickstart
Diffstat (limited to 'manas')
-rw-r--r--manas/__init__.py0
-rw-r--r--manas/config/__init__.py0
-rw-r--r--manas/config/app_cfg.py25
-rw-r--r--manas/config/environment.py5
-rw-r--r--manas/config/middleware.py14
-rw-r--r--manas/controllers/__init__.py0
-rw-r--r--manas/controllers/error.py41
-rw-r--r--manas/controllers/root.py38
-rw-r--r--manas/controllers/secc.py26
-rw-r--r--manas/controllers/template.py27
-rw-r--r--manas/i18n/ru/LC_MESSAGES/manas.po24
-rw-r--r--manas/lib/__init__.py0
-rw-r--r--manas/lib/app_globals.py13
-rw-r--r--manas/lib/base.py67
-rw-r--r--manas/lib/helpers.py0
-rw-r--r--manas/model/__init__.py45
-rw-r--r--manas/model/identity.py161
-rw-r--r--manas/model/model.template24
-rw-r--r--manas/public/css/style.css206
-rw-r--r--manas/public/favicon.icobin0 -> 1104 bytes
-rw-r--r--manas/public/images/error.pngbin0 -> 1471 bytes
-rw-r--r--manas/public/images/grad_blue_7x80.pngbin0 -> 3346 bytes
-rw-r--r--manas/public/images/grad_orange_11x100.pngbin0 -> 3136 bytes
-rw-r--r--manas/public/images/header_inner.pngbin0 -> 37537 bytes
-rw-r--r--manas/public/images/info.pngbin0 -> 1672 bytes
-rw-r--r--manas/public/images/logo.gifbin0 -> 23864 bytes
-rw-r--r--manas/public/images/logo.pngbin0 -> 20663 bytes
-rw-r--r--manas/public/images/ok.pngbin0 -> 1519 bytes
-rw-r--r--manas/public/images/star.pngbin0 -> 2332 bytes
-rw-r--r--manas/public/images/strype.pngbin0 -> 1378 bytes
-rw-r--r--manas/public/images/tg2_04.gifbin0 -> 22775 bytes
-rw-r--r--manas/public/images/tg_under_the_hood.pngbin0 -> 4010 bytes
-rw-r--r--manas/public/images/under_the_hood_blue.pngbin0 -> 2667 bytes
-rw-r--r--manas/templates/__init__.py0
-rw-r--r--manas/templates/about.html66
-rw-r--r--manas/templates/debug.html20
-rw-r--r--manas/templates/footer.html15
-rw-r--r--manas/templates/header.html17
-rw-r--r--manas/templates/index.html42
-rw-r--r--manas/templates/login.html23
-rw-r--r--manas/templates/master.html28
-rw-r--r--manas/templates/sidebars.html33
-rw-r--r--manas/tests/__init__.py40
-rw-r--r--manas/tests/functional/__init__.py0
-rw-r--r--manas/tests/test_models.py0
-rw-r--r--manas/websetup.py54
46 files changed, 1054 insertions, 0 deletions
diff --git a/manas/__init__.py b/manas/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/__init__.py
diff --git a/manas/config/__init__.py b/manas/config/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/config/__init__.py
diff --git a/manas/config/app_cfg.py b/manas/config/app_cfg.py
new file mode 100644
index 0000000..3f9da94
--- /dev/null
+++ b/manas/config/app_cfg.py
@@ -0,0 +1,25 @@
+from tg.config import AppConfig, Bunch
+from tg.environment import make_load_environment
+import manas
+from manas import model
+from manas.lib import app_globals, helpers
+
+base_config = AppConfig()
+base_config.renderers = []
+
+base_config.package = manas
+
+#Set the default renderer
+base_config.default_renderer = 'genshi'
+base_config.renderers.append('genshi')
+
+#Configure the base SQLALchemy Setup
+base_config.use_sqlalchemy = True
+base_config.model = manas.model
+#Configure the authentication backend
+base_config.auth_backend = 'sqlalchemy'
+base_config.sa_auth = Bunch()
+base_config.sa_auth.dbsession = model.DBSession
+base_config.sa_auth.user = model.User
+base_config.sa_auth.user_criterion = model.User.user_name
+base_config.sa_auth.user_id_column = 'user_id'
diff --git a/manas/config/environment.py b/manas/config/environment.py
new file mode 100644
index 0000000..d617357
--- /dev/null
+++ b/manas/config/environment.py
@@ -0,0 +1,5 @@
+from tg.environment import make_load_environment
+from manas.config.app_cfg import base_config
+
+#Use base_config to setup the environment loader function
+load_environment = make_load_environment(base_config)
diff --git a/manas/config/middleware.py b/manas/config/middleware.py
new file mode 100644
index 0000000..8656e8e
--- /dev/null
+++ b/manas/config/middleware.py
@@ -0,0 +1,14 @@
+"""Pylons middleware initialization"""
+from tg.middleware import setup_tg_wsgi_app
+from manas.config.app_cfg import base_config
+from manas.config.environment import load_environment
+
+#Use base_config to setup the nessisary WSGI App factory.
+#make_base_app will wrap the TG2 app with all the middleware it needs.
+make_base_app = setup_tg_wsgi_app(load_environment, base_config)
+
+def make_app(global_conf, full_stack=True, **app_conf):
+ app = make_base_app(global_conf, full_stack=True, **app_conf)
+ #Wrap your base turbogears app with custom middleware
+ return app
+ \ No newline at end of file
diff --git a/manas/controllers/__init__.py b/manas/controllers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/controllers/__init__.py
diff --git a/manas/controllers/error.py b/manas/controllers/error.py
new file mode 100644
index 0000000..4a5dd8e
--- /dev/null
+++ b/manas/controllers/error.py
@@ -0,0 +1,41 @@
+import os.path
+
+import paste.fileapp
+from pylons import request
+from pylons.controllers.util import forward
+from pylons.middleware import error_document_template, media_path
+
+from manas.lib.base import BaseController
+
+class ErrorController(BaseController):
+ """Generates error documents as and when they are required.
+
+ The ErrorDocuments middleware forwards to ErrorController when error
+ related status codes are returned from the application.
+
+ This behaviour can be altered by changing the parameters to the
+ ErrorDocuments middleware in your config/middleware.py file.
+ """
+
+ def document(self):
+ """Render the error document"""
+ resp = request.environ.get('pylons.original_response')
+ page = error_document_template % \
+ dict(prefix=request.environ.get('SCRIPT_NAME', ''),
+ code=request.params.get('code', resp.status_int),
+ message=request.params.get('message', resp.body))
+ return page
+
+ def img(self, id):
+ """Serve stock images"""
+ return self._serve_file(os.path.join(media_path, 'img', id))
+
+ def style(self, id):
+ """Serve stock stylesheets"""
+ return self._serve_file(os.path.join(media_path, 'style', id))
+
+ def _serve_file(self, path):
+ """Call Paste's FileApp (a WSGI application) to serve the file
+ at the specified path
+ """
+ return forward(paste.fileapp.FileApp(path))
diff --git a/manas/controllers/root.py b/manas/controllers/root.py
new file mode 100644
index 0000000..afd47c1
--- /dev/null
+++ b/manas/controllers/root.py
@@ -0,0 +1,38 @@
+"""Main Controller"""
+from manas.lib.base import BaseController
+from tg import expose, flash
+from pylons.i18n import ugettext as _
+#from tg import redirect, validate
+#from manas.model import DBSession, metadata
+#from dbsprockets.dbmechanic.frameworks.tg2 import DBMechanic
+#from dbsprockets.saprovider import SAProvider
+from tg.ext.repoze.who import authorize
+from manas.controllers.secc import Secc
+
+class RootController(BaseController):
+ #admin = DBMechanic(SAProvider(metadata), '/admin')
+ secc = Secc()
+
+ @expose('manas.templates.index')
+ def index(self):
+ return dict(page='index')
+
+ @expose('manas.templates.about')
+ def about(self):
+ return dict(page='about')
+
+ @expose('manas.templates.index')
+ @authorize.require(authorize.has_permission('manage'))
+ def manage_permission_only(self, **kw):
+ return dict(page='managers stuff')
+
+ @expose('manas.templates.index')
+ @authorize.require(authorize.is_user('editor'))
+ def editor_user_only(self, **kw):
+ return dict(page='editor stuff')
+
+ @expose('manas.templates.login')
+ def login(self, **kw):
+ came_from = kw.get('came_from', '/')
+ return dict(page='login', header=lambda *arg: None,
+ footer=lambda *arg: None, came_from=came_from)
diff --git a/manas/controllers/secc.py b/manas/controllers/secc.py
new file mode 100644
index 0000000..98137f0
--- /dev/null
+++ b/manas/controllers/secc.py
@@ -0,0 +1,26 @@
+"""Test Secure Controller"""
+from manas.lib.base import BaseController, SecureController
+from tg import expose, flash
+from pylons.i18n import ugettext as _
+#from tg import redirect, validate
+#from manas.model import DBSession, metadata
+#from dbsprockets.dbmechanic.frameworks.tg2 import DBMechanic
+#from dbsprockets.saprovider import SAProvider
+from tg.ext.repoze.who import authorize
+
+
+class Secc(SecureController):
+
+ require = authorize.has_permission('manage')
+
+ @expose('manas.templates.index')
+ def index(self):
+ flash(_("Secure Controller here"))
+ return dict(page='index')
+
+ @expose('manas.templates.index')
+ def some_where(self):
+ """should be protected because of the require attr
+ at the controller level.
+ """
+ return dict()
diff --git a/manas/controllers/template.py b/manas/controllers/template.py
new file mode 100644
index 0000000..4d41453
--- /dev/null
+++ b/manas/controllers/template.py
@@ -0,0 +1,27 @@
+from ${package}.lib.base import *
+
+class TemplateController(BaseController):
+
+ def view(self, url):
+ """By default, the final controller tried to fulfill the request
+ when no other routes match. It may be used to display a template
+ when all else fails, e.g.::
+
+ def view(self, url):
+ return render('/%s' % url)
+
+ Or if you're using Mako and want to explicitly send a 404 (Not
+ Found) response code when the requested template doesn't exist::
+
+ import mako.exceptions
+
+ def view(self, url):
+ try:
+ return render('/%s' % url)
+ except mako.exceptions.TopLevelLookupException:
+ abort(404)
+
+ By default this controller aborts the request with a 404 (Not
+ Found)
+ """
+ abort(404)
diff --git a/manas/i18n/ru/LC_MESSAGES/manas.po b/manas/i18n/ru/LC_MESSAGES/manas.po
new file mode 100644
index 0000000..36532d4
--- /dev/null
+++ b/manas/i18n/ru/LC_MESSAGES/manas.po
@@ -0,0 +1,24 @@
+# Russian translations for ${package}.
+# Copyright (C) 2008 ORGANIZATION
+# This file is distributed under the same license as the ${package} project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ${package} 0.0.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-01-13 14:00+0200\n"
+"PO-Revision-Date: 2008-01-13 14:00+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ru <LL@li.org>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.1\n"
+
+#: ${package}/controllers/root.py:13
+msgid "Your application is now running"
+msgstr "Ваши приложение успешно запущено"
+
diff --git a/manas/lib/__init__.py b/manas/lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/lib/__init__.py
diff --git a/manas/lib/app_globals.py b/manas/lib/app_globals.py
new file mode 100644
index 0000000..342c8cb
--- /dev/null
+++ b/manas/lib/app_globals.py
@@ -0,0 +1,13 @@
+"""The application's Globals object"""
+
+class Globals(object):
+ """Globals acts as a container for objects available throughout the
+ life of the application
+ """
+
+ def __init__(self):
+ """One instance of Globals is created during application
+ initialization and is available during requests via the 'g'
+ variable
+ """
+ pass
diff --git a/manas/lib/base.py b/manas/lib/base.py
new file mode 100644
index 0000000..d27284a
--- /dev/null
+++ b/manas/lib/base.py
@@ -0,0 +1,67 @@
+"""The base Controller API
+
+Provides the BaseController class for subclassing.
+"""
+from tg import TGController, tmpl_context
+from pylons.templating import render_genshi as render
+from pylons import request
+
+import manas.model as model
+
+from pylons.i18n import _, ungettext, N_
+from tw.api import WidgetBunch
+
+class Controller(object):
+ """Base class for a web application's controller.
+
+ Currently, this provides positional parameters functionality
+ via a standard default method.
+ """
+
+class BaseController(TGController):
+ """Base class for the root of a web application.
+
+ Your web application should have one of these. The root of
+ your application is used to compute URLs used by your app.
+ """
+
+ def __call__(self, environ, start_response):
+ """Invoke the Controller"""
+ # TGController.__call__ dispatches to the Controller method
+ # the request is routed to. This routing information is
+ # available in environ['pylons.routes_dict']
+
+ # Create a container to send widgets to the template. Only those sent
+ # in here will have their resources automatically included in the
+ # template
+ try:
+ return TGController.__call__(self, environ, start_response)
+ finally:
+ #after everything is done clear out the Database Session
+ #to eliminate possible cross request DBSession polution.
+ model.DBSession.remove()
+ tmpl_context.identity = request.environ.get('repoze.who.identity')
+
+class SecureController(BaseController):
+ """this is a SecureController implementation for the
+ tg.ext.repoze.who plugin.
+ it will permit to protect whole controllers with a single predicate
+ placed at the controller level.
+ The only thing you need to have is a 'require' attribute which must
+ be a callable. This callable will only be authorized to return True
+ if the user is allowed and False otherwise. This may change to convey info
+ when securecontroller is fully debugged...
+ """
+
+ def check_security(self):
+ errors = []
+ environ = request.environ
+ identity = environ.get('repoze.who.identity')
+ if not hasattr(self, "require") or \
+ self.require is None or \
+ self.require.eval_with_object(identity, errors):
+ return True
+
+ # if we did not return this is an error :)
+ # TODO: do something with the errors variable like informing our user...
+ return False
diff --git a/manas/lib/helpers.py b/manas/lib/helpers.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/lib/helpers.py
diff --git a/manas/model/__init__.py b/manas/model/__init__.py
new file mode 100644
index 0000000..f98adc6
--- /dev/null
+++ b/manas/model/__init__.py
@@ -0,0 +1,45 @@
+"""The application's model objects"""
+
+from sqlalchemy.orm import scoped_session, sessionmaker
+from sqlalchemy import MetaData
+
+
+# Global session manager. DBSession() returns the session object
+# appropriate for the current web request.
+DBSession = scoped_session(sessionmaker(autoflush=True, transactional=True))
+
+# Global metadata. If you have multiple databases with overlapping table
+# names, you'll need a metadata for each database.
+metadata = MetaData()
+
+#####
+# Generally you will not want to define your table's mappers, and data objects
+# here in __init__ but will want to create modules them in the model directory
+# and import them at the bottom of this file.
+#
+######
+
+def init_model(engine):
+ """Call me before using any of the tables or classes in the model."""
+
+ # If you are using reflection to introspect your database and create
+ # table objects for you, your tables must be defined and mapped inside
+ # the init_model function, so that the engine is available if you
+ # use the model outside tg2, you need to make sure this is called before
+ # you use the model.
+
+ #
+ # See the following example:
+
+ #global t_reflected
+
+ #t_reflected = Table("Reflected", metadata,
+ # autoload=True, autoload_with=engine)
+
+ #mapper(Reflected, t_reflected)
+
+# Import your model modules here.
+from identity import User, Group, Permission
+from identity import users_table, groups_table, permissions_table, \
+ user_group_table, group_permission_table
+
diff --git a/manas/model/identity.py b/manas/model/identity.py
new file mode 100644
index 0000000..1efcd51
--- /dev/null
+++ b/manas/model/identity.py
@@ -0,0 +1,161 @@
+from pylons import config
+from sqlalchemy import *
+from sqlalchemy.orm import mapper, relation
+from manas.model import metadata
+
+import datetime
+from sqlalchemy import ForeignKey
+import md5
+import sha
+
+groups_table = Table('tg_group', metadata,
+ Column('group_id', Integer, primary_key=True),
+ Column('group_name', Unicode(16), unique=True),
+ Column('display_name', Unicode(255)),
+ Column('created', DateTime, default=datetime.datetime.now)
+)
+
+users_table = Table('tg_user', metadata,
+ Column('user_id', Integer, primary_key=True),
+ Column('user_name', Unicode(16), unique=True),
+ Column('email_address', Unicode(255), unique=True),
+ Column('display_name', Unicode(255)),
+ Column('password', Unicode(40)),
+ Column('created', DateTime, default=datetime.datetime.now)
+)
+
+permissions_table = Table('tg_permission', metadata,
+ Column('permission_id', Integer, primary_key=True),
+ Column('permission_name', Unicode(16), unique=True),
+ Column('description', Unicode(255))
+)
+
+user_group_table = Table('tg_user_group', metadata,
+ Column('user_id', Integer, ForeignKey('tg_user.user_id',
+ onupdate="CASCADE", ondelete="CASCADE")),
+ Column('group_id', Integer, ForeignKey('tg_group.group_id',
+ onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+group_permission_table = Table('tg_group_permission', metadata,
+ Column('group_id', Integer, ForeignKey('tg_group.group_id',
+ onupdate="CASCADE", ondelete="CASCADE")),
+ Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
+ onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# identity model
+class Group(object):
+ """An ultra-simple group definition.
+ """
+ def __repr__(self):
+ return '<Group: name=%s>' % self.group_name
+
+class User(object):
+ """Reasonably basic User definition. Probably would want additional
+ attributes.
+ """
+ def __repr__(self):
+ return '<User: email="%s", display name="%s">' % (
+ self.email_address, self.display_name)
+
+ def permissions(self):
+ perms = set()
+ for g in self.groups:
+ perms = perms | set(g.permissions)
+ return perms
+ permissions = property(permissions)
+
+ def by_email_address(klass, email):
+ """A class method that can be used to search users
+ based on their email addresses since it is unique.
+ """
+ session = DBSession()
+ return session.query(klass).filter(klass.email_address==email).first()
+
+ by_email_address = classmethod(by_email_address)
+
+ def by_user_name(klass, username):
+ """A class method that permits to search users
+ based on their user_name attribute.
+ """
+ session = DBSession()
+ return session.query(klass).filter(klass.user_name==username).first()
+
+ by_user_name = classmethod(by_user_name)
+
+ def _set_password(self, password):
+ """encrypts password on the fly using the encryption
+ algo defined in the configuration
+ """
+ algorithm = config.get('authorize.hashmethod', None)
+ self._password = self.__encrypt_password(algorithm, password)
+
+ def _get_password(self):
+ """returns password
+ """
+ return self._password
+
+ password = property(_get_password, _set_password)
+
+ def __encrypt_password(self, algorithm, password):
+ """Hash the given password with the specified algorithm. Valid values
+ for algorithm are 'md5' and 'sha1'. All other algorithm values will
+ be essentially a no-op."""
+ hashed_password = password
+
+ if isinstance(password, unicode):
+ password_8bit = password.encode('UTF-8')
+
+ else:
+ password_8bit = password
+
+ if "md5" == algorithm:
+ hashed_password = md5.new(password_8bit).hexdigest()
+
+ elif "sha1" == algorithm:
+ hashed_password = sha.new(password_8bit).hexdigest()
+
+ # TODO: re-add the possibility to provide own hasing algo
+ # here... just get the real config...
+
+ #elif "custom" == algorithm:
+ # custom_encryption_path = turbogears.config.get(
+ # "identity.custom_encryption", None )
+ #
+ # if custom_encryption_path:
+ # custom_encryption = turbogears.util.load_class(
+ # custom_encryption_path)
+
+ # if custom_encryption:
+ # hashed_password = custom_encryption(password_8bit)
+
+ # make sure the hased password is an UTF-8 object at the end of the
+ # process because SQLAlchemy _wants_ a unicode object for Unicode columns
+ if not isinstance(hashed_password, unicode):
+ hashed_password = hashed_password.decode('UTF-8')
+
+ return hashed_password
+
+ def validate_password(self, password):
+ """Check the password against existing credentials.
+ """
+ algorithm = config.get('authorize.hashmethod', None)
+ return self.password == self.__encrypt_password(algorithm, password)
+
+class Permission(object):
+ """A relationship that determines what each Group can do
+ """
+ pass
+
+
+mapper(User, users_table,
+ properties=dict(_password=users_table.c.password))
+
+mapper(Group, groups_table,
+ properties=dict(users=relation(User,
+ secondary=user_group_table, backref='groups')))
+
+mapper(Permission, permissions_table,
+ properties=dict(groups=relation(Group,
+ secondary=group_permission_table, backref='permissions')))
diff --git a/manas/model/model.template b/manas/model/model.template
new file mode 100644
index 0000000..2cf9c40
--- /dev/null
+++ b/manas/model/model.template
@@ -0,0 +1,24 @@
+from pylons import config
+from sqlalchemy import *
+from sqlalchemy.orm import mapper, relation
+from manas.model import metadata
+
+# Normal tables may be defined and mapped at module level.
+
+foo_table = Table("Foo", metadata,
+ Column("id", types.Integer, primary_key=True),
+ Column("bar", types.String(255), nullable=False),
+ )
+
+class Foo(object):
+ def __init__(self, **kw):
+ """automatically mapping attributes"""
+ for key, value in kw.iteritems():
+ setattr(self, key, value)
+
+mapper(Foo, foo_table)
+
+Classes for reflected tables may be defined here, but the table and
+mapping itself must be done in the init_model function.
+
+
diff --git a/manas/public/css/style.css b/manas/public/css/style.css
new file mode 100644
index 0000000..4da3dd8
--- /dev/null
+++ b/manas/public/css/style.css
@@ -0,0 +1,206 @@
+/*
+ * Quick mash-up of CSS for the TG quick start page.
+ */
+
+html, body {
+ color: black;
+ background-color: #ddd;
+ font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif;
+ font-size: 83%;
+ margin: 0;
+ padding: 0;
+}
+
+td, th {padding:3px;border:none;}
+tr th {text-align:left;background-color:#f0f0f0;color:#333;}
+tr.odd td {background-color:#edf3fe;}
+tr.even td {background-color:#fff;}
+
+
+a.link, a, a.active {
+ color: #369;
+}
+
+.white {
+ color: white;
+}
+
+h1,h2,h3,h4,h5,h6 {
+ font-family: "Century Schoolbook L", Georgia, serif;
+ font-weight: bold;
+}
+
+
+#main_content {
+ color: black;
+ font-size: 127%;
+ background-color: white;
+ width: 757px;
+ margin: 0 auto 1em auto;
+ border: 1px solid #aaa;
+ border-top: 0px solid #aaa;
+ padding: 10px;
+ clear: both;
+}
+
+
+.sidebar {
+ border: 1px solid #cce;
+ background-color: #ffffab;
+ margin: 0.5em;
+ margin-left: 1.5em;
+ padding: 1em;
+ float: right;
+ width: 200px;
+ font-size: 88%;
+ background-color:#fffe1;
+ background-repeat:repeat-x;
+}
+
+.sidebar h2 {
+ margin-top: 0;
+ color: black;
+}
+
+.sidebar ul {
+ margin-left: 1.5em;
+ padding-left: 0;
+}
+
+
+
+#sb_top {
+ clear: right;
+}
+
+#sb_bottom {
+ clear: right;
+}
+
+#getting_started {
+ margin-left: 20px;
+}
+
+#getting_started_steps a {
+ text-decoration: none;
+}
+
+#getting_started_steps a:hover {
+ text-decoration: underline;
+}
+
+#getting_started_steps li {
+ font-size: 80%;
+ margin-bottom: 0.5em;
+}
+
+#getting_started_steps h2 {
+ font-size: 120%;
+}
+
+#getting_started_steps p {
+ font: 100% "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif;
+}
+
+#getting_started_steps,.alogo,.headtext {
+ font-family: "Century Schoolbook L", Georgia, serif;
+ font-weight: bold;
+}
+
+
+#header {
+ height: 125px;
+ width: 777px;
+ background: blue URL('../images/strype.png') repeat-x;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ margin: 0 auto 0 auto;
+ color: white;
+}
+
+.alogo {
+ font-size: 36px;
+ padding-left: 5px;
+ padding-top: 5px;
+ padding-right: 20px;
+ float: left;
+}
+
+.headtext {
+ color: white;
+ font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif;
+ font-size: 44px;
+ padding-left: 20px;
+ padding-top: 20px;
+ padding-right: 20px;
+ margin-bottom: 0px;
+}
+
+.currentpage {
+ color: white;
+ font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif;
+ font-size: 18px;
+ padding-top: 20px;
+ padding-right: 20px;
+ margin-bottom: 0px;
+ float: left;
+}
+
+#footer {
+ border: 0px solid #aaa;
+ color: #888;
+ background-color: white;
+ padding: 10px;
+ font-size: 90%;
+ text-align: center;
+ width: 600px;
+ margin-left: 40px;
+}
+
+.flogo {
+ padding-left: 20px;
+ padding-top: 0px;
+ padding-right: 20px;
+ margin-bottom: 0px;
+ float: left;
+}
+
+.foottext{
+}
+
+.code {
+ font-family: monospace;
+ font-size: 127%;
+}
+
+span.code {
+ font-weight: bold;
+ background: #eee;
+}
+
+#status_block {
+ margin: 0 auto 0.5em auto;
+ padding: 5px 15px 15px 55px;
+ background: #eef URL('../images/ok.png') left center no-repeat;
+ border: 1px solid #cce;
+ width: 680px;
+ font-size: 120%;
+ font-weight: bolder;
+}
+
+.notice {
+ margin: 0.5em auto 0.5em auto;
+ padding: 15px 10px 15px 55px;
+ width: 680px;
+ background: #eef URL('../images/info.png') left center no-repeat;
+ border: 1px solid #cce;
+}
+
+.fielderror {
+ color: red;
+ font-weight: bold;
+}
+
+div.clearingdiv {
+ clear:both;
+}
diff --git a/manas/public/favicon.ico b/manas/public/favicon.ico
new file mode 100644
index 0000000..840986e
--- /dev/null
+++ b/manas/public/favicon.ico
Binary files differ
diff --git a/manas/public/images/error.png b/manas/public/images/error.png
new file mode 100644
index 0000000..73c7d7f
--- /dev/null
+++ b/manas/public/images/error.png
Binary files differ
diff --git a/manas/public/images/grad_blue_7x80.png b/manas/public/images/grad_blue_7x80.png
new file mode 100644
index 0000000..8eb30fa
--- /dev/null
+++ b/manas/public/images/grad_blue_7x80.png
Binary files differ
diff --git a/manas/public/images/grad_orange_11x100.png b/manas/public/images/grad_orange_11x100.png
new file mode 100644
index 0000000..5b9a1d0
--- /dev/null
+++ b/manas/public/images/grad_orange_11x100.png
Binary files differ
diff --git a/manas/public/images/header_inner.png b/manas/public/images/header_inner.png
new file mode 100644
index 0000000..2b2d87d
--- /dev/null
+++ b/manas/public/images/header_inner.png
Binary files differ
diff --git a/manas/public/images/info.png b/manas/public/images/info.png
new file mode 100644
index 0000000..668f9c0
--- /dev/null
+++ b/manas/public/images/info.png
Binary files differ
diff --git a/manas/public/images/logo.gif b/manas/public/images/logo.gif
new file mode 100644
index 0000000..593126c
--- /dev/null
+++ b/manas/public/images/logo.gif
Binary files differ
diff --git a/manas/public/images/logo.png b/manas/public/images/logo.png
new file mode 100644
index 0000000..767021d
--- /dev/null
+++ b/manas/public/images/logo.png
Binary files differ
diff --git a/manas/public/images/ok.png b/manas/public/images/ok.png
new file mode 100644
index 0000000..c402ac5
--- /dev/null
+++ b/manas/public/images/ok.png
Binary files differ
diff --git a/manas/public/images/star.png b/manas/public/images/star.png
new file mode 100644
index 0000000..f9b5aaa
--- /dev/null
+++ b/manas/public/images/star.png
Binary files differ
diff --git a/manas/public/images/strype.png b/manas/public/images/strype.png
new file mode 100644
index 0000000..fd03dd5
--- /dev/null
+++ b/manas/public/images/strype.png
Binary files differ
diff --git a/manas/public/images/tg2_04.gif b/manas/public/images/tg2_04.gif
new file mode 100644
index 0000000..5e71843
--- /dev/null
+++ b/manas/public/images/tg2_04.gif
Binary files differ
diff --git a/manas/public/images/tg_under_the_hood.png b/manas/public/images/tg_under_the_hood.png
new file mode 100644
index 0000000..bc9c79c
--- /dev/null
+++ b/manas/public/images/tg_under_the_hood.png
Binary files differ
diff --git a/manas/public/images/under_the_hood_blue.png b/manas/public/images/under_the_hood_blue.png
new file mode 100644
index 0000000..90e84b7
--- /dev/null
+++ b/manas/public/images/under_the_hood_blue.png
Binary files differ
diff --git a/manas/templates/__init__.py b/manas/templates/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/templates/__init__.py
diff --git a/manas/templates/about.html b/manas/templates/about.html
new file mode 100644
index 0000000..f1aa30d
--- /dev/null
+++ b/manas/templates/about.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <xi:include href="header.html" />
+ <xi:include href="sidebars.html" />
+ <xi:include href="footer.html" />
+ <xi:include href="master.html" />
+
+<head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title>Learning TurboGears 2.0: Quick guide to the Quickstart pages.</title>
+</head>
+
+<body>
+ ${sidebar_top()}
+ ${sidebar_bottom()}
+ <div id="getting_started">
+ <h2>Architectural basics of a quickstart TG2 site.</h2>
+ <p>The TG2 quickstart command produces this basic TG site. Here's how it works.</p>
+ <ol id="getting_started_steps">
+ <li class="getting_started">
+ <h3>Code my data model</h3>
+ <p> When you want a model for storing favorite links or wiki content, the <strong>/model</strong> folder in your site is ready to go. .</p>
+ <p> You can build a dynamic site without any data model at all. There still be a default data-model template for you if you didn't enable identity in quickstart. If you enable identity, you'll got identity data-model settled for you.</p>
+ </li>
+ <li class="getting_started">
+ <h3>Design my site architecture</h3>
+ <p> The "<span class="code">root.py</span>" file under the <strong>/controllers</strong> folder has your URLs. When you called this url (<span class="code"><a href="/about">about</a></span>), the command went through the RootController class to the <span class="code">about</span><span class="code">()</span> method. </p>
+ <p> Those Python methods are responsible to create the dictionary of variables that will be used in your web views (template).</p>
+ </li>
+ <li class="getting_started">
+ <h3>Reuse the web page elements</h3>
+ <p> A web page viewed by user could be constructed by single or several reusable templates under <strong>/templates</strong>. Take 'about' page for example, each reusable templates generating a part of the page. We'll cover them in the order of where they are found, listed near the top of the about.html template</p>
+ <p> <strong><span class="code">header.html</span></strong> - The "header.html" template contains the HTML code to display the 'header': The blue gradient, TG2 logo, and some site text at the top of every page it is included on. When the "about.html" template is called, it includes this "header.html" template (and the others) with a <span class="code">&lt;xi:include /&gt;</span> tag, part of the Genshi templating system. The "header.html" template is not a completely static HTML -- it also dynamically displays the current page name with a Genshi template method called "replace" with the code: <span class="code">&lt;span py:replace="page"/&gt;</span>. It means replace this <span class="code">&lt;span /&gt;</span> region with the contents found in the variable 'page' that has been sent in the dictionary to this "about.html" template, and is available through that namespace for use by this "header.html" template. That's how it changes in the header depending on what page you are visiting.
+ </p>
+ <p> <strong><span class="code">sidebars.html</span></strong> - The sidebars (navigation areas on the right side of the page) are generated as two separate <span class="code">py:def</span> blocks in the "sidebars.html" template. The <span class="code">py:def</span> construct is best thought of as a "macro" code... a simple way to separate and reuse common code snippets. All it takes to include these on the "about.html" page template is to write <span class="code">
+<br/><br/>
+ &#36; &#123;sidebar_top()&#125;
+<br/>
+ &#36; &#123;sidebar_bottom()&#125;
+<br/><br/>
+ </span> in the page where they are wanted. CSS styling (in "/public/css/style.css") floats them off to the right side. You can remove a sidebar or add more of them, and the CSS will place them one atop the other.</p>
+ <p>This is, of course, also exactly how the header and footer templates are also displayed in their proper places, but we'll cover that in the "master.html" template below. (Also, there should NOT be a space between the $ sign and the curly braces, or it will not work.</p>
+ <p>Oh, and in sidebar_top we've added a dynamic menu that shows the link to this page at the top when you're at the "index" page, and shows a link to the Home (index) page when you're here. Study the "sidebars.html" template to see how we used <span class="code">py:choose</span> for that.</p>
+ <p> <strong><span class="code">footer.html</span></strong> - The "footer.html" block is simple, but also utilizes a special "replace" method to set the current YEAR in the footer copyright message. The code is: <span class="code">&lt;span py:replace="now.strftime('%Y')"&gt;</span> and it uses the variable "now" that was passed in with the dictionary of variables. But because "now" is a datetime object, we can use the Python <span class="code">"strftime()"</span> method with the "replace" call to say "Just Display The Year Here". Simple, elegant; we format the date display in the template (the View in the Model/View/Controller architecture) rather than formatting it in the Controller method and sending it to the template as a string variable.</p>
+ <p> <strong><span class="code">master.html</span></strong> - The "master.html" template is called last, by design. The "master.html" template controls the overall design of the page we're looking at, calling first the "header" py:def macro, then the putting everything from this "about.html" template into the "main_content" div, and then calling the "footer" macro at the end. Thus the "master.html" template provides the overall architecture for each page in this site.</p>
+ <p>But why then shouldn't we call it first? Isn't it the most important? Perhaps, but that's precisely why we call it LAST. The "master.html" template needs to know where to find everything else, everything that it will use in py:def macros to build the page. So that means we call the other templates first, and then call "master.html". </p>
+ <p>There's more to the "master.html" template... study it to see how
+ the &lt;title&gt; tags and static JS and CSS files are brought into
+ the page. Templating with Genshi is a powerful tool and we've only
+ scratched the surface. There are also a few little CSS tricks
+ hidden in these pages, like the use of a "clearingdiv" to make
+ sure that your footer stays below the sidebars and always looks
+ right. That's not TG2 at work, just CSS. You'll need all your
+ skills to build a fine web app, but TG2 will make the hard parts
+ easier so that you can concentrate more on good design and content
+ rather than struggling with mechanics.</p>
+ </li>
+ </ol>
+ <p>Good luck with TurboGears 2!</p>
+ </div>
+</body>
+</html>
diff --git a/manas/templates/debug.html b/manas/templates/debug.html
new file mode 100644
index 0000000..b415a72
--- /dev/null
+++ b/manas/templates/debug.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title>Sample Template, for looking at template locals</title>
+</head>
+
+<body>
+ <h1>All objects from locals():</h1>
+
+ <div py:for="item in sorted(locals()['data'].keys())">
+ ${item}: ${repr(locals()['data'][item])}</div>
+</body>
+</html>
diff --git a/manas/templates/footer.html b/manas/templates/footer.html
new file mode 100644
index 0000000..17486b5
--- /dev/null
+++ b/manas/templates/footer.html
@@ -0,0 +1,15 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ py:strip="">
+<py:def function="footer">
+<div id="footer">
+ <div class="flogo">
+ <img src="/images/under_the_hood_blue.png" alt="TurboGears" />
+ </div>
+ <div class="foottext">
+ <p>TurboGears is a open source front-to-back web development
+ framework written in Python. Copyright (c) 2005-2008 </p>
+ </div>
+</div>
+</py:def>
+</html> \ No newline at end of file
diff --git a/manas/templates/header.html b/manas/templates/header.html
new file mode 100644
index 0000000..6d533be
--- /dev/null
+++ b/manas/templates/header.html
@@ -0,0 +1,17 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ py:strip="">
+<py:def function="header">
+ <div id="header">
+ <div class="alogo">
+ <img src="/images/logo.png" alt="TG2!"/>
+ </div>
+ <div class="headtext">
+ Welcome to TurboGears 2
+ </div>
+ <div class="currentpage">
+ Now Viewing: <span py:replace="page"/>
+ </div>
+ </div>
+</py:def>
+</html> \ No newline at end of file
diff --git a/manas/templates/index.html b/manas/templates/index.html
new file mode 100644
index 0000000..95e7d8b
--- /dev/null
+++ b/manas/templates/index.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <xi:include href="header.html" />
+ <xi:include href="sidebars.html" />
+ <xi:include href="footer.html" />
+ <xi:include href="master.html" />
+
+<head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title>Welcome to TurboGears 2.0, standing on the
+ shoulders of giants, since 2007</title>
+</head>
+
+<body>
+ ${sidebar_top()}
+ <div id="getting_started">
+ <p>TurboGears 2 is rapid web application development toolkit designed to make your life easier.</p>
+ <ol id="getting_started_steps">
+ <li class="getting_started">
+ <h3>Code your data model</h3>
+ <p> Design your data model, Create the database, and Add some bootstrap data.</p>
+ </li>
+ <li class="getting_started">
+ <h3>Design your URL architecture</h3>
+ <p> Decide your URLs, Program your controller methods, Design your
+ templates, and place some static files (CSS and/or JavaScript). </p>
+ </li>
+ <li class="getting_started">
+ <h3>Distribute your app</h3>
+ <p> Test your source, Generate project documents, Build a distribution.</p>
+ </li>
+ </ol>
+ </div>
+ <div class="clearingdiv" />
+ <div class="notice"> Thank you for choosing TurboGears.
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/manas/templates/login.html b/manas/templates/login.html
new file mode 100644
index 0000000..d0430df
--- /dev/null
+++ b/manas/templates/login.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+<title>Login Form</title>
+</head>
+
+<body>
+
+<form action="/login_handler?came_from=${came_from}" method="POST">
+ Login: <input type="text" name="login"></input><br/>
+ Password: <input type="password" name="password"></input><br/>
+ <input type="submit" name="submit"/>
+</form>
+
+</body>
+</html>
diff --git a/manas/templates/master.html b/manas/templates/master.html
new file mode 100644
index 0000000..3c5a5d8
--- /dev/null
+++ b/manas/templates/master.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ py:strip="">
+<?python
+import tg
+tg_flash = tg.get_flash()
+?>
+<head py:match="head" py:attrs="select('@*')">
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title py:replace="''">Your title goes here</title>
+ <meta py:replace="select('*')"/>
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/style.css" />
+</head>
+
+<body py:match="body" py:attrs="select('@*')">
+ ${header()}
+ <div id="main_content">
+ <div id="status_block" py:if="tg_flash" class="flash" py:content="tg_flash"></div>
+ <div py:replace="select('*|text()')"/>
+ <!-- End of main_content -->
+ <div class="clearingdiv" />
+ ${footer()}
+ </div>
+</body>
+</html>
diff --git a/manas/templates/sidebars.html b/manas/templates/sidebars.html
new file mode 100644
index 0000000..cde5deb
--- /dev/null
+++ b/manas/templates/sidebars.html
@@ -0,0 +1,33 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ py:strip="">
+
+<py:def function="sidebar_top">
+ <div id="sb_top" class="sidebar">
+ <h2>Get Started with TG2</h2>
+ <ul class="links">
+ <li py:choose="">
+ <span py:when="page=='index'"><a href="/about">About this page</a> A quick guide to this TG2 site </span>
+ <span py:otherwise=""><a href="/">Home</a> Back to your Quickstart Home page </span>
+ </li>
+ <li><a href="http://www.turbogears.org/2.0/docs/">TG2 Documents</a> - Read everything in the Getting Started section</li>
+ <li><a href="http://docs.turbogears.org/1.0">TG1 docs</a> (still useful, although a lot has changed for TG2) </li>
+ <li><a href="http://groups.google.com/group/turbogears"> Join the TG Mail List</a> for general TG use/topics </li>
+ </ul>
+ </div>
+</py:def>
+
+<py:def function="sidebar_bottom">
+ <div id="sb_bottom" class="sidebar">
+ <h2>Developing TG2</h2>
+ <ul class="links">
+ <li><a href="http://docs.turbogears.org/2.0/RoughDocs/">TG2 Documents</a> A work in progress</li>
+ <li><a href="http://trac.turbogears.org/query?milestone=2.0&amp;order=priority">TG2 Trac tickets</a> What's happening now in TG2 dev</li>
+ <li><a href="http://trac.turbogears.org/timeline">TG Dev timeline</a> (recent ticket updates, svn checkins, wiki changes)</li>
+ <li><a href="http://trac.turbogears.org/browser">TG2 Trac repository</a> TG2 is in /trunk</li>
+ <li><a href="http://groups.google.com/group/turbogears-trunk"> Join the TG-Trunk Mail List</a> just for TG2 discuss/dev </li>
+ </ul>
+ </div>
+</py:def>
+
+</html>
diff --git a/manas/tests/__init__.py b/manas/tests/__init__.py
new file mode 100644
index 0000000..91ff2e7
--- /dev/null
+++ b/manas/tests/__init__.py
@@ -0,0 +1,40 @@
+"""Pylons application test package
+
+When the test runner finds and executes tests within this directory,
+this file will be loaded to setup the test environment.
+
+It registers the root directory of the project in sys.path and
+pkg_resources, in case the project hasn't been installed with
+setuptools. It also initializes the application via websetup (paster
+setup-app) with the project's test.ini configuration file.
+"""
+import os
+import sys
+from unittest import TestCase
+
+import pkg_resources
+import paste.fixture
+import paste.script.appinstall
+from paste.deploy import loadapp
+from routes import url_for
+
+__all__ = ['url_for', 'TestController']
+
+here_dir = os.path.dirname(os.path.abspath(__file__))
+conf_dir = os.path.dirname(os.path.dirname(here_dir))
+
+sys.path.insert(0, conf_dir)
+pkg_resources.working_set.add_entry(conf_dir)
+pkg_resources.require('Paste')
+pkg_resources.require('PasteScript')
+
+test_file = os.path.join(conf_dir, 'test.ini')
+cmd = paste.script.appinstall.SetupCommand('setup-app')
+cmd.run([test_file])
+
+class TestController(TestCase):
+
+ def __init__(self, *args, **kwargs):
+ wsgiapp = loadapp('config:test.ini', relative_to=conf_dir)
+ self.app = paste.fixture.TestApp(wsgiapp)
+ TestCase.__init__(self, *args, **kwargs)
diff --git a/manas/tests/functional/__init__.py b/manas/tests/functional/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/tests/functional/__init__.py
diff --git a/manas/tests/test_models.py b/manas/tests/test_models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/manas/tests/test_models.py
diff --git a/manas/websetup.py b/manas/websetup.py
new file mode 100644
index 0000000..dfbbc26
--- /dev/null
+++ b/manas/websetup.py
@@ -0,0 +1,54 @@
+"""Setup the manas application"""
+import logging
+
+from paste.deploy import appconfig
+from pylons import config
+
+from manas.config.environment import load_environment
+
+log = logging.getLogger(__name__)
+
+def setup_config(command, filename, section, vars):
+ """Place any commands to setup manas here"""
+ conf = appconfig('config:' + filename)
+ load_environment(conf.global_conf, conf.local_conf)
+ # Load the models
+ from manas import model
+ print "Creating tables"
+ model.metadata.create_all(bind=config['pylons.app_globals'].sa_engine)
+
+ u = model.User()
+ u.user_name = u'manager'
+ u.display_name = u'Exemple manager'
+ u.email_address = u'manager@somedomain.com'
+ u.password = u'managepass'
+
+ model.DBSession.save(u)
+
+ g = model.Group()
+ g.group_name = u'managers'
+ g.display_name = u'Managers Group'
+
+ g.users.append(u)
+
+ model.DBSession.save(g)
+
+ p = model.Permission()
+ p.permission_name = u'manage'
+ p.description = u'This permission give an administrative right to the bearer'
+ p.groups.append(g)
+
+ model.DBSession.save(p)
+ model.DBSession.flush()
+
+ u1 = model.User()
+ u1.user_name = u'editor'
+ u1.display_name = u'Exemple editor'
+ u1.email_address = u'editor@somedomain.com'
+ u1.password = u'editpass'
+
+ model.DBSession.save(u1)
+ model.DBSession.flush()
+
+ model.DBSession.commit()
+ print "Successfully setup"