├── examples └── tg2 │ ├── migration │ ├── __init__.py │ ├── versions │ │ └── __init__.py │ └── migrate.cfg │ ├── tg2 │ ├── templates │ │ ├── __init__.py │ │ ├── movie.mak │ │ ├── header.mak │ │ ├── simple_mako.mak │ │ ├── error.mak │ │ ├── login.mak │ │ ├── environ.mak │ │ ├── data.mak │ │ ├── index.mak │ │ ├── authentication.mak │ │ ├── master.mak │ │ └── about.mak │ ├── config │ │ ├── __init__.py │ │ ├── environment.py │ │ ├── middleware.py │ │ ├── app_cfg.py │ │ └── deployment.ini_tmpl │ ├── lib │ │ ├── __init__.py │ │ ├── helpers.py │ │ ├── app_globals.py │ │ └── base.py │ ├── __init__.py │ ├── controllers │ │ ├── __init__.py │ │ ├── controller.template │ │ ├── secure.py │ │ ├── template.py │ │ ├── error.py │ │ └── root.py │ ├── public │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── ok.png │ │ │ ├── add.png │ │ │ ├── error.png │ │ │ ├── info.png │ │ │ ├── star.png │ │ │ ├── delete.png │ │ │ ├── inputbg.png │ │ │ ├── loginbg.png │ │ │ ├── menubg.png │ │ │ ├── pagebg.png │ │ │ ├── pencil.png │ │ │ ├── strype2.png │ │ │ ├── warning.png │ │ │ ├── contentbg.png │ │ │ ├── headerbg.png │ │ │ ├── header_inner2.png │ │ │ ├── loginbottombg.png │ │ │ ├── loginheader-left.png │ │ │ ├── loginheader-right.png │ │ │ ├── menu-item-actibg.png │ │ │ ├── menu-item-border.png │ │ │ ├── under_the_hood_blue.png │ │ │ └── menu-item-actibg-first.png │ │ └── css │ │ │ ├── admin.css │ │ │ └── style.css │ ├── tests │ │ ├── functional │ │ │ ├── __init__.py │ │ │ ├── test_root.py │ │ │ └── test_authentication.py │ │ ├── models │ │ │ ├── test_auth.py │ │ │ └── __init__.py │ │ └── __init__.py │ ├── websetup │ │ ├── __init__.py │ │ ├── schema.py │ │ └── bootstrap.py │ ├── model │ │ ├── model.template │ │ ├── __init__.py │ │ └── auth.py │ └── i18n │ │ └── ru │ │ └── LC_MESSAGES │ │ └── tg2.po │ ├── MANIFEST.in │ ├── ez_setup │ ├── README.txt │ └── __init__.py │ ├── test.ini │ ├── README.txt │ ├── fedmsg.d │ ├── ssl.py │ ├── relay.py │ └── base.py │ ├── setup.cfg │ ├── setup.py │ └── development.ini ├── fedmsg_middleware ├── __init__.py └── middleware.py ├── MANIFEST.in ├── .gitignore ├── setup.py ├── README.rst └── LICENSE /examples/tg2/migration/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/tg2/migration/versions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/tg2/tg2/config/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /examples/tg2/tg2/lib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /fedmsg_middleware/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | from middleware import * 3 | -------------------------------------------------------------------------------- /examples/tg2/tg2/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """The tg2 package""" 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE 3 | recursive-include fedmsg_middleware/resources * 4 | -------------------------------------------------------------------------------- /examples/tg2/tg2/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Controllers for the tg2 application.""" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | *.swp 4 | *.egg* 5 | *.db* 6 | *.pdf 7 | dist 8 | build 9 | examples/tg2/data 10 | -------------------------------------------------------------------------------- /examples/tg2/tg2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/favicon.ico -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/ok.png -------------------------------------------------------------------------------- /examples/tg2/tg2/tests/functional/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Functional test suite for the controllers of the application.""" -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/add.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/error.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/info.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/star.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/delete.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/inputbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/inputbg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/loginbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/loginbg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/menubg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/menubg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/pagebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/pagebg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/pencil.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/strype2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/strype2.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/warning.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/contentbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/contentbg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/headerbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/headerbg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/movie.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | 3 | <%def name="content()"> 4 | ${tmpl_context.form() |n} 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/tg2/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include tg2/public * 2 | include tg2/public/favicon.ico 3 | recursive-include tg2/i18n * 4 | recursive-include tg2/templates * 5 | -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/header_inner2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/header_inner2.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/loginbottombg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/loginbottombg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/loginheader-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/loginheader-left.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/loginheader-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/loginheader-right.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/menu-item-actibg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/menu-item-actibg.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/menu-item-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/menu-item-border.png -------------------------------------------------------------------------------- /examples/tg2/tg2/lib/helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """WebHelpers used in tg2.""" 4 | 5 | from webhelpers import date, feedgenerator, html, number, misc, text 6 | -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/under_the_hood_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/under_the_hood_blue.png -------------------------------------------------------------------------------- /examples/tg2/tg2/public/images/menu-item-actibg-first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/fedmsg_middleware/master/examples/tg2/tg2/public/images/menu-item-actibg-first.png -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/header.mak: -------------------------------------------------------------------------------- 1 | <%def name="header()"> 2 | 8 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/simple_mako.mak: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/tg2/tg2/config/environment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """WSGI environment setup for tg2.""" 3 | 4 | from tg2.config.app_cfg import base_config 5 | 6 | __all__ = ['load_environment'] 7 | 8 | #Use base_config to setup the environment loader function 9 | load_environment = base_config.make_load_environment() 10 | -------------------------------------------------------------------------------- /examples/tg2/tg2/lib/app_globals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """The application's Globals object""" 4 | 5 | __all__ = ['Globals'] 6 | 7 | 8 | class Globals(object): 9 | """Container for objects available throughout the life of the application. 10 | 11 | One instance of Globals is created during application initialization and 12 | is available during requests via the 'app_globals' variable. 13 | 14 | """ 15 | 16 | def __init__(self): 17 | """Do nothing, by default.""" 18 | pass 19 | -------------------------------------------------------------------------------- /examples/tg2/tg2/websetup/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Setup the tg2 application""" 3 | 4 | import logging 5 | 6 | from tg2.config.environment import load_environment 7 | 8 | __all__ = ['setup_app'] 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | from schema import setup_schema 13 | import bootstrap 14 | 15 | def setup_app(command, conf, vars): 16 | """Place any commands to setup tg2 here""" 17 | load_environment(conf.global_conf, conf.local_conf) 18 | setup_schema(command, conf, vars) 19 | bootstrap.bootstrap(command, conf, vars) 20 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/error.mak: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | A ${code} Error has Occurred 8 | 9 | 10 | 11 |

Error ${code}

12 | 13 | <% 14 | import re 15 | mf = re.compile(r'( 19 | 20 |
${fixmessage(message) | n}
21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/tg2/tg2/model/model.template: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Sample model module.""" 3 | 4 | from sqlalchemy import * 5 | from sqlalchemy.orm import mapper, relation 6 | from sqlalchemy import Table, ForeignKey, Column 7 | from sqlalchemy.types import Integer, Unicode 8 | #from sqlalchemy.orm import relation, backref 9 | 10 | from tg2.model import DeclarativeBase, metadata, DBSession 11 | 12 | 13 | class SampleModel(DeclarativeBase): 14 | __tablename__ = 'sample_model' 15 | 16 | #{ Columns 17 | 18 | id = Column(Integer, primary_key=True) 19 | 20 | data = Column(Unicode(255), nullable=False) 21 | 22 | #} 23 | -------------------------------------------------------------------------------- /examples/tg2/ez_setup/README.txt: -------------------------------------------------------------------------------- 1 | This directory exists so that Subversion-based projects can share a single 2 | copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it 3 | automatically updated in their projects when ``setuptools`` is updated. 4 | 5 | For your convenience, you may use the following svn:externals definition:: 6 | 7 | ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup 8 | 9 | You can set this by executing this command in your project directory:: 10 | 11 | svn propedit svn:externals . 12 | 13 | And then adding the line shown above to the file that comes up for editing. 14 | Then, whenever you update your project, ``ez_setup`` will be updated as well. 15 | -------------------------------------------------------------------------------- /examples/tg2/test.ini: -------------------------------------------------------------------------------- 1 | # 2 | # tg2 - TurboGears 2 testing environment configuration 3 | # 4 | # The %(here)s variable will be replaced with the parent directory of this file 5 | # 6 | [DEFAULT] 7 | debug = true 8 | # Uncomment and replace with the address which should receive any error reports 9 | # email_to = you@yourdomain.com 10 | smtp_server = localhost 11 | error_email_from = paste@localhost 12 | 13 | [server:main] 14 | use = egg:Paste#http 15 | host = 0.0.0.0 16 | port = 5000 17 | 18 | [app:main] 19 | sqlalchemy.url = sqlite:///:memory: 20 | use = config:development.ini 21 | 22 | [app:main_without_authn] 23 | use = main 24 | skip_authentication = True 25 | 26 | # Add additional test specific configuration options as necessary. 27 | -------------------------------------------------------------------------------- /examples/tg2/tg2/controllers/controller.template: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Sample controller module""" 3 | 4 | # turbogears imports 5 | from tg import expose 6 | #from tg import redirect, validate, flash 7 | 8 | # third party imports 9 | #from tg.i18n import ugettext as _ 10 | #from repoze.what import predicates 11 | 12 | # project specific imports 13 | from tg2.lib.base import BaseController 14 | #from tg2.model import DBSession, metadata 15 | 16 | 17 | class SampleController(BaseController): 18 | #Uncomment this line if your controller requires an authenticated user 19 | #allow_only = authorize.not_anonymous() 20 | 21 | @expose('tg2.templates.index') 22 | def index(self): 23 | return dict(page='index') 24 | -------------------------------------------------------------------------------- /examples/tg2/README.txt: -------------------------------------------------------------------------------- 1 | This file is for you to describe the tg2 application. Typically 2 | you would include information such as the information below: 3 | 4 | Installation and Setup 5 | ====================== 6 | 7 | Install ``tg2`` using the setup.py script:: 8 | 9 | $ cd tg2 10 | $ python setup.py install 11 | 12 | Create the project database for any model classes defined:: 13 | 14 | $ paster setup-app development.ini 15 | 16 | Start the paste http server:: 17 | 18 | $ paster serve development.ini 19 | 20 | While developing you may want the server to reload after changes in package files (or its dependencies) are saved. This can be achieved easily by adding the --reload option:: 21 | 22 | $ paster serve --reload development.ini 23 | 24 | Then you are ready to go. 25 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/login.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | <%def name="title()">Login Form 3 |
4 |
5 |

Login

6 |
7 | 8 | 9 | 10 |
11 |
-------------------------------------------------------------------------------- /examples/tg2/tg2/templates/environ.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | 3 | <%def name="title()"> 4 | Learning TurboGears 2.1: Information about TG and WSGI 5 | 6 | 7 | ${parent.sidebar_top()} 8 |

The WSGI nature of the framework

9 |

In this page you can see all the WSGI variables your request object has, 10 | the ones in capital letters are required by the spec, then a sorted by 11 | component list of variables provided by the Components, and at last 12 | the "wsgi." namespace with very useful information about your WSGI Server

13 |

The keys in the environment are: 14 | 15 | %for key in sorted(environment): 16 | 17 | 18 | 19 | 20 | %endfor 21 |
${key}${environment[key]}
22 | 23 |

24 | 25 | 26 | <%def name="sidebar_bottom()"> 27 | -------------------------------------------------------------------------------- /examples/tg2/tg2/i18n/ru/LC_MESSAGES/tg2.po: -------------------------------------------------------------------------------- 1 | # Russian translations for ${package}. 2 | # Copyright (C) 2008 ORGANIZATION 3 | # This file is distributed under the same license as the ${package} project. 4 | # FIRST AUTHOR , 2008. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: ${package} 0.0.0\n" 9 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 10 | "POT-Creation-Date: 2008-01-13 14:00+0200\n" 11 | "PO-Revision-Date: 2008-01-13 14:00+0200\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: ru \n" 14 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " 15 | "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 0.9.1\n" 20 | 21 | #: ${package}/controllers/root.py:13 22 | msgid "Your application is now running" 23 | msgstr "Ваши приложение успешно запущено" 24 | 25 | -------------------------------------------------------------------------------- /examples/tg2/tg2/websetup/schema.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Setup the tg2 application""" 3 | 4 | import logging 5 | from tg import config 6 | import transaction 7 | 8 | def setup_schema(command, conf, vars): 9 | """Place any commands to setup tg2 here""" 10 | # Load the models 11 | 12 | # 13 | from tg2 import model 14 | # 15 | 16 | 17 | # 18 | print "Creating tables" 19 | model.metadata.create_all(bind=config['pylons.app_globals'].sa_engine) 20 | # 21 | transaction.commit() 22 | from migrate.versioning.shell import main 23 | from migrate.exceptions import DatabaseAlreadyControlledError 24 | try: 25 | main(argv=['version_control'], url=config['sqlalchemy.url'], repository='migration', name='migration') 26 | except DatabaseAlreadyControlledError: 27 | print 'Database already under version control' 28 | -------------------------------------------------------------------------------- /examples/tg2/migration/migrate.cfg: -------------------------------------------------------------------------------- 1 | [db_settings] 2 | # Used to identify which repository this database is versioned under. 3 | # You can use the name of your project. 4 | repository_id=migration 5 | 6 | # The name of the database table used to track the schema version. 7 | # This name shouldn't already be used by your project. 8 | # If this is changed once a database is under version control, you'll need to 9 | # change the table name in each database too. 10 | version_table=migrate_version 11 | 12 | # When committing a change script, Migrate will attempt to generate the 13 | # sql for all supported databases; normally, if one of them fails - probably 14 | # because you don't have that database installed - it is ignored and the 15 | # commit continues, perhaps ending successfully. 16 | # Databases in this list MUST compile successfully during a commit, or the 17 | # entire commit will fail. List the databases your application will actually 18 | # be using to ensure your updates to that database work properly. 19 | # This must be a list; example: ['postgres','sqlite'] 20 | required_dbs=[] 21 | -------------------------------------------------------------------------------- /examples/tg2/tg2/lib/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """The base Controller API.""" 4 | 5 | from tg import TGController, tmpl_context 6 | from tg.render import render 7 | from tg import request 8 | from tg.i18n import ugettext as _, ungettext 9 | import tg2.model as model 10 | 11 | __all__ = ['BaseController'] 12 | 13 | 14 | class BaseController(TGController): 15 | """ 16 | Base class for the controllers in the application. 17 | 18 | Your web application should have one of these. The root of 19 | your application is used to compute URLs used by your app. 20 | 21 | """ 22 | 23 | def __call__(self, environ, start_response): 24 | """Invoke the Controller""" 25 | # TGController.__call__ dispatches to the Controller method 26 | # the request is routed to. This routing information is 27 | # available in environ['pylons.routes_dict'] 28 | 29 | request.identity = request.environ.get('repoze.who.identity') 30 | tmpl_context.identity = request.identity 31 | return TGController.__call__(self, environ, start_response) 32 | -------------------------------------------------------------------------------- /examples/tg2/tg2/controllers/secure.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Sample controller with all its actions protected.""" 3 | from tg import expose, flash 4 | from tg.i18n import ugettext as _, lazy_ugettext as l_ 5 | from repoze.what.predicates import has_permission 6 | 7 | from tg2.lib.base import BaseController 8 | 9 | __all__ = ['SecureController'] 10 | 11 | class SecureController(BaseController): 12 | """Sample controller-wide authorization""" 13 | 14 | # The predicate that must be met for all the actions in this controller: 15 | allow_only = has_permission('manage', 16 | msg=l_('Only for people with the "manage" permission')) 17 | 18 | @expose('tg2.templates.index') 19 | def index(self): 20 | """Let the user know that's visiting a protected controller.""" 21 | flash(_("Secure Controller here")) 22 | return dict(page='index') 23 | 24 | @expose('tg2.templates.index') 25 | def some_where(self): 26 | """Let the user know that this action is protected too.""" 27 | return dict(page='some_where') 28 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/data.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | 3 | <%def name="title()"> 4 | Welcome to TurboGears 2.1, standing on the shoulders of giants, since 2007 5 | 6 | 7 | ${parent.sidebar_top()} 8 | 9 |

Content Type Dispatch

10 |

11 | This page shows how you can provide multiple pages 12 | directly from the same controller method. This page is generated 13 | from the expose decorator with the template defintion provided. 14 | You can provide a url with parameters and this page will display 15 | the parameters as html, and the json version will express 16 | the entries as JSON. Here, try it out: /data.html?a=1&b=2 17 |

18 | 19 |

Click here for the JSON Version of this page.

20 |

The data provided in the template call is: 21 | 22 | %for key, value in params.iteritems(): 23 | 24 | 25 | 26 | 27 | %endfor 28 |
${key}${value}
29 | 30 | 31 | <%def name="sidebar_bottom()"> 32 | -------------------------------------------------------------------------------- /examples/tg2/tg2/controllers/template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Fallback controller.""" 3 | 4 | from tg2.lib.base import BaseController 5 | from tg import abort 6 | 7 | __all__ = ['TemplateController'] 8 | 9 | 10 | class TemplateController(BaseController): 11 | """ 12 | The fallback controller for tg2. 13 | 14 | By default, the final controller tried to fulfill the request 15 | when no other routes match. It may be used to display a template 16 | when all else fails, e.g.:: 17 | 18 | def view(self, url): 19 | return render('/%s' % url) 20 | 21 | Or if you're using Mako and want to explicitly send a 404 (Not 22 | Found) response code when the requested template doesn't exist:: 23 | 24 | import mako.exceptions 25 | 26 | def view(self, url): 27 | try: 28 | return render('/%s' % url) 29 | except mako.exceptions.TopLevelLookupException: 30 | abort(404) 31 | 32 | """ 33 | 34 | def view(self, url): 35 | """Abort the request with a 404 HTTP status code.""" 36 | abort(404) 37 | -------------------------------------------------------------------------------- /examples/tg2/tg2/controllers/error.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Error controller""" 3 | 4 | from tg import request, expose 5 | 6 | __all__ = ['ErrorController'] 7 | 8 | 9 | class ErrorController(object): 10 | """ 11 | Generates error documents as and when they are required. 12 | 13 | The ErrorDocuments middleware forwards to ErrorController when error 14 | related status codes are returned from the application. 15 | 16 | This behaviour can be altered by changing the parameters to the 17 | ErrorDocuments middleware in your config/middleware.py file. 18 | 19 | """ 20 | 21 | @expose('tg2.templates.error') 22 | def document(self, *args, **kwargs): 23 | """Render the error document""" 24 | resp = request.environ.get('pylons.original_response') 25 | default_message = ("

We're sorry but we weren't able to process " 26 | " this request.

") 27 | values = dict(prefix=request.environ.get('SCRIPT_NAME', ''), 28 | code=request.params.get('code', resp.status_int), 29 | message=request.params.get('message', default_message)) 30 | return values 31 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/index.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | 3 | <%def name="title()"> 4 | Welcome to TurboGears 2.1, standing on the shoulders of giants, since 2007 5 | 6 | 7 | ${parent.sidebar_top()} 8 | 9 |
10 |

Presentation

11 |

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

12 |
    13 |
  1. 14 |

    Code your data model

    15 |

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

    16 |
  2. 17 |
  3. 18 |

    Design your URL architecture

    19 |

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

    21 |
  4. 22 |
  5. 23 |

    Distribute your app

    24 |

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

    25 |
  6. 26 |
27 |
28 |
29 |
Thank you for choosing TurboGears. 30 |
31 | 32 | <%def name="sidebar_bottom()"> 33 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import sys, os 3 | 4 | f = open('README.rst') 5 | long_description = f.read() 6 | f.close() 7 | 8 | version = '0.0.3' 9 | 10 | setup(name='fedmsg_middleware', 11 | version=version, 12 | description="WSGI middleware for display fedmsg messages.", 13 | long_description=long_description, 14 | classifiers=[ 15 | 'Development Status :: 4 - Beta', 16 | 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', 17 | 'Intended Audience :: Developers', 18 | 'License :: OSI Approved :: MIT License', 19 | ], 20 | author='Ralph Bean', 21 | author_email='rbean@redhat.com', 22 | url='http://github.com/ralphbean/fedmsg_middleware', 23 | license='LGPLv2+', 24 | packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), 25 | include_package_data=True, 26 | zip_safe=False, 27 | install_requires=[ 28 | 'webob', 29 | 'weberror', 30 | 'BeautifulSoup<4.0a1', 31 | 'moksha.wsgi', 32 | 33 | # TODO -- this only *really* needs fedmsg.config and and fedmsg.text. 34 | # Can we not install the whole twisted beast? 35 | 'fedmsg', 36 | ], 37 | entry_points=""" 38 | [paste.filter_app_factory] 39 | middleware = fedmsg_middleware:make_middleware 40 | main = fedmsg_middleware:make_middleware 41 | """, 42 | ) 43 | -------------------------------------------------------------------------------- /examples/tg2/fedmsg.d/ssl.py: -------------------------------------------------------------------------------- 1 | # This file is part of fedmsg. 2 | # Copyright (C) 2012 Red Hat, Inc. 3 | # 4 | # fedmsg is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 2.1 of the License, or (at your option) any later version. 8 | # 9 | # fedmsg is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with fedmsg; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | # 18 | # Authors: Ralph Bean 19 | # 20 | import os 21 | import socket 22 | 23 | SEP = os.path.sep 24 | here = os.getcwd() 25 | hostname = socket.gethostname() 26 | 27 | config = dict( 28 | sign_messages=False, 29 | validate_signatures=False, 30 | ssldir="/etc/pki/fedmsg", 31 | 32 | crl_location="https://fedoraproject.org/fedmsg/crl.pem", 33 | crl_cache="/tmp/crl.pem", 34 | crl_cache_expiry=10, 35 | 36 | certnames={ 37 | # In prod/stg, map hostname to the name of the cert in ssldir. 38 | # Unfortunately, we can't use socket.getfqdn() 39 | #"app01.stg": "app01.stg.phx2.fedoraproject.org", 40 | }, 41 | ) 42 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Fedmsg WSGI Middleware (Notifications) 2 | ====================================== 3 | 4 | Installation 5 | ------------ 6 | 7 | You could install it yourself with `pip`:: 8 | 9 | $ pip install fedmsg_middleware 10 | 11 | Or you could add ``fedmsg_middleware`` to the list of required packages in the 12 | ``setup.py`` file of your project. 13 | 14 | Usage in TurboGears 2 15 | --------------------- 16 | 17 | Simply edit ``myapp/config/middleware.py`` and add the following to 18 | ``make_app(...)``:: 19 | 20 | # Wrap your base TurboGears 2 application with custom middleware here 21 | import fedmsg_middleware 22 | app = fedmsg_middleware.make_middleware(app) 23 | 24 | Usage in Pyramid 25 | ---------------- 26 | 27 | Edit ``myapp/__init__.py`` and replace the ``return config.make_wsgi_app()`` 28 | line with the following:: 29 | 30 | import fedmsg_middleware 31 | app = config.make_wsgi_app() 32 | app = fedmsg_middleware.make_middleware(app) 33 | return app 34 | 35 | Usage in a PasteDeploy pipeline 36 | ------------------------------- 37 | 38 | You can enable it in your PasteDeploy pipeline like so:: 39 | 40 | [pipeline:main] 41 | pipeline = 42 | fedmsg_middleware 43 | my-app 44 | 45 | [filter:fedmsg_middleware] 46 | use = egg:fedmsg_middleware 47 | topic = org.fedoraproject.prod.koji.* 48 | 49 | [app:myapp] 50 | ... 51 | 52 | Get the source 53 | -------------- 54 | 55 | The code and bug tracker live over at 56 | http://github.com/ralphbean/fedmsg_middleware. 57 | Please fork and improve! 58 | -------------------------------------------------------------------------------- /examples/tg2/tg2/public/css/admin.css: -------------------------------------------------------------------------------- 1 | .admin_grid{ 2 | margin-top: 1em; 3 | width: 400px; 4 | } 5 | .admin_grid img{ 6 | border: 0; 7 | vertical-align: middle; 8 | } 9 | 10 | a.add_link, a.edit_link{ 11 | background-repeat: no-repeat; 12 | padding-left: 20px; 13 | } 14 | a.add_link{ 15 | background-image: url('/images/add.png'); 16 | } 17 | a.edit_link{ 18 | background-image: url('/images/pencil.png'); 19 | } 20 | 21 | /* admin list view */ 22 | .crud_table .grid { 23 | border-collapse: collapse; 24 | border: 1px solid #ccc; 25 | } 26 | .crud_table .grid .odd{ 27 | background-color: #eee; 28 | } 29 | .crud_table .grid th{ 30 | background: url("/images/menubg.png") no-repeat scroll -20px top transparent; 31 | color: #fff; 32 | font-weight: normal; 33 | text-transform: capitalize; 34 | } 35 | .crud_table .grid td a{ 36 | color: #286571; 37 | } 38 | .crud_table .grid td.col_0 div{ 39 | float: left; 40 | } 41 | .crud_table .grid td.col_0 .edit_link, .crud_table .grid td.col_0 .delete-button{ 42 | display:block; 43 | height:17px; 44 | width:20px; 45 | padding:0; 46 | text-indent:-400px; 47 | background-repeat: no-repeat; 48 | } 49 | .crud_table .grid td.col_0 .delete-button{ 50 | font-size: inherit; 51 | background-image: url('/images/delete.png'); 52 | cursor: pointer; 53 | } 54 | 55 | /* admin add/edit view */ 56 | .crud_edit label, .crud_add label{ 57 | font-size: 12px; 58 | } 59 | .crud_edit input, .crud_add input, 60 | .crud_edit select, .crud_add select{ 61 | width: 15em; 62 | } -------------------------------------------------------------------------------- /examples/tg2/fedmsg.d/relay.py: -------------------------------------------------------------------------------- 1 | # This file is part of fedmsg. 2 | # Copyright (C) 2012 Red Hat, Inc. 3 | # 4 | # fedmsg is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 2.1 of the License, or (at your option) any later version. 8 | # 9 | # fedmsg is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with fedmsg; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | # 18 | # Authors: Ralph Bean 19 | # 20 | 21 | config = dict( 22 | endpoints={ 23 | # This is the output side of the relay to which all other 24 | # services can listen. 25 | "relay_outbound": [ 26 | "tcp://127.0.0.1:4001", 27 | ], 28 | }, 29 | 30 | # This is the address of an active->passive relay. It is used for the 31 | # fedmsg-logger command which requires another service with a stable 32 | # listening address for it to send messages to. 33 | # It is also used by the git-hook, for the same reason. 34 | # It is also used by the mediawiki php plugin which, due to the oddities of 35 | # php, can't maintain a single passive-bind endpoint of it's own. 36 | relay_inbound=[ 37 | "tcp://127.0.0.1:2003", 38 | ], 39 | ) 40 | -------------------------------------------------------------------------------- /examples/tg2/tg2/config/middleware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """WSGI middleware initialization for the tg2 application.""" 3 | 4 | from tg2.config.app_cfg import base_config 5 | from tg2.config.environment import load_environment 6 | 7 | 8 | __all__ = ['make_app'] 9 | 10 | # Use base_config to setup the necessary PasteDeploy application factory. 11 | # make_base_app will wrap the TG2 app with all the middleware it needs. 12 | make_base_app = base_config.setup_tg_wsgi_app(load_environment) 13 | 14 | from moksha.wsgi.middleware import make_moksha_middleware 15 | from fedmsg_middleware import make_middleware 16 | 17 | def make_app(global_conf, full_stack=True, **app_conf): 18 | """ 19 | Set tg2 up with the settings found in the PasteDeploy configuration 20 | file used. 21 | 22 | :param global_conf: The global settings for tg2 (those 23 | defined under the ``[DEFAULT]`` section). 24 | :type global_conf: dict 25 | :param full_stack: Should the whole TG2 stack be set up? 26 | :type full_stack: str or bool 27 | :return: The tg2 application with all the relevant middleware 28 | loaded. 29 | 30 | This is the PasteDeploy factory for the tg2 application. 31 | 32 | ``app_conf`` contains all the application-specific settings (those defined 33 | under ``[app:main]``. 34 | 35 | 36 | """ 37 | wrap_app = lambda app: make_moksha_middleware( 38 | make_middleware(app), 39 | app_conf, 40 | ) 41 | app = make_base_app(global_conf, 42 | full_stack=True, 43 | wrap_app=wrap_app, 44 | **app_conf) 45 | return app 46 | -------------------------------------------------------------------------------- /examples/tg2/fedmsg.d/base.py: -------------------------------------------------------------------------------- 1 | # This file is part of fedmsg. 2 | # Copyright (C) 2012 Red Hat, Inc. 3 | # 4 | # fedmsg is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 2.1 of the License, or (at your option) any later version. 8 | # 9 | # fedmsg is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with fedmsg; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | # 18 | # Authors: Ralph Bean 19 | # 20 | config = dict( 21 | # Set this to dev if you're hacking on fedmsg or an app. 22 | # Set to stg or prod if running in the Fedora Infrastructure 23 | environment="dev", 24 | 25 | # Default is 0 26 | high_water_mark=0, 27 | io_threads=1, 28 | 29 | ## For the fedmsg-hub and fedmsg-relay. ## 30 | 31 | # We almost always want the fedmsg-hub to be sending messages with zmq as 32 | # opposed to amqp or stomp. 33 | zmq_enabled=True, 34 | 35 | # When subscribing to messages, we want to allow splats ('*') so we tell the 36 | # hub to not be strict when comparing messages topics to subscription 37 | # topics. 38 | zmq_strict=False, 39 | 40 | # Number of seconds to sleep after initializing waiting for sockets to sync. 41 | post_init_sleep=0.2, 42 | ) 43 | -------------------------------------------------------------------------------- /examples/tg2/tg2/tests/models/test_auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Test suite for the TG app's models""" 3 | from nose.tools import eq_ 4 | 5 | from tg2 import model 6 | from tg2.tests.models import ModelTest 7 | 8 | class TestGroup(ModelTest): 9 | """Unit test case for the ``Group`` model.""" 10 | klass = model.Group 11 | attrs = dict( 12 | group_name = u"test_group", 13 | display_name = u"Test Group" 14 | ) 15 | 16 | 17 | class TestUser(ModelTest): 18 | """Unit test case for the ``User`` model.""" 19 | 20 | klass = model.User 21 | attrs = dict( 22 | user_name = u"ignucius", 23 | email_address = u"ignucius@example.org" 24 | ) 25 | 26 | def test_obj_creation_username(self): 27 | """The obj constructor must set the user name right""" 28 | eq_(self.obj.user_name, u"ignucius") 29 | 30 | def test_obj_creation_email(self): 31 | """The obj constructor must set the email right""" 32 | eq_(self.obj.email_address, u"ignucius@example.org") 33 | 34 | def test_no_permissions_by_default(self): 35 | """User objects should have no permission by default.""" 36 | eq_(len(self.obj.permissions), 0) 37 | 38 | def test_getting_by_email(self): 39 | """Users should be fetcheable by their email addresses""" 40 | him = model.User.by_email_address(u"ignucius@example.org") 41 | eq_(him, self.obj) 42 | 43 | 44 | class TestPermission(ModelTest): 45 | """Unit test case for the ``Permission`` model.""" 46 | 47 | klass = model.Permission 48 | attrs = dict( 49 | permission_name = u"test_permission", 50 | description = u"This is a test Description" 51 | ) 52 | -------------------------------------------------------------------------------- /examples/tg2/tg2/websetup/bootstrap.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Setup the tg2 application""" 3 | 4 | import logging 5 | from tg import config 6 | from tg2 import model 7 | import transaction 8 | 9 | def bootstrap(command, conf, vars): 10 | """Place any commands to setup tg2 here""" 11 | 12 | # 55 | -------------------------------------------------------------------------------- /examples/tg2/tg2/tests/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Unit test suite for the models of the application.""" 3 | from nose.tools import assert_equals 4 | from tg2.model import DBSession 5 | from tg2.tests import setup_db, teardown_db 6 | 7 | __all__ = ['ModelTest'] 8 | 9 | # Create an empty database before we start our tests for this module 10 | def setup(): 11 | """Function called by nose on module load""" 12 | setup_db() 13 | 14 | # Tear down that database 15 | def teardown(): 16 | """Function called by nose after all tests in this module ran""" 17 | teardown_db() 18 | 19 | 20 | class ModelTest(object): 21 | """Base unit test case for the models.""" 22 | 23 | klass = None 24 | attrs = {} 25 | 26 | def setUp(self): 27 | """Prepare model test fixture.""" 28 | try: 29 | new_attrs = {} 30 | new_attrs.update(self.attrs) 31 | new_attrs.update(self.do_get_dependencies()) 32 | self.obj = self.klass(**new_attrs) 33 | DBSession.add(self.obj) 34 | DBSession.flush() 35 | return self.obj 36 | except: 37 | DBSession.rollback() 38 | raise 39 | 40 | def tearDown(self): 41 | """Finish model test fixture.""" 42 | DBSession.rollback() 43 | 44 | def do_get_dependencies(self): 45 | """Get model test dependencies. 46 | 47 | Use this method to pull in other objects that need to be created 48 | for this object to be build properly. 49 | 50 | """ 51 | return {} 52 | 53 | def test_create_obj(self): 54 | """Model objects can be created""" 55 | pass 56 | 57 | def test_query_obj(self): 58 | """Model objects can be queried""" 59 | obj = DBSession.query(self.klass).one() 60 | for key, value in self.attrs.iteritems(): 61 | assert_equals(getattr(obj, key), value) 62 | -------------------------------------------------------------------------------- /examples/tg2/setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = dev 3 | tag_svn_revision = true 4 | 5 | [nosetests] 6 | with-pylons=test.ini 7 | 8 | # Babel configuration 9 | [compile_catalog] 10 | domain = tg2 11 | directory = tg2/i18n 12 | statistics = true 13 | 14 | [extract_messages] 15 | add_comments = TRANSLATORS: 16 | output_file = tg2/i18n/tg2.pot 17 | width = 80 18 | keywords = l_ 19 | 20 | [init_catalog] 21 | domain = tg2 22 | input_file = tg2/i18n/tg2.pot 23 | output_dir = tg2/i18n 24 | 25 | [update_catalog] 26 | domain = tg2 27 | input_file = tg2/i18n/tg2.pot 28 | output_dir = tg2/i18n 29 | previous = true 30 | 31 | # Static files extraction for TW 32 | [archive_tw_resources] 33 | output = tg2/public/toscawidgets/ 34 | distributions = tg2 35 | #yuicompressor = /home/someuser/bin/yuicompressor.jar 36 | #compresslevel = 2 37 | onepass = true 38 | 39 | [aliases] 40 | # A handy alias to make a release to pypi 41 | release = egg_info -RDb "" sdist bdist_egg register upload 42 | tg2develop = develop -i http://tg.gy/current 43 | tg2deps = easy_install -i http://tg.gy/current AddOns BytecodeAssembler Chameleon coverage DecoratorTools Extremes Genshi Jinja2 Kajiki kid PEAK_Rules repoze.tm2 repoze.what repoze.what.plugins.sql repoze.what_pylons repoze.what_quickstart repoze.who repoze.who_friendlyform repoze.who.plugins.sa repoze.who_testutil simplegeneric sprox SQLAlchemy SymbolType tgext.admin tgext.crud ToscaWidgets transaction TurboJson TurboKid tw.forms zope.interface zope.sqlalchemy 44 | tgupgrade = easy_install -i http://tg.gy/current -U AddOns Babel Beaker BytecodeAssembler Chameleon coverage decorator DecoratorTools Extremes Genshi Jinja2 Kajiki kid Mako MarkupSafe nose Paste PasteDeploy PasteScript PEAK_Rules Pygments Pylons repoze.tm2 repoze.what repoze.what.plugins.sql repoze.what_pylons repoze.what_quickstart repoze.who repoze.who_friendlyform repoze.who.plugins.sa repoze.who_testutil simplegeneric simplejson sprox SQLAlchemy SymbolType Tempita tgext.admin tgext.crud ToscaWidgets transaction TurboJson TurboKid tw.forms WebError WebFlash WebHelpers WebOb WebTest zope.interface zope.sqlalchemy 45 | -------------------------------------------------------------------------------- /examples/tg2/tg2/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Unit and functional test suite for tg2.""" 3 | 4 | from os import path 5 | import sys 6 | 7 | from tg import config 8 | from paste.deploy import loadapp 9 | from paste.script.appinstall import SetupCommand 10 | from routes import url_for 11 | from webtest import TestApp 12 | from nose.tools import eq_ 13 | 14 | from tg2 import model 15 | 16 | __all__ = ['setup_db', 'teardown_db', 'TestController', 'url_for'] 17 | def setup_db(): 18 | """Method used to build a database""" 19 | engine = config['pylons.app_globals'].sa_engine 20 | model.init_model(engine) 21 | model.metadata.create_all(engine) 22 | 23 | def teardown_db(): 24 | """Method used to destroy a database""" 25 | engine = config['pylons.app_globals'].sa_engine 26 | model.metadata.drop_all(engine) 27 | 28 | class TestController(object): 29 | """ 30 | Base functional test case for the controllers. 31 | 32 | The tg2 application instance (``self.app``) set up in this test 33 | case (and descendants) has authentication disabled, so that developers can 34 | test the protected areas independently of the :mod:`repoze.who` plugins 35 | used initially. This way, authentication can be tested once and separately. 36 | 37 | Check tg2.tests.functional.test_authentication for the repoze.who 38 | integration tests. 39 | 40 | This is the officially supported way to test protected areas with 41 | repoze.who-testutil (http://code.gustavonarea.net/repoze.who-testutil/). 42 | 43 | """ 44 | 45 | application_under_test = 'main_without_authn' 46 | 47 | def setUp(self): 48 | """Method called by nose before running each test""" 49 | # Loading the application: 50 | conf_dir = config.here 51 | wsgiapp = loadapp('config:test.ini#%s' % self.application_under_test, 52 | relative_to=conf_dir) 53 | self.app = TestApp(wsgiapp) 54 | # Setting it up: 55 | test_file = path.join(conf_dir, 'test.ini') 56 | cmd = SetupCommand('setup-app') 57 | cmd.run([test_file]) 58 | 59 | def tearDown(self): 60 | """Method called by nose after running each test""" 61 | # Cleaning up the database: 62 | model.DBSession.remove() 63 | teardown_db() 64 | -------------------------------------------------------------------------------- /examples/tg2/setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #quckstarted Options: 3 | # 4 | # sqlalchemy: True 5 | # auth: sqlalchemy 6 | # mako: True 7 | # 8 | # 9 | 10 | import sys 11 | 12 | try: 13 | from setuptools import setup, find_packages 14 | except ImportError: 15 | from ez_setup import use_setuptools 16 | use_setuptools() 17 | from setuptools import setup, find_packages 18 | 19 | testpkgs=['WebTest >= 1.2.3', 20 | 'nose', 21 | 'coverage', 22 | 'wsgiref', 23 | 'repoze.who-testutil >= 1.0.1', 24 | ] 25 | install_requires=[ 26 | "TurboGears2 >= 2.1.5", 27 | "Mako", 28 | "zope.sqlalchemy >= 0.4", 29 | "repoze.tm2 >= 1.0a5", 30 | "sqlalchemy", 31 | "sqlalchemy-migrate", 32 | "repoze.what >= 1.0.8", 33 | "repoze.who-friendlyform >= 1.0.4", 34 | "repoze.what-pylons >= 1.0", 35 | "repoze.who==1.0.19", 36 | "tgext.admin >= 0.3.11", 37 | "repoze.what-quickstart", 38 | "repoze.what.plugins.sql>=1.0.1", 39 | "tw.forms", 40 | ] 41 | 42 | if sys.version_info[:2] == (2,4): 43 | testpkgs.extend(['hashlib', 'pysqlite']) 44 | install_requires.extend(['hashlib', 'pysqlite']) 45 | 46 | print install_requires 47 | 48 | setup( 49 | name='tg2', 50 | version='0.1', 51 | description='', 52 | author='', 53 | author_email='', 54 | #url='', 55 | setup_requires=["PasteScript >= 1.7"], 56 | paster_plugins=['PasteScript', 'Pylons', 'TurboGears2', 'tg.devtools'], 57 | packages=find_packages(exclude=['ez_setup']), 58 | install_requires=install_requires, 59 | include_package_data=True, 60 | test_suite='nose.collector', 61 | tests_require=testpkgs, 62 | package_data={'tg2': ['i18n/*/LC_MESSAGES/*.mo', 63 | 'templates/*/*', 64 | 'public/*/*']}, 65 | message_extractors={'tg2': [ 66 | ('**.py', 'python', None), 67 | ('templates/**.mako', 'mako', None), 68 | ('public/**', 'ignore', None)]}, 69 | 70 | entry_points=""" 71 | [paste.app_factory] 72 | main = tg2.config.middleware:make_app 73 | 74 | [paste.app_install] 75 | main = pylons.util:PylonsInstaller 76 | """, 77 | dependency_links=[ 78 | "http://tg.gy/215/" 79 | ], 80 | zip_safe=False 81 | ) 82 | -------------------------------------------------------------------------------- /examples/tg2/tg2/config/app_cfg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Global configuration file for TG2-specific settings in tg2. 4 | 5 | This file complements development/deployment.ini. 6 | 7 | Please note that **all the argument values are strings**. If you want to 8 | convert them into boolean, for example, you should use the 9 | :func:`paste.deploy.converters.asbool` function, as in:: 10 | 11 | from paste.deploy.converters import asbool 12 | setting = asbool(global_conf.get('the_setting')) 13 | 14 | """ 15 | 16 | from tg.configuration import AppConfig 17 | 18 | import tg2 19 | from tg2 import model 20 | from tg2.lib import app_globals, helpers 21 | 22 | base_config = AppConfig() 23 | base_config.renderers = [] 24 | 25 | base_config.package = tg2 26 | 27 | #Enable json in expose 28 | base_config.renderers.append('json') 29 | #Set the default renderer 30 | base_config.default_renderer = 'mako' 31 | base_config.renderers.append('mako') 32 | #Configure the base SQLALchemy Setup 33 | base_config.use_sqlalchemy = True 34 | base_config.model = tg2.model 35 | base_config.DBSession = tg2.model.DBSession 36 | # Configure the authentication backend 37 | 38 | # YOU MUST CHANGE THIS VALUE IN PRODUCTION TO SECURE YOUR APP 39 | base_config.sa_auth.cookie_secret = "ChangeME" 40 | 41 | base_config.auth_backend = 'sqlalchemy' 42 | base_config.sa_auth.dbsession = model.DBSession 43 | 44 | # what is the class you want to use to search for users in the database 45 | base_config.sa_auth.user_class = model.User 46 | # what is the class you want to use to search for groups in the database 47 | base_config.sa_auth.group_class = model.Group 48 | # what is the class you want to use to search for permissions in the database 49 | base_config.sa_auth.permission_class = model.Permission 50 | 51 | # override this if you would like to provide a different who plugin for 52 | # managing login and logout of your application 53 | base_config.sa_auth.form_plugin = None 54 | 55 | # override this if you are using a different charset for the login form 56 | base_config.sa_auth.charset = 'utf-8' 57 | 58 | # You may optionally define a page where you want users to be redirected to 59 | # on login: 60 | base_config.sa_auth.post_login_url = '/post_login' 61 | 62 | # You may optionally define a page where you want users to be redirected to 63 | # on logout: 64 | base_config.sa_auth.post_logout_url = '/post_logout' 65 | 66 | base_config.use_toscawidgets = False 67 | base_config.use_toscawidgets2 = True 68 | -------------------------------------------------------------------------------- /examples/tg2/tg2/model/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """The application's model objects""" 3 | 4 | from zope.sqlalchemy import ZopeTransactionExtension 5 | from sqlalchemy.orm import scoped_session, sessionmaker 6 | #from sqlalchemy import MetaData 7 | from sqlalchemy.ext.declarative import declarative_base 8 | 9 | # Global session manager: DBSession() returns the Thread-local 10 | # session object appropriate for the current web request. 11 | maker = sessionmaker(autoflush=True, autocommit=False, 12 | extension=ZopeTransactionExtension()) 13 | DBSession = scoped_session(maker) 14 | 15 | # Base class for all of our model classes: By default, the data model is 16 | # defined with SQLAlchemy's declarative extension, but if you need more 17 | # control, you can switch to the traditional method. 18 | DeclarativeBase = declarative_base() 19 | 20 | # There are two convenient ways for you to spare some typing. 21 | # You can have a query property on all your model classes by doing this: 22 | # DeclarativeBase.query = DBSession.query_property() 23 | # Or you can use a session-aware mapper as it was used in TurboGears 1: 24 | # DeclarativeBase = declarative_base(mapper=DBSession.mapper) 25 | 26 | # Global metadata. 27 | # The default metadata is the one from the declarative base. 28 | metadata = DeclarativeBase.metadata 29 | 30 | # If you have multiple databases with overlapping table names, you'll need a 31 | # metadata for each database. Feel free to rename 'metadata2'. 32 | #metadata2 = MetaData() 33 | 34 | ##### 35 | # Generally you will not want to define your table's mappers, and data objects 36 | # here in __init__ but will want to create modules them in the model directory 37 | # and import them at the bottom of this file. 38 | # 39 | ###### 40 | 41 | def init_model(engine): 42 | """Call me before using any of the tables or classes in the model.""" 43 | DBSession.configure(bind=engine) 44 | 45 | # If you are using reflection to introspect your database and create 46 | # table objects for you, your tables must be defined and mapped inside 47 | # the init_model function, so that the engine is available if you 48 | # use the model outside tg2, you need to make sure this is called before 49 | # you use the model. 50 | 51 | # 52 | # See the following example: 53 | 54 | #global t_reflected 55 | 56 | #t_reflected = Table("Reflected", metadata, 57 | # autoload=True, autoload_with=engine) 58 | 59 | #mapper(Reflected, t_reflected) 60 | 61 | # Import your model modules here. 62 | from tg2.model.auth import User, Group, Permission 63 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/authentication.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | <%def name="title()">Learning TurboGears 2.1: Quick guide to authentication. 3 | 4 | ${parent.sidebar_top()} 5 | ${parent.sidebar_bottom()} 6 |
7 |

Authentication & Authorization in a TG2 site.

8 |

If you have access to this page, this means you have enabled authentication and authorization 9 | in the quickstart to create your project.

10 |

11 | The paster command will have created a few specific controllers for you. But before you 12 | go to play with those controllers you'll need to make sure your application has been 13 | properly bootstapped. 14 | This is dead easy, here is how to do this: 15 |

16 | 17 | 18 | paster setup-app development.ini 19 | 20 | 21 |

22 | inside your application's folder and you'll get a database setup (using the preferences you have 23 | set in your development.ini file). This database will also have been prepopulated with some 24 | default logins/passwords so that you can test the secured controllers and methods. 25 |

26 |

27 | To change the comportement of this setup-app command you just need to edit the websetup.py file. 28 |

29 |

30 | Now try to visiting the manage_permission_only URL. You will be challenged with a login/password form. 31 |

32 |

33 | Only managers are authorized to visit this method. You will need to log-in using: 34 |

35 | 36 | login: manager 37 | 38 |

39 |

40 | 41 | password: managepass 42 | 43 |

44 |

45 |

46 | Another protected resource is editor_user_only. This one is protected by a different set of permissions. 47 | You will need to be editor with a password of editpass to be able to access it. 48 |

49 |

50 | The last kind of protected resource in this quickstarted app is a full so called secure controller. This controller is protected globally. 51 | Instead of having a @require decorator on each method, we have set an allow_only attribute at the class level. All the methods in this controller will 52 | require the same level of access. You need to be manager to access secc or secc/some_where. 53 |

54 |
55 | -------------------------------------------------------------------------------- /examples/tg2/tg2/tests/functional/test_root.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Functional test suite for the root controller. 4 | 5 | This is an example of how functional tests can be written for controllers. 6 | 7 | As opposed to a unit-test, which test a small unit of functionality, 8 | functional tests exercise the whole application and its WSGI stack. 9 | 10 | Please read http://pythonpaste.org/webtest/ for more information. 11 | 12 | """ 13 | from nose.tools import assert_true 14 | 15 | from tg2.tests import TestController 16 | 17 | 18 | class TestRootController(TestController): 19 | """Tests for the method in the root controller.""" 20 | 21 | def test_index(self): 22 | """The front page is working properly""" 23 | response = self.app.get('/') 24 | msg = 'TurboGears 2 is rapid web application development toolkit '\ 25 | 'designed to make your life easier.' 26 | # You can look for specific strings: 27 | assert_true(msg in response) 28 | 29 | # You can also access a BeautifulSoup'ed response in your tests 30 | # (First run $ easy_install BeautifulSoup 31 | # and then uncomment the next two lines) 32 | 33 | #links = response.html.findAll('a') 34 | #print links 35 | #assert_true(links, "Mummy, there are no links here!") 36 | 37 | def test_environ(self): 38 | """Displaying the wsgi environ works""" 39 | response = self.app.get('/environ.html') 40 | assert_true('The keys in the environment are: ' in response) 41 | 42 | def test_data(self): 43 | """The data display demo works with HTML""" 44 | response = self.app.get('/data.html?a=1&b=2') 45 | expected = """\ 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
a1
b2
56 | """ 57 | assert expected in response, response 58 | 59 | def test_data_json(self): 60 | """The data display demo works with JSON""" 61 | resp = self.app.get('/data.json?a=1&b=2') 62 | assert '"a": "1", "b": "2"' in resp, resp 63 | 64 | def test_secc_with_manager(self): 65 | """The manager can access the secure controller""" 66 | # Note how authentication is forged: 67 | environ = {'REMOTE_USER': 'manager'} 68 | resp = self.app.get('/secc', extra_environ=environ, status=200) 69 | assert 'Secure Controller here' in resp.body, resp.body 70 | 71 | def test_secc_with_editor(self): 72 | """The editor cannot access the secure controller""" 73 | environ = {'REMOTE_USER': 'editor'} 74 | self.app.get('/secc', extra_environ=environ, status=403) 75 | # It's enough to know that authorization was denied with a 403 status 76 | 77 | def test_secc_with_anonymous(self): 78 | """Anonymous users must not access the secure controller""" 79 | self.app.get('/secc', status=401) 80 | # It's enough to know that authorization was denied with a 401 status 81 | -------------------------------------------------------------------------------- /examples/tg2/tg2/config/deployment.ini_tmpl: -------------------------------------------------------------------------------- 1 | # 2 | # tg2 - TurboGears configuration 3 | # 4 | # The %(here)s variable will be replaced with the parent directory of this file 5 | # 6 | [DEFAULT] 7 | # WARGING == If debug is not set to false, you'll get the interactive 8 | # debugger on production, which is a huge security hole. 9 | 10 | debug = false 11 | email_to = you@yourdomain.com 12 | smtp_server = localhost 13 | error_email_from = paste@localhost 14 | 15 | [server:main] 16 | use = egg:Paste#http 17 | host = 0.0.0.0 18 | port = 8080 19 | 20 | [sa_auth] 21 | cookie_secret = ${app_instance_secret} 22 | 23 | [app:main] 24 | use = egg:tg2 25 | full_stack = true 26 | cache_dir = %(here)s/data 27 | beaker.session.key = tg2 28 | beaker.session.secret = ${app_instance_secret} 29 | app_instance_uuid = ${app_instance_uuid} 30 | 31 | # If you'd like to fine-tune the individual locations of the cache data dirs 32 | # for the Cache data, or the Session saves, un-comment the desired settings 33 | # here: 34 | #beaker.cache.data_dir = %(here)s/data/cache 35 | #beaker.session.data_dir = %(here)s/data/sessions 36 | # Specify the database for SQLAlchemy to use via 37 | # turbogears.database 38 | # %(here) may include a ':' character on Windows environments; this can 39 | # invalidate the URI when specifying a SQLite db via path name 40 | sqlalchemy.url = sqlite:///%(here)s/somedb.db 41 | sqlalchemy.echo = False 42 | 43 | # This line ensures that Genshi will render xhtml when sending the 44 | # output. Change to html or xml, as desired. 45 | templating.genshi.method = xhtml 46 | 47 | # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* 48 | # Debug mode will enable the interactive debugging tool, allowing ANYONE to 49 | # execute malicious code after an exception is raised. 50 | #set debug = false 51 | 52 | # Logging configuration 53 | # Add additional loggers, handlers, formatters here 54 | # Uses python's logging config file format 55 | # http://docs.python.org/lib/logging-config-fileformat.html 56 | 57 | [loggers] 58 | keys = root, tg2, sqlalchemy, auth 59 | 60 | [handlers] 61 | keys = console 62 | 63 | [formatters] 64 | keys = generic 65 | 66 | # If you create additional loggers, add them as a key to [loggers] 67 | [logger_root] 68 | level = INFO 69 | handlers = console 70 | 71 | [logger_tg2] 72 | level = INFO 73 | handlers = 74 | qualname = tg2 75 | 76 | [logger_sqlalchemy] 77 | level = WARN 78 | handlers = 79 | qualname = sqlalchemy.engine 80 | # "level = INFO" logs SQL queries. 81 | # "level = DEBUG" logs SQL queries and results. 82 | # "level = WARN" logs neither. (Recommended for production systems.) 83 | 84 | # A logger for authentication, identification and authorization -- this is 85 | # repoze.who and repoze.what: 86 | [logger_auth] 87 | level = WARN 88 | handlers = 89 | qualname = auth 90 | 91 | # If you create additional handlers, add them as a key to [handlers] 92 | [handler_console] 93 | class = StreamHandler 94 | args = (sys.stderr,) 95 | level = NOTSET 96 | formatter = generic 97 | 98 | # If you create additional formatters, add them as a key to [formatters] 99 | [formatter_generic] 100 | format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s 101 | datefmt = %Y-%m-%d %H:%M:%S 102 | -------------------------------------------------------------------------------- /examples/tg2/tg2/tests/functional/test_authentication.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Integration tests for the :mod:`repoze.who`-powered authentication sub-system. 4 | 5 | As tg2 grows and the authentication method changes, only these tests 6 | should be updated. 7 | 8 | """ 9 | 10 | from tg2.tests import TestController 11 | 12 | 13 | class TestAuthentication(TestController): 14 | """Tests for the default authentication setup. 15 | 16 | By default in TurboGears 2, :mod:`repoze.who` is configured with the same 17 | plugins specified by repoze.what-quickstart (which are listed in 18 | http://code.gustavonarea.net/repoze.what-quickstart/#repoze.what.plugins.quickstart.setup_sql_auth). 19 | 20 | As the settings for those plugins change, or the plugins are replaced, 21 | these tests should be updated. 22 | 23 | """ 24 | 25 | application_under_test = 'main' 26 | 27 | def test_forced_login(self): 28 | """Anonymous users are forced to login 29 | 30 | Test that anonymous users are automatically redirected to the login 31 | form when authorization is denied. Next, upon successful login they 32 | should be redirected to the initially requested page. 33 | 34 | """ 35 | # Requesting a protected area 36 | resp = self.app.get('/secc/', status=302) 37 | assert resp.location.startswith('http://localhost/login') 38 | # Getting the login form: 39 | resp = resp.follow(status=200) 40 | form = resp.form 41 | # Submitting the login form: 42 | form['login'] = u'manager' 43 | form['password'] = 'managepass' 44 | post_login = form.submit(status=302) 45 | # Being redirected to the initially requested page: 46 | assert post_login.location.startswith('http://localhost/post_login') 47 | initial_page = post_login.follow(status=302) 48 | assert 'authtkt' in initial_page.request.cookies, \ 49 | "Session cookie wasn't defined: %s" % initial_page.request.cookies 50 | assert initial_page.location.startswith('http://localhost/secc/'), \ 51 | initial_page.location 52 | 53 | def test_voluntary_login(self): 54 | """Voluntary logins must work correctly""" 55 | # Going to the login form voluntarily: 56 | resp = self.app.get('/login', status=200) 57 | form = resp.form 58 | # Submitting the login form: 59 | form['login'] = u'manager' 60 | form['password'] = 'managepass' 61 | post_login = form.submit(status=302) 62 | # Being redirected to the home page: 63 | assert post_login.location.startswith('http://localhost/post_login') 64 | home_page = post_login.follow(status=302) 65 | assert 'authtkt' in home_page.request.cookies, \ 66 | 'Session cookie was not defined: %s' % home_page.request.cookies 67 | assert home_page.location == 'http://localhost/' 68 | 69 | def test_logout(self): 70 | """Logouts must work correctly""" 71 | # Logging in voluntarily the quick way: 72 | resp = self.app.get('/login_handler?login=manager&password=managepass', 73 | status=302) 74 | resp = resp.follow(status=302) 75 | assert 'authtkt' in resp.request.cookies, \ 76 | 'Session cookie was not defined: %s' % resp.request.cookies 77 | # Logging out: 78 | resp = self.app.get('/logout_handler', status=302) 79 | assert resp.location.startswith('http://localhost/post_logout') 80 | # Finally, redirected to the home page: 81 | home_page = resp.follow(status=302) 82 | authtkt = home_page.request.cookies.get('authtkt') 83 | assert not authtkt or authtkt == 'INVALID', \ 84 | 'Session cookie was not deleted: %s' % home_page.request.cookies 85 | assert home_page.location == 'http://localhost/', home_page.location 86 | -------------------------------------------------------------------------------- /examples/tg2/tg2/controllers/root.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Main Controller""" 3 | 4 | from tg import expose, flash, require, url, lurl, request, redirect 5 | from tg.i18n import ugettext as _, lazy_ugettext as l_ 6 | from tg2 import model 7 | from repoze.what import predicates 8 | from tg2.controllers.secure import SecureController 9 | from tg2.model import DBSession, metadata 10 | from tgext.admin.tgadminconfig import TGAdminConfig 11 | from tgext.admin.controller import AdminController 12 | 13 | from tg2.lib.base import BaseController 14 | from tg2.controllers.error import ErrorController 15 | 16 | __all__ = ['RootController'] 17 | 18 | 19 | class RootController(BaseController): 20 | """ 21 | The root controller for the tg2 application. 22 | 23 | All the other controllers and WSGI applications should be mounted on this 24 | controller. For example:: 25 | 26 | panel = ControlPanelController() 27 | another_app = AnotherWSGIApplication() 28 | 29 | Keep in mind that WSGI applications shouldn't be mounted directly: They 30 | must be wrapped around with :class:`tg.controllers.WSGIAppController`. 31 | 32 | """ 33 | secc = SecureController() 34 | admin = AdminController(model, DBSession, config_type=TGAdminConfig) 35 | 36 | error = ErrorController() 37 | 38 | @expose('tg2.templates.index') 39 | def index(self): 40 | """Handle the front-page.""" 41 | return dict(page='index') 42 | 43 | @expose('tg2.templates.about') 44 | def about(self): 45 | """Handle the 'about' page.""" 46 | return dict(page='about') 47 | 48 | @expose('tg2.templates.environ') 49 | def environ(self): 50 | """This method showcases TG's access to the wsgi environment.""" 51 | return dict(environment=request.environ) 52 | 53 | @expose('tg2.templates.data') 54 | @expose('json') 55 | def data(self, **kw): 56 | """This method showcases how you can use the same controller for a data page and a display page""" 57 | return dict(params=kw) 58 | @expose('tg2.templates.authentication') 59 | def auth(self): 60 | """Display some information about auth* on this application.""" 61 | return dict(page='auth') 62 | 63 | @expose('tg2.templates.index') 64 | @require(predicates.has_permission('manage', msg=l_('Only for managers'))) 65 | def manage_permission_only(self, **kw): 66 | """Illustrate how a page for managers only works.""" 67 | return dict(page='managers stuff') 68 | 69 | @expose('tg2.templates.index') 70 | @require(predicates.is_user('editor', msg=l_('Only for the editor'))) 71 | def editor_user_only(self, **kw): 72 | """Illustrate how a page exclusive for the editor works.""" 73 | return dict(page='editor stuff') 74 | 75 | @expose('tg2.templates.login') 76 | def login(self, came_from=lurl('/')): 77 | """Start the user login.""" 78 | login_counter = request.environ['repoze.who.logins'] 79 | if login_counter > 0: 80 | flash(_('Wrong credentials'), 'warning') 81 | return dict(page='login', login_counter=str(login_counter), 82 | came_from=came_from) 83 | 84 | @expose() 85 | def post_login(self, came_from=lurl('/')): 86 | """ 87 | Redirect the user to the initially requested page on successful 88 | authentication or redirect her back to the login page if login failed. 89 | 90 | """ 91 | if not request.identity: 92 | login_counter = request.environ['repoze.who.logins'] + 1 93 | redirect('/login', 94 | params=dict(came_from=came_from, __logins=login_counter)) 95 | userid = request.identity['repoze.who.userid'] 96 | flash(_('Welcome back, %s!') % userid) 97 | redirect(came_from) 98 | 99 | @expose() 100 | def post_logout(self, came_from=lurl('/')): 101 | """ 102 | Redirect the user to the initially requested page on logout and say 103 | goodbye as well. 104 | 105 | """ 106 | flash(_('We hope to see you soon!')) 107 | redirect(came_from) 108 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/master.mak: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | ${self.meta()} 6 | ${self.title()} 7 | 8 | 9 | 10 | 11 | ${self.header()} 12 | ${self.main_menu()} 13 | ${self.content_wrapper()} 14 | ${self.footer()} 15 | 16 | 17 | <%def name="content_wrapper()"> 18 |
19 |
20 | % if page: 21 |
22 | Now Viewing: ${page} 23 |
24 | % endif 25 | <% 26 | flash=tg.flash_obj.render('flash', use_js=False) 27 | %> 28 | % if flash: 29 | ${flash | n} 30 | % endif 31 | ${self.body()} 32 |
33 | 34 | 35 | <%def name="body_class()"> 36 | 37 | <%def name="meta()"> 38 | 39 | 40 | 41 | <%def name="title()"> 42 | <%def name="sidebar_top()"> 43 | 58 | 59 | 60 | <%def name="sidebar_bottom()"> 61 | 72 | 73 | 74 | <%def name="header()"> 75 | 81 | 82 | <%def name="footer()"> 83 | 87 |
88 |

TurboGears is a open source front-to-back web development 89 | framework written in Python. Copyright (c) 2005-2009

90 |
91 |
92 |
93 | 94 | <%def name="main_menu()"> 95 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /fedmsg_middleware/middleware.py: -------------------------------------------------------------------------------- 1 | 2 | import BeautifulSoup 3 | import webob 4 | import json 5 | 6 | import fedmsg.config 7 | import fedmsg.text as t 8 | 9 | from moksha.common.lib.helpers import get_moksha_appconfig 10 | from moksha.wsgi.widgets.api import get_moksha_socket 11 | from moksha.wsgi.widgets.api import LiveWidget 12 | from tw2.jqplugins.gritter import gritter_resources 13 | 14 | truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) 15 | 16 | 17 | class FedmsgMiddleware(object): 18 | """ WSGI middleware that injects a moksha socket for fedmsg popups """ 19 | 20 | def __init__(self, app, config=None): 21 | """ Configuration arguments are documented in README.rst """ 22 | 23 | self.app = app 24 | self.config = config 25 | 26 | if not self.config: 27 | self.config = get_moksha_appconfig() 28 | 29 | # Initialize fedmsg its own config in /etc/fedmsg.d/ 30 | self.fedmsg_config = fedmsg.config.load_config(None, []) 31 | t.make_processors(**self.fedmsg_config) 32 | 33 | def __call__(self, environ, start_response): 34 | """ Process a request. """ 35 | 36 | req = webob.Request(environ) 37 | 38 | if self.should_respond(req): 39 | # Is this an ajax request asking for fedmsg.text information? 40 | # If so.. forget everything else and just handle that before anyone 41 | # else notices ;o 42 | resp = self.serve_response(req) 43 | return resp(environ, start_response) 44 | 45 | # Register our fancy widget with the moksha middleware. 46 | PopupNotification.display() 47 | 48 | # Pass the request on to the app that we wrap. 49 | resp = req.get_response(self.app, catch_exc_info=True) 50 | 51 | # Should we modify their response and inject the moksha_socket? 52 | # We'll do so in a hopefully delicate manner that 53 | # doesn't disturb other javascript (like jQuery). 54 | if self.should_inject(req, resp): 55 | resp = self.inject(resp) 56 | 57 | return resp(environ, start_response) 58 | 59 | def should_respond(self, req): 60 | """ Determine if this is an AJAX request for fedmsg.text metadata """ 61 | return req.environ['PATH_INFO'] == "/__fedmsg.text__" 62 | 63 | def serve_response(self, req): 64 | """ Translate a fedmsg message into metadata for gritter. """ 65 | msg = json.loads(req.GET['json']) 66 | 67 | proc = t.msg2processor(msg) 68 | text = """{text}""".format( 69 | text=t.msg2subtitle(msg, proc, **self.fedmsg_config), 70 | link=t.msg2link(msg, proc, **self.fedmsg_config), 71 | ) 72 | metadata = dict( 73 | title="fedmsg -> " + t.msg2title(msg, proc, **self.fedmsg_config), 74 | text=text, 75 | image=t.msg2secondary_icon(msg, proc, **self.fedmsg_config), 76 | time=10000, # 10 seconds until messages disappear 77 | ) 78 | 79 | resp = webob.Response( 80 | content_type='application/json', 81 | body=json.dumps(metadata), 82 | ) 83 | return resp 84 | 85 | def should_inject(self, req, resp): 86 | """ Determine if this request should be modified. Boolean. """ 87 | 88 | if resp.status != "200 OK": 89 | return False 90 | 91 | content_type = resp.headers.get('Content-Type', 'text/plain').lower() 92 | if not 'html' in content_type: 93 | return False 94 | 95 | # Did the wrapped app already inject a moksha socket? 96 | if 'moksha_websocket = ' in resp.body: 97 | return False 98 | 99 | return True 100 | 101 | def inject(self, resp): 102 | """ Inject notification machinery into this response! 103 | 104 | Insert javascript into the tag. 105 | """ 106 | 107 | soup = BeautifulSoup.BeautifulSoup(resp.body) 108 | 109 | if not soup.html: 110 | return resp 111 | 112 | if not soup.html.head: 113 | soup.html.insert(0, BeautifulSoup.Tag(soup, "head")) 114 | 115 | def add_payload(payload): 116 | payload = BeautifulSoup.BeautifulSoup(payload) 117 | soup.html.body.insert(len(soup.html.body), payload) 118 | 119 | socket = get_moksha_socket(self.config) 120 | 121 | add_payload(PopupNotification.display()) 122 | add_payload(socket().display()) 123 | 124 | resp.body = str(soup.prettify()) 125 | return resp 126 | 127 | 128 | class PopupNotification(LiveWidget): 129 | topic = "*" 130 | onmessage = """ 131 | (function(json){ 132 | // Make an ajax request to get the fedmsg.text metadata and use that 133 | // metadata to make the gritter popup. 134 | $.ajax("/__fedmsg.text__", { 135 | data: {json: JSON.stringify(json)}, 136 | success: $.gritter.add, 137 | }); 138 | })(json); 139 | """ 140 | resources = LiveWidget.resources + gritter_resources 141 | backend = "websocket" 142 | 143 | # Don't actually produce anything when you call .display() on this widget. 144 | inline_engine_name = "mako" 145 | template = "" 146 | 147 | 148 | def make_middleware(app=None, *args, **kw): 149 | app = FedmsgMiddleware(app, *args, **kw) 150 | return app 151 | -------------------------------------------------------------------------------- /examples/tg2/development.ini: -------------------------------------------------------------------------------- 1 | # 2 | # tg2 - Pylons development environment configuration 3 | # 4 | # The %(here)s variable will be replaced with the parent directory of this file 5 | # 6 | # This file is for deployment specific config options -- other configuration 7 | # that is always required for the app is done in the config directory, 8 | # and generally should not be modified by end users. 9 | 10 | [DEFAULT] 11 | debug = true 12 | # Uncomment and replace with the address which should receive any error reports 13 | #email_to = you@yourdomain.com 14 | smtp_server = localhost 15 | error_email_from = paste@localhost 16 | 17 | [server:main] 18 | use = egg:Paste#http 19 | host = 127.0.0.1 20 | port = 8080 21 | 22 | [sa_auth] 23 | cookie_secret = a072dfb6-ffe8-46f2-91b7-fad4aaecc17f 24 | 25 | [app:main] 26 | use = egg:tg2 27 | full_stack = true 28 | #lang = ru 29 | cache_dir = %(here)s/data 30 | beaker.session.key = tg2 31 | beaker.session.secret = a072dfb6-ffe8-46f2-91b7-fad4aaecc17f 32 | 33 | # Disable template autoreload to boost performances in production 34 | #auto_reload_templates = false 35 | 36 | # If you'd like to fine-tune the individual locations of the cache data dirs 37 | # for the Cache data, or the Session saves, un-comment the desired settings 38 | # here: 39 | #beaker.cache.data_dir = %(here)s/data/cache 40 | #beaker.session.data_dir = %(here)s/data/sessions 41 | 42 | # pick the form for your database 43 | # %(here) may include a ':' character on Windows environments; this can 44 | # invalidate the URI when specifying a SQLite db via path name 45 | # sqlalchemy.url=postgres://username:password@hostname:port/databasename 46 | # sqlalchemy.url=mysql://username:password@hostname:port/databasename 47 | 48 | 49 | # If you have sqlite, here's a simple default to get you started 50 | # in development 51 | 52 | sqlalchemy.url = sqlite:///%(here)s/devdata.db 53 | #echo shouldn't be used together with the logging module. 54 | sqlalchemy.echo = false 55 | sqlalchemy.echo_pool = false 56 | sqlalchemy.pool_recycle = 3600 57 | 58 | # This line ensures that Genshi will render xhtml when sending the 59 | # output. Change to html or xml, as desired. 60 | templating.genshi.method = xhtml 61 | 62 | # if you are using Mako and want to be able to reload 63 | # the mako template from disk during the development phase 64 | # you should say 'true' here 65 | # This option is only used for mako templating engine 66 | # WARNING: if you want to deploy your application using a zipped egg 67 | # (ie: if your application's setup.py defines zip-safe=True, then you 68 | # MUST put "false" for the production environment because there will 69 | # be no disk and real files to compare time with. 70 | # On the contrary if your application defines zip-safe=False and is 71 | # deployed in an unzipped manner, then you can leave this option to true 72 | templating.mako.reloadfromdisk = true 73 | 74 | # the compiled template dir is a directory that must be readable by your 75 | # webserver. It will be used to store the resulting templates once compiled 76 | # by the TemplateLookup system. 77 | # During development you generally don't need this option since paste's HTTP 78 | # server will have access to you development directories, but in production 79 | # you'll most certainly want to have apache or nginx to write in a directory 80 | # that does not contain any source code in any form for obvious security reasons. 81 | templating.mako.compiled_templates_dir = %(here)s/data/templates 82 | 83 | # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* 84 | # Debug mode will enable the interactive debugging tool, allowing ANYONE to 85 | # execute malicious code after an exception is raised. 86 | #set debug = false 87 | 88 | # Logging configuration 89 | # Add additional loggers, handlers, formatters here 90 | # Uses python's logging config file format 91 | # http://docs.python.org/lib/logging-config-fileformat.html 92 | 93 | #turn this setting to "min" if you would like tw to produce minified 94 | #javascript files (if your library supports that) 95 | toscawidgets.framework.resource_variant=debug 96 | 97 | moksha.domain = localhost 98 | 99 | moksha.livesocket = True 100 | moksha.livesocket.backend = websocket 101 | moksha.livesocket.websocket.port = 9998 102 | 103 | moksha.socket.notify = True 104 | 105 | zmq_enabled = True 106 | zmq_strict = False 107 | zmq_publish_endpoints = tcp://*:3000 108 | # Listen to myself, the local fedmsg-relay, and to the fedora infrastructure bus 109 | zmq_subscribe_endpoints = tcp://127.0.0.1:3000,tcp://127.0.0.1:4001,tcp://hub.fedoraproject.org:9940 110 | 111 | [loggers] 112 | keys = root, tg2, sqlalchemy, auth 113 | 114 | [handlers] 115 | keys = console 116 | 117 | [formatters] 118 | keys = generic 119 | 120 | # If you create additional loggers, add them as a key to [loggers] 121 | [logger_root] 122 | level = INFO 123 | handlers = console 124 | 125 | [logger_tg2] 126 | level = DEBUG 127 | handlers = 128 | qualname = tg2 129 | 130 | [logger_sqlalchemy] 131 | level = INFO 132 | handlers = 133 | qualname = sqlalchemy.engine 134 | # "level = INFO" logs SQL queries. 135 | # "level = DEBUG" logs SQL queries and results. 136 | # "level = WARN" logs neither. (Recommended for production systems.) 137 | 138 | # A logger for authentication, identification and authorization -- this is 139 | # repoze.who and repoze.what: 140 | [logger_auth] 141 | level = WARN 142 | handlers = 143 | qualname = auth 144 | 145 | # If you create additional handlers, add them as a key to [handlers] 146 | [handler_console] 147 | class = StreamHandler 148 | args = (sys.stderr,) 149 | level = NOTSET 150 | formatter = generic 151 | 152 | # If you create additional formatters, add them as a key to [formatters] 153 | [formatter_generic] 154 | format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s 155 | datefmt = %H:%M:%S 156 | -------------------------------------------------------------------------------- /examples/tg2/tg2/templates/about.mak: -------------------------------------------------------------------------------- 1 | <%inherit file="local:templates.master"/> 2 | 3 | <%def name="title()"> 4 | Learning TurboGears 2.1: Quick guide to the Quickstart pages. 5 | 6 | 7 | ${parent.sidebar_top()} 8 | ${parent.sidebar_bottom()} 9 |
10 |

Architectural basics of a quickstart TG2 site.

11 |

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

12 |
    13 |
  1. 14 |

    Code my data model

    15 |

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

    17 |

    You can build a dynamic site without any data model at all. There 18 | still be a default data-model template for you if you didn't enable 19 | authentication and authorization in quickstart. If you have enabled 20 | authorization, the auth data-model is ready-made.

    21 |
  2. 22 |
  3. 23 |

    Design my URL structure

    24 |

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

    29 |

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

    31 |
  4. 32 |
  5. 33 |

    Reuse the web page elements

    34 |

    A web page viewed by user could be constructed by single or 35 | several reusable templates under /templates. 36 | Take 'about' page for example, each reusable template generating 37 | a part of the page.

    38 | 39 |

    about.mak - This is the 40 | template that created this page. If you take a look at this template 41 | you will see that there are a lot of wacky symbols, if you are not familiar 42 | with Mako you should probably 43 | check out the docs on their site.

    44 | 45 |

    Let's take a look at what this template does in order of execution. 46 | The first thing this template does 47 | is inherit from master.mak. We'll 48 | go into the details of this later, but for now just know that this 49 | template is allowed to both call on master.mak, and also override it's 50 | capabilites. This inheritance is what gives mako it's power to 51 | provide reusable templates. 52 |

    53 |

    But um, whats that 'local:' stuff about?

    54 |

    55 | Well, TG wants to provide re-usable components like tgext.admin, and also 56 | provide a way for you to use your default site layouts. This is done 57 | easily by providing a shortcut to the namespace of your project, so the 58 | component template finder can find your master.html and format 59 | itself the way you want it to. 60 |

    61 |

    The next thing about.mak does is to create a function called title() 62 | which provides the title for the document. This overrides the title 63 | method provided by master.mak, and therefore, when the template 64 | is rendered, it will use the title provided by this funciton. 65 | 66 |

    67 | Next, there are a couple of calls to the master template to set up the 68 | boxes you see in the page over on the right, We'll examine what 69 | these are in the master template in a second. Finally, we get to the meat 70 | of the document, and that's pretty much all about.mak does 71 |

    72 | 73 |

    master.mak - 74 | This template provides the overall layout for your project, and 75 | allows you to override different elements of the overall structure 76 | of your default look-and-feel. Let's take a look at this template 77 | from top to bottom again.

    78 |

    The first 15 lines provide an overall layout for the document, 79 | with definitions for the header, title and body. Each one of these 80 | sections has been turned into a method that you can change within your 81 | child templates, but you do not have to provide any single one of them 82 | TurboGears extensions may however expect these methods to be provided 83 | to display their content properly. Keep this in mind if you decide 84 | to alter your master.mak. You are of course always free to modify 85 | your master template, but doing so can render your extensions useless, 86 | so tread carefully.

    87 | 88 |

    The next section describes the overall layout for the body of the 89 | document, with flash messages appearing at the top, and self.body() 90 | appearing at the bottom. self.body will take whatever is not defined 91 | as a method in your child template and render it in this space. This 92 | is really useful because it allows you to be freestyle with your 93 | body definition. If you don't want to override any of the master 94 | template defines, all you have to do is inherit the master, and then 95 | provide the code you want to appear surrounded by your header and footer. 96 |

    97 | 98 |

    99 | Next are the title, header, footer and sidebar defines. These are all of 100 | course overriddable. 101 |

    102 | 103 |

    The final define sets up the menubar. This provides some links, 104 | as well as a way of highlighing the page you are currently on. 105 |

    106 |
  6. 107 |
108 |

Good luck with TurboGears 2!

109 |
' % self.group_name).encode('utf-8') 82 | 83 | def __unicode__(self): 84 | return self.group_name 85 | 86 | #} 87 | 88 | 89 | # The 'info' argument we're passing to the email_address and password columns 90 | # contain metadata that Rum (http://python-rum.org/) can use generate an 91 | # admin interface for your models. 92 | class User(DeclarativeBase): 93 | """ 94 | User definition. 95 | 96 | This is the user definition used by :mod:`repoze.who`, which requires at 97 | least the ``user_name`` column. 98 | 99 | """ 100 | __tablename__ = 'tg_user' 101 | 102 | #{ Columns 103 | 104 | user_id = Column(Integer, autoincrement=True, primary_key=True) 105 | 106 | user_name = Column(Unicode(16), unique=True, nullable=False) 107 | 108 | email_address = Column(Unicode(255), unique=True, nullable=False, 109 | info={'rum': {'field':'Email'}}) 110 | 111 | display_name = Column(Unicode(255)) 112 | 113 | _password = Column('password', Unicode(128), 114 | info={'rum': {'field':'Password'}}) 115 | 116 | created = Column(DateTime, default=datetime.now) 117 | 118 | #{ Special methods 119 | 120 | def __repr__(self): 121 | return ('' % ( 122 | self.user_name, self.email_address, self.display_name)).encode('utf-8') 123 | 124 | def __unicode__(self): 125 | return self.display_name or self.user_name 126 | 127 | #{ Getters and setters 128 | 129 | @property 130 | def permissions(self): 131 | """Return a set with all permissions granted to the user.""" 132 | perms = set() 133 | for g in self.groups: 134 | perms = perms | set(g.permissions) 135 | return perms 136 | 137 | @classmethod 138 | def by_email_address(cls, email): 139 | """Return the user object whose email address is ``email``.""" 140 | return DBSession.query(cls).filter_by(email_address=email).first() 141 | 142 | @classmethod 143 | def by_user_name(cls, username): 144 | """Return the user object whose user name is ``username``.""" 145 | return DBSession.query(cls).filter_by(user_name=username).first() 146 | 147 | @classmethod 148 | def _hash_password(cls, password): 149 | # Make sure password is a str because we cannot hash unicode objects 150 | if isinstance(password, unicode): 151 | password = password.encode('utf-8') 152 | salt = sha256() 153 | salt.update(os.urandom(60)) 154 | hash = sha256() 155 | hash.update(password + salt.hexdigest()) 156 | password = salt.hexdigest() + hash.hexdigest() 157 | # Make sure the hashed password is a unicode object at the end of the 158 | # process because SQLAlchemy _wants_ unicode objects for Unicode cols 159 | if not isinstance(password, unicode): 160 | password = password.decode('utf-8') 161 | return password 162 | 163 | def _set_password(self, password): 164 | """Hash ``password`` on the fly and store its hashed version.""" 165 | self._password = self._hash_password(password) 166 | 167 | def _get_password(self): 168 | """Return the hashed version of the password.""" 169 | return self._password 170 | 171 | password = synonym('_password', descriptor=property(_get_password, 172 | _set_password)) 173 | 174 | #} 175 | 176 | def validate_password(self, password): 177 | """ 178 | Check the password against existing credentials. 179 | 180 | :param password: the password that was provided by the user to 181 | try and authenticate. This is the clear text version that we will 182 | need to match against the hashed one in the database. 183 | :type password: unicode object. 184 | :return: Whether the password is valid. 185 | :rtype: bool 186 | 187 | """ 188 | hash = sha256() 189 | if isinstance(password, unicode): 190 | password = password.encode('utf-8') 191 | hash.update(password + str(self.password[:64])) 192 | return self.password[64:] == hash.hexdigest() 193 | 194 | 195 | class Permission(DeclarativeBase): 196 | """ 197 | Permission definition for :mod:`repoze.what`. 198 | 199 | Only the ``permission_name`` column is required by :mod:`repoze.what`. 200 | 201 | """ 202 | 203 | __tablename__ = 'tg_permission' 204 | 205 | #{ Columns 206 | 207 | permission_id = Column(Integer, autoincrement=True, primary_key=True) 208 | 209 | permission_name = Column(Unicode(63), unique=True, nullable=False) 210 | 211 | description = Column(Unicode(255)) 212 | 213 | #{ Relations 214 | 215 | groups = relation(Group, secondary=group_permission_table, 216 | backref='permissions') 217 | 218 | #{ Special methods 219 | 220 | def __repr__(self): 221 | return ('' % self.permission_name).encode('utf-8') 222 | 223 | def __unicode__(self): 224 | return self.permission_name 225 | 226 | #} 227 | 228 | 229 | #} 230 | -------------------------------------------------------------------------------- /examples/tg2/ez_setup/__init__.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap setuptools installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import sys 17 | DEFAULT_VERSION = "0.6c7" 18 | DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] 19 | 20 | md5_data = { 21 | 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 22 | 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 23 | 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 24 | 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 25 | 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 26 | 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 27 | 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 28 | 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 29 | 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 30 | 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 31 | 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 32 | 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 33 | 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 34 | 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 35 | 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 36 | 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 37 | 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 38 | 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 39 | 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 40 | 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 41 | 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 42 | 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 43 | 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 44 | 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 45 | 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 46 | 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 47 | 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 48 | } 49 | 50 | import sys, os 51 | 52 | def _validate_md5(egg_name, data): 53 | if egg_name in md5_data: 54 | from md5 import md5 55 | digest = md5(data).hexdigest() 56 | if digest != md5_data[egg_name]: 57 | print >>sys.stderr, ( 58 | "md5 validation of %s failed! (Possible download problem?)" 59 | % egg_name 60 | ) 61 | sys.exit(2) 62 | return data 63 | 64 | 65 | def use_setuptools( 66 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 67 | download_delay=15 68 | ): 69 | """Automatically find/download setuptools and make it available on sys.path 70 | 71 | `version` should be a valid setuptools version number that is available 72 | as an egg for download under the `download_base` URL (which should end with 73 | a '/'). `to_dir` is the directory where setuptools will be downloaded, if 74 | it is not already available. If `download_delay` is specified, it should 75 | be the number of seconds that will be paused before initiating a download, 76 | should one be required. If an older version of setuptools is installed, 77 | this routine will print a message to ``sys.stderr`` and raise SystemExit in 78 | an attempt to abort the calling script. 79 | """ 80 | try: 81 | import setuptools 82 | if setuptools.__version__ == '0.0.1': 83 | print >>sys.stderr, ( 84 | "You have an obsolete version of setuptools installed. Please\n" 85 | "remove it from your system entirely before rerunning this script." 86 | ) 87 | sys.exit(2) 88 | except ImportError: 89 | egg = download_setuptools(version, download_base, to_dir, download_delay) 90 | sys.path.insert(0, egg) 91 | import setuptools; setuptools.bootstrap_install_from = egg 92 | 93 | import pkg_resources 94 | try: 95 | pkg_resources.require("setuptools>="+version) 96 | 97 | except pkg_resources.VersionConflict, e: 98 | # XXX could we install in a subprocess here? 99 | print >>sys.stderr, ( 100 | "The required version of setuptools (>=%s) is not available, and\n" 101 | "can't be installed while this script is running. Please install\n" 102 | " a more recent version first.\n\n(Currently using %r)" 103 | ) % (version, e.args[0]) 104 | sys.exit(2) 105 | 106 | def download_setuptools( 107 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 108 | delay = 15 109 | ): 110 | """Download setuptools from a specified location and return its filename 111 | 112 | `version` should be a valid setuptools version number that is available 113 | as an egg for download under the `download_base` URL (which should end 114 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 115 | `delay` is the number of seconds to pause before an actual download attempt. 116 | """ 117 | import urllib2, shutil 118 | egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) 119 | url = download_base + egg_name 120 | saveto = os.path.join(to_dir, egg_name) 121 | src = dst = None 122 | if not os.path.exists(saveto): # Avoid repeated downloads 123 | try: 124 | from distutils import log 125 | if delay: 126 | log.warn(""" 127 | --------------------------------------------------------------------------- 128 | This script requires setuptools version %s to run (even to display 129 | help). I will attempt to download it for you (from 130 | %s), but 131 | you may need to enable firewall access for this script first. 132 | I will start the download in %d seconds. 133 | 134 | (Note: if this machine does not have network access, please obtain the file 135 | 136 | %s 137 | 138 | and place it in this directory before rerunning this script.) 139 | ---------------------------------------------------------------------------""", 140 | version, download_base, delay, url 141 | ); from time import sleep; sleep(delay) 142 | log.warn("Downloading %s", url) 143 | src = urllib2.urlopen(url) 144 | # Read/write all in one block, so we don't create a corrupt file 145 | # if the download is interrupted. 146 | data = _validate_md5(egg_name, src.read()) 147 | dst = open(saveto,"wb"); dst.write(data) 148 | finally: 149 | if src: src.close() 150 | if dst: dst.close() 151 | return os.path.realpath(saveto) 152 | 153 | def main(argv, version=DEFAULT_VERSION): 154 | """Install or upgrade setuptools and EasyInstall""" 155 | 156 | try: 157 | import setuptools 158 | except ImportError: 159 | egg = None 160 | try: 161 | egg = download_setuptools(version, delay=0) 162 | sys.path.insert(0,egg) 163 | from setuptools.command.easy_install import main 164 | return main(list(argv)+[egg]) # we're done here 165 | finally: 166 | if egg and os.path.exists(egg): 167 | os.unlink(egg) 168 | else: 169 | if setuptools.__version__ == '0.0.1': 170 | # tell the user to uninstall obsolete version 171 | use_setuptools(version) 172 | 173 | req = "setuptools>="+version 174 | import pkg_resources 175 | try: 176 | pkg_resources.require(req) 177 | except pkg_resources.VersionConflict: 178 | try: 179 | from setuptools.command.easy_install import main 180 | except ImportError: 181 | from easy_install import main 182 | main(list(argv)+[download_setuptools(delay=0)]) 183 | sys.exit(0) # try to force an exit 184 | else: 185 | if argv: 186 | from setuptools.command.easy_install import main 187 | main(argv) 188 | else: 189 | print "Setuptools version",version,"or greater has been installed." 190 | print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' 191 | 192 | 193 | 194 | def update_md5(filenames): 195 | """Update our built-in md5 registry""" 196 | 197 | import re 198 | from md5 import md5 199 | 200 | for name in filenames: 201 | base = os.path.basename(name) 202 | f = open(name,'rb') 203 | md5_data[base] = md5(f.read()).hexdigest() 204 | f.close() 205 | 206 | data = [" %r: %r,\n" % it for it in md5_data.items()] 207 | data.sort() 208 | repl = "".join(data) 209 | 210 | import inspect 211 | srcfile = inspect.getsourcefile(sys.modules[__name__]) 212 | f = open(srcfile, 'rb'); src = f.read(); f.close() 213 | 214 | match = re.search("\nmd5_data = {\n([^}]+)}", src) 215 | if not match: 216 | print >>sys.stderr, "Internal error!" 217 | sys.exit(2) 218 | 219 | src = src[:match.start(1)] + repl + src[match.end(1):] 220 | f = open(srcfile,'w') 221 | f.write(src) 222 | f.close() 223 | 224 | 225 | if __name__=='__main__': 226 | if len(sys.argv)>2 and sys.argv[1]=='--md5update': 227 | update_md5(sys.argv[2:]) 228 | else: 229 | main(sys.argv[1:]) 230 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | --------------------------------------------------------------------------------