Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

playdoh'd

  • Loading branch information...
commit d982316369015c6d8ba29ff07cce63c32228e61b 1 parent c07ece6
@rlr rlr authored
Showing with 708 additions and 90 deletions.
  1. +5 −0 .gitignore
  2. +3 −0  .gitmodules
  3. 0  {work → apps}/__init__.py
  4. 0  {system/tests → apps/commons}/__init__.py
  5. +10 −0 apps/commons/context_processors.py
  6. +68 −0 apps/commons/helpers.py
  7. +58 −0 apps/commons/middleware.py
  8. 0  system/__init__.py → apps/commons/models.py
  9. 0  apps/commons/tests/__init__.py
  10. +50 −0 apps/commons/tests/test_migrations.py
  11. +114 −0 apps/commons/urlresolvers.py
  12. 0  apps/system/__init__.py
  13. 0  { → apps}/system/forms.py
  14. 0  { → apps}/system/models.py
  15. +11 −15 { → apps}/system/templates/system/admin.html
  16. +38 −0 apps/system/templates/system/index.html
  17. 0  apps/system/tests/__init__.py
  18. 0  { → apps}/system/tests/test_useragent.py
  19. +1 −1  { → apps}/system/tests/test_views.py
  20. 0  { → apps}/system/urls.py
  21. 0  { → apps}/system/useragent.py
  22. +11 −14 { → apps}/system/views.py
  23. 0  apps/work/__init__.py
  24. 0  { → apps}/work/models.py
  25. +2 −3 { → apps}/work/templates/work/work.html
  26. 0  { → apps}/work/tests.py
  27. 0  { → apps}/work/urls.py
  28. +3 −3 { → apps}/work/views.py
  29. +40 −0 manage.py
  30. 0  media/js/{ → libs}/jquery.js
  31. 0  media/js/{ → libs}/json2.js
  32. +2 −2 migrations/001-add-worker-engines.sql
  33. +2 −2 migrations/002-add-start-token.sql
  34. +7 −0 requirements/compiled.txt
  35. +16 −0 requirements/dev.txt
  36. +23 −0 requirements/prod.txt
  37. +171 −28 settings.py
  38. +40 −0 settings_local.py-dist
  39. +0 −10 settings_local.py-example
  40. +17 −12 templates/base.html
  41. 0  {deploy → wsgi}/application.wsgi
  42. +16 −0 wsgi/playdoh.wsgi
View
5 .gitignore
@@ -5,3 +5,8 @@ pip-log.txt
.DS_Store
*.db
settings_local.py
+*-min.css
+*-all.css
+*-min.js
+*-all.js
+vendor
View
3  .gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor"]
+ path = vendor
+ url = git://github.com/mozilla/playdoh-lib.git
View
0  work/__init__.py → apps/__init__.py
File renamed without changes
View
0  system/tests/__init__.py → apps/commons/__init__.py
File renamed without changes
View
10 apps/commons/context_processors.py
@@ -0,0 +1,10 @@
+from django.conf import settings
+from django.utils import translation
+
+
+def i18n(request):
+ return {'LANGUAGES': settings.LANGUAGES,
+ 'LANG': settings.LANGUAGE_URL_MAP.get(translation.get_language())
+ or translation.get_language(),
+ 'DIR': 'rtl' if translation.get_language_bidi() else 'ltr',
+ }
View
68 apps/commons/helpers.py
@@ -0,0 +1,68 @@
+import cgi
+import datetime
+import urllib
+import urlparse
+
+from django.conf import settings
+from django.template import defaultfilters
+from django.utils.html import strip_tags
+
+from jingo import register
+import jinja2
+
+from .urlresolvers import reverse
+
+
+# Yanking filters from Django.
+register.filter(strip_tags)
+register.filter(defaultfilters.timesince)
+register.filter(defaultfilters.truncatewords)
+
+
+
+@register.function
+def thisyear():
+ """The current year."""
+ return jinja2.Markup(datetime.date.today().year)
+
+
+@register.function
+def url(viewname, *args, **kwargs):
+ """Helper for Django's ``reverse`` in templates."""
+ return reverse(viewname, args=args, kwargs=kwargs)
+
+
+@register.filter
+def urlparams(url_, hash=None, **query):
+ """
+Add a fragment and/or query paramaters to a URL.
+
+New query params will be appended to exising parameters, except duplicate
+names, which will be replaced.
+"""
+ url = urlparse.urlparse(url_)
+ fragment = hash if hash is not None else url.fragment
+
+ # Use dict(parse_qsl) so we don't get lists of values.
+ q = url.query
+ query_dict = dict(urlparse.parse_qsl(smart_str(q))) if q else {}
+ query_dict.update((k, v) for k, v in query.items())
+
+ query_string = _urlencode([(k, v) for k, v in query_dict.items()
+ if v is not None])
+ new = urlparse.ParseResult(url.scheme, url.netloc, url.path, url.params,
+ query_string, fragment)
+ return new.geturl()
+
+def _urlencode(items):
+ """A Unicode-safe URLencoder."""
+ try:
+ return urllib.urlencode(items)
+ except UnicodeEncodeError:
+ return urllib.urlencode([(k, smart_str(v)) for k, v in items])
+
+
+@register.filter
+def urlencode(txt):
+ """Url encode a path."""
+ return urllib.quote_plus(txt)
View
58 apps/commons/middleware.py
@@ -0,0 +1,58 @@
+"""
+Taken from zamboni.amo.middleware.
+
+This is django-localeurl, but with mozilla style capital letters in
+the locale codes.
+"""
+
+import urllib
+
+from django.http import HttpResponsePermanentRedirect
+from django.utils.encoding import smart_str
+
+import tower
+
+from . import urlresolvers
+from .helpers import urlparams
+
+class LocaleURLMiddleware(object):
+ """
+ 1. Search for the locale.
+ 2. Save it in the request.
+ 3. Strip them from the URL.
+ """
+
+ def process_request(self, request):
+ prefixer = urlresolvers.Prefixer(request)
+ urlresolvers.set_url_prefix(prefixer)
+ full_path = prefixer.fix(prefixer.shortened_path)
+
+ if 'lang' in request.GET:
+ # Blank out the locale so that we can set a new one. Remove lang
+ # from the query params so we don't have an infinite loop.
+ prefixer.locale = ''
+ new_path = prefixer.fix(prefixer.shortened_path)
+ query = dict((smart_str(k), request.GET[k]) for k in request.GET)
+ query.pop('lang')
+ return HttpResponsePermanentRedirect(urlparams(new_path, **query))
+
+ if full_path != request.path:
+ query_string = request.META.get('QUERY_STRING', '')
+ full_path = urllib.quote(full_path.encode('utf-8'))
+
+ if query_string:
+ full_path = '%s?%s' % (full_path, query_string)
+
+ response = HttpResponsePermanentRedirect(full_path)
+
+ # Vary on Accept-Language if we changed the locale
+ old_locale = prefixer.locale
+ new_locale, _ = prefixer.split_path(full_path)
+ if old_locale != new_locale:
+ response['Vary'] = 'Accept-Language'
+
+ return response
+
+ request.path_info = '/' + prefixer.shortened_path
+ request.locale = prefixer.locale
+ tower.activate(prefixer.locale)
View
0  system/__init__.py → apps/commons/models.py
File renamed without changes
View
0  apps/commons/tests/__init__.py
No changes.
View
50 apps/commons/tests/test_migrations.py
@@ -0,0 +1,50 @@
+import re
+from os import listdir
+from os.path import join, dirname
+
+import test_utils
+
+import manage
+
+
+class MigrationTests(test_utils.TestCase):
+ """Sanity checks for the SQL migration scripts."""
+
+ @staticmethod
+ def _migrations_path():
+ """Return the absolute path to the migration script folder."""
+ return manage.path('migrations')
+
+ def test_unique(self):
+ """Assert that the numeric prefixes of the DB migrations are unique."""
+ leading_digits = re.compile(r'^\d+')
+ seen_numbers = set()
+ path = self._migrations_path()
+ for filename in listdir(path):
+ match = leading_digits.match(filename)
+ if match:
+ number = match.group()
+ if number in seen_numbers:
+ self.fail('There is more than one migration #%s in %s.' %
+ (number, path))
+ seen_numbers.add(number)
+
+ def test_innodb_and_utf8(self):
+ """Make sure each created table uses the InnoDB engine and UTF-8."""
+ # Heuristic: make sure there are at least as many "ENGINE=InnoDB"s as
+ # "CREATE TABLE"s. (There might be additional "InnoDB"s in ALTER TABLE
+ # statements, which are fine.)
+ path = self._migrations_path()
+ for filename in sorted(listdir(path)):
+ with open(join(path, filename)) as f:
+ contents = f.read()
+ creates = contents.count('CREATE TABLE')
+ engines = contents.count('ENGINE=InnoDB')
+ encodings = (contents.count('CHARSET=utf8') +
+ contents.count('CHARACTER SET utf8'))
+ assert engines >= creates, ("There weren't as many "
+ 'occurrences of "ENGINE=InnoDB" as of "CREATE TABLE" in '
+ 'migration %s.' % filename)
+ assert encodings >= creates, ("There weren't as many "
+ 'UTF-8 declarations as "CREATE TABLE" occurrences in '
+ 'migration %s.' % filename)
View
114 apps/commons/urlresolvers.py
@@ -0,0 +1,114 @@
+from django.conf import settings
+from django.core.urlresolvers import reverse as django_reverse
+from django.utils.thread_support import currentThread
+from django.utils.translation.trans_real import parse_accept_lang_header
+
+
+# Thread-local storage for URL prefixes. Access with (get|set)_url_prefix.
+_prefixes = {}
+
+
+def set_url_prefix(prefix):
+ """Set the ``prefix`` for the current thread."""
+ _prefixes[currentThread()] = prefix
+
+
+def get_url_prefix():
+ """Get the prefix for the current thread, or None."""
+ return _prefixes.get(currentThread())
+
+
+def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
+ """Wraps Django's reverse to prepend the correct locale."""
+ prefixer = get_url_prefix()
+
+ if prefixer:
+ prefix = prefix or '/'
+ url = django_reverse(viewname, urlconf, args, kwargs, prefix)
+ if prefixer:
+ return prefixer.fix(url)
+ else:
+ return url
+
+
+def find_supported(test):
+ return [settings.LANGUAGE_URL_MAP[x] for
+ x in settings.LANGUAGE_URL_MAP if
+ x.split('-', 1)[0] == test.lower().split('-', 1)[0]]
+
+
+class Prefixer(object):
+
+ def __init__(self, request):
+ self.request = request
+ split = self.split_path(request.path_info)
+ self.locale, self.shortened_path = split
+
+ def split_path(self, path_):
+ """
+ Split the requested path into (locale, path).
+
+ locale will be empty if it isn't found.
+ """
+ path = path_.lstrip('/')
+
+ # Use partitition instead of split since it always returns 3 parts
+ first, _, rest = path.partition('/')
+
+ lang = first.lower()
+ if lang in settings.LANGUAGE_URL_MAP:
+ return settings.LANGUAGE_URL_MAP[lang], rest
+ else:
+ supported = find_supported(first)
+ if len(supported):
+ return supported[0], rest
+ else:
+ return '', path
+
+ def get_language(self):
+ """
+ Return a locale code we support on the site using the
+ user's Accept-Language header to determine which is best. This
+ mostly follows the RFCs but read bug 439568 for details.
+ """
+ if 'lang' in self.request.GET:
+ lang = self.request.GET['lang'].lower()
+ if lang in settings.LANGUAGE_URL_MAP:
+ return settings.LANGUAGE_URL_MAP[lang]
+
+ if self.request.META.get('HTTP_ACCEPT_LANGUAGE'):
+ best = self.get_best_language(
+ self.request.META['HTTP_ACCEPT_LANGUAGE'])
+ if best:
+ return best
+ return settings.LANGUAGE_CODE
+
+ def get_best_language(self, accept_lang):
+ """Given an Accept-Language header, return the best-matching language."""
+ LUM = settings.LANGUAGE_URL_MAP
+ PREFIXES = dict((x.split('-')[0], LUM[x]) for x in LUM)
+ langs = dict(LUM)
+ langs.update((k.split('-')[0], v) for k, v in LUM.items() if
+ k.split('-')[0] not in langs)
+ ranked = parse_accept_lang_header(accept_lang)
+ for lang, _ in ranked:
+ lang = lang.lower()
+ if lang in langs:
+ return langs[lang]
+ pre = lang.split('-')[0]
+ if pre in langs:
+ return langs[pre]
+ # Could not find an acceptable language.
+ return False
+
+ def fix(self, path):
+ path = path.lstrip('/')
+ url_parts = [self.request.META['SCRIPT_NAME']]
+
+ if path.partition('/')[0] not in settings.SUPPORTED_NONLOCALES:
+ locale = self.locale if self.locale else self.get_language()
+ url_parts.append(locale)
+
+ url_parts.append(path)
+
+ return '/'.join(url_parts)
View
0  apps/system/__init__.py
No changes.
View
0  system/forms.py → apps/system/forms.py
File renamed without changes
View
0  system/models.py → apps/system/models.py
File renamed without changes
View
26 system/templates/system/admin.html → apps/system/templates/system/admin.html
@@ -1,22 +1,18 @@
{% extends "base.html" %}
-
-{% block head %}
- <script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.js"></script>
- <script type="text/javascript" src="{{ MEDIA_URL }}js/system.js"></script>
-{% endblock %}
+{% set scripts = ('system',) %}
{% block content %}
<div id="right">
{% if test_suite %}
- <form action="{% url system.create_edit_test_suite test_suite.id %}" method="post">
+ <form action="{{ url('system.create_edit_test_suite', test_suite.id) }}" method="post">
{% else %}
- <form action="{% url system.create_edit_test_suite %}" method="post">
+ <form action="{{ url('system.create_edit_test_suite') }}" method="post">
{% endif %}
+{{ csrf() }}
<table>
-{{ form.as_table }}
-{% csrf_token %}
+ {{ form.as_table() }}
</table>
{% if test_suite %}
<input type="submit" value="Edit test suite" />
@@ -38,16 +34,16 @@
<th>{{ ts.name }}</th>
<th>{{ ts.slug }}</th>
<th><a href="{{ ts.url }}">{{ ts.url }}</a></th>
- <th><a href="{% url system.edit_test_suite ts.id %}">[edit]</a>
- <a href="{% url system.delete_test_suite ts.id %}">[delete]</a></th>
+ <th><a href="{{ url('system.edit_test_suite', ts.id) }}">[edit]</a>
+ <a href="{{ url('system.delete_test_suite', ts.id) }}">[delete]</a></th>
<th>
<ul>
- {% for tk in ts.active_tokens %}
+ {% for tk in ts.active_tokens() %}
<li>{{ tk.token }}</li>
{% endfor %}
<li>
- <form method="post" action="{% url system.generate_token %}">
- {% csrf_token %}
+ <form method="post" action="{{ url('system.generate_token') }}">
+ {{ csrf() }}
<input type="hidden" name="test_suite_id" value="{{ ts.id }}"/>
<button type="submit">Generate token</button>
</form>
@@ -65,4 +61,4 @@
</ul>
</div>
-{% endblock %}
+{% endblock %}
View
38 apps/system/templates/system/index.html
@@ -0,0 +1,38 @@
+{% extends "base.html" %}
+{% set scripts = ('system',) %}
+
+{% block content %}
+
+<div id="right">
+ <p>There
+ {% if workers.count == 1 %}
+ is {{ workers.count() }} web browser
+ {% else %}
+ are {{ workers.count() }} web browsers
+ {% endif %}
+ connected and ready to
+ run JavaScript tests.</p>
+ <ul>
+ {% for worker in workers %}
+ <li>{{ worker.user_agent }}</li>
+ {% endfor %}
+ </ul>
+ <p>To add your web browser to the work pool, open this URL:
+ <a href="{{ url('work') }}">{{ url('work') }}</a>
+ </p>
+ <h2>Test Suites</h2>
+ <p>Number of available test suites: {{ test_suites.count() }}</p>
+ <ul>
+ {% for ts in test_suites %}
+ <li>{{ ts.name }}</li>
+ {% endfor %}
+ </ul>
+</div>
+<div id="left">
+ <ul class="nav">
+ <li><a href="{{ url('system.test_suites') }}">Manage test suites</a></li>
+ <li><a href="https://github.com/kumar303/jstestnet">Source code for this site</a></li>
+ </ul>
+</div>
+
+{% endblock %}
View
0  apps/system/tests/__init__.py
No changes.
View
0  system/tests/test_useragent.py → apps/system/tests/test_useragent.py
File renamed without changes
View
2  system/tests/test_views.py → apps/system/tests/test_views.py
@@ -316,7 +316,7 @@ def is_login_page(r):
def test_create_test_suite(self):
r = self.client.get(reverse('system.test_suites'))
eq_(r.status_code, 200)
- assert 'form' in r.context[0]
+ assert 'form' in r.context
r = self.client.post(reverse('system.create_edit_test_suite'), {
'name': 'Zamboni',
'slug': 'zamboni',
View
0  system/urls.py → apps/system/urls.py
File renamed without changes
View
0  system/useragent.py → apps/system/useragent.py
File renamed without changes
View
25 system/views.py → apps/system/views.py
@@ -9,13 +9,14 @@
from django.template import loader, RequestContext
from django.views.decorators.csrf import csrf_view_exempt
+import jingo
+
+from common.decorators import json_view, post_required
from common.stdlib import json
from system.models import TestSuite, Token
from system.forms import TestSuiteForm
-from common.decorators import json_view, post_required
-import work.views
from work.models import Worker, WorkQueue, TestRun, TestRunQueue
-
+import work.views
class InvalidToken(Exception):
"""An invalid or expired token sent to start_tests."""
@@ -28,12 +29,9 @@ def test_suites(request, test_suite_id=None, form=None):
TestSuite, pk=test_suite_id) if test_suite_id else None
if not form:
form = TestSuiteForm(instance=test_suite)
- return render_to_response('system/admin.html', dict(
- test_suites=test_suites,
- form=form,
- test_suite=test_suite
- ),
- context_instance=RequestContext(request))
+
+ data = dict(test_suites=test_suites, form=form, test_suite=test_suite)
+ return jingo.render(request, 'system/admin.html', data)
@staff_member_required
@@ -184,9 +182,8 @@ def restart_workers(request):
def status(request):
work.views.collect_garbage()
- return render_to_response('system/index.html', dict(
- workers=(Worker.objects.filter(is_alive=True)
+
+ data = dict(workers=(Worker.objects.filter(is_alive=True)
.exclude(last_heartbeat=None)),
- test_suites=TestSuite.objects.all().order_by('name')
- ),
- context_instance=RequestContext(request))
+ test_suites=TestSuite.objects.all().order_by('name'))
+ return jingo.render(request, 'system/index.html', data)
View
0  apps/work/__init__.py
No changes.
View
0  work/models.py → apps/work/models.py
File renamed without changes
View
5 work/templates/work/work.html → apps/work/templates/work/work.html
@@ -5,14 +5,13 @@
<script type="text/javascript">
var WORKER_ID = {{ worker_id }};
</script>
- <script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.js"></script>
- <script type="text/javascript" src="{{ MEDIA_URL }}js/json2.js"></script>
- <script type="text/javascript" src="{{ MEDIA_URL }}js/work.js"></script>
</head>
<body>
<p><strong>Status:</strong> <span id="msg">Loading...</span></p>
<h3>History</h3>
<ul id="history"></ul>
<div id="iframes"></div>
+ {{ js('common') }}
+ {{ js('work') }}
</body>
</html>
View
0  work/tests.py → apps/work/tests.py
File renamed without changes
View
0  work/urls.py → apps/work/urls.py
File renamed without changes
View
6 work/views.py → apps/work/views.py
@@ -4,6 +4,8 @@
from django.shortcuts import render_to_response, get_object_or_404
from django.template import loader, RequestContext
+import jingo
+
from common.stdlib import json
from common.decorators import json_view
from work.models import Worker, WorkQueue, TestRun
@@ -91,6 +93,4 @@ def work(request):
collect_garbage()
worker = Worker()
worker.save()
- return render_to_response('work/work.html',
- {'worker_id': worker.id},
- context_instance=RequestContext(request))
+ return jingo.render(request, 'work/work.html', {'worker_id': worker.id})
View
40 manage.py
@@ -1,7 +1,35 @@
#!/usr/bin/env python
import os
+import site
import sys
+ROOT = os.path.dirname(os.path.abspath(__file__))
+path = lambda *a: os.path.join(ROOT,*a)
+
+
+# Adjust the python path and put local packages in front.
+prev_sys_path = list(sys.path)
+
+site.addsitedir(path('apps'))
+site.addsitedir(path('lib'))
+
+# Local (project) vendor library
+site.addsitedir(path('vendor-local'))
+site.addsitedir(path('vendor-local/lib/python'))
+
+# Global (upstream) vendor library
+site.addsitedir(path('vendor'))
+site.addsitedir(path('vendor/lib/python'))
+
+
+# Move the new items to the front of sys.path. (via virtualenv)
+new_sys_path = []
+for item in list(sys.path):
+ if item not in prev_sys_path:
+ new_sys_path.append(item)
+ sys.path.remove(item)
+sys.path[:0] = new_sys_path
+
from django.core.management import execute_manager, setup_environ
try:
@@ -17,5 +45,17 @@
" Please come back and try again later.")
raise
+# If we want to use django settings anywhere, we need to set up the required
+# environment variables.
+setup_environ(settings)
+
+# Configure Celery
+import djcelery
+djcelery.setup_loader()
+
+# Monkey-patch django forms to avoid having to use Jinja2's |safe everywhere.
+import safe_django_forms
+safe_django_forms.monkeypatch()
+
if __name__ == "__main__":
execute_manager(settings)
View
0  media/js/jquery.js → media/js/libs/jquery.js
File renamed without changes
View
0  media/js/json2.js → media/js/libs/json2.js
File renamed without changes
View
4 migrations/001-add-worker-engines.sql
@@ -4,8 +4,8 @@ CREATE TABLE `work_workerengine` (
`worker_id` integer NOT NULL,
`engine` varchar(50) NOT NULL,
`version` varchar(10) NOT NULL
-)
-;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+
ALTER TABLE `work_workerengine` ADD CONSTRAINT `worker_id_refs_id_e503fac9` FOREIGN KEY (`worker_id`) REFERENCES `work_worker` (`id`);
CREATE INDEX `work_workerengine_20fc5b84` ON `work_workerengine` (`worker_id`);
CREATE INDEX `work_workerengine_93906ca3` ON `work_workerengine` (`engine`);
View
4 migrations/002-add-start-token.sql
@@ -5,8 +5,8 @@ CREATE TABLE `system_token` (
`active` bool NOT NULL,
`created` datetime,
`last_modified` datetime
-)
-;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+
ALTER TABLE `system_token` ADD CONSTRAINT `test_suite_id_refs_id_3268f678`
FOREIGN KEY (`test_suite_id`) REFERENCES `system_testsuite` (`id`);
CREATE INDEX `system_token_34d728db` ON `system_token` (`active`);
View
7 requirements/compiled.txt
@@ -0,0 +1,7 @@
+MySQL-python==1.2.3c1
+Jinja2==2.5.5
+
+# for bcrypt passwords
+hmac==20101005
+hashlib==20081119
+py-bcrypt==0.2
View
16 requirements/dev.txt
@@ -0,0 +1,16 @@
+# This file pulls in everything a developer needs. If it's a basic package
+# needed to run the site, it belongs in requirements/prod.txt. If it's a
+# package for developers (testing, docs, etc.), it goes in this file.
+
+-r prod.txt
+
+# Documentation
+Sphinx==1.0.6
+
+# Testing
+nose==1.0.0
+-e git://github.com/jbalogh/django-nose.git#egg=django_nose
+-e git://github.com/jbalogh/test-utils.git#egg=test-utils
+
+# L10n
+translate-toolkit==1.8.0
View
23 requirements/prod.txt
@@ -0,0 +1,23 @@
+# Django stuff
+-e git://github.com/django/django@36c82ac8#egg=django
+
+# Templates
+-e git://github.com/jbalogh/jingo.git#egg=jingo
+-e git://github.com/jsocol/jingo-minify.git#egg=jingo-minify
+GitPython==0.1.7
+
+# Various tidbits
+-e git://github.com/jsocol/commonware.git#egg=commonware
+-e git://github.com/mozilla/nuggets.git#egg=nuggets
+
+# Security
+-e git://github.com/fwenzel/django-sha2.git#egg=django-sha2
+
+# Celery: Message queue
+celery
+django-celery
+
+# L10n
+Babel>=0.9.4
+-e git://github.com/clouserw/tower.git#egg=tower
+-e git://github.com/fwenzel/django-mozilla-product-details#egg=django-mozilla-product-details
View
199 settings.py
@@ -2,6 +2,15 @@
import os
+from django.utils.functional import lazy
+
+# Make file paths relative to settings.
+ROOT = os.path.dirname(os.path.abspath(__file__))
+path = lambda *a: os.path.join(ROOT, *a)
+
+ROOT_PACKAGE = os.path.basename(ROOT)
+
+
DEBUG = False
TEMPLATE_DEBUG = DEBUG
@@ -11,19 +20,13 @@
MANAGERS = ADMINS
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
- 'NAME': 'jstestnet_dev', # Or path to database file if using sqlite3.
- 'USER': 'jstestnet_dev', # Not used with sqlite3.
- 'PASSWORD': 'test', # Not used with sqlite3.
- 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
- 'PORT': '', # Set to empty string for default. Not used with sqlite3.
- 'OPTIONS': {'init_command': 'SET storage_engine=InnoDB'},
- 'TEST_CHARSET': 'utf8',
- 'TEST_COLLATION': 'utf8_general_ci',
- }
-}
+DATABASES = {} # See settings_local.
+
+# Site ID is used by Django's Sites framework.
+SITE_ID = 1
+
+
+## Internationalization.
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
@@ -34,12 +37,6 @@
# system time zone.
TIME_ZONE = 'America/Chicago'
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
@@ -48,9 +45,42 @@
# calendars according to the current locale
USE_L10N = True
+# Gettext text domain
+TEXT_DOMAIN = 'messages'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-US'
+
+# Accepted locales
+KNOWN_LANGUAGES = ('en-US',)
+
+# List of RTL locales known to this project. Subset of LANGUAGES.
+RTL_LANGUAGES = () # ('ar', 'fa', 'fa-IR', 'he')
+
+LANGUAGE_URL_MAP = dict([(i.lower(), i) for i in KNOWN_LANGUAGES])
+
+# Override Django's built-in with our native names
+class LazyLangs(dict):
+ def __new__(self):
+ from product_details import product_details
+ return dict([(lang.lower(), product_details.languages[lang]['native'])
+ for lang in KNOWN_LANGUAGES])
+
+# Where to store product details etc.
+PROD_DETAILS_DIR = path('lib/product_details_json')
+
+LANGUAGES = lazy(LazyLangs, dict)()
+
+# Paths that don't require a locale code in the URL.
+SUPPORTED_NONLOCALES = []
+
+
+## Media and templates.
+
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'media')
+MEDIA_ROOT = path('media')
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
@@ -72,25 +102,85 @@
# 'django.template.loaders.eggs.Loader',
)
+TEMPLATE_CONTEXT_PROCESSORS = (
+ 'django.contrib.auth.context_processors.auth',
+ 'django.core.context_processors.debug',
+ 'django.core.context_processors.media',
+ 'django.core.context_processors.request',
+ 'django.core.context_processors.csrf',
+ 'django.contrib.messages.context_processors.messages',
+
+ 'commons.context_processors.i18n',
+ #'jingo_minify.helpers.build_ids',
+)
+
+TEMPLATE_DIRS = (
+ path('templates'),
+)
+
+def JINJA_CONFIG():
+ import jinja2
+ from django.conf import settings
+# from caching.base import cache
+ config = {'extensions': ['tower.template.i18n', 'jinja2.ext.do',
+ 'jinja2.ext.with_', 'jinja2.ext.loopcontrols'],
+ 'finalize': lambda x: x if x is not None else ''}
+# if 'memcached' in cache.scheme and not settings.DEBUG:
+ # We're passing the _cache object directly to jinja because
+ # Django can't store binary directly; it enforces unicode on it.
+ # Details: http://jinja.pocoo.org/2/documentation/api#bytecode-cache
+ # and in the errors you get when you try it the other way.
+# bc = jinja2.MemcachedBytecodeCache(cache._cache,
+# "%sj2:" % settings.CACHE_PREFIX)
+# config['cache_size'] = -1 # Never clear the cache
+# config['bytecode_cache'] = bc
+ return config
+
+# Bundles is a dictionary of two dictionaries, css and js, which list css files
+# and js files that can be bundled together by the minify app.
+MINIFY_BUNDLES = {
+ 'css': {
+ 'common': (
+ 'css/minimalist/style.css',
+ 'css/style.css',
+ ),
+ },
+ 'js': {
+ 'common': (
+ 'js/libs/jquery.js',
+ 'js/libs/json2.js',
+ ),
+ 'system': (
+ 'js/system.js',
+ ),
+ 'work': (
+ 'js/work.js',
+ ),
+ }
+}
+
+
+## Middlewares, apps, URL configs.
+
MIDDLEWARE_CLASSES = (
+ #'commons.middleware.LocaleURLMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
-)
-
-ROOT_URLCONF = '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(os.path.dirname(__file__), 'templates')
+ 'commonware.middleware.FrameOptionsHeader',
)
+ROOT_URLCONF = '%s.urls' % ROOT_PACKAGE
+
INSTALLED_APPS = (
+ 'commons', # Content common to most playdoh-based apps.
+ 'jingo_minify',
+ 'tower', # for ./manage.py extract (L10n)
'django.contrib.auth',
+ 'django_sha2', # Load after auth to monkey-patch it.
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
@@ -99,9 +189,62 @@
'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
+
+ # Third-party apps
+ 'commonware.response.cookies',
+ #'djcelery',
+ #'product_details',
+
+ # Local apps
'system',
'work',
)
+# Tells the extract script what files to look for L10n in and what function
+# handles the extraction. The Tower library expects this.
+DOMAIN_METHODS = {
+ 'messages': [
+ ('apps/**.py',
+ 'tower.management.commands.extract.extract_tower_python'),
+ ('**/templates/**.html',
+ 'tower.management.commands.extract.extract_tower_template'),
+ ],
+
+ ## Use this if you have localizable HTML files:
+ #'lhtml': [
+ # ('**/templates/**.lhtml',
+ # 'tower.management.commands.extract.extract_tower_template'),
+ #],
+
+ ## Use this if you have localizable JS files:
+ #'javascript': [
+ # Make sure that this won't pull in strings from external libraries you
+ # may use.
+ # ('media/js/**.js', 'javascript'),
+ #],
+}
+
+# Path to Java. Used for compress_assets.
+JAVA_BIN = '/usr/bin/java'
+
+## Auth
+PWD_ALGORITHM = 'bcrypt'
+HMAC_KEYS = {
+ #'2011-01-01': 'cheesecake',
+}
+
+## Tests
+TEST_RUNNER = 'test_utils.runner.RadicalTestSuiteRunner'
+
+## Celery
+#BROKER_HOST = 'localhost'
+#BROKER_PORT = 5672
+#BROKER_USER = 'playdoh'
+#BROKER_PASSWORD = 'playdoh'
+#BROKER_VHOST = 'playdoh'
+#BROKER_CONNECTION_TIMEOUT = 0.1
+#CELERY_RESULT_BACKEND = 'amqp'
+#CELERY_IGNORE_RESULT = True
+
LOGIN_URL = "/admin-contrib/"
LOGOUT_URL = "/admin-contrib/logout"
View
40 settings_local.py-dist
@@ -0,0 +1,40 @@
+# This is an example settings_local.py file.
+# Copy it and add your local settings here.
+
+from settings import *
+
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'jstestnet_dev',
+ 'USER': 'jstestnet_dev',
+ 'PASSWORD': 'test',
+ 'HOST': '',
+ 'PORT': '',
+ 'OPTIONS': {
+ 'init_command': 'SET storage_engine=InnoDB',
+ 'charset' : 'utf8',
+ 'use_unicode' : True,
+ },
+ 'TEST_CHARSET': 'utf8',
+ 'TEST_COLLATION': 'utf8_general_ci',
+ },
+}
+
+ADMINS = (
+ # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+
+DEBUG = TEMPLATE_DEBUG = True
+
+INSTALLED_APPS += (
+ 'django_nose',
+)
+
+HMAC_KEYS = {
+ #'2011-01-01': 'cheesecake',
+}
View
10 settings_local.py-example
@@ -1,10 +0,0 @@
-from settings import *
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-INSTALLED_APPS += (
- 'django_nose',
-)
-
-TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
View
29 templates/base.html
@@ -1,19 +1,24 @@
<!DOCTYPE html>
-<html lang="en-US">
+<html LANG="{{ LANG }}" dir="{{ DIR }}">
<head>
- <title>JS TestNet</title>
- <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/minimalist/style.css" media="screen" />
- <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/style.css" media="screen" />
- {% block head %}{% endblock %}
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <title>{% block page_title %}JS TestNet{% endblock %}</title>
+ {{ css('common') }}
+ {% for style in styles %}
+ {{ css(style) }}
+ {% endfor %}
</head>
<body>
-<div id="header">
- <h1>JS TestNet</h1>
-</div>
-<div id="content">
+ <div id="header">
+ <h1>JS TestNet</h1>
+ </div>
+ <div id="content">
+ {% block content %}{% endblock %}
+ </div>
-{% block content %}{% endblock %}
-
-</div>
+ {{ js('common') }}
+ {% for script in scripts %}
+ {{ js(script) }}
+ {% endfor %}
</body>
</html>
View
0  deploy/application.wsgi → wsgi/application.wsgi
File renamed without changes
View
16 wsgi/playdoh.wsgi
@@ -0,0 +1,16 @@
+import os
+import site
+
+os.environ['CELERY_LOADER'] = 'django'
+
+# Add the app dir to the python path so we can import manage.
+wsgidir = os.path.dirname(__file__)
+site.addsitedir(os.path.abspath(os.path.join(wsgidir, '../')))
+
+# manage adds /apps, /lib, and /vendor to the Python path.
+import manage
+
+import django.core.handlers.wsgi
+application = django.core.handlers.wsgi.WSGIHandler()
+
+# vim: ft=python
Please sign in to comment.
Something went wrong with that request. Please try again.