Commit 0ca3e37c authored by Jason Robinson's avatar Jason Robinson
Browse files

Code moved from subversion to github, version 0.3.6 initial commit

parents
Copyright 2010 Jason Robinson. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY JASON ROBINSON ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JASON ROBINSON OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Jason Robinson.
include argonaut/config/deployment.ini_tmpl
recursive-include argonaut/public *
recursive-include argonaut/templates *
ARGONAUT
========
Version 0.3.6
Author: Jason Robinson
jaywink (at) basshero.org
http://www.basshero.org
1. Description
==============
Argonaut is a blogging engine built with Pylons. It is lightweight
and can be deployed on many types of web servers running Python.
The application is still very much in alpha stages and as such
there are bugs in the system and a lot of features which have
not been implemented.
For more information please see the following links:
Authors webpage
http://www.basshero.org
Pylons HQ
http://www.pylonshq.com
2. Licence
==========
Argonaut is distributed under the FreeBSD license. This means you can use,
copy and distribute the code and application for free and with no obligations.
It is however required that the included copyright is included with the application
using source or binary components. Please see the file LICENSE.txt for the full
license.
The licenses of the JavaScript components included with Argonaut do not
enforce any additional requirements for reuse or distribution. Please see the
licenses of these components and any included icons in the folder 'third_party_licenses'.
3. Installation
===============
3.1 Prequisites for install
---------------------------
- Python 2.4 - 2.7 [http://www.python.org]
- Pylons 1.0 [http://pylonshq.com/]
- Python setuptools (easy_install) [http://pypi.python.org/pypi/setuptools]
Please see Pylons documentation to get started with Pylons [http://pylonshq.com/docs/en/0.9.7/gettingstarted/].
3.2 Other components
--------------------
In addition to Pylons, Argonaut uses the following components:
- Mako (templates, the View) [http://www.makotemplates.org/]
- SQLAlchemy (the Model) [http://www.sqlalchemy.org/]
- Sqlalchemy-migrate (database migration) [http://pypi.python.org/pypi/sqlalchemy-migrate]
- repoze.what (authentication and access rights) [http://what.repoze.org/docs/1.0/]
- CKEditor (for writing posts) [http://ckeditor.com/]
- AddToAny (for sharing) [http://www.addtoany.com/]
- jQuery (for additional magic) [http://jquery.com/]
- Simple3color (default theme) [http://www.oswd.org/design/preview/id/3533]
- TurboMail (for notifications) [http://www.python-turbomail.org/]
- pwi (Picasa Webalbum Integrator javascript library, for gallery) [http://code.google.com/p/pwi/]
Of these the JavaScript components CKEditor, jQuery, pwi and AddToAny are
distributed along with this package. The Python components are downloaded
and installed by easy_install.
3.3 Installation and Setup
--------------------------
Prequisites for install must be fulfilled. Install Argonaut using easy_install:
easy_install argonaut
OR with local .egg file
easy_install <path_to_egg_file>
Make a config file as follows:
paster make-config argonaut config.ini
Tweak the config file as appropriate. Please see Pylons application
setup pages for hints on editing the config file [http://pythonpaste.org/deploy/].
After this run the following to setup the application:
paster setup-app config.ini#arg
Then you are ready to go.
You can test the installation by running:
paster serve config.ini
After this visit the link http://127.0.0.1:5000
Optionally you can extract the source and run Argonaut by launching development
mode via setup.py. Just extract the source, install Pylons and in the Argonaut
base directory run:
python setup.py develop
paster serve development.ini
4. Updating from a previous version
===================================
TBD
5. Usage
========
5.1 Modifying the site
----------------------
Argonaut features templates that can be used to control the site
structure, layout and texts. Unfortunately in this early version
there is no admin panel so all editing must be made to the files
directly.
Template files are situated in argonaut/templates. Please see
Mako documentation on how to change the templates.
5.2 Configuration
-----------------
During application setup a database will be created in the form
that is configured in config.ini. In addition to blog data, Argonaut
also stores some configuration values in the database. These are
stored in the table 'config'.
5.3 Users
---------
The default user for writing posts is 'admin', password 'admin'.
Currently users can only be added directly to the database. An
admin panel will be added later.
5.4 Other usage notes
---------------------
Proper documentation and usage will be added slowly over
future releases :)
6. Support
==========
Please contact the author at jaywink (at) basshero.org for support,
ideas and bug reports.
7. Changelog
============
0.3.6 October 2011
- Multiple fixes to latest blog posts comments and sharing
- Comments are loaded by default when viewing a post
0.3.5 15th September 2011
- Added post sharing to Diaspora.
- Added parent_id column to table pages. Only those pages with parent_id = null are shown
from base.mako template in the menu.
0.3.4 30th March 2011
- Added custom javascript template that is loaded from base template for site wide usage.
- Removed usage of sqlalchemy-migrate since it was causing problems. A new type of DB migration
will be thought out later.
0.3.3 29th March 2011
- Fixed routing for landing page with lowest page_order
- Post commenting can now be disabled with the config setting comments_enabled, true/false
- Fixed character encoding problem when getting page name from database
- Base page now only displays links to active pages
- Added a new page type, pwi_gallery, which is a gallery page using the jquery plugin 'pwi'
(Picasa Webalbum Integrator javascript library, http://code.google.com/p/pwi/).
0.3.2 20th February 2011
- Added 'media' and 'social' models to the database. Media contains links to
images and other media items. Social contains links to contact information or
other sites. Links are given a media ID which is displayed in the Social -box with
an url to the site or contact information.
0.3 13th February 2011
- sqlalchemy-migrate is now used to do automatic database schema
upgrades according to model changes. Added as dependency, installed
via easy_install automatically. Implementation thanks to:
http://shane.caraveo.com/2010/09/13/database-migrations-for-sqlalchemy-part-duex/
- Default landing page is now the one with the lowest page_order setting.
- Pages are now mapped to a page type. Default page types are 'blog', 'archives' and
'tags'.
- Page urls are now determined from page type, but can also be customised.
- Custom page support has been added. Custom pages are mapped to page type 'custom'
which redirects traffic to a mako template file as indicated in the pages table.
- Version number will now be displayed in the Powered by -section in the main template.
Removed these text strings and urls from the configuration table and placed them in
the base template.
Updating from version 0.2:
- Pre-upgrade the database table 'pages' needs to be dropped for an easy upgrade. After
this the script 'paster setup-app [config_file]#arg' needs to be run to create the
table with the new schema. The rest of the database changes should be handled by
the automatic schema update mechanism.
0.2 6th December 2010
- Initial Pylons version release
0.1.x The 0.1 versions are the old PHP powered versions which were never released.
File added
#
# argonaut - Pylons configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = true
email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
# turbomail
mail.on = true
mail.manager = immediate
mail.transport = smtp
mail.smtp.server = localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 5000
[app:arg]
use = egg:argonaut
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = argonaut
beaker.session.secret = ${app_instance_secret}
app_instance_uuid = ${app_instance_uuid}
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# SQLAlchemy database URL
sqlalchemy.url = sqlite:///production.db
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
set debug = false
#set cookie secret for repoze.what
cookie_secret = 'gfdgheteagrfioeqwhidhiluhiu345ih65h554t98tye8f9y8vyfah78dsguydscfusgbcfueywgbofg3478r4378rg43bfier'
#remove dispatch.fcgi
[filter:prefix]
use = egg:PasteDeploy#prefix
prefix = /
# to fix bug with pastedeploy and filter-with, this hint:
# http://www.mail-archive.com/pylons-discuss@googlegroups.com/msg08524.html
[pipeline:main]
pipeline = prefix arg
# Logging configuration
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s] [%(threadName)s] %(message)s
"""Pylons environment configuration"""
import os
from mako.lookup import TemplateLookup
from pylons.configuration import PylonsConfig
from pylons.error import handle_mako_error
from sqlalchemy import engine_from_config
import argonaut.lib.app_globals as app_globals
import argonaut.lib.helpers
from argonaut.config.routing import make_map
from argonaut.model import init_model
import argonaut.model.meta as meta
from paste.deploy.converters import asbool
def load_environment(global_conf, app_conf):
"""Configure the Pylons environment via the ``pylons.config``
object
"""
config = PylonsConfig()
# Pylons paths
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
paths = dict(root=root,
controllers=os.path.join(root, 'controllers'),
static_files=os.path.join(root, 'public'),
templates=[os.path.join(root, 'templates')])
# Initialize config with the basic options
config.init_app(global_conf, app_conf, package='argonaut', paths=paths)
config['routes.map'] = make_map(config)
config['pylons.app_globals'] = app_globals.Globals(config)
config['pylons.h'] = argonaut.lib.helpers
# Setup cache object as early as possible
import pylons
pylons.cache._push_object(config['pylons.app_globals'].cache)
# Create the Mako TemplateLookup, with the default auto-escaping
config['pylons.app_globals'].mako_lookup = TemplateLookup(
directories=paths['templates'],
error_handler=handle_mako_error,
module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
input_encoding='utf-8', default_filters=['escape'],
imports=['from webhelpers.html import escape'])
# Setup the SQLAlchemy database engine
engine = engine_from_config(config, 'sqlalchemy.')
init_model(engine)
# CONFIGURATION OPTIONS HERE (note: all config options will override
# any Pylons config options)
return config
"""Pylons middleware initialization"""
from beaker.middleware import SessionMiddleware
from paste.cascade import Cascade
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from pylons.middleware import ErrorHandler, StatusCodeRedirect
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware
from argonaut.lib.authentication import add_auth
from argonaut.config.environment import load_environment
def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
"""Create a Pylons WSGI application and return it
``global_conf``
The inherited configuration for this application. Normally from
the [DEFAULT] section of the Paste ini file.
``full_stack``
Whether this application provides a full WSGI stack (by default,
meaning it handles its own exceptions and errors). Disable
full_stack when this application is "managed" by another WSGI
middleware.
``static_files``
Whether this application serves its own static files; disable
when another web server is responsible for serving them.
``app_conf``
The application's local configuration. Normally specified in
the [app:<name>] section of the Paste ini file (where <name>
defaults to main).
"""
# Configure the Pylons environment
config = load_environment(global_conf, app_conf)
# The Pylons WSGI app
app = PylonsApp(config=config)
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'], singleton=False)
app = SessionMiddleware(app, config)
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
# Add the custom repoze.what middleware app here and pass it the config
# variable
app = add_auth(app, config)
if asbool(full_stack):
# Handle Python exceptions
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
if asbool(config['debug']):
app = StatusCodeRedirect(app)
else:
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
# Establish the Registry for this application
app = RegistryManager(app)
if asbool(static_files):
# Serve static files
static_app = StaticURLParser(config['pylons.paths']['static_files'])
app = Cascade([static_app, app])
app.config = config
return app
"""Routes configuration
The more specific and detailed routes should be defined first so they
may take precedent over the more generic routes. For more information
refer to the routes manual at http://routes.groovie.org/docs/
"""
from routes import Mapper
def make_map(config):
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])
map.minimization = False
map.explicit = True
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect('/error/{action}', controller='error')
map.connect('/error/{action}/{id}', controller='error')
# CUSTOM ROUTES HERE
# main blog post url
map.connect('blog_post', '/blog/{id}/{subject}', controller='blog', action='view', requirements={'id':'\d+'})
# backup with slash
map.connect('/blog/{id}/{subject}/', controller='blog', action='view', requirements={'id':'\d+'})
# backup without subject
map.connect('/blog/{id}/', controller='blog', action='view', requirements={'id':'\d+'})
map.connect('/blog/{id}', controller='blog', action='view', requirements={'id':'\d+'})
# fix for subjects with slashes
map.connect('/blog/{id}/*subject', controller='blog', action='view', requirements={'id':'\d+'})
# support for old basshero 2.0 urls
map.connect('/{id}/{subject}', controller='blog', action='view', requirements={'id':'\d+'})
map.connect('/{id}/{subject}/', controller='blog', action='view', requirements={'id':'\d+'})
map.connect('/{id}/', controller='blog', action='view', requirements={'id':'\d+'})
# generic blog url
map.connect('/blog/{action}/{id}', controller='blog')
# generic controller urls
map.connect('/{controller}/{action}/{year}/{month}/{day}')
map.connect('/{controller}/{action}/{id}')
map.connect('/{controller}/{action}')
# base mapped to first landing page
map.connect('/', controller='landing', action='first_page')
return map
import logging
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from argonaut.lib.base import BaseController, render
from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector
log = logging.getLogger(__name__)
class AccountController(BaseController):
def login(self):
"""
This is where the login form should be rendered.
Without the login counter, we won't be able to tell if the user has
tried to log in with wrong credentials
"""
identity = request.environ.get('repoze.who.identity')
came_from = str(request.GET.get('came_from', '')) or \
url('/')
if identity:
redirect(url(came_from))
else:
c.came_from = came_from
c.login_counter = request.environ['repoze.who.logins'] + 1
return render('/forms/login.mako')
@ActionProtector(not_anonymous())
def welcome(self):
identity = request.environ.get('repoze.who.identity')
return 'Welcome user %s' % identity['repoze.who.userid']
@ActionProtector(not_anonymous())
def test_user_access(self):
return 'You are inside user section'
@ActionProtector(has_permission('Write post'))
def test_post_access(self):
return 'You are able to post'
import logging
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from argonaut.lib.base import BaseController, render
from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector
log = logging.getLogger(__name__)
class AdminController(BaseController):
def index(self):
return "Index"
</
import logging
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from argonaut.lib.base import BaseController, render
import argonaut.lib.helpers as h
import webhelpers.paginate as paginate
from webhelpers.html.tags import *
from formencode import htmlfill
from repoze.what.predicates import has_permission
from repoze.what.plugins.pylonshq import ActionProtector
log = logging.getLogger(__name__)
class BlogController(BaseController):
def latest(self):
c.posts_count = h.post.get_many(amount=10, active_only=True, count_only=True)
c.posts = h.post.get_many(amount=10, active_only=True, order='desc')
c.post = None
if c.posts is None:
abort(404)
page_id = int(h.page.get_page_id_with_type('blog'))
return render('/blog/view.mako', extra_vars={'page_id':page_id, 'post_count':c.posts_count, 'page':'latest'})
def view(self,id):
c.post = h.post.get(int(id))
c.posts = None
if c.post is None:
abort(404)
page_id = int(h.page.get_page_id_with_type('blog'))
return render('/blog/view.mako', extra_vars={'page_id':page_id, 'post_count':1, 'page':'view'})
def archives(self):
try:
if request.params.get('filter'):
if h.forms.validate(h.forms.FilterForm()):
posts = h.post.get_many(filter=request.params.get('filter'), amount=100, active_only=True, order='desc')
else:
raise Exception
else:
raise Exception
except Exception, error:
posts = h.post.get_many(active_only=True, order='desc', amount=100)
if posts is None: