Commits

sirex  committed c236279

Initial.

  • Participants

Comments (0)

Files changed (30)

+syntax:glob
+
+*.egg-info
+*.mo
+*.pyc
+*.swp
+.conf_check_*
+.installed.cfg
+.lock-wafbuild
+.mr.developer.cfg
+.sass-cache
+.waf*
+config.log
+
+syntax:regexp
+
+^project/settings.py$
+^project/urls.py$
+
+^bin$
+^buildout.cfg$
+^c4che$
+^configure$
+^docs/build$
+^downloads$
+^eggs$
+^develop-eggs$
+^env$
+^initial_data.json$
+^parts$
+^tags$
+^var$
+#!/usr/bin/make
+
+PROJECT=project
+TESTS = 
+COVERAGE_INCLUDES = --include=project/*
+
+
+.PHONY: all
+all: c4che/_cache.py env
+	env/bin/python waf
+
+.PHONY: run
+run: all
+	bin/django runserver
+
+.PHONY: pull
+pull:
+	bin/develop up
+
+c4che/_cache.py:
+	if [ -x configure ] ; then \
+	    ./configure ; \
+	else \
+	    ./waf configure --project-name=$(PROJECT) ; \
+	fi
+
+env:
+	./waf virtualenv
+
+
+# Helpers
+
+.PHONY: clean
+clean:
+	./waf distclean
+
+.PHONY: clean
+messages:
+	./waf makemessages
+
+.PHONY: tags
+tags: all
+	bin/ctags -v
+
+.PHONY: todo
+todo:
+	@egrep -nirI 'FIXME|TODO|XXX' $(PROJECT) config wscript
+
+test: all
+	bin/django test $(TESTS)
+
+coverage: all
+	bin/coverage run $(COVERAGE_INCLUDES) bin/django test $(TESTS)
+	bin/coverage html -d var/htmlcov/ $(COVERAGE_INCLUDES)
+	bin/coverage report $(COVERAGE_INCLUDES)
+	@echo "Also try xdg-open var/htmlcov/index.html"
+
+graph: all
+	bin/django graph_models \
+	    --group-models \
+	    --all-applications \
+	    -o var/graph.png
+	xdg-open var/graph.png

File bootstrap.py

+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+"""
+
+import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess
+from optparse import OptionParser
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    quote = str
+
+# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
+stdout, stderr = subprocess.Popen(
+    [sys.executable, '-Sc',
+     'try:\n'
+     '    import ConfigParser\n'
+     'except ImportError:\n'
+     '    print 1\n'
+     'else:\n'
+     '    print 0\n'],
+    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+has_broken_dash_S = bool(int(stdout.strip()))
+
+# In order to be more robust in the face of system Pythons, we want to
+# run without site-packages loaded.  This is somewhat tricky, in
+# particular because Python 2.6's distutils imports site, so starting
+# with the -S flag is not sufficient.  However, we'll start with that:
+if not has_broken_dash_S and 'site' in sys.modules:
+    # We will restart with python -S.
+    args = sys.argv[:]
+    args[0:0] = [sys.executable, '-S']
+    args = map(quote, args)
+    os.execv(sys.executable, args)
+# Now we are running with -S.  We'll get the clean sys.path, import site
+# because distutils will do it later, and then reset the path and clean
+# out any namespace packages from site-packages that might have been
+# loaded by .pth files.
+clean_path = sys.path[:]
+import site
+sys.path[:] = clean_path
+for k, v in sys.modules.items():
+    if k in ('setuptools', 'pkg_resources') or (
+        hasattr(v, '__path__') and
+        len(v.__path__)==1 and
+        not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
+        # This is a namespace package.  Remove it.
+        sys.modules.pop(k)
+
+is_jython = sys.platform.startswith('java')
+
+setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
+distribute_source = 'http://python-distribute.org/distribute_setup.py'
+
+# parsing arguments
+def normalize_to_url(option, opt_str, value, parser):
+    if value:
+        if '://' not in value: # It doesn't smell like a URL.
+            value = 'file://%s' % (
+                urllib.pathname2url(
+                    os.path.abspath(os.path.expanduser(value))),)
+        if opt_str == '--download-base' and not value.endswith('/'):
+            # Download base needs a trailing slash to make the world happy.
+            value += '/'
+    else:
+        value = None
+    name = opt_str[2:].replace('-', '_')
+    setattr(parser.values, name, value)
+
+usage = '''\
+[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
+
+Bootstraps a buildout-based project.
+
+Simply run this script in a directory containing a buildout.cfg, using the
+Python that you want bin/buildout to use.
+
+Note that by using --setup-source and --download-base to point to
+local resources, you can keep this script from going over the network.
+'''
+
+parser = OptionParser(usage=usage)
+parser.add_option("-v", "--version", dest="version",
+                          help="use a specific zc.buildout version")
+parser.add_option("-d", "--distribute",
+                   action="store_true", dest="use_distribute", default=False,
+                   help="Use Distribute rather than Setuptools.")
+parser.add_option("--setup-source", action="callback", dest="setup_source",
+                  callback=normalize_to_url, nargs=1, type="string",
+                  help=("Specify a URL or file location for the setup file. "
+                        "If you use Setuptools, this will default to " +
+                        setuptools_source + "; if you use Distribute, this "
+                        "will default to " + distribute_source +"."))
+parser.add_option("--download-base", action="callback", dest="download_base",
+                  callback=normalize_to_url, nargs=1, type="string",
+                  help=("Specify a URL or directory for downloading "
+                        "zc.buildout and either Setuptools or Distribute. "
+                        "Defaults to PyPI."))
+parser.add_option("--eggs",
+                  help=("Specify a directory for storing eggs.  Defaults to "
+                        "a temporary directory that is deleted when the "
+                        "bootstrap script completes."))
+parser.add_option("-t", "--accept-buildout-test-releases",
+                  dest='accept_buildout_test_releases',
+                  action="store_true", default=False,
+                  help=("Normally, if you do not specify a --version, the "
+                        "bootstrap script and buildout gets the newest "
+                        "*final* versions of zc.buildout and its recipes and "
+                        "extensions for you.  If you use this flag, "
+                        "bootstrap and buildout will get the newest releases "
+                        "even if they are alphas or betas."))
+parser.add_option("-c", None, action="store", dest="config_file",
+                   help=("Specify the path to the buildout configuration "
+                         "file to be used."))
+
+options, args = parser.parse_args()
+
+# if -c was provided, we push it back into args for buildout's main function
+if options.config_file is not None:
+    args += ['-c', options.config_file]
+
+if options.eggs:
+    eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
+else:
+    eggs_dir = tempfile.mkdtemp()
+
+if options.setup_source is None:
+    if options.use_distribute:
+        options.setup_source = distribute_source
+    else:
+        options.setup_source = setuptools_source
+
+if options.accept_buildout_test_releases:
+    args.append('buildout:accept-buildout-test-releases=true')
+args.append('bootstrap')
+
+try:
+    import pkg_resources
+    import setuptools # A flag.  Sometimes pkg_resources is installed alone.
+    if not hasattr(pkg_resources, '_distribute'):
+        raise ImportError
+except ImportError:
+    ez_code = urllib2.urlopen(
+        options.setup_source).read().replace('\r\n', '\n')
+    ez = {}
+    exec ez_code in ez
+    setup_args = dict(to_dir=eggs_dir, download_delay=0)
+    if options.download_base:
+        setup_args['download_base'] = options.download_base
+    if options.use_distribute:
+        setup_args['no_fake'] = True
+    ez['use_setuptools'](**setup_args)
+    if 'pkg_resources' in sys.modules:
+        reload(sys.modules['pkg_resources'])
+    import pkg_resources
+    # This does not (always?) update the default working set.  We will
+    # do it.
+    for path in sys.path:
+        if path not in pkg_resources.working_set.entries:
+            pkg_resources.working_set.add_entry(path)
+
+cmd = [quote(sys.executable),
+       '-c',
+       quote('from setuptools.command.easy_install import main; main()'),
+       '-mqNxd',
+       quote(eggs_dir)]
+
+if not has_broken_dash_S:
+    cmd.insert(1, '-S')
+
+find_links = options.download_base
+if not find_links:
+    find_links = os.environ.get('bootstrap-testing-find-links')
+if find_links:
+    cmd.extend(['-f', quote(find_links)])
+
+if options.use_distribute:
+    setup_requirement = 'distribute'
+else:
+    setup_requirement = 'setuptools'
+ws = pkg_resources.working_set
+setup_requirement_path = ws.find(
+    pkg_resources.Requirement.parse(setup_requirement)).location
+env = dict(
+    os.environ,
+    PYTHONPATH=setup_requirement_path)
+
+requirement = 'zc.buildout'
+version = options.version
+if version is None and not options.accept_buildout_test_releases:
+    # Figure out the most recent final version of zc.buildout.
+    import setuptools.package_index
+    _final_parts = '*final-', '*final'
+    def _final_version(parsed_version):
+        for part in parsed_version:
+            if (part[:1] == '*') and (part not in _final_parts):
+                return False
+        return True
+    index = setuptools.package_index.PackageIndex(
+        search_path=[setup_requirement_path])
+    if find_links:
+        index.add_find_links((find_links,))
+    req = pkg_resources.Requirement.parse(requirement)
+    if index.obtain(req) is not None:
+        best = []
+        bestv = None
+        for dist in index[req.project_name]:
+            distv = dist.parsed_version
+            if _final_version(distv):
+                if bestv is None or distv > bestv:
+                    best = [dist]
+                    bestv = distv
+                elif distv == bestv:
+                    best.append(dist)
+        if best:
+            best.sort()
+            version = best[-1].version
+if version:
+    requirement = '=='.join((requirement, version))
+cmd.append(requirement)
+
+if is_jython:
+    import subprocess
+    exitcode = subprocess.Popen(cmd, env=env).wait()
+else: # Windows prefers this, apparently; otherwise we would prefer subprocess
+    exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
+if exitcode != 0:
+    sys.stdout.flush()
+    sys.stderr.flush()
+    print ("An error occurred when trying to install zc.buildout. "
+           "Look above this message for any errors that "
+           "were output by easy_install.")
+    sys.exit(exitcode)
+
+ws.add_entry(eggs_dir)
+ws.require(requirement)
+import zc.buildout.buildout
+zc.buildout.buildout.main(args)
+if not options.eggs: # clean up temporary egg directory
+    shutil.rmtree(eggs_dir)

File config/apache.conf

+# $NOTE
+#compiler-settings
+cheetahVarStartToken = @
+#end compiler-settings
+<VirtualHost *:80>
+    ServerName @SERVER_NAME
+    ServerAdmin @SERVER_ADMIN
+
+    DocumentRoot @TOP/var/www/
+    <Directory @TOP/var/www/>
+        Order allow,deny
+        Allow from all
+    </Directory>
+
+    WSGIDaemonProcess @SERVER_NAME #slurp
+#if @WSGI_USER
+user=@WSGI_USER #slurp
+#end if
+#if @WSGI_GROUP
+group=@WSGI_GROUP #slurp
+#end if
+processes=1 threads=1
+    WSGIProcessGroup @SERVER_NAME
+
+    WSGIScriptAlias / @TOP/bin/django.wsgi
+
+    <Directory @TOP/bin/>
+        Order allow,deny
+        Allow from all
+    </Directory>
+
+    #if @USE_HTPASSWD
+    <Location />
+      AuthType Basic
+      AuthName "@SERVER_NAME"
+      AuthUserFile @TOP/var/etc/htpasswd
+      Require valid-user 
+    </Location>
+    #end if
+
+    Alias /media/ @TOP/var/www/media/
+    Alias /static/ @TOP/var/www/static/
+
+    LogLevel warn
+    ErrorLog @TOP/var/log/apache-error.log
+    CustomLog @TOP/var/log/apache-access.log combined
+
+
+    # ======================================================================
+    # HTML5 Boilerplate - http://html5boilerplate.com/
+    # ======================================================================
+
+
+    # Apache configuration file
+    # httpd.apache.org/docs/2.2/mod/quickreference.html
+
+    # Note .htaccess files are an overhead, this logic should be in your Apache
+    # config if possible httpd.apache.org/docs/2.2/howto/htaccess.html
+
+    # Techniques in here adapted from all over, including:
+    #   Kroc Camen: camendesign.com/.htaccess
+    #   perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/
+    #   Sample .htaccess file of CMS MODx: modxcms.com
+
+
+    ###
+    ### If you run a webserver other than apache, consider:
+    ### github.com/paulirish/html5-boilerplate-server-configs
+    ###
+
+
+    # ----------------------------------------------------------------------
+    # Better website experience for IE users
+    # ----------------------------------------------------------------------
+
+    # Force the latest IE version, in various cases when it may fall back to
+    # IE7 mode github.com/rails/rails/commit/123eb25#commitcomment-118920 Use
+    # ChromeFrame if it's installed for a better experience for the poor IE
+    # folk
+
+    <IfModule mod_headers.c>
+        Header set X-UA-Compatible "IE=Edge,chrome=1"
+        # mod_headers can't match by content-type, but we don't want to send this header on *everything*...
+        <FilesMatch "\.(js|css|gif|png|jpe?g|pdf|xml|oga|ogg|m4a|ogv|mp4|m4v|webm|svg|svgz|eot|ttf|otf|woff|ico|webp|appcache|manifest|htc|crx|xpi|safariextz|vcf)$" >
+          Header unset X-UA-Compatible
+        </FilesMatch>
+    </IfModule>
+
+
+    # ----------------------------------------------------------------------
+    # Cross-domain AJAX requests
+    # ----------------------------------------------------------------------
+
+    # Serve cross-domain ajax requests, disabled.   
+    # enable-cors.org
+    # code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
+
+    #  <IfModule mod_headers.c>
+    #    Header set Access-Control-Allow-Origin "*"
+    #  </IfModule>
+
+
+    # ----------------------------------------------------------------------
+    # Webfont access
+    # ----------------------------------------------------------------------
+
+    # Allow access from all domains for webfonts.
+    # Alternatively you could only whitelist your
+    # subdomains like "subdomain.example.com".
+
+    <FilesMatch "\.(ttf|ttc|otf|eot|woff|font.css)$">
+      <IfModule mod_headers.c>
+        Header set Access-Control-Allow-Origin "*"
+      </IfModule>
+    </FilesMatch>
+
+
+    # ----------------------------------------------------------------------
+    # Proper MIME type for all files
+    # ----------------------------------------------------------------------
+
+
+    # JavaScript
+    #   Normalize to standard type (it's sniffed in IE anyways) 
+    #   tools.ietf.org/html/rfc4329#section-7.2
+    AddType application/javascript         js
+
+    # Audio
+    AddType audio/ogg                      oga ogg
+    AddType audio/mp4                      m4a
+
+    # Video
+    AddType video/ogg                      ogv
+    AddType video/mp4                      mp4 m4v
+    AddType video/webm                     webm
+
+    # SVG.
+    #   Required for svg webfonts on iPad
+    #   twitter.com/FontSquirrel/status/14855840545
+    AddType     image/svg+xml              svg svgz 
+    AddEncoding gzip                       svgz
+                                           
+    # Webfonts                             
+    AddType application/vnd.ms-fontobject  eot
+    AddType application/x-font-ttf    ttf ttc
+    AddType font/opentype                  otf
+    AddType application/x-font-woff        woff
+
+    # Assorted types                                      
+    AddType image/x-icon                   ico
+    AddType image/webp                     webp
+    AddType text/cache-manifest            appcache manifest
+    AddType text/x-component               htc
+    AddType application/x-chrome-extension crx
+    AddType application/x-xpinstall        xpi
+    AddType application/octet-stream       safariextz
+    AddType text/x-vcard                   vcf
+
+
+    # Force deflate for mangled headers
+    # developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
+    <IfModule mod_setenvif.c>
+      <IfModule mod_headers.c>
+        SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+        RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+      </IfModule>
+    </IfModule>
+
+    # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
+    <IfModule filter_module>
+      FilterDeclare   COMPRESS
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
+      FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
+      FilterChain     COMPRESS
+      FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
+    </IfModule>
+
+
+    # ----------------------------------------------------------------------
+    # Expires headers (for better cache control)
+    # ----------------------------------------------------------------------
+
+    # These are pretty far-future expires headers.
+    # They assume you control versioning with cachebusting query params like
+    #   <script src="application.js?20100608">
+    # Additionally, consider that outdated proxies may miscache 
+    #   www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
+
+    # If you don't use filenames to version, lower the CSS  and JS to something
+    # like "access plus 1 week" or so.
+
+    <IfModule mod_expires.c>
+      ExpiresActive on
+
+    # Perhaps better to whitelist expires rules? Perhaps.
+      ExpiresDefault                          "access plus 1 month"
+
+    # cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing
+    # HTML5)
+      ExpiresByType text/cache-manifest       "access plus 0 seconds"
+
+    # Your document html 
+      ExpiresByType text/html                 "access plus 0 seconds"
+      
+    # Data
+      ExpiresByType text/xml                  "access plus 0 seconds"
+      ExpiresByType application/xml           "access plus 0 seconds"
+      ExpiresByType application/json          "access plus 0 seconds"
+
+    # Feed
+      ExpiresByType application/rss+xml       "access plus 1 hour"
+      ExpiresByType application/atom+xml      "access plus 1 hour"
+
+    # Favicon (cannot be renamed)
+      ExpiresByType image/x-icon              "access plus 1 week" 
+
+    # Media: images, video, audio
+      ExpiresByType image/gif                 "access plus 1 month"
+      ExpiresByType image/png                 "access plus 1 month"
+      ExpiresByType image/jpg                 "access plus 1 month"
+      ExpiresByType image/jpeg                "access plus 1 month"
+      ExpiresByType video/ogg                 "access plus 1 month"
+      ExpiresByType audio/ogg                 "access plus 1 month"
+      ExpiresByType video/mp4                 "access plus 1 month"
+      ExpiresByType video/webm                "access plus 1 month"
+      
+    # HTC files  (css3pie)
+      ExpiresByType text/x-component          "access plus 1 month"
+      
+    # Webfonts
+      ExpiresByType font/truetype             "access plus 1 month"
+      ExpiresByType font/opentype             "access plus 1 month"
+      ExpiresByType application/x-font-woff   "access plus 1 month"
+      ExpiresByType image/svg+xml             "access plus 1 month"
+      ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
+        
+    # CSS and JavaScript
+      ExpiresByType text/css                  "access plus 1 year"
+      ExpiresByType application/javascript    "access plus 1 year"
+      
+      <IfModule mod_headers.c>
+        Header append Cache-Control "public"
+      </IfModule>
+      
+    </IfModule>
+
+
+    # ----------------------------------------------------------------------
+    # ETag removal
+    # ----------------------------------------------------------------------
+
+    # FileETag None is not enough for every server.
+    <IfModule mod_headers.c>
+      Header unset ETag
+    </IfModule>
+
+    # Since we're sending far-future expires, we don't need ETags for
+    # static content.
+    #   developer.yahoo.com/performance/rules.html#etags
+    FileETag None
+
+
+    # ----------------------------------------------------------------------
+    # Suppress or force the "www." at the beginning of URLs
+    # ----------------------------------------------------------------------
+
+    # The same content should never be available under two different URLs -
+    # especially not with and without "www." at the beginning, since this can
+    # cause SEO problems (duplicate content).  That's why you should choose one
+    # of the alternatives and redirect the other one.
+
+    # By default option 1 (no "www.") is activated. Remember: Shorter URLs are
+    # sexier.  no-www.org/faq.php?q=class_b
+
+    # If you rather want to use option 2, just comment out all option 1 lines
+    # and uncomment option 2.
+    # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
+
+    # ----------------------------------------------------------------------
+
+    # Option 1:
+    # Rewrite "www.example.com -> example.com" 
+
+    # <IfModule mod_rewrite.c>
+    #   RewriteCond %{HTTPS} !=on
+    #   RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+    #   RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
+    # </IfModule>
+
+    # ----------------------------------------------------------------------
+
+    # Option 2:
+    # To rewrite "example.com -> www.example.com" uncomment the following
+    # lines.  Be aware that the following rule might not be a good idea if you
+    # use "real" subdomains for certain parts of your website.
+
+    # <IfModule mod_rewrite.c>
+    #   RewriteCond %{HTTPS} !=on
+    #   RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
+    #   RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
+    # </IfModule>
+
+
+    # ----------------------------------------------------------------------
+    # UTF-8 encoding
+    # ----------------------------------------------------------------------
+
+    # Use UTF-8 encoding for anything served text/plain or text/html
+    AddDefaultCharset utf-8
+
+    # Force UTF-8 for a number of file formats
+    AddCharset utf-8 .html .css .js .xml .json .rss .atom
+
+
+    # ----------------------------------------------------------------------
+    # A little more security
+    # ----------------------------------------------------------------------
+
+
+    # "-Indexes" will have Apache block users from browsing folders without a
+    # default document Usually you should leave this activated, because you
+    # shouldn't allow everybody to surf through every folder on your server
+    # (which includes rather private places like CMS system folders).
+    Options -Indexes
+
+</VirtualHost>

File config/buildout.cfg

+# $NOTE
+#compiler-settings
+cheetahVarStartToken = @
+#end compiler-settings
+[buildout]
+parts =
+    mkdir
+    django
+    scripts
+    #if @JQUERY_VERSION
+    jquery
+    #end if
+    modernizr
+    #if @TWITTER_BOOTSTRAP
+    twitter-bootstrap
+    #end if
+    #if @DEVELOPMENT
+    ctags
+    #end if
+
+eggs =
+    South
+    django
+    django-mediagenerator
+    #if @DEVELOPMENT
+    coverage
+    django-debug-toolbar
+    django-extensions
+    django-test-utils
+    Werkzeug
+    ipdb
+    ipython
+    django-pdb
+    #end if
+
+extra-paths =
+    @PROJECT_NAME
+
+extensions =
+    mr.developer
+
+sources = sources
+sources-dir = parts
+auto-checkout = *
+#always-checkout = force
+
+versions = versions
+
+[sources]
+# my.package = svn http://example.com/svn/my.package/trunk
+# some.other.package = git git://example.com/git/some.other.package.git
+# bzr.package = bzr bzr+ssh://bazaar.launchpad.net/~user/project/branch/
+
+[versions]
+django = 1.4
+#if @DEVELOPMENT
+ipython = 0.10
+#end if
+
+[django]
+recipe = djangorecipe
+project = @PROJECT_NAME
+settings = settings
+eggs = ${buildout:eggs}
+extra-paths = ${buildout:extra-paths}
+#if @DEVELOPMENT
+wsgi = false
+#else
+wsgi = true
+wsgilog = ${buildout:directory}/var/log/wsgi.log
+#end if
+
+[mkdir]
+recipe = z3c.recipe.mkdir
+paths =
+    ${buildout:directory}/var/etc
+    ${buildout:directory}/var/log
+    ${buildout:directory}/var/www/media
+    ${buildout:directory}/var/www/static
+    ${buildout:directory}/var/mediagenerator
+    ${buildout:directory}/var/cache
+
+
+#if @JQUERY_VERSION
+[jquery]
+recipe = hexagonit.recipe.download
+url = http://code.jquery.com/jquery-@{JQUERY_VERSION}.js
+destination = ${buildout:parts-directory}/jquery/js
+filename = jquery.js
+download-only = true
+hash-name = false
+#end if
+
+[modernizr]
+recipe = hexagonit.recipe.download
+url = http://www.modernizr.com/downloads/modernizr.js
+destination = ${buildout:parts-directory}/modernizr/js
+filename = modernizr.js
+download-only = true
+hash-name = false
+
+#if @TWITTER_BOOTSTRAP
+[twitter-bootstrap]
+recipe = hexagonit.recipe.download
+url = http://twitter.github.com/bootstrap/assets/bootstrap.zip
+destination = ${buildout:parts-directory}/twitter-bootstrap
+strip-top-level-dir = true
+hash-name = false
+#end if
+
+#if @DEVELOPMENT
+[ctags]
+recipe = z3c.recipe.tag:tags
+eggs = ${buildout:eggs}
+extra-paths = ${buildout:extra-paths}
+
+[scripts]
+recipe = zc.recipe.egg
+eggs = ${buildout:eggs}
+extra-paths = ${buildout:directory}
+interpreter = python
+#end if

File config/initial_data.json

+[
+    {
+        "pk": 1, 
+        "model": "sites.site", 
+        "fields": {
+            "domain": "$SERVER_NAME", 
+            "name": "$SERVER_NAME"
+        }
+    }#slurp
+    #if $DEVELOPMENT
+, 
+    {
+        "pk": 1, 
+        "model": "auth.user", 
+        "fields": {
+            "username": "admin", 
+            "first_name": "", 
+            "last_name": "", 
+            "is_active": true, 
+            "is_superuser": true, 
+            "is_staff": true, 
+            "last_login": "2010-11-06 16:08:43", 
+            "groups": [], 
+            "user_permissions": [], 
+            "password": "sha1$0fe83$8e34f3d207949188b2c7e74a0740f1a8b68eeab5", 
+            "email": "$SERVER_ADMIN", 
+            "date_joined": "2010-11-06 16:07:59"
+        }
+    }#slurp
+    #end if
+
+]

File config/settings.py

+#encoding: utf-8
+# coding: utf-8
+
+# $NOTE
+
+import os
+
+PROJECT_DIR = os.path.realpath(os.path.dirname(__file__))
+BUILDOUT_DIR = os.path.abspath(os.path.join(PROJECT_DIR, '..'))
+
+ugettext = lambda s: s
+
+#if $DEVELOPMENT
+DEBUG = True
+#else
+DEBUG = False
+#end if
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    #if $PRODUCTION
+    ('Server Admin', '$SERVER_ADMIN'),
+    #end if
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        #if $USE_SQLITE or $DEVELOPMENT
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BUILDOUT_DIR, 'var', 'db'),
+        #else
+        'ENGINE': 'django.db.backends.mysql',
+        'OPTIONS': {
+            'init_command': 'SET storage_engine=INNODB',
+            'read_default_file': os.path.join(BUILDOUT_DIR, 'var', 'etc',
+                                              'my.cnf'),
+        },
+        #end if
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'UTC'
+
+LANGUAGES = (
+    #for $lang in $LANGUAGES
+    ('$lang', ugettext(u'$LANG_NAMES[$lang]')),
+    #end for
+)
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = '$LANGUAGE_CODE'
+
+LOCALE_PATHS = (
+    os.path.join(PROJECT_DIR, 'locale'),
+)
+
+SITE_ID = 1
+
+# Django-registration settings.
+ACCOUNT_ACTIVATION_DAYS = 7
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = os.path.join(BUILDOUT_DIR, 'var', 'www', 'media')
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = '/media/'
+
+STATIC_ROOT = os.path.join(BUILDOUT_DIR, 'var', 'www', 'static')
+STATIC_URL = '/static/'
+STATICFILES_DIRS = (
+    os.path.join(PROJECT_DIR, 'static'),
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '$SECRET_KEY'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+    'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.locale.LocaleMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.transaction.TransactionMiddleware',
+    #if $DEVELOPMENT
+    'debug_toolbar.middleware.DebugToolbarMiddleware',
+    'django_pdb.middleware.PdbMiddleware',
+    #end if
+)
+
+ROOT_URLCONF = '${PROJECT_NAME}.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+    os.path.join(PROJECT_DIR, 'templates'),
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    'django.contrib.auth.context_processors.auth',
+    'django.core.context_processors.debug',
+    'django.core.context_processors.i18n',
+    'django.core.context_processors.media',
+    'django.core.context_processors.static',
+    'django.core.context_processors.request',
+    '${PROJECT_NAME}.context_processors.settings_for_context',
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.sitemaps',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'django.contrib.admin',
+    'django.contrib.markup',
+    'south',
+
+    'htlog',
+
+    #if $DEVELOPMENT
+    'debug_toolbar',
+    'django_extensions',
+    'test_utils',
+    'django_pdb',
+    #end if
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}
+
+#if $DEVELOPMENT
+INTERNAL_IPS = (
+    '127.0.0.1',
+)
+
+EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
+EMAIL_FILE_PATH = os.path.join(BUILDOUT_DIR, 'var', 'mail')
+
+CACHES = {
+    'default': {
+        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+    }
+}
+
+DEBUG_TOOLBAR_CONFIG = {
+    'INTERCEPT_REDIRECTS': False,
+}
+#end if
+
+JQUERY_VERSION = '$JQUERY_VERSION'
+
+PROTOCOL = 'http'
+DEFAULT_FROM_EMAIL = '$SERVER_ADMIN'

File config/urls.py

+from django.conf import settings
+from django.conf.urls.defaults import include
+from django.conf.urls.defaults import patterns
+from django.conf.urls.defaults import url
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+
+
+urlpatterns = patterns('',
+    url(r'^$', 'htlog.views.session_create', name='index'),
+    url(r'', include('htlog.urls')),
+)
+
+if settings.DEBUG:
+    urlpatterns += staticfiles_urlpatterns()

File project/__init__.py

Empty file added.

File project/context_processors.py

+from django.conf import settings
+
+
+def settings_for_context(context):
+    ret = {}
+    settings_var_names = (
+        'JQUERY_VERSION',
+    )
+    for name in settings_var_names:
+        if hasattr(settings, name):
+            ret[name] = getattr(settings, name)
+    return ret

File project/decorators.py

+from functools import wraps
+
+from django.http import HttpResponse, HttpResponseBadRequest
+from django.utils import simplejson
+from django.views.decorators.csrf import csrf_exempt
+
+
+class JsonResponse(HttpResponse):
+    def __init__(self, data):
+        super(JsonResponse, self).__init__(content=simplejson.dumps(data),
+                                           mimetype='application/json')
+
+
+def ajax_request(*allowed_methods):
+    allowed_methods = allowed_methods or ('POST',)
+    def decorator(view_func):
+        @wraps(view_func)
+        def wrapper(request, *args, **kwargs):
+            if request.method not in allowed_methods or not request.is_ajax():
+                return HttpResponseBadRequest()
+            response = view_func(request, *args, **kwargs)
+            if isinstance(response, HttpResponse):
+                return response
+            else:
+                return JsonResponse(response)
+        return wrapper
+    return csrf_exempt(decorator)

File project/htlog/__init__.py

Empty file added.

File project/htlog/models.py

+import base64
+import json
+import random
+import string
+import urllib
+import urlparse
+
+from django.conf import settings
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+from django.db import models
+
+from .utils import is_binary_string
+
+
+def base36(number, alphabet=string.digits + string.lowercase):
+    if number == 0:
+        return alphabet[0]
+
+    val = ''
+    sign = ''
+    if number < 0:
+        sign = '-'
+        number = - number
+
+    while number != 0:
+        number, i = divmod(number, len(alphabet))
+        val = alphabet[i] + val
+
+    return sign + val
+
+
+def get_unique_id(pk, size=8, alphabet=string.digits + string.lowercase):
+    uniq = base36(pk, alphabet)
+    uniq_size = len(uniq)
+    if size > uniq_size:
+        size = size - uniq_size
+        fill = ''.join(random.choice(alphabet) for c in range(size))
+    else:
+        fill = ''
+    return fill + uniq
+
+
+class Session(models.Model):
+    slug = models.CharField(max_length='20', unique=True, default='')
+    created = models.DateTimeField(auto_now_add=True)
+    updated = models.DateTimeField(auto_now=True)
+
+    def generate_slug(self):
+        assert self.pk
+        if not self.slug:
+            self.slug = get_unique_id(self.pk)
+
+
+class Request(models.Model):
+    session = models.ForeignKey(Session)
+    created = models.DateTimeField(auto_now_add=True)
+
+    method = models.CharField(max_length=20, blank=True)
+    url = models.TextField(blank=True)
+    query = models.TextField(blank=True)
+
+    remote_addr = models.CharField(max_length=20, blank=True)
+    remote_host = models.CharField(max_length=255, blank=True)
+    user_agent = models.CharField(max_length=255, blank=True)
+    content_length = models.PositiveIntegerField(blank=True)
+    content_type = models.CharField(max_length=255, blank=True)
+    referer = models.TextField(blank=True)
+
+    _headers = models.TextField(db_column='headers', blank=True)
+    _body = models.TextField(db_column='body', blank=True)
+
+
+    def set_headers(self, data):
+        self._headers = json.dumps(data)
+
+    def get_headers(self):
+        return json.loads(self._headers)
+
+    headers = property(get_headers, set_headers)
+
+
+    def set_body(self, data):
+        self._body = base64.encodestring(data)
+
+    def get_body(self):
+        return base64.decodestring(self._body)
+
+    body = property(get_body, set_body)
+
+
+    def get_url(self):
+        if self.query:
+            return '%s?%s' % (self.url, self.query)
+        else:
+            return self.url
+
+    def get_GET(self):
+        return urlparse.parse_qsl(self.query)
+
+    def has_form_data(self):
+        return (self.content_type == 'application/x-www-form-urlencoded' and
+                self.content_length and not is_binary_string(self.body))
+
+    def get_form_data(self):
+        if self.has_form_data():
+            return urlparse.parse_qsl(self.body)
+
+    def get_cleaned_headers(self):
+        for key in ('content_type', 'content_length', 'remote_addr',
+                    'remote_host',):
+            val = getattr(self, key)
+            if val:
+                yield (key.replace('_', '-'), val)
+
+        for key, val in self.headers:
+            if key not in 'cookie':
+                yield (key, val)
+
+    def get_absolute_url(self):
+        return '%s://%s%s%s' % (
+            settings.PROTOCOL, Site.objects.get_current().domain,
+            reverse('session_request', args=[self.session.slug, ''])[:-1],
+            self.get_url()
+        )
+
+    def get_curl_command(self):
+        cmd = ['curl']
+        cmd.append('-X%s' % self.method)
+
+        if self.has_form_data():
+            query = urllib.urlencode(self.get_form_data())
+            query = query.replace("'", "\\'")
+            cmd.append("-d '%s'" % query)
+
+        url = self.get_absolute_url()
+        url = url.replace("'", "\\'")
+        cmd.append("'%s'" % url)
+
+        return ' '.join(cmd)

File project/htlog/templates/htlog/request_curl.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+<p>
+  <a href="{% url session_log session.slug %}">« back to session log</a> |
+  <a href="{% url request_details session.slug req.pk %}">http</a>
+</p>
+<pre>{{ req.get_curl_command }}</pre>
+{% endblock %}

File project/htlog/templates/htlog/request_details.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+<p>
+  <a href="{% url session_log session.slug %}">« back to session log</a> |
+  <a href="{% url request_curl session.slug req.pk %}">curl</a>
+</p>
+<pre>{{ req.method }} {{ req.get_url }}</pre>
+<table>
+  <tbody>
+
+    {% if req.query %}
+    <tr><td colspan="2"><h1>GET</h1></td></tr>
+    {% for key, val in req.get_GET %}
+    <tr>
+      <td>{{ key }}</td>
+      <td>{{ val }}</td>
+    </tr>
+    {% endfor %}
+    {% endif %}
+
+    {% if req.has_form_data %}
+    <tr><td colspan="2"><h1>{{ req.method }}</h1></td></tr>
+    {% for key, val in req.get_form_data %}
+    <tr>
+      <td>{{ key }}</td>
+      <td>{{ val }}</td>
+    </tr>
+    {% endfor %}
+    {% endif %}
+
+    <tr><td colspan="2"><h1>{% trans "Headers" %}</h1></td></tr>
+    {% for key, val in req.get_cleaned_headers %}
+    <tr>
+      <td>{{ key }}</td>
+      <td>{{ val }}</td>
+    </tr>
+    {% endfor %}
+
+  </tbody>
+</table>
+{% endblock %}

File project/htlog/templates/htlog/session_create.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+<form method="POST">
+  {% csrf_token %}
+  <button>{% trans "Create New Session" %}</button>
+</form>
+{% endblock %}

File project/htlog/templates/htlog/session_log.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+{% if requests %}
+<table>
+  <thead>
+    <tr>
+      <th>{% trans "Datetime" %}</th>
+      <th>{% trans "Method" %}</th>
+      <th>{% trans "Remote addr" %}</th>
+      <th>{% trans "URL" %}</th>
+    </tr>
+  </thead>
+  <tbody>
+    {% for req in requests %}
+    <tr>
+      <td>
+        <a href="{% url request_details session.slug req.pk %}">
+          {{ req.created|date:'SHORT_DATETIME_FORMAT' }}
+        </a>
+      </td>
+      <td>{{ req.method }}</td>
+      <td>{{ req.remote_addr }}</td>
+      <td>{{ req.get_url }}</td>
+    </tr>
+    {% endfor %}
+  </tbody>
+</table>
+{% else %}
+{% trans "(no requests has been made to this session)" %}
+{% endif %}
+{% endblock %}

File project/htlog/tests.py

+from django.core.urlresolvers import reverse
+from django.test import TestCase
+
+from .models import Session
+
+
+class TestSessionCreate(TestCase):
+    def test_session_create(self):
+        response = self.client.get(reverse('session_create'))
+        self.assertEqual(response.status_code, 200)
+
+    def test_session_create_post(self):
+        response = self.client.post(reverse('session_create'))
+        session = Session.objects.latest('pk')
+        session_log_url = reverse('session_log', args=[session.slug])
+        self.assertRedirects(response, session_log_url)
+
+
+class TestSession(TestCase):
+    def setUp(self):
+        self.session = Session.objects.create()
+        self.session.generate_slug()
+        self.session.save()
+
+    def test_session_log(self):
+        session_log_url = reverse('session_log', args=[self.session.slug])
+        response = self.client.get(session_log_url)
+        self.assertEqual(response.status_code, 200)
+
+
+class TestRequest(TestCase):
+    def setUp(self):
+        self.session = Session.objects.create()
+        self.session.generate_slug()
+        self.session.save()
+
+    def test_request(self):
+        request_url = reverse('session_request', args=[self.session.slug, ''])
+        response = self.client.get(request_url)
+        self.assertEqual(response.status_code, 200)

File project/htlog/urls.py

+from django.conf.urls.defaults import include
+from django.conf.urls.defaults import patterns
+from django.conf.urls.defaults import url
+
+requesturls = patterns('htlog.views', 
+    url(r'^$', 'request_details', name='request_details'),
+    url(r'^curl/$', 'request_details', {'template': 'curl'},
+        name='request_curl'),
+)
+
+sessionurls = patterns('htlog.views', 
+    url(r'^r/(?P<url>.*)', 'session_request', name='session_request'),
+    url(r'^$', 'session_log', name='session_log'),
+    url(r'^(?P<request_id>\d+)/', include(requesturls)),
+)
+
+urlpatterns = patterns('htlog.views',
+    url(r'^$', 'session_create', name='session_create'),
+    url(r'^(?P<session_slug>[a-z0-9]{8})/', include(sessionurls)),
+)

File project/htlog/utils.py

+# Use file(1)'s choices for what's text and what's not.
+TEXTCHARS = ''.join(map(chr, [7,8,9,10,12,13,27] + range(0x20, 0x100)))
+ALLBYTES = ''.join(map(chr, range(256)))
+
+
+def get_int(s):
+    try:
+        return int(s)
+    except ValueError:
+        return 0
+
+
+def is_binary_string(bytes):
+    nontext = bytes.translate(ALLBYTES, TEXTCHARS)
+    return bool(nontext)

File project/htlog/views.py

+import datetime
+
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
+from django.shortcuts import redirect
+from django.shortcuts import render
+from django.views.decorators.csrf import csrf_exempt
+
+from .models import Request
+from .models import Session
+from .utils import get_int
+
+
+def request_details(request, session_slug, request_id, template='details'):
+    session = get_object_or_404(Session, slug=session_slug)
+    req = get_object_or_404(Request, session__slug=session.slug, pk=request_id)
+    return render(request, 'htlog/request_%s.html' % template, {
+        'session': session,
+        'req': req,
+    })
+
+
+@csrf_exempt
+def session_request(request, session_slug, url):
+    session = get_object_or_404(Session, slug=session_slug)
+    session.updated = datetime.datetime.utcnow()
+
+    r = Request(session=session)
+    r.method = request.method
+    r.url = '/' + url
+    r.query = request.META.get('QUERY_STRING', '')
+    r.remote_addr = request.META.get('REMOTE_ADDR', '')
+    r.remote_host = request.META.get('REMOTE_HOST', '')
+    r.user_agent = request.META.get('HTTP_USER_AGENT', '')
+    r.content_length = get_int(request.META.get('CONTENT_LENGTH', 0))
+    r.content_type = request.META.get('CONTENT_TYPE', '')
+    r.referer = request.META.get('HTTP_REFERER', '')
+    r.headers = [(k.lower().replace('_', '-')[5:], v)
+                 for k, v in request.META.items()
+                 if k.startswith('HTTP_')]
+    r.body = request.read((1024 * 1024) * 2)  # limit to 2M
+    r.save()
+
+    return HttpResponse('OK')
+
+
+def session_log(request, session_slug):
+    session = get_object_or_404(Session, slug=session_slug)
+    return render(request, 'htlog/session_log.html', {
+        'session': session,
+        'requests': session.request_set.order_by('-created')[:20],
+    })
+
+
+def session_create(request):
+    if request.method == 'POST':
+        session = Session.objects.create()
+        session.generate_slug()
+        session.save()
+        return redirect('session_log', session.slug)
+    return render(request, 'htlog/session_create.html')

File project/static/favicon.ico

Added
New image

File project/templates/403.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block title %}
+<h1>{% trans "Forbidden" %}</h1>
+{% endblock %}
+
+{% block content %}
+<p>{% trans "You don't have permission to access this page." %}</p>
+{% endblock %}

File project/templates/404.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block title %}
+<h1>{% trans "Page not found" %}</h1>
+{% endblock %}
+
+{% block content %}
+<p>{% trans "We're sorry! This page is not available." %}</p>
+{% endblock %}

File project/templates/500.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block title %}
+<h1>{% trans "Internal Server Error" %}</h1>
+{% endblock %}
+
+{% block content %}
+<p>{% trans "The server encountered an internal error or misconfiguration and was unable to complete your request." %}</p>
+{% endblock %}

File project/templates/base.html

+{% extends "root.html" %}
+{% load i18n %}
+
+{% block all %}
+<!doctype html>
+<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
+<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
+<!--[if IE 7]>    <html class="no-js ie7 oldie" lang="en"> <![endif]-->
+<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en"> <![endif]-->
+<!-- Consider adding an manifest.appcache: h5bp.com/d/Offline -->
+<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
+<head>
+  <meta charset="utf-8">
+
+  <!-- Use the .htaccess and remove these lines to avoid edge case issues.
+       More info: h5bp.com/b/378 -->
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+
+  <title>{% block title %}{% endblock %}</title>
+  <meta name="description" content="">
+  <meta name="author" content="">
+
+  <!-- Mobile viewport optimized: j.mp/bplateviewport -->
+  <meta name="viewport" content="width=device-width,initial-scale=1">
+
+  <link rel="shortcut icon" type="image/x-icon" href="{{ STATIC_URL }}favicon.ico">
+
+  <!-- CSS: implied media=all -->
+  <!-- CSS concatenated and minified via ant build script-->
+  <!-- end CSS-->
+
+  <!-- More ideas for your <head> here: h5bp.com/d/head-Tips -->
+</head>
+
+<body>
+
+  <div id="container">
+    <header>
+
+    </header>
+    <div id="main" role="main">
+        {% block content %}
+        
+        {% endblock %}
+    </div>
+    <footer>
+
+    </footer>
+  </div> <!--! end of #container -->
+
+
+  <!-- JavaScript at the bottom for fast page loading -->
+
+  <!-- scripts concatenated and minified via ant build script-->
+  <!-- end scripts-->
+
+	
+  <!-- Change UA-XXXXX-X to be your site's ID
+  <script>
+    window._gaq = [['_setAccount','UAXXXXXXXX1'],['_trackPageview'],['_trackPageLoadTime']];
+    Modernizr.load({
+      load: ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js'
+    });
+  </script>
+  -->
+
+
+  <!-- Prompt IE 6 users to install Chrome Frame. Remove this if you want to support IE 6.
+       chromium.org/developers/how-tos/chrome-frame-getting-started -->
+  <!--[if lt IE 7 ]>
+    <script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js"></script>
+    <script>window.attachEvent('onload',function(){CFInstall.check({mode:'overlay'})})</script>
+  <![endif]-->
+  
+</body>
+</html>
+{% endblock %}

File project/templates/index.html

+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+{% trans "Hello World!" %}
+{% endblock %}

File project/templates/root.html

+{% block all %}
+{% endblock %}

File waf

Binary file added.
+#!/usr/bin/env python
+# encoding: utf-8
+
+import codecs
+import functools
+import os
+import random
+import re
+import shutil
+import subprocess
+import sys
+
+from waflib import Context, Logs
+
+top = '.'
+out = '.'
+
+def options(ctx):
+    ctx.load('compiler_c python ruby')
+    PROJECT_NAME = 'project'
+
+    gr = ctx.get_option_group('configure options')
+    gr.add_option('--project-name', action='store', default=PROJECT_NAME,
+                  help='project name')
+
+    gr.add_option('--production', action='store_true', default=False,
+                  help='build production environment')
+
+    gr.add_option('--server-name', action='store',
+                  default='127.0.0.1:8000',
+                  help='server domain name, example: www.example.com')
+
+    gr.add_option('--server-admin', action='store', default='admin@example.com',
+                  help='server admin email address')
+
+    gr.add_option('--use-sqlite', action='store_true', default=True,
+                  help='force Sqlite database in production')
+
+    gr.add_option('--wsgi-user', action='store',
+                  help='the user that the daemon processes should be run as')
+
+    gr.add_option('--wsgi-group', action='store',
+                  help='the primary group that the daemon processes should be run as')
+
+    gr.add_option('--use-htpasswd', action='store_true', default=False,
+                  help='use http authenticaiton')
+
+    gr.add_option('--languages', action='store', default='en',
+                  help='comma separated list of two letter languages codes')
+
+    gr.add_option('--use-jquery', action='store', default='1.7',
+                  help='jQuery version, or empty if you don\'t want to use it')
+
+    gr.add_option('--secret-key', action='store',
+                  help='set this to a random string -- the longer, the better')
+
+    gr.add_option('--mysql-username', action='store', default='root',
+                  help='MySQL database user name.')
+
+    gr.add_option('--mysql-password', action='store', default='',
+                  help='MySQL database user password.')
+
+    gr.add_option('--mysql-dbname', action='store', default=PROJECT_NAME,
+                  help='MySQL database name.')
+
+    gr.add_option('--twitter-bootstrap', action='store_true', default=True,
+                  help='Use Twitter bootstrap framework.')
+
+    gr = ctx.add_option_group('setup options')
+    gr.add_option("--use-pkg-add", action="store_true", dest="use_pkg_add",
+                  default=False, help="use pkg_add in BSD systems")
+
+    gr.add_option("--dry-run", action="store_true", dest="dry_run", default=False,
+                  help="print commands, but do net execute")
+
+
+def _get_secret_key(length=50):
+    chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
+    return ''.join([random.choice(chars) for i in range(length)])
+
+
+def _create_configure_file(ctx):
+    """Store all configuration options to 'configure' file.
+
+    Next time, if you want to to modify some configuration options, you simply
+    can change configure file and and execute it using this command::
+
+        ./configure
+
+    """
+    import pipes
+
+    configure = sys.argv[2:]
+    if not ctx.options.secret_key:
+        configure.append("--secret-key=%s" % ctx.env.SECRET_KEY)
+
+    configure = (['%s' % ' '.join(sys.argv[:2])] +
+                 [pipes.quote(c) for c in configure])
+
+    f = open('configure', 'w')
+    f.write('#!/bin/sh\n')
+    f.write(' \\\n    '.join(configure) + '\n')
+    f.close()
+
+    os.chmod('configure', 0700)
+
+
+def configure(ctx):
+    ctx.find_program('buildout', mandatory=False)
+    ctx.find_program('virtualenv')
+
+    ctx.load('compiler_c python')
+
+    ctx.check_python_version((2,6))
+    ctx.check_python_headers()
+
+    ctx.env.TOP = ctx.path.abspath()
+    ctx.env.PROJECT_NAME = ctx.options.project_name
+    ctx.env.PRODUCTION = ctx.options.production
+    ctx.env.USE_SQLITE = ctx.options.use_sqlite
+    ctx.env.LANGUAGES = ctx.options.languages.split(',') or ['en']
+    ctx.env.LANGUAGE_CODE = ctx.env.LANGUAGES[0]
+    ctx.env.SERVER_ADMIN = ctx.options.server_admin
+    ctx.env.SERVER_NAME = ctx.options.server_name
+    ctx.env.JQUERY_VERSION = ctx.options.use_jquery
+    ctx.env.SECRET_KEY = ctx.options.secret_key or _get_secret_key()
+
+    ctx.env.WSGI_USER = ctx.options.wsgi_user
+    ctx.env.WSGI_GROUP = ctx.options.wsgi_group
+    ctx.env.USE_HTPASSWD = ctx.options.use_htpasswd
+
+    ctx.env.MYSQL_USERNAME = ctx.options.mysql_username
+    ctx.env.MYSQL_PASSWORD = ctx.options.mysql_password
+    ctx.env.MYSQL_DBNAME = ctx.options.mysql_dbname
+
+    ctx.env.TWITTER_BOOTSTRAP = ctx.options.twitter_bootstrap
+
+    if ctx.env.PRODUCTION:
+        ctx.env.DEVELOPMENT = False
+    else:
+        ctx.env.DEVELOPMENT = True
+
+    if not os.path.exists('configure'):
+        _create_configure_file(ctx)
+
+
+def _subst(task):
+    from Cheetah.Template import Template
+
+    infilename = task.inputs[0].abspath()
+    outfilename = task.outputs[0].abspath()
+
+    c = {
+        'NOTE': 'DO NOT MODIFY! This file is generated from %s '
+                'template.' % infilename,
+    }
+    t = Template(file=infilename, searchList=[c, dict(task.generator.env)])
+
+    outfile = codecs.open(outfilename, encoding='utf-8', mode='w')
+    outfile.write(unicode(t))
+    outfile.close()
+
+
+def build(ctx):
+    import babel
+
+    ctx.env.LANG_NAMES = {}
+    for lang in ctx.env.LANGUAGES:
+        ctx.env.LANG_NAMES[lang] = babel.Locale(lang).english_name
+
+    p = ctx.env.PROJECT_NAME
+    glob = ctx.path.ant_glob
+    bld = functools.partial(ctx, update_outputs=True)
+
+    if ctx.env.PRODUCTION:
+        bld(rule=_subst, source='config/apache.conf c4che/_cache.py',
+            target='var/etc/apache.conf')
+
+        bld(rule=_subst, source='config/my.cnf',
+            target='var/etc/my.cnf')
+
+    bld(rule=_subst, source='config/buildout.cfg c4che/_cache.py',
+        target='buildout.cfg')
+
+    bld(rule=_subst, source='config/settings.py c4che/_cache.py',
+        target='%s/settings.py' % p)
+
+    bld(rule=_subst, source='config/urls.py c4che/_cache.py',
+        target='%s/urls.py' % p)
+
+    bld(rule=_subst, source='config/initial_data.json c4che/_cache.py',
+        target='initial_data.json')
+
+    bld(rule='env/bin/python bootstrap.py --distribute', target='bin/buildout',
+        source='buildout.cfg')
+
+    bld(rule='bin/buildout -N', name='buildout', target='bin/django',
+        source='bin/buildout buildout.cfg %s/settings.py' % p)
+
+    bld(rule=('bin/django syncdb --all --noinput && '
+              'bin/django migrate --fake && '
+              'touch ${TGT}'),
+        after='buildout', target='var/db')
+
+    bld(rule='bin/django collectstatic --noinput --verbosity=0',
+        source=(glob('%s/static/**/*' % p)),
+        after='buildout')
+
+    for lang in ctx.env.LANGUAGES:
+        s = (p, lang)
+        if os.path.exists('%s/locale/%s' % s):
+
+            bld(rule='cd %s ; ../bin/django compilemessages -l %s' % s,
+                source='%s/locale/%s/LC_MESSAGES/django.po' % s,
+                target='%s/locale/%s/LC_MESSAGES/django.mo' % s,
+                after='buildout')
+
+
+def makemessages(ctx):
+    for lang in ctx.env.LANGUAGES:
+        s = (ctx.env.PROJECT_NAME, lang)
+        if os.path.exists('%s/locale/%s' % s):
+            _sh('cd %s ; ../bin/django makemessages -l %s '
+                '-e html,txt' % s)
+
+
+def distclean(ctx):
+    for pth in [
+        # buildout generated files
+        'bin', 'develop-eggs', '.installed.cfg', '.mr.developer.cfg',
+
+        # waf generated files
+        '.lock-wafbuild', 'config.log', 'c4che', Context.DBFILE,
+
+        # project specific generated files
+        'buildout.cfg', '%s/settings.py' % ctx.env.PROJECT_NAME,
+
+        # parts
+        'parts/twitter-bootstrap',
+    ]:
+        if os.path.exists(pth):
+            Logs.info('cleaning: %s' % pth)
+            if os.path.isdir(pth):
+                shutil.rmtree(pth, ignore_errors=True)
+            else:
+                os.unlink(pth)
+
+    cleanpyc(ctx)
+
+
+def cleanpyc(ctx):
+    "Clean *.pyc files from sources."
+    Logs.info('cleaning: *.pyc')
+    for pth in ctx.path.ant_glob('%s/**/*.pyc' % ctx.env.PROJECT_NAME):
+        os.unlink(pth.abspath())
+
+
+def _get_platform():
+    import platform
+
+    python_version = sys.version[:3]
+    uname = os.uname()[0].lower()
+    if uname == 'linux':
+        if python_version > '2.6':
+            name, release = platform.linux_distribution()[:2]
+        else:
+            name, release = platform.linux_distribution()[:2]
+        if name and release:
+            return (uname, name.lower(), release)
+    return (uname, '', '')
+
+
+def _sh(cmd, dry_run=False):
+    print(cmd)
+    if not dry_run:
+        rcode = subprocess.call(cmd, shell=True)
+        if rcode > 0:
+            sys.exit(rcode)
+
+
+def virtualenv(ctx):
+    """initialize virtualenv environment"""
+    uname, name, release = _get_platform()
+    sh = functools.partial(_sh, dry_run=ctx.options.dry_run)
+    if uname == 'darwin':
+        pyver = sys.version[:3]
+        sh('virtualenv-%s --no-site-packages env' % pyver)
+    else:
+        sh('virtualenv --no-site-packages env')
+    sh('env/bin/pip install Cheetah Babel')
+
+
+class PackageSet(set):
+    def replace(self, *args):
+        if not isinstance(args[0], tuple):
+            args = (args[0:2],)
+        for old, new in args:
+            self.remove(old)
+            self.add(new)
+
+    def replace_all(self, search, replace):
+        repl = re.compile(search)
+        for old in self:
+            new = repl.sub(replace, old)
+            if old != new:
+                self.replace(old, new)
+
+
+def setup(ctx):
+    """install all required dependencies"""
+
+    sh = functools.partial(_sh, dry_run=ctx.options.dry_run)
+
+    if not ctx.options.dry_run and not os.geteuid() == 0:
+        sys.exit("Only root can run this script.")
+
+    uname, name, release = _get_platform()
+
+    packages = PackageSet([
+        # Build
+        'build-essential',
+
+        # Gettext
+        'gettext',
+
+        # Python
+        'python-dev',
+        'python-virtualenv',
+    ])
+
+    if name == 'ubuntu' or name == 'debian':
+        sh('apt-get install %s' % ' '.join(packages))
+
+    elif name == 'fedora':
+        packages.remove('build-essential')
+        packages.replace_all('-dev$', '-devel')
+        sh('yum groupinstall "Development Tools"')
+        sh('yum install %s' % ' '.join(packages))
+
+    elif uname == 'darwin':
+        pyver = sys.version[:3].replace('.', '')
+        packages.remove('build-essential')
+        packages.remove('python-dev')
+        packages.replace(
+                ('python-virtualenv', 'py%s-virtualenv' % pyver),
+        )
+
+        sh('port select --set python python%s' % pyver)
+        sh('port -v install %s' % ' '.join(packages))
+        sh('ln -s /opt/local/bin/virtualenv-%s /opt/local/bin/virtualenv' % pyver)
+
+    elif uname == 'freebsd':
+        if ctx.options.use_pkg_add:
+            sh('pkg_add -r %s' % ' '.join(packages))
+        else:
+            for pkg in packages:
+                sh('cd /usr/ports/%s && make install clean' % pkg)
+
+    else:
+        sys.exit('Sorry, your platform is not supported...')
+
+# --------
+# Hack to pass ``BuildContext`` to commands other than ``build``.
+from waflib.Build import BuildContext
+def use_build_context_for(*cmds):
+    for cmd in cmds:
+        type('BldCtx_' + cmd, (BuildContext,), {'cmd': cmd, 'fun': cmd})
+use_build_context_for('makemessages', 'distclean', 'cleanpyc')
+# --------