From ef6aa096bbb8f52c2bad76f3b086167db326da89 Mon Sep 17 00:00:00 2001 From: Luke Macken Date: Fri, 27 Jun 2008 21:43:09 -0400 Subject: Initial TG2 quickstart --- README.txt | 19 +++ development.ini | 98 ++++++++++++ ez_setup/README.txt | 14 ++ ez_setup/__init__.py | 229 ++++++++++++++++++++++++++++ manas.egg-info/PKG-INFO | 10 ++ manas.egg-info/SOURCES.txt | 32 ++++ manas.egg-info/dependency_links.txt | 1 + manas.egg-info/entry_points.txt | 7 + manas.egg-info/paste_deploy_config.ini_tmpl | 68 +++++++++ manas.egg-info/paster_plugins.txt | 4 + manas.egg-info/requires.txt | 2 + manas.egg-info/top_level.txt | 1 + manas/__init__.py | 0 manas/config/__init__.py | 0 manas/config/app_cfg.py | 25 +++ manas/config/environment.py | 5 + manas/config/middleware.py | 14 ++ manas/controllers/__init__.py | 0 manas/controllers/error.py | 41 +++++ manas/controllers/root.py | 38 +++++ manas/controllers/secc.py | 26 ++++ manas/controllers/template.py | 27 ++++ manas/i18n/ru/LC_MESSAGES/manas.po | 24 +++ manas/lib/__init__.py | 0 manas/lib/app_globals.py | 13 ++ manas/lib/base.py | 67 ++++++++ manas/lib/helpers.py | 0 manas/model/__init__.py | 45 ++++++ manas/model/identity.py | 161 +++++++++++++++++++ manas/model/model.template | 24 +++ manas/public/css/style.css | 206 +++++++++++++++++++++++++ manas/public/favicon.ico | Bin 0 -> 1104 bytes manas/public/images/error.png | Bin 0 -> 1471 bytes manas/public/images/grad_blue_7x80.png | Bin 0 -> 3346 bytes manas/public/images/grad_orange_11x100.png | Bin 0 -> 3136 bytes manas/public/images/header_inner.png | Bin 0 -> 37537 bytes manas/public/images/info.png | Bin 0 -> 1672 bytes manas/public/images/logo.gif | Bin 0 -> 23864 bytes manas/public/images/logo.png | Bin 0 -> 20663 bytes manas/public/images/ok.png | Bin 0 -> 1519 bytes manas/public/images/star.png | Bin 0 -> 2332 bytes manas/public/images/strype.png | Bin 0 -> 1378 bytes manas/public/images/tg2_04.gif | Bin 0 -> 22775 bytes manas/public/images/tg_under_the_hood.png | Bin 0 -> 4010 bytes manas/public/images/under_the_hood_blue.png | Bin 0 -> 2667 bytes manas/templates/__init__.py | 0 manas/templates/about.html | 66 ++++++++ manas/templates/debug.html | 20 +++ manas/templates/footer.html | 15 ++ manas/templates/header.html | 17 +++ manas/templates/index.html | 42 +++++ manas/templates/login.html | 23 +++ manas/templates/master.html | 28 ++++ manas/templates/sidebars.html | 33 ++++ manas/tests/__init__.py | 40 +++++ manas/tests/functional/__init__.py | 0 manas/tests/test_models.py | 0 manas/websetup.py | 54 +++++++ setup.cfg | 31 ++++ setup.py | 38 +++++ test.ini | 21 +++ tests/test_controller.py | 12 ++ 62 files changed, 1641 insertions(+) create mode 100644 README.txt create mode 100644 development.ini create mode 100644 ez_setup/README.txt create mode 100644 ez_setup/__init__.py create mode 100644 manas.egg-info/PKG-INFO create mode 100644 manas.egg-info/SOURCES.txt create mode 100644 manas.egg-info/dependency_links.txt create mode 100644 manas.egg-info/entry_points.txt create mode 100644 manas.egg-info/paste_deploy_config.ini_tmpl create mode 100644 manas.egg-info/paster_plugins.txt create mode 100644 manas.egg-info/requires.txt create mode 100644 manas.egg-info/top_level.txt create mode 100644 manas/__init__.py create mode 100644 manas/config/__init__.py create mode 100644 manas/config/app_cfg.py create mode 100644 manas/config/environment.py create mode 100644 manas/config/middleware.py create mode 100644 manas/controllers/__init__.py create mode 100644 manas/controllers/error.py create mode 100644 manas/controllers/root.py create mode 100644 manas/controllers/secc.py create mode 100644 manas/controllers/template.py create mode 100644 manas/i18n/ru/LC_MESSAGES/manas.po create mode 100644 manas/lib/__init__.py create mode 100644 manas/lib/app_globals.py create mode 100644 manas/lib/base.py create mode 100644 manas/lib/helpers.py create mode 100644 manas/model/__init__.py create mode 100644 manas/model/identity.py create mode 100644 manas/model/model.template create mode 100644 manas/public/css/style.css create mode 100644 manas/public/favicon.ico create mode 100644 manas/public/images/error.png create mode 100644 manas/public/images/grad_blue_7x80.png create mode 100644 manas/public/images/grad_orange_11x100.png create mode 100644 manas/public/images/header_inner.png create mode 100644 manas/public/images/info.png create mode 100644 manas/public/images/logo.gif create mode 100644 manas/public/images/logo.png create mode 100644 manas/public/images/ok.png create mode 100644 manas/public/images/star.png create mode 100644 manas/public/images/strype.png create mode 100644 manas/public/images/tg2_04.gif create mode 100644 manas/public/images/tg_under_the_hood.png create mode 100644 manas/public/images/under_the_hood_blue.png create mode 100644 manas/templates/__init__.py create mode 100644 manas/templates/about.html create mode 100644 manas/templates/debug.html create mode 100644 manas/templates/footer.html create mode 100644 manas/templates/header.html create mode 100644 manas/templates/index.html create mode 100644 manas/templates/login.html create mode 100644 manas/templates/master.html create mode 100644 manas/templates/sidebars.html create mode 100644 manas/tests/__init__.py create mode 100644 manas/tests/functional/__init__.py create mode 100644 manas/tests/test_models.py create mode 100644 manas/websetup.py create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test.ini create mode 100644 tests/test_controller.py diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..2796e80 --- /dev/null +++ b/README.txt @@ -0,0 +1,19 @@ +This file is for you to describe the manas application. Typically +you would include information such as the information below: + +Installation and Setup +====================== + +Install ``manas`` using easy_install:: + + easy_install manas + +Make a config file as follows:: + + paster make-config manas config.ini + +Tweak the config file as appropriate and then setup the application:: + + paster setup-app config.ini + +Then you are ready to go. diff --git a/development.ini b/development.ini new file mode 100644 index 0000000..b44e1b4 --- /dev/null +++ b/development.ini @@ -0,0 +1,98 @@ +# +# manas - Pylons development environment configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +# This file is for deployment specific config options -- other configuration +# that is always required for the app is done in the config directory, +# and generally should not be modified by end users. + +[DEFAULT] +debug = true +# Uncomment and replace with the address which should receive any error reports +#email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 8080 + +[app:main] +use = egg:manas +full_stack = true +#lang = ru +cache_dir = %(here)s/data +beaker.session.key = manas +beaker.session.secret = somesecret + +# If you'd like to fine-tune the individual locations of the cache data dirs +# for the Cache data, or the Session saves, un-comment the desired settings +# here: +#beaker.cache.data_dir = %(here)s/data/cache +#beaker.session.data_dir = %(here)s/data/sessions + +# pick the form for your database +# %(here) may include a ':' character on Windows environments; this can +# invalidate the URI when specifying a SQLite db via path name +# sqlalchemy.url=postgres://username:password:port@hostname/databasename +# sqlalchemy.url=mysql://username:password@hostname:port/databasename + + +# If you have sqlite, here's a simple default to get you started +# in development + +sqlalchemy.url = sqlite:///%(here)s/devdata.db +sqlalchemy.echo = true +sqlalchemy.echo_pool = false +sqlalchemy.pool_recycle = 3600 + +# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* +# Debug mode will enable the interactive debugging tool, allowing ANYONE to +# execute malicious code after an exception is raised. +#set debug = false + +# Logging configuration +# Add additional loggers, handlers, formatters here +# Uses python's logging config file format +# http://docs.python.org/lib/logging-config-fileformat.html + +[loggers] +keys = root, manas, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +# If you create additional loggers, add them as a key to [loggers] +[logger_root] +level = INFO +handlers = console + +[logger_manas] +level = DEBUG +handlers = +qualname = manas + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +# If you create additional handlers, add them as a key to [handlers] +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +# If you create additional formatters, add them as a key to [formatters] +[formatter_generic] +format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/ez_setup/README.txt b/ez_setup/README.txt new file mode 100644 index 0000000..77c986d --- /dev/null +++ b/ez_setup/README.txt @@ -0,0 +1,14 @@ +This directory exists so that Subversion-based projects can share a single +copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it +automatically updated in their projects when ``setuptools`` is updated. + +For your convenience, you may use the following svn:externals definition:: + + ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup + +You can set this by executing this command in your project directory:: + + svn propedit svn:externals . + +And then adding the line shown above to the file that comes up for editing. +Then, whenever you update your project, ``ez_setup`` will be updated as well. diff --git a/ez_setup/__init__.py b/ez_setup/__init__.py new file mode 100644 index 0000000..6473c14 --- /dev/null +++ b/ez_setup/__init__.py @@ -0,0 +1,229 @@ +#!python +"""Bootstrap setuptools installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import sys +DEFAULT_VERSION = "0.6c7" +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] + +md5_data = { + 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', + 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', + 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', + 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', + 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', + 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', + 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', + 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', + 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', + 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', + 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', + 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', + 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', + 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', + 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', + 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', + 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', + 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', + 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', + 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', + 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', + 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', + 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', + 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', + 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', + 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', + 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', +} + +import sys, os + +def _validate_md5(egg_name, data): + if egg_name in md5_data: + from md5 import md5 + digest = md5(data).hexdigest() + if digest != md5_data[egg_name]: + print >>sys.stderr, ( + "md5 validation of %s failed! (Possible download problem?)" + % egg_name + ) + sys.exit(2) + return data + + +def use_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + download_delay=15 +): + """Automatically find/download setuptools and make it available on sys.path + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end with + a '/'). `to_dir` is the directory where setuptools will be downloaded, if + it is not already available. If `download_delay` is specified, it should + be the number of seconds that will be paused before initiating a download, + should one be required. If an older version of setuptools is installed, + this routine will print a message to ``sys.stderr`` and raise SystemExit in + an attempt to abort the calling script. + """ + try: + import setuptools + if setuptools.__version__ == '0.0.1': + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) + except ImportError: + egg = download_setuptools(version, download_base, to_dir, download_delay) + sys.path.insert(0, egg) + import setuptools; setuptools.bootstrap_install_from = egg + + import pkg_resources + try: + pkg_resources.require("setuptools>="+version) + + except pkg_resources.VersionConflict, e: + # XXX could we install in a subprocess here? + print >>sys.stderr, ( + "The required version of setuptools (>=%s) is not available, and\n" + "can't be installed while this script is running. Please install\n" + " a more recent version first.\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + +def download_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + delay = 15 +): + """Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download attempt. + """ + import urllib2, shutil + egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + url = download_base + egg_name + saveto = os.path.join(to_dir, egg_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + from distutils import log + if delay: + log.warn(""" +--------------------------------------------------------------------------- +This script requires setuptools version %s to run (even to display +help). I will attempt to download it for you (from +%s), but +you may need to enable firewall access for this script first. +I will start the download in %d seconds. + +(Note: if this machine does not have network access, please obtain the file + + %s + +and place it in this directory before rerunning this script.) +---------------------------------------------------------------------------""", + version, download_base, delay, url + ); from time import sleep; sleep(delay) + log.warn("Downloading %s", url) + src = urllib2.urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = _validate_md5(egg_name, src.read()) + dst = open(saveto,"wb"); dst.write(data) + finally: + if src: src.close() + if dst: dst.close() + return os.path.realpath(saveto) + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + + try: + import setuptools + except ImportError: + egg = None + try: + egg = download_setuptools(version, delay=0) + sys.path.insert(0,egg) + from setuptools.command.easy_install import main + return main(list(argv)+[egg]) # we're done here + finally: + if egg and os.path.exists(egg): + os.unlink(egg) + else: + if setuptools.__version__ == '0.0.1': + # tell the user to uninstall obsolete version + use_setuptools(version) + + req = "setuptools>="+version + import pkg_resources + try: + pkg_resources.require(req) + except pkg_resources.VersionConflict: + try: + from setuptools.command.easy_install import main + except ImportError: + from easy_install import main + main(list(argv)+[download_setuptools(delay=0)]) + sys.exit(0) # try to force an exit + else: + if argv: + from setuptools.command.easy_install import main + main(argv) + else: + print "Setuptools version",version,"or greater has been installed." + print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' + + + +def update_md5(filenames): + """Update our built-in md5 registry""" + + import re + from md5 import md5 + + for name in filenames: + base = os.path.basename(name) + f = open(name,'rb') + md5_data[base] = md5(f.read()).hexdigest() + f.close() + + data = [" %r: %r,\n" % it for it in md5_data.items()] + data.sort() + repl = "".join(data) + + import inspect + srcfile = inspect.getsourcefile(sys.modules[__name__]) + f = open(srcfile, 'rb'); src = f.read(); f.close() + + match = re.search("\nmd5_data = {\n([^}]+)}", src) + if not match: + print >>sys.stderr, "Internal error!" + sys.exit(2) + + src = src[:match.start(1)] + repl + src[match.end(1):] + f = open(srcfile,'w') + f.write(src) + f.close() + + +if __name__=='__main__': + if len(sys.argv)>2 and sys.argv[1]=='--md5update': + update_md5(sys.argv[2:]) + else: + main(sys.argv[1:]) diff --git a/manas.egg-info/PKG-INFO b/manas.egg-info/PKG-INFO new file mode 100644 index 0000000..ec54845 --- /dev/null +++ b/manas.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: manas +Version: 0.1dev +Summary: UNKNOWN +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/manas.egg-info/SOURCES.txt b/manas.egg-info/SOURCES.txt new file mode 100644 index 0000000..b85862e --- /dev/null +++ b/manas.egg-info/SOURCES.txt @@ -0,0 +1,32 @@ +README.txt +setup.cfg +setup.py +manas/__init__.py +manas/websetup.py +manas.egg-info/PKG-INFO +manas.egg-info/SOURCES.txt +manas.egg-info/dependency_links.txt +manas.egg-info/entry_points.txt +manas.egg-info/paste_deploy_config.ini_tmpl +manas.egg-info/paster_plugins.txt +manas.egg-info/requires.txt +manas.egg-info/top_level.txt +manas/config/__init__.py +manas/config/app_cfg.py +manas/config/environment.py +manas/config/middleware.py +manas/controllers/__init__.py +manas/controllers/error.py +manas/controllers/root.py +manas/controllers/secc.py +manas/controllers/template.py +manas/lib/__init__.py +manas/lib/app_globals.py +manas/lib/base.py +manas/lib/helpers.py +manas/model/__init__.py +manas/model/identity.py +manas/templates/__init__.py +manas/tests/__init__.py +manas/tests/test_models.py +manas/tests/functional/__init__.py \ No newline at end of file diff --git a/manas.egg-info/dependency_links.txt b/manas.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/manas.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/manas.egg-info/entry_points.txt b/manas.egg-info/entry_points.txt new file mode 100644 index 0000000..5bda1da --- /dev/null +++ b/manas.egg-info/entry_points.txt @@ -0,0 +1,7 @@ + + [paste.app_factory] + main = manas.config.middleware:make_app + + [paste.app_install] + main = pylons.util:PylonsInstaller + \ No newline at end of file diff --git a/manas.egg-info/paste_deploy_config.ini_tmpl b/manas.egg-info/paste_deploy_config.ini_tmpl new file mode 100644 index 0000000..eb69733 --- /dev/null +++ b/manas.egg-info/paste_deploy_config.ini_tmpl @@ -0,0 +1,68 @@ +# +# manas - Pylons configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = true +email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 8080 + +[app:main] +use = egg:manas +full_stack = true +cache_dir = %(here)s/data +beaker.session.key = manas +beaker.session.secret = ${app_instance_secret} +app_instance_uuid = ${app_instance_uuid} + +# If you'd like to fine-tune the individual locations of the cache data dirs +# for the Cache data, or the Session saves, un-comment the desired settings +# here: +#beaker.cache.data_dir = %(here)s/data/cache +#beaker.session.data_dir = %(here)s/data/sessions +# Specify the database for SQLAlchemy to use via +# turbogears.database +# %(here) may include a ':' character on Windows environments; this can +# invalidate the URI when specifying a SQLite db via path name +sqlalchemy.url = sqlite:///%(here)s/somedb.db +sqlalchemy.echo = False + +# Specify the database for SQLObject to use. +#sqlobject.dburi = sqlite://%(here)s/somedb.db + +# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* +# Debug mode will enable the interactive debugging tool, allowing ANYONE to +# execute malicious code after an exception is raised. +set debug = false + +# Logging configuration +# Uses python's logging config file format +# http://docs.python.org/lib/logging-config-fileformat.html +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s diff --git a/manas.egg-info/paster_plugins.txt b/manas.egg-info/paster_plugins.txt new file mode 100644 index 0000000..ab508e5 --- /dev/null +++ b/manas.egg-info/paster_plugins.txt @@ -0,0 +1,4 @@ +PasteScript +Pylons +TurboGears2 +tg.devtools diff --git a/manas.egg-info/requires.txt b/manas.egg-info/requires.txt new file mode 100644 index 0000000..11721d9 --- /dev/null +++ b/manas.egg-info/requires.txt @@ -0,0 +1,2 @@ +TurboGears2 +ToscaWidgets >= 0.9.1 \ No newline at end of file diff --git a/manas.egg-info/top_level.txt b/manas.egg-info/top_level.txt new file mode 100644 index 0000000..ea1b20b --- /dev/null +++ b/manas.egg-info/top_level.txt @@ -0,0 +1 @@ +manas diff --git a/manas/__init__.py b/manas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/manas/config/__init__.py b/manas/config/__init__.py new file mode 100644 index 0000000..e69de29 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 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 , 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 \n" +"Language-Team: ru \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 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 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 '' % self.group_name + +class User(object): + """Reasonably basic User definition. Probably would want additional + attributes. + """ + def __repr__(self): + return '' % ( + 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 Binary files /dev/null and b/manas/public/favicon.ico differ diff --git a/manas/public/images/error.png b/manas/public/images/error.png new file mode 100644 index 0000000..73c7d7f Binary files /dev/null and b/manas/public/images/error.png 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 Binary files /dev/null and b/manas/public/images/grad_blue_7x80.png 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 Binary files /dev/null and b/manas/public/images/grad_orange_11x100.png differ diff --git a/manas/public/images/header_inner.png b/manas/public/images/header_inner.png new file mode 100644 index 0000000..2b2d87d Binary files /dev/null and b/manas/public/images/header_inner.png differ diff --git a/manas/public/images/info.png b/manas/public/images/info.png new file mode 100644 index 0000000..668f9c0 Binary files /dev/null and b/manas/public/images/info.png differ diff --git a/manas/public/images/logo.gif b/manas/public/images/logo.gif new file mode 100644 index 0000000..593126c Binary files /dev/null and b/manas/public/images/logo.gif differ diff --git a/manas/public/images/logo.png b/manas/public/images/logo.png new file mode 100644 index 0000000..767021d Binary files /dev/null and b/manas/public/images/logo.png differ diff --git a/manas/public/images/ok.png b/manas/public/images/ok.png new file mode 100644 index 0000000..c402ac5 Binary files /dev/null and b/manas/public/images/ok.png differ diff --git a/manas/public/images/star.png b/manas/public/images/star.png new file mode 100644 index 0000000..f9b5aaa Binary files /dev/null and b/manas/public/images/star.png differ diff --git a/manas/public/images/strype.png b/manas/public/images/strype.png new file mode 100644 index 0000000..fd03dd5 Binary files /dev/null and b/manas/public/images/strype.png differ diff --git a/manas/public/images/tg2_04.gif b/manas/public/images/tg2_04.gif new file mode 100644 index 0000000..5e71843 Binary files /dev/null and b/manas/public/images/tg2_04.gif 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 Binary files /dev/null and b/manas/public/images/tg_under_the_hood.png 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 Binary files /dev/null and b/manas/public/images/under_the_hood_blue.png differ diff --git a/manas/templates/__init__.py b/manas/templates/__init__.py new file mode 100644 index 0000000..e69de29 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 @@ + + + + + + + + + + + Learning TurboGears 2.0: Quick guide to the Quickstart pages. + + + + ${sidebar_top()} + ${sidebar_bottom()} +
+

Architectural basics of a quickstart TG2 site.

+

The TG2 quickstart command produces this basic TG site. Here's how it works.

+
    +
  1. +

    Code my data model

    +

    When you want a model for storing favorite links or wiki content, the /model folder in your site is ready to go. .

    +

    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.

    +
  2. +
  3. +

    Design my site architecture

    +

    The "root.py" file under the /controllers folder has your URLs. When you called this url (about), the command went through the RootController class to the about() method.

    +

    Those Python methods are responsible to create the dictionary of variables that will be used in your web views (template).

    +
  4. +
  5. +

    Reuse the web page elements

    +

    A web page viewed by user could be constructed by single or several reusable templates under /templates. 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

    +

    header.html - 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 <xi:include /> 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 py:replace="page"/>. It means replace this <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. +

    +

    sidebars.html - The sidebars (navigation areas on the right side of the page) are generated as two separate py:def blocks in the "sidebars.html" template. The py:def 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 +

    + $ {sidebar_top()} +
    + $ {sidebar_bottom()} +

    +
    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.

    +

    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.

    +

    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 py:choose for that.

    +

    footer.html - 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 py:replace="now.strftime('%Y')"> 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 "strftime()" 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.

    +

    master.html - 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.

    +

    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".

    +

    There's more to the "master.html" template... study it to see how + the <title> 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.

    +
  6. +
+

Good luck with TurboGears 2!

+
+ + 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 @@ + + + + + + + + Sample Template, for looking at template locals + + + +

All objects from locals():

+ +
+ ${item}: ${repr(locals()['data'][item])}
+ + 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 @@ + + + + + \ 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 @@ + + + + + \ 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 @@ + + + + + + + + + + + Welcome to TurboGears 2.0, standing on the + shoulders of giants, since 2007 + + + + ${sidebar_top()} +
+

TurboGears 2 is rapid web application development toolkit designed to make your life easier.

+
    +
  1. +

    Code your data model

    +

    Design your data model, Create the database, and Add some bootstrap data.

    +
  2. +
  3. +

    Design your URL architecture

    +

    Decide your URLs, Program your controller methods, Design your + templates, and place some static files (CSS and/or JavaScript).

    +
  4. +
  5. +

    Distribute your app

    +

    Test your source, Generate project documents, Build a distribution.

    +
  6. +
+
+
+
Thank you for choosing TurboGears. +
+ + \ 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 @@ + + + + + + + +Login Form + + + + +
+ Login:
+ Password:
+ +
+ + + 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 @@ + + + + + + Your title goes here + + + + + + ${header()} +
+
+
+ +
+ ${footer()} +
+ + 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 @@ + + + + + + + + + + + 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 diff --git a/manas/tests/test_models.py b/manas/tests/test_models.py new file mode 100644 index 0000000..e69de29 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" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..4f9966f --- /dev/null +++ b/setup.cfg @@ -0,0 +1,31 @@ +[egg_info] +tag_build = dev +tag_svn_revision = true + +[easy_install] +find_links = http://www.pylonshq.com/download/ + +[nosetests] +with-pylons=test.ini + +# Babel configuration +[compile_catalog] +domain = manas +directory = manas/i18n +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = manas/i18n/manas.pot +width = 80 + +[init_catalog] +domain = manas +input_file = manas/i18n/manas.pot +output_dir = manas/i18n + +[update_catalog] +domain = manas +input_file = manas/i18n/manas.pot +output_dir = manas/i18n +previous = true diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6f16f8d --- /dev/null +++ b/setup.py @@ -0,0 +1,38 @@ +try: + from setuptools import setup, find_packages +except ImportError: + from ez_setup import use_setuptools + use_setuptools() + from setuptools import setup, find_packages + +setup( + name='manas', + version='0.1', + description='', + author='', + author_email='', + #url='', + install_requires=[ + "TurboGears2", + "ToscaWidgets >= 0.9.1" + ], + packages=find_packages(exclude=['ez_setup']), + include_package_data=True, + test_suite='nose.collector', + package_data={'manas': ['i18n/*/LC_MESSAGES/*.mo', + 'templates/*/*', + 'public/*/*']}, + #message_extractors = {'manas': [ + # ('**.py', 'python', None), + # ('templates/**.mako', 'mako', None), + # ('templates/**.html', 'genshi', None), + # ('public/**', 'ignore', None)]}, + + entry_points=""" + [paste.app_factory] + main = manas.config.middleware:make_app + + [paste.app_install] + main = pylons.util:PylonsInstaller + """, +) \ No newline at end of file diff --git a/test.ini b/test.ini new file mode 100644 index 0000000..df583ef --- /dev/null +++ b/test.ini @@ -0,0 +1,21 @@ +# +# manas - Pylons testing environment configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = true +# Uncomment and replace with the address which should receive any error reports +# email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5000 + +[app:main] +use = config:development.ini + +# Add additional test specific configuration options as necessary. diff --git a/tests/test_controller.py b/tests/test_controller.py new file mode 100644 index 0000000..c6bb8f4 --- /dev/null +++ b/tests/test_controller.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from paste.fixture import TestApp + +import os.path +config = 'config:'+(os.path.abspath(os.path.basename(__name__)+'/../../development.ini#main')) + +app = TestApp(config) + +class TestTGController: + def test_index(self): + resp = app.get('/') + assert 'TurboGears 2 is a open source front-to-back web development' in resp.body, resp.body -- cgit