28 | {% if request.user.is_authenticated() %}
29 | Welcome {{ request.user.first_name }}. Create New DLLLog Out
30 | {% else %}
31 | Log In
32 | {% endif %}
33 |
34 |
35 |
--------------------------------------------------------------------------------
/locale/en_US/LC_MESSAGES/messages.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PACKAGE VERSION\n"
4 | "Report-Msgid-Bugs-To: \n"
5 | "POT-Creation-Date: 2011-05-26 18:11-0700\n"
6 | "PO-Revision-Date: 2011-05-26 18:11-0700\n"
7 | "Last-Translator: Automatically generated\n"
8 | "Language-Team: none\n"
9 | "Language: en_US\n"
10 | "MIME-Version: 1.0\n"
11 | "Content-Type: text/plain; charset=UTF-8\n"
12 | "Content-Transfer-Encoding: 8bit\n"
13 | "X-Generator: Translate Toolkit 1.8.0\n"
14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15 |
16 | #: apps/examples/templates/examples/home.html:5
17 | msgid "Hello world"
18 | msgstr "Hello world"
19 |
20 | #. This is a localizer comment
21 | #: apps/examples/templates/examples/home.html:9
22 | msgid "This is a test view."
23 | msgstr "This is a test view."
24 |
25 | #: apps/examples/templates/examples/home.html:11
26 | msgid "Learn you some Playdoh and then go build something awesome."
27 | msgstr "Learn you some Playdoh and then go build something awesome."
28 |
29 | #: apps/examples/templates/examples/home.html:17
30 | msgid "Current locale: %(LANG)s. Available locales: %(langs)s."
31 | msgstr "Current locale: %(LANG)s. Available locales: %(langs)s."
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, Mozilla
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 | * Neither the name of the copyright owner nor the names of its contributors
13 | may be used to endorse or promote products derived from this software
14 | without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/settings/local.py-dist:
--------------------------------------------------------------------------------
1 | # This is an example settings_local.py file.
2 | # Copy it and add your local settings here.
3 |
4 | from settings import *
5 |
6 |
7 | DATABASES = {
8 | 'default': {
9 | 'ENGINE': 'django.db.backends.mysql',
10 | 'NAME': '',
11 | 'USER': '',
12 | 'PASSWORD': '',
13 | 'HOST': '',
14 | 'PORT': '',
15 | 'OPTIONS': {
16 | 'init_command': 'SET storage_engine=InnoDB',
17 | 'charset' : 'utf8',
18 | 'use_unicode' : True,
19 | },
20 | 'TEST_CHARSET': 'utf8',
21 | 'TEST_COLLATION': 'utf8_general_ci',
22 | },
23 | # 'slave': {
24 | # ...
25 | # },
26 | }
27 |
28 | # Recipients of traceback emails and other notifications.
29 | ADMINS = (
30 | # ('Your Name', 'your_email@domain.com'),
31 | )
32 | MANAGERS = ADMINS
33 |
34 | # Debugging displays nice error messages, but leaks memory. Set this to False
35 | # on all server instances and True only for development.
36 | DEBUG = TEMPLATE_DEBUG = True
37 |
38 | # Is this a development instance? Set this to True on development/master
39 | # instances and False on stage/prod.
40 | DEV = True
41 |
42 | # Playdoh ships with sha512 password hashing by default. Bcrypt+HMAC is safer,
43 | # so it is recommended. Please read ,
44 | # then switch this to bcrypt and pick a secret HMAC key for your application.
45 | #PWD_ALGORITHM = 'bcrypt'
46 | #HMAC_KEYS = { # for bcrypt only
47 | # '2011-01-01': 'cheesecake',
48 | #}
49 |
50 | AUTH_LDAP_SERVER_URI = 'ldap://pm-ns.mozilla.org'
51 | AUTH_LDAP_BIND_DN = ''
52 | AUTH_LDAP_BIND_PASSWORD = ''
53 |
--------------------------------------------------------------------------------
/lib/product_details_json/firefox_history_stability_releases.json:
--------------------------------------------------------------------------------
1 | {"1.0.1":"2005-02-24","1.0.2":"2005-03-23","1.0.3":"2005-04-15","1.0.4":"2005-05-11","1.0.5":"2005-07-12","1.0.6":"2005-07-19","1.0.7":"2005-09-20","1.0.8":"2006-04-13","1.5.0.1":"2006-02-01","1.5.0.2":"2006-04-13","1.5.0.3":"2006-05-02","1.5.0.4":"2006-06-01","1.5.0.5":"2006-07-26","1.5.0.6":"2006-08-02","1.5.0.7":"2006-09-14","1.5.0.8":"2006-11-07","1.5.0.9":"2006-12-19","1.5.0.10":"2007-02-23","1.5.0.11":"2007-03-20","1.5.0.12":"2007-05-30","2.0.0.1":"2006-12-19","2.0.0.2":"2007-02-23","2.0.0.3":"2007-03-20","2.0.0.4":"2007-05-30","2.0.0.5":"2007-07-17","2.0.0.6":"2007-07-30","2.0.0.7":"2007-09-18","2.0.0.8":"2007-10-18","2.0.0.9":"2007-11-01","2.0.0.10":"2007-11-26","2.0.0.11":"2007-11-30","2.0.0.12":"2008-02-07","2.0.0.13":"2008-03-25","2.0.0.14":"2008-04-16","2.0.0.15":"2008-07-01","2.0.0.16":"2008-07-15","2.0.0.17":"2008-09-23","2.0.0.18":"2008-11-12","2.0.0.19":"2008-12-16","2.0.0.20":"2008-12-18","3.0.1":"2008-07-16","3.0.2":"2008-09-23","3.0.3":"2008-09-26","3.0.4":"2008-11-12","3.0.5":"2008-12-16","3.0.6":"2009-02-03","3.0.7":"2009-03-04","3.0.8":"2009-03-27","3.0.9":"2009-04-21","3.0.10":"2009-04-27","3.0.11":"2009-06-11","3.0.12":"2009-07-21","3.0.13":"2009-08-03","3.0.14":"2009-09-09","3.0.15":"2009-10-27","3.0.16":"2009-12-15","3.0.17":"2010-01-05","3.0.18":"2010-02-17","3.0.19":"2010-03-30","3.5.1":"2009-07-17","3.5.2":"2009-08-03","3.5.3":"2009-09-09","3.5.4":"2009-10-27","3.5.5":"2009-11-05","3.5.6":"2009-12-15","3.5.7":"2010-01-05","3.5.8":"2010-02-17","3.5.9":"2010-03-30","3.5.10":"2010-06-22","3.5.11":"2010-07-20","3.5.12":"2010-09-07","3.5.13":"2010-09-15","3.5.14":"2010-10-19","3.5.15":"2010-10-27","3.6.16":"2010-12-09","3.6.2":"2010-03-22","3.6.3":"2010-04-01","3.6.4":"2010-06-22","3.6.6":"2010-06-26","3.6.7":"2010-07-20","3.6.8":"2010-07-23","3.6.9":"2010-09-07","3.6.10":"2010-09-15","3.6.11":"2010-10-19","3.6.12":"2010-10-27","3.6.13":"2010-12-09"}
--------------------------------------------------------------------------------
/lib/product_details_json/firefox_beta_builds.json:
--------------------------------------------------------------------------------
1 | {"ast":{"3.6.13":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.5}},"4.0b10":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.6}}},"es-CL":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.5}}},"es-MX":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.5}}},"gd":{"4.0b10":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.5}},"3.6.13":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.5}}},"kk":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.6}}},"ku":{"3.5.16":[],"3.6.13":{"Windows":{"filesize":7.9},"OS X":{"filesize":19},"Linux":{"filesize":9.6}}},"mn":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":[]},"or":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":{"Windows":{"filesize":8},"OS X":{"filesize":19},"Linux":{"filesize":9.8}}},"rm":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"4.0b10":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.5}},"3.6.13":{"Windows":{"filesize":7.8},"OS X":{"filesize":19},"Linux":{"filesize":9.6}}},"ta":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":{"Windows":{"filesize":8},"OS X":{"filesize":19},"Linux":{"filesize":9.8}}},"ta-LK":{"3.5.16":{"Windows":{"filesize":7.6},"OS X":{"filesize":17.4},"Linux":{"filesize":9.2}},"3.6.13":{"Windows":{"filesize":7.9},"OS X":{"filesize":19},"Linux":{"filesize":9.6}}}}
--------------------------------------------------------------------------------
/apps/users/urls.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | from django.conf.urls.defaults import *
37 | import views
38 |
39 | urlpatterns = patterns('',
40 | url('^login/', views.login, name='users.login'),
41 | url('^logout/', views.logout, name='users.logout'),
42 | url('^settings/', views.settings_page, name='users.settings'),
43 | )
44 |
--------------------------------------------------------------------------------
/apps/commons/helpers.py:
--------------------------------------------------------------------------------
1 | import cgi
2 | import datetime
3 | import urllib
4 | import urlparse
5 |
6 | from django.conf import settings
7 | from django.template import defaultfilters
8 | from django.utils.html import strip_tags
9 |
10 | from jingo import register
11 | import jinja2
12 |
13 | from .urlresolvers import reverse
14 |
15 |
16 | # Yanking filters from Django.
17 | register.filter(strip_tags)
18 | register.filter(defaultfilters.timesince)
19 | register.filter(defaultfilters.truncatewords)
20 |
21 |
22 |
23 | @register.function
24 | def thisyear():
25 | """The current year."""
26 | return jinja2.Markup(datetime.date.today().year)
27 |
28 |
29 | @register.function
30 | def url(viewname, *args, **kwargs):
31 | """Helper for Django's ``reverse`` in templates."""
32 | return reverse(viewname, args=args, kwargs=kwargs)
33 |
34 |
35 | @register.filter
36 | def urlparams(url_, hash=None, **query):
37 | """
38 | Add a fragment and/or query paramaters to a URL.
39 |
40 | New query params will be appended to exising parameters, except duplicate
41 | names, which will be replaced.
42 | """
43 | url = urlparse.urlparse(url_)
44 | fragment = hash if hash is not None else url.fragment
45 |
46 | # Use dict(parse_qsl) so we don't get lists of values.
47 | q = url.query
48 | query_dict = dict(urlparse.parse_qsl(smart_str(q))) if q else {}
49 | query_dict.update((k, v) for k, v in query.items())
50 |
51 | query_string = _urlencode([(k, v) for k, v in query_dict.items()
52 | if v is not None])
53 | new = urlparse.ParseResult(url.scheme, url.netloc, url.path, url.params,
54 | query_string, fragment)
55 | return new.geturl()
56 |
57 | def _urlencode(items):
58 | """A Unicode-safe URLencoder."""
59 | try:
60 | return urllib.urlencode(items)
61 | except UnicodeEncodeError:
62 | return urllib.urlencode([(k, smart_str(v)) for k, v in items])
63 |
64 |
65 | @register.filter
66 | def urlencode(txt):
67 | """Url encode a path."""
68 | return urllib.quote_plus(txt)
69 |
--------------------------------------------------------------------------------
/apps/commons/middleware.py:
--------------------------------------------------------------------------------
1 | """
2 | Taken from zamboni.amo.middleware.
3 |
4 | This is django-localeurl, but with mozilla style capital letters in
5 | the locale codes.
6 | """
7 |
8 | import urllib
9 |
10 | from django.http import HttpResponsePermanentRedirect
11 | from django.utils.encoding import smart_str
12 |
13 | import tower
14 |
15 | from . import urlresolvers
16 | from .helpers import urlparams
17 |
18 | class LocaleURLMiddleware(object):
19 | """
20 | 1. Search for the locale.
21 | 2. Save it in the request.
22 | 3. Strip them from the URL.
23 | """
24 |
25 | def process_request(self, request):
26 | prefixer = urlresolvers.Prefixer(request)
27 | urlresolvers.set_url_prefix(prefixer)
28 | full_path = prefixer.fix(prefixer.shortened_path)
29 |
30 | if 'lang' in request.GET:
31 | # Blank out the locale so that we can set a new one. Remove lang
32 | # from the query params so we don't have an infinite loop.
33 | prefixer.locale = ''
34 | new_path = prefixer.fix(prefixer.shortened_path)
35 | query = dict((smart_str(k), request.GET[k]) for k in request.GET)
36 | query.pop('lang')
37 | return HttpResponsePermanentRedirect(urlparams(new_path, **query))
38 |
39 | if full_path != request.path:
40 | query_string = request.META.get('QUERY_STRING', '')
41 | full_path = urllib.quote(full_path.encode('utf-8'))
42 |
43 | if query_string:
44 | full_path = '%s?%s' % (full_path, query_string)
45 |
46 | response = HttpResponsePermanentRedirect(full_path)
47 |
48 | # Vary on Accept-Language if we changed the locale
49 | old_locale = prefixer.locale
50 | new_locale, _ = prefixer.split_path(full_path)
51 | if old_locale != new_locale:
52 | response['Vary'] = 'Accept-Language'
53 |
54 | return response
55 |
56 | request.path_info = '/' + prefixer.shortened_path
57 | request.locale = prefixer.locale
58 | tower.activate(prefixer.locale)
59 |
--------------------------------------------------------------------------------
/apps/users/models.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | from django.db import models
37 | from django.contrib.auth.models import User
38 |
39 |
40 | def get_user_profile(user):
41 | try:
42 | return user.get_profile()
43 | except UserProfile.DoesNotExist:
44 | return UserProfile.objects.create(user=user)
45 |
46 |
47 | class UserProfile(models.Model):
48 | user = models.ForeignKey(User)
49 | notes = models.TextField(blank=True)
50 |
--------------------------------------------------------------------------------
/apps/commons/tests/test_migrations.py:
--------------------------------------------------------------------------------
1 | import re
2 | from os import listdir
3 | from os.path import join, dirname
4 |
5 | import test_utils
6 |
7 | import manage
8 |
9 |
10 | class MigrationTests(test_utils.TestCase):
11 | """Sanity checks for the SQL migration scripts."""
12 |
13 | @staticmethod
14 | def _migrations_path():
15 | """Return the absolute path to the migration script folder."""
16 | return manage.path('migrations')
17 |
18 | def test_unique(self):
19 | """Assert that the numeric prefixes of the DB migrations are unique."""
20 | leading_digits = re.compile(r'^\d+')
21 | seen_numbers = set()
22 | path = self._migrations_path()
23 | for filename in listdir(path):
24 | match = leading_digits.match(filename)
25 | if match:
26 | number = match.group()
27 | if number in seen_numbers:
28 | self.fail('There is more than one migration #%s in %s.' %
29 | (number, path))
30 | seen_numbers.add(number)
31 |
32 | def test_innodb_and_utf8(self):
33 | """Make sure each created table uses the InnoDB engine and UTF-8."""
34 | # Heuristic: make sure there are at least as many "ENGINE=InnoDB"s as
35 | # "CREATE TABLE"s. (There might be additional "InnoDB"s in ALTER TABLE
36 | # statements, which are fine.)
37 | path = self._migrations_path()
38 | for filename in sorted(listdir(path)):
39 | with open(join(path, filename)) as f:
40 | contents = f.read()
41 | creates = contents.count('CREATE TABLE')
42 | engines = contents.count('ENGINE=InnoDB')
43 | encodings = (contents.count('CHARSET=utf8') +
44 | contents.count('CHARACTER SET utf8'))
45 | assert engines >= creates, ("There weren't as many "
46 | 'occurrences of "ENGINE=InnoDB" as of "CREATE TABLE" in '
47 | 'migration %s.' % filename)
48 | assert encodings >= creates, ("There weren't as many "
49 | 'UTF-8 declarations as "CREATE TABLE" occurrences in '
50 | 'migration %s.' % filename)
51 |
--------------------------------------------------------------------------------
/apps/users/templates/users/settings.html:
--------------------------------------------------------------------------------
1 | {#
2 |
37 | #}
38 |
39 | {% extends "base.html" %}
40 |
41 | {% block content %}
42 |
56 | {% endblock %}
57 |
--------------------------------------------------------------------------------
/settings/base.py:
--------------------------------------------------------------------------------
1 | # Django settings file for a project based on the playdoh template.
2 |
3 | from funfactory.settings_base import *
4 |
5 | MINIFY_BUNDLES = {
6 | 'css': {
7 | 'common_css': (
8 | 'css/dll/main.css',
9 | ),
10 | },
11 | 'js': {
12 | 'common_js': (
13 | 'js/libs/jquery-1.6.2.js',
14 | 'js/dll/nav.js',
15 | ),
16 | 'detail_js': (
17 | 'js/dll/detail.js',
18 | ),
19 | }
20 | }
21 |
22 | INSTALLED_APPS += (
23 | 'dll',
24 | 'users',
25 | )
26 |
27 | # Because Jinja2 is the default template loader, add any non-Jinja templated
28 | # apps here:
29 | JINGO_EXCLUDE_APPS = [
30 | 'admin',
31 | 'debug_toolbar',
32 | ]
33 |
34 | # Tells the extract script what files to look for L10n in and what function
35 | # handles the extraction. The Tower library expects this.
36 |
37 | # # Use this if you have localizable HTML files:
38 | # DOMAIN_METHODS['lhtml'] = [
39 | # ('**/templates/**.lhtml',
40 | # 'tower.management.commands.extract.extract_tower_template'),
41 | # ]
42 |
43 | # # Use this if you have localizable HTML files:
44 | # DOMAIN_METHODS['javascript'] = [
45 | # # Make sure that this won't pull in strings from external libraries you
46 | # # may use.
47 | # ('media/js/**.js', 'javascript'),
48 | # ]
49 |
50 | LOGGING = dict(loggers=dict(playdoh = {'level': logging.DEBUG}))
51 |
52 | AUTH_PROFILE_MODULE = 'users.UserProfile'
53 | LOGIN_URL = '/users/login/'
54 | LOGOUT_REDIRECT_URL = '/'
55 | LOGIN_REDIRECT_URL = '/'
56 |
57 | try:
58 | ## LDAP
59 | import ldap
60 |
61 | AUTHENTICATION_BACKENDS = (
62 | 'users.email_auth_backend.EmailOrUsernameModelBackend',
63 | 'users.auth.backends.MozillaLDAPBackend',
64 | 'django.contrib.auth.backends.ModelBackend',
65 | )
66 |
67 | # these must be set in settings/local.py!
68 | AUTH_LDAP_SERVER_URI = ''
69 | AUTH_LDAP_BIND_DN = ''
70 | AUTH_LDAP_BIND_PASSWORD = ''
71 |
72 | AUTH_LDAP_START_TLS = True
73 | AUTH_LDAP_USER_ATTR_MAP = {
74 | "first_name": "givenName",
75 | "last_name": "sn",
76 | "email": "mail",
77 | }
78 | from django_auth_ldap.config import LDAPSearch
79 | AUTH_LDAP_USER_SEARCH = LDAPSearch(
80 | "dc=mozilla",
81 | ldap.SCOPE_SUBTREE,
82 | "mail=%(user)s"
83 | )
84 |
85 | except ImportError:
86 | AUTHENTICATION_BACKENDS = (
87 | 'users.email_auth_backend.EmailOrUsernameModelBackend',
88 | 'django.contrib.auth.backends.ModelBackend',
89 | )
90 |
--------------------------------------------------------------------------------
/apps/users/email_auth_backend.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | from django.contrib.auth.models import User
37 |
38 |
39 | class EmailOrUsernameModelBackend(object):
40 |
41 | supports_object_permissions = False
42 | supports_anonymous_user = False
43 | supports_inactive_user = False
44 |
45 | def authenticate(self, username=None, password=None):
46 | if '@' in username:
47 | kwargs = {'email__iexact': username}
48 | else:
49 | kwargs = {'username': username}
50 | try:
51 | user = User.objects.get(**kwargs)
52 | if user.check_password(password):
53 | return user
54 | except User.DoesNotExist:
55 | return None
56 |
57 | def get_user(self, user_id):
58 | try:
59 | return User.objects.get(pk=user_id)
60 | except User.DoesNotExist: # pragma: no cover
61 | return None
62 |
--------------------------------------------------------------------------------
/apps/users/forms.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | from django import forms
37 | from django.contrib.auth.models import User
38 | import django.contrib.auth.forms
39 |
40 |
41 | class EmailInput(forms.widgets.Input):
42 | input_type = 'email'
43 |
44 | def render(self, name, value, attrs=None):
45 | if attrs is None:
46 | attrs = {}
47 | attrs.update(dict(autocorrect='off',
48 | autocapitalize='off',
49 | spellcheck='false'))
50 | return super(EmailInput, self).render(name, value, attrs=attrs)
51 |
52 | class AuthenticationForm(django.contrib.auth.forms.AuthenticationForm):
53 | """override the authentication form because we use the email address as the
54 | key to authentication."""
55 | # allows for using email to log in
56 | username = forms.CharField(label="Username", max_length=75,
57 | widget=EmailInput())
58 | rememberme = forms.BooleanField(label="Remember me", required=False)
59 |
60 | class SettingsForm(forms.Form):
61 | username = forms.CharField(label="Username", max_length=75)
62 |
63 | def __init__(self, user, *args, **kwargs):
64 | self.user = user
65 | super(SettingsForm, self).__init__(*args, **kwargs)
66 |
67 | def clean_username(self):
68 | value = self.cleaned_data['username'].strip()
69 | if (User.objects
70 | .filter(username__iexact=value)
71 | .exclude(pk=self.user.pk)
72 | .exists()):
73 | raise forms.ValidationError("Username already used by someone else")
74 | return value
75 |
--------------------------------------------------------------------------------
/apps/dll/templates/dll/view.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block extrahead %}
4 |
5 | {{ js('detail_js') }}
6 |
7 | {% endblock %}
8 |
9 | {% block mainbody %}
10 |
83 | {% endblock %}
--------------------------------------------------------------------------------
/media/css/dll/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial,Helvetica,sans-serif;
3 | font-size: 12px;
4 | background-color: #d8ffc1;
5 | margin: 0px;
6 | padding: 0px;
7 | }
8 |
9 | .page-header {
10 | background-color: #377810;
11 | background: -moz-linear-gradient(center top , #73A058 0%, #377810 100%);
12 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #73A058), color-stop(1, #377810)); /* webkit */
13 | box-shadow: 0 0 3px #666666;
14 | -moz-box-shadow: 0 0 3px #666666;
15 | -webkit-box-shadow: 0 0 3px #666666;
16 | height: 75px;
17 | position: relative;
18 | }
19 |
20 | .page-header p {
21 | font-size: 30px;
22 | color: white;
23 | margin: 0px;
24 | padding: 15px;
25 | }
26 |
27 | .page-header a {
28 | color: White;
29 | text-decoration: none;
30 | }
31 |
32 | .page-header input {
33 | top: 20px;
34 | right: 20px;
35 | position: absolute;
36 | border-radius: 0.5em;
37 | -webkit-border-radius: 0.5em;
38 | -moz-border-radius: 0.5em;
39 | border: 1px solid #A0A0A0;
40 | width: 240px;
41 | background: url("/media/img/dll/search.png") no-repeat scroll 5px center #FFFFFF;
42 | font-size: 12px;
43 | padding: 3px 4px 4px 28px
44 | }
45 |
46 | .body {
47 | margin: 1em 2em;
48 | position: relative;
49 | text-align: left;
50 | }
51 |
52 | .right {
53 | text-align: right;
54 | }
55 |
56 | .panel {
57 | background: #FFFFFF;
58 | border-radius: 9px;
59 | -webkit-border-radius: 9px;
60 | -moz-border-radius: 9px;
61 | box-shadow: 0 0 3px #CCCCCC;
62 | -moz-box-shadow: 0 0 3px #CCCCCC;
63 | -webkit-box-shadow: 0 0 3px #CCCCCC;
64 | border: 1px solid #D7D7D7;
65 | padding: 10px;
66 | }
67 |
68 | .panel table {
69 | border-collapse: collapse;
70 | width: 100%;
71 | font-size: 10pt;
72 | }
73 |
74 | .panel th, span.comment-meta {
75 | text-align: left;
76 | background-color: #85c162;
77 | }
78 |
79 | span.comment-meta {
80 | font-weight: bold;
81 |
82 | }
83 |
84 | span.comment-meta, span.comment-data {
85 | display: block;
86 | padding: 3px;
87 | }
88 |
89 | .single-comment {
90 | font-size: 11pt;
91 | width: 600px;
92 | border: 1px solid #D7D7D7;
93 | margin: 10px;
94 | }
95 |
96 | .panel td, th {
97 | border: 1px solid Black;
98 | padding: 3px;
99 | }
100 |
101 | .panel tr:hover {
102 | background-color: #d0f5ba;
103 | }
104 |
105 | a {
106 | color: #3B7B15;
107 | }
108 |
109 | .pagination {
110 | font-size: 11pt;
111 | text-align: right;
112 | padding: 3px 0px 5px;
113 | }
114 |
115 | ul.tab-list {
116 | list-style: none;
117 | padding: 0;
118 | margin: 5px 0px 0px 8px;
119 | }
120 |
121 | ul.tab-list li {
122 | display: inline;
123 | border: solid #85C162;
124 | border-width: 1px 1px 0 1px;
125 | margin: 0 0.3em 0 0;
126 | padding: 3px 3px 0px 3px;
127 | font-size: 11pt;
128 | }
129 |
130 | ul.tab-list li.active {
131 | background-color: #85C162;
132 | padding-top: 7px;
133 | border-radius: 5px 5px 0px 0px;
134 | -webkit-border-radius: 5px 5px 0px 0px;
135 | -moz-border-radius: 5px 5px 0px 0px;
136 | }
137 |
138 | ul.tab-list a {
139 | color: black;
140 | text-decoration: none;
141 | }
142 |
143 | .information {
144 | border: 1px solid #85C162;
145 | padding: 5px;
146 | }
147 |
148 | .out-of-focus {
149 | display: none;
150 | }
151 |
152 | table.history td {
153 | width: 25%;
154 | }
--------------------------------------------------------------------------------
/apps/dll/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.db.models.signals import pre_save
3 | from django.dispatch import receiver
4 | from django.contrib.auth.models import User
5 | import datetime
6 |
7 |
8 | class File(models.Model):
9 | """The actual DLL file itself"""
10 | STATUS_UNKNOWN = 'unknown'
11 | STATUS_VALID = 'valid'
12 | STATUS_MALWARE = 'malware'
13 | STATUS_CHOICES = (
14 | (STATUS_UNKNOWN, 'Unknown'),
15 | (STATUS_VALID, 'Valid'),
16 | (STATUS_MALWARE, 'Malware')
17 | )
18 | date_created = models.DateTimeField(default=datetime.datetime.utcnow)
19 | date_modified = models.DateTimeField(default=datetime.datetime.utcnow,
20 | auto_now=True)
21 | created_by = models.ForeignKey(User, related_name="created_by")
22 | modified_by = models.ForeignKey(User, related_name="modified_by")
23 | file_name = models.CharField(max_length=200, unique=True)
24 | common_name = models.CharField(max_length=200, blank=True, null=True)
25 | vendor = models.CharField(max_length=200, blank=True, null=True)
26 | distributors = models.CharField(max_length=200, blank=True, null=True)
27 | md5_hash = models.CharField(max_length=32, blank=True, null=True)
28 | debug = models.CharField(max_length=60, blank=True, null=True)
29 | status = models.CharField(max_length=10, choices=STATUS_CHOICES)
30 | released = models.DateField(blank=True, null=True)
31 | obsolete = models.BooleanField(default=False)
32 | replaced_by = models.CharField(max_length=200, blank=True, null=True)
33 | details = models.TextField(blank=True, null=True)
34 |
35 | def __unicode__(self):
36 | return self.file_name
37 |
38 |
39 | class Comment(models.Model):
40 | """Comments users have made on given DLL files"""
41 | user = models.ForeignKey(User)
42 | dll = models.ForeignKey(File)
43 | date = models.DateTimeField(default=datetime.datetime.utcnow,
44 | auto_now=True)
45 | comment = models.TextField()
46 |
47 |
48 | class FileHistory(models.Model):
49 | """A historical record of the DLL file and the changes made to it over
50 | time"""
51 | dll = models.ForeignKey(File)
52 | user = models.ForeignKey(User)
53 | date_changed = models.DateTimeField(auto_now=True)
54 | field = models.CharField(max_length=40)
55 | original_state = models.CharField(max_length=200, blank=True, null=True)
56 | changed_state = models.CharField(max_length=200, blank=True, null=True)
57 |
58 |
59 | @receiver(pre_save, sender=File)
60 | def compare_history(sender, instance, **kwargs):
61 | if not File.objects.filter(pk=instance.pk).exists():
62 | return sender
63 | EVALUATE = ('file_name', 'common_name', 'vendor', 'distributors',
64 | 'md5_hash', 'debug', 'status', 'released', 'obsolete',
65 | 'replaced_by', 'details', )
66 |
67 | existing = File.objects.get(pk=instance.id)
68 | for key in EVALUATE:
69 | if getattr(existing, key) != getattr(instance, key) and any([getattr(existing, key), getattr(instance, key)]):
70 | user = User.objects.get(pk=instance.modified_by_id)
71 | FileHistory.objects.create(user=user,
72 | dll=existing,
73 | field=key,
74 | original_state=getattr(existing, key),
75 | changed_state=getattr(instance, key))
76 | return sender
77 |
--------------------------------------------------------------------------------
/apps/users/monkeypatch_template_engine.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | from django.template import loader
37 | from django.template.response import SimpleTemplateResponse
38 |
39 | import jingo
40 |
41 |
42 | def jinja_for_django(template_name, context=None, **kw):
43 | """
44 | If you want to use some built in logic (or a contrib app) but need to
45 | override the templates to work with Jinja, replace the object's
46 | render_to_response function with this one. That will render a Jinja
47 | template through Django's functions. An example can be found in the users
48 | app.
49 | """
50 | if context is None:
51 | context = {}
52 | context_instance = kw.pop('context_instance')
53 | request = context_instance['request']
54 | for d in context_instance.dicts:
55 | context.update(d)
56 | return jingo.render(request, template_name, context, **kw)
57 |
58 |
59 | ## We monkeypatch SimpleTemplateResponse.rendered_content to use our jinja
60 | ## rendering pipeline (most of the time). The exception is the admin app, where
61 | ## we render their Django templates and pipe the result through jinja to render
62 | ## our page skeleton.
63 | #def rendered_content(self):
64 | # template = self.template_name
65 | # context_instance = self.resolve_context(self.context_data)
66 | # request = context_instance['request']
67 | #
68 | # # Gross, let's figure out if we're in the admin.
69 | # if self._current_app == 'admin':
70 | # source = loader.render_to_string(template, context_instance)
71 | # template = jingo.env.from_string(source)
72 | # # This interferes with our media() helper.
73 | # if 'media' in self.context_data:
74 | # del self.context_data['media']
75 | #
76 | # return jingo.render_to_string(request, template, self.context_data)
77 | #
78 | #SimpleTemplateResponse.rendered_content = property(rendered_content)
79 | #
80 |
--------------------------------------------------------------------------------
/apps/commons/tests/test_accepted_locales.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 |
4 | from django.conf import settings
5 | import test_utils
6 |
7 | import manage
8 |
9 |
10 | class AcceptedLocalesTest(test_utils.TestCase):
11 | """Test lazy evaluation of locale related settings.
12 |
13 | Verify that some localization-related settings are lazily evaluated based
14 | on the current value of the DEV variable. Depending on the value,
15 | DEV_LANGUAGES or PROD_LANGUAGES should be used.
16 |
17 | """
18 | locale = manage.path('locale')
19 | locale_bkp = manage.path('locale_bkp')
20 |
21 | @classmethod
22 | def setup_class(cls):
23 | """Create a directory structure for locale/.
24 |
25 | Back up the existing locale/ directory and create the following
26 | hierarchy in its place:
27 |
28 | - locale/en-US/LC_MESSAGES
29 | - locale/fr/LC_MESSAGES
30 | - locale/templates/LC_MESSAGES
31 | - locale/empty_file
32 |
33 | Also, set PROD_LANGUAGES to ('en-US',).
34 |
35 | """
36 | if os.path.exists(cls.locale_bkp):
37 | raise Exception('A backup of locale/ exists at %s which might '
38 | 'mean that previous tests didn\'t end cleanly. '
39 | 'Skipping the test suite.' % cls.locale_bkp)
40 | cls.DEV = settings.DEV
41 | cls.PROD_LANGUAGES = settings.PROD_LANGUAGES
42 | cls.DEV_LANGUAGES = settings.DEV_LANGUAGES
43 | settings.PROD_LANGUAGES = ('en-US',)
44 | os.rename(cls.locale, cls.locale_bkp)
45 | for loc in ('en-US', 'fr', 'templates'):
46 | os.makedirs(os.path.join(cls.locale, loc, 'LC_MESSAGES'))
47 | open(os.path.join(cls.locale, 'empty_file'), 'w').close()
48 |
49 | @classmethod
50 | def teardown_class(cls):
51 | """Remove the testing locale/ dir and bring back the backup."""
52 |
53 | settings.DEV = cls.DEV
54 | settings.PROD_LANGUAGES = cls.PROD_LANGUAGES
55 | settings.DEV_LANGUAGES = cls.DEV_LANGUAGES
56 | shutil.rmtree(cls.locale)
57 | os.rename(cls.locale_bkp, cls.locale)
58 |
59 | def test_build_dev_languages(self):
60 | """Test that the list of dev locales is built properly.
61 |
62 | On dev instances, the list of accepted locales should correspond to
63 | the per-locale directories in locale/.
64 |
65 | """
66 | settings.DEV = True
67 | assert (settings.DEV_LANGUAGES == ['en-US', 'fr'] or
68 | settings.DEV_LANGUAGES == ['fr', 'en-US']), \
69 | 'DEV_LANGUAGES do not correspond to the contents of locale/.'
70 |
71 | def test_dev_languages(self):
72 | """Test the accepted locales on dev instances.
73 |
74 | On dev instances, allow locales defined in DEV_LANGUAGES.
75 |
76 | """
77 | settings.DEV = True
78 | # simulate the successful result of the DEV_LANGUAGES list
79 | # comprehension defined in settings.
80 | settings.DEV_LANGUAGES = ['en-US', 'fr']
81 | assert settings.LANGUAGE_URL_MAP == {'en-us': 'en-US', 'fr': 'fr'}, \
82 | ('DEV is True, but DEV_LANGUAGES are not used to define the '
83 | 'allowed locales.')
84 |
85 | def test_prod_languages(self):
86 | """Test the accepted locales on prod instances.
87 |
88 | On stage/prod instances, allow locales defined in PROD_LANGUAGES.
89 |
90 | """
91 | settings.DEV = False
92 | assert settings.LANGUAGE_URL_MAP == {'en-us': 'en-US'}, \
93 | ('DEV is False, but PROD_LANGUAGES are not used to define the '
94 | 'allowed locales.')
95 |
--------------------------------------------------------------------------------
/apps/users/auth/backends.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | from django.contrib.auth.models import User
37 | from django_auth_ldap.backend import LDAPBackend
38 |
39 |
40 | class MozillaLDAPBackend(LDAPBackend):
41 | """Overriding this class so that I can transform emails to usernames.
42 |
43 | At Mozilla we use email addresses as the username but in django I want,
44 | for example:
45 | INPUT:
46 | username -> pbengtsson@mozilla.com
47 | OUTPUT:
48 | username -> pbengtsson
49 | email -> pbengtsson@mozilla.com
50 |
51 | I can map (in settings.AUTH_LDAP_USER_ATTR_MAP):
52 | 'username' -> 'uid'
53 | and
54 | 'email -> 'mail
55 |
56 | But that means that the second time a user logs in, it's not going to
57 | find a username that is 'pbengtsson@mozilla.com' so it'll go ahead and
58 | create it again and you'll end up with duplicates once the attribute
59 | conversion is done.
60 |
61 | The other thing that this backend accomplishes is to change username
62 | entirely. Suppose, for example, that your mozilla LDAP email is
63 | 'pbengtsson@mozilla.com' but you prefer your own custom alias of
64 | 'peterbe'. What it does then, is looking at existing users that match
65 | the *email address* and returns the username for that one.
66 | """
67 |
68 | def get_or_create_user(self, username, ldap_user):
69 | """
70 | This must return a (User, created) 2-tuple for the given LDAP user.
71 | username is the Django-friendly username of the user. ldap_user.dn is
72 | the user's DN and ldap_user.attrs contains all of their LDAP attributes.
73 | """
74 | # users on this site can't change their email but they can change their
75 | # username
76 | if ldap_user.attrs.get('mail'):
77 | for user in (User.objects
78 | .filter(email__iexact=ldap_user.attrs.get('mail')[0])):
79 | return (user, False)
80 |
81 | # use the default from django-auth-ldap
82 | return User.objects.get_or_create(
83 | username__iexact=username,
84 | defaults={'username': username.lower()}
85 | )
86 |
87 |
88 | def ldap_to_django_username(self, username):
89 | """Allow users to use a different username"""
90 | try:
91 | return User.objects.get(email=username).username
92 | except User.DoesNotExist:
93 | return username.split('@')[0]
94 |
--------------------------------------------------------------------------------
/apps/commons/urlresolvers.py:
--------------------------------------------------------------------------------
1 | from threading import local
2 |
3 | from django.conf import settings
4 | from django.core.urlresolvers import reverse as django_reverse
5 | from django.utils.translation.trans_real import parse_accept_lang_header
6 |
7 |
8 | # Thread-local storage for URL prefixes. Access with (get|set)_url_prefix.
9 | _local = local()
10 |
11 |
12 | def set_url_prefix(prefix):
13 | """Set the ``prefix`` for the current thread."""
14 | _local.prefix = prefix
15 |
16 |
17 | def get_url_prefix():
18 | """Get the prefix for the current thread, or None."""
19 | return getattr(_local, 'prefix', None)
20 |
21 |
22 | def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
23 | """Wraps Django's reverse to prepend the correct locale."""
24 | prefixer = get_url_prefix()
25 |
26 | if prefixer:
27 | prefix = prefix or '/'
28 | url = django_reverse(viewname, urlconf, args, kwargs, prefix)
29 | if prefixer:
30 | return prefixer.fix(url)
31 | else:
32 | return url
33 |
34 |
35 | def find_supported(test):
36 | return [settings.LANGUAGE_URL_MAP[x] for
37 | x in settings.LANGUAGE_URL_MAP if
38 | x.split('-', 1)[0] == test.lower().split('-', 1)[0]]
39 |
40 |
41 | class Prefixer(object):
42 |
43 | def __init__(self, request):
44 | self.request = request
45 | split = self.split_path(request.path_info)
46 | self.locale, self.shortened_path = split
47 |
48 | def split_path(self, path_):
49 | """
50 | Split the requested path into (locale, path).
51 |
52 | locale will be empty if it isn't found.
53 | """
54 | path = path_.lstrip('/')
55 |
56 | # Use partitition instead of split since it always returns 3 parts
57 | first, _, rest = path.partition('/')
58 |
59 | lang = first.lower()
60 | if lang in settings.LANGUAGE_URL_MAP:
61 | return settings.LANGUAGE_URL_MAP[lang], rest
62 | else:
63 | supported = find_supported(first)
64 | if len(supported):
65 | return supported[0], rest
66 | else:
67 | return '', path
68 |
69 | def get_language(self):
70 | """
71 | Return a locale code we support on the site using the
72 | user's Accept-Language header to determine which is best. This
73 | mostly follows the RFCs but read bug 439568 for details.
74 | """
75 | if 'lang' in self.request.GET:
76 | lang = self.request.GET['lang'].lower()
77 | if lang in settings.LANGUAGE_URL_MAP:
78 | return settings.LANGUAGE_URL_MAP[lang]
79 |
80 | if self.request.META.get('HTTP_ACCEPT_LANGUAGE'):
81 | best = self.get_best_language(
82 | self.request.META['HTTP_ACCEPT_LANGUAGE'])
83 | if best:
84 | return best
85 | return settings.LANGUAGE_CODE
86 |
87 | def get_best_language(self, accept_lang):
88 | """Given an Accept-Language header, return the best-matching language."""
89 | LUM = settings.LANGUAGE_URL_MAP
90 | PREFIXES = dict((x.split('-')[0], LUM[x]) for x in LUM)
91 | langs = dict(LUM)
92 | langs.update((k.split('-')[0], v) for k, v in LUM.items() if
93 | k.split('-')[0] not in langs)
94 | ranked = parse_accept_lang_header(accept_lang)
95 | for lang, _ in ranked:
96 | lang = lang.lower()
97 | if lang in langs:
98 | return langs[lang]
99 | pre = lang.split('-')[0]
100 | if pre in langs:
101 | return langs[pre]
102 | # Could not find an acceptable language.
103 | return False
104 |
105 | def fix(self, path):
106 | path = path.lstrip('/')
107 | url_parts = [self.request.META['SCRIPT_NAME']]
108 |
109 | if path.partition('/')[0] not in settings.SUPPORTED_NONLOCALES:
110 | locale = self.locale if self.locale else self.get_language()
111 | url_parts.append(locale)
112 |
113 | url_parts.append(path)
114 |
115 | return '/'.join(url_parts)
116 |
--------------------------------------------------------------------------------
/apps/dll/views.py:
--------------------------------------------------------------------------------
1 | import collections
2 | from time import mktime
3 |
4 | from django import http
5 | from django.shortcuts import redirect, get_object_or_404
6 | from django.contrib.auth.decorators import login_required
7 |
8 | from django.views.decorators.csrf import csrf_exempt
9 | import bleach
10 | import jingo
11 |
12 | from dll.forms import FileForm, CommentForm, SearchForm
13 | from dll.models import File, Comment, FileHistory
14 | from django.core.paginator import Paginator, EmptyPage, InvalidPage
15 |
16 | from django.db.models import Q
17 |
18 | PAGE_LENGTH = 50
19 |
20 |
21 | def home(request, page_no):
22 | dll_list = File.objects.all().order_by('-date_created')
23 | paginator = Paginator(dll_list, PAGE_LENGTH)
24 | try:
25 | page = int(page_no)
26 | except (ValueError, TypeError):
27 | page = 1
28 |
29 | try:
30 | dlls = paginator.page(page)
31 | except (EmptyPage, InvalidPage):
32 | dlls = paginator.page(paginator.num_pages)
33 |
34 | data = {'dlls': dlls, 'last_page': paginator.num_pages, }
35 | return jingo.render(request, 'dll/index.html', data)
36 |
37 |
38 | @csrf_exempt
39 | def search(request):
40 | search = SearchForm(data=request.GET)
41 | if search.is_valid():
42 | term = search.cleaned_data['term']
43 | results = File.objects.filter(Q(file_name__icontains=term) |
44 | Q(common_name__icontains=term) |
45 | Q(vendor__icontains=term) |
46 | Q(distributors__icontains=term))
47 | else:
48 | term = ''
49 | results = []
50 | data = {'count': len(results), 'dlls': results, 'term': term, }
51 | return jingo.render(request, 'dll/search.html', data)
52 |
53 |
54 | def view(request, dllname):
55 | thefile = get_object_or_404(File, file_name__exact=dllname)
56 | comments = Comment.objects.order_by('date').filter(dll__exact=thefile)
57 | hist = FileHistory.objects.filter(dll__exact=thefile)
58 | history = _organize_history(hist)
59 | data = {'dllname': dllname, 'dlldata': thefile, 'comments': comments,
60 | 'history': history}
61 | return jingo.render(request, 'dll/view.html', data)
62 |
63 |
64 | @login_required
65 | def create(request):
66 | """Main view."""
67 | if request.method == 'POST':
68 | form = FileForm(request.POST)
69 | if form.is_valid():
70 | form.instance.created_by = request.user
71 | form.instance.modified_by = request.user
72 | form.save()
73 | return redirect('dll.edit', form.cleaned_data['file_name'])
74 | else:
75 | form = FileForm()
76 | data = {'form': form}
77 | return jingo.render(request, 'dll/create.html', data)
78 |
79 |
80 | def edit(request, dllname):
81 | if not request.user.is_authenticated():
82 | return redirect('dll.view', dllname)
83 | thefile = get_object_or_404(File, file_name__exact=dllname)
84 | comments = Comment.objects.order_by('date').filter(dll__exact=thefile)
85 | hist = FileHistory.objects.filter(dll__exact=thefile)
86 | history = _organize_history(hist)
87 | form = FileForm(instance=thefile)
88 | comment_form = CommentForm()
89 | if request.method == 'POST':
90 | if 'update_file' in request.POST:
91 | form = FileForm(request.POST, instance=thefile)
92 | if form.is_valid():
93 | form.instance.modified_by = request.user
94 | form.save()
95 | return redirect('dll.edit', thefile.file_name)
96 | elif 'update_comment' in request.POST:
97 | comment_form = CommentForm(request.POST)
98 | if comment_form.is_valid():
99 | Comment.objects.create(user=request.user,
100 | dll=thefile,
101 | comment=comment_form.cleaned_data['comment'])
102 | return redirect('dll.edit', thefile.file_name)
103 | data = {'dllname': dllname, 'form': form, 'comment_form': comment_form,
104 | 'comments': comments, 'history': history}
105 | return jingo.render(request, 'dll/edit.html', data)
106 |
107 |
108 | def _organize_history(resultset):
109 | res = collections.defaultdict(list)
110 | for x in resultset:
111 | res[x.date_changed].append(x)
112 | return res
113 |
--------------------------------------------------------------------------------
/apps/users/views.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | import logging
37 | from django import http
38 | from django.shortcuts import redirect
39 | from django.core.urlresolvers import reverse
40 | from django.contrib.auth import REDIRECT_FIELD_NAME
41 | from django.contrib.auth.decorators import login_required
42 | from django.db import transaction
43 | from django.contrib import messages
44 | import django.contrib.auth.views
45 | from django.conf import settings
46 | import jingo
47 | import forms
48 | from models import get_user_profile
49 | from django.shortcuts import render_to_response as django_render_to_response
50 | from django.contrib.auth import logout as auth_logout
51 |
52 | def login(request):
53 | # mostly copied from zamboni
54 | logout(request)
55 |
56 | from monkeypatch_template_engine import jinja_for_django as jfd
57 | django.contrib.auth.views.render_to_response = jfd
58 | r = django.contrib.auth.views.login(request,
59 | template_name='users/login.html',
60 | redirect_field_name=REDIRECT_FIELD_NAME,
61 | authentication_form=forms.AuthenticationForm)
62 |
63 | if isinstance(r, http.HttpResponseRedirect):
64 | # Succsesful log in according to django. Now we do our checks. I do
65 | # the checks here instead of the form's clean() because I want to use
66 | # the messages framework and it's not available in the request there
67 | user = get_user_profile(request.user)
68 | rememberme = request.POST.get('rememberme', None)
69 | if rememberme:
70 | request.session.set_expiry(settings.SESSION_COOKIE_AGE)
71 | logging.debug((u'User (%s) logged in successfully with '
72 | '"remember me" set') % user)
73 |
74 | return r
75 |
76 |
77 | def logout(request):
78 | auth_logout(request)
79 | next = request.GET.get('next') or settings.LOGOUT_REDIRECT_URL
80 | response = http.HttpResponseRedirect(next)
81 | return response
82 |
83 |
84 | @transaction.commit_on_success
85 | @login_required
86 | def settings_page(request):
87 | data = {}
88 | if request.method == 'POST':
89 | form = forms.SettingsForm(user=request.user, data=request.POST)
90 | if form.is_valid():
91 | username = form.cleaned_data['username']
92 | request.user.username = username
93 | request.user.save()
94 |
95 | messages.info(
96 | request,
97 | "Username changed to %s" % username
98 | )
99 | return redirect(reverse('cal.home'))
100 |
101 | else:
102 | initial = {'username': request.user.username}
103 | form = forms.SettingsForm(user=request.user, initial=initial)
104 |
105 | data['form'] = form
106 | return jingo.render(request, 'users/settings.html', data)
107 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 |
15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
16 |
17 | help:
18 | @echo "Please use \`make ' where is one of"
19 | @echo " html to make standalone HTML files"
20 | @echo " dirhtml to make HTML files named index.html in directories"
21 | @echo " singlehtml to make a single large HTML file"
22 | @echo " pickle to make pickle files"
23 | @echo " json to make JSON files"
24 | @echo " htmlhelp to make HTML files and a HTML help project"
25 | @echo " qthelp to make HTML files and a qthelp project"
26 | @echo " devhelp to make HTML files and a Devhelp project"
27 | @echo " epub to make an epub"
28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
29 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
30 | @echo " text to make text files"
31 | @echo " man to make manual pages"
32 | @echo " changes to make an overview of all changed/added/deprecated items"
33 | @echo " linkcheck to check all external links for integrity"
34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
35 |
36 | clean:
37 | -rm -rf $(BUILDDIR)/*
38 |
39 | html:
40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
41 | @echo
42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
43 |
44 | dirhtml:
45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
48 |
49 | singlehtml:
50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
51 | @echo
52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
53 |
54 | pickle:
55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
56 | @echo
57 | @echo "Build finished; now you can process the pickle files."
58 |
59 | json:
60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
61 | @echo
62 | @echo "Build finished; now you can process the JSON files."
63 |
64 | htmlhelp:
65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
66 | @echo
67 | @echo "Build finished; now you can run HTML Help Workshop with the" \
68 | ".hhp project file in $(BUILDDIR)/htmlhelp."
69 |
70 | qthelp:
71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
72 | @echo
73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/playdoh.qhcp"
76 | @echo "To view the help file:"
77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/playdoh.qhc"
78 |
79 | devhelp:
80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
81 | @echo
82 | @echo "Build finished."
83 | @echo "To view the help file:"
84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/playdoh"
85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/playdoh"
86 | @echo "# devhelp"
87 |
88 | epub:
89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
90 | @echo
91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
92 |
93 | latex:
94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
95 | @echo
96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
98 | "(use \`make latexpdf' here to do that automatically)."
99 |
100 | latexpdf:
101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
102 | @echo "Running LaTeX files through pdflatex..."
103 | make -C $(BUILDDIR)/latex all-pdf
104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
105 |
106 | text:
107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
108 | @echo
109 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
110 |
111 | man:
112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
113 | @echo
114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
115 |
116 | changes:
117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
118 | @echo
119 | @echo "The overview file is in $(BUILDDIR)/changes."
120 |
121 | linkcheck:
122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
123 | @echo
124 | @echo "Link check complete; look for any errors in the above output " \
125 | "or in $(BUILDDIR)/linkcheck/output.txt."
126 |
127 | doctest:
128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
129 | @echo "Testing of doctests in the sources finished, look at the " \
130 | "results in $(BUILDDIR)/doctest/output.txt."
131 |
--------------------------------------------------------------------------------
/bin/update_site.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """
3 | Usage: update_site.py [options]
4 | Updates a server's sources, vendor libraries, packages CSS/JS
5 | assets, migrates the database, and other nifty deployment tasks.
6 |
7 | Options:
8 | -h, --help show this help message and exit
9 | -e ENVIRONMENT, --environment=ENVIRONMENT
10 | Type of environment. One of (prod|dev|stage) Example:
11 | update_site.py -e stage
12 | -v, --verbose Echo actions before taking them.
13 | """
14 |
15 | import os
16 | import sys
17 | from textwrap import dedent
18 | from optparse import OptionParser
19 | from hashlib import md5
20 |
21 | # Constants
22 | PROJECT = 0
23 | VENDOR = 1
24 |
25 | ENV_BRANCH = {
26 | # 'environment': [PROJECT_BRANCH, VENDOR_BRANCH],
27 | 'dev': ['base', 'master'],
28 | 'stage': ['master', 'master'],
29 | 'prod': ['prod', 'master'],
30 | }
31 |
32 | # The URL of the SVN repository with the localization files (*.po). If you set
33 | # it to a non-empty value, remember to `git rm --cached -r locale` in the root
34 | # of the project. Example:
35 | # LOCALE_REPO_URL = 'https://svn.mozilla.org/projects/l10n-misc/trunk/playdoh/locale'
36 | LOCALE_REPO_URL = ''
37 |
38 | GIT_PULL = "git pull -q origin %(branch)s"
39 | GIT_SUBMODULE = "git submodule update --init"
40 | SVN_CO = "svn checkout --force %(url)s locale"
41 | SVN_UP = "svn update"
42 | COMPILE_MO = "./bin/compile-mo.sh %(localedir)s %(unique)s"
43 |
44 | EXEC = 'exec'
45 | CHDIR = 'chdir'
46 |
47 |
48 | def update_site(env, debug):
49 | """Run through commands to update this site."""
50 | error_updating = False
51 | here = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
52 | locale = os.path.join(here, 'locale')
53 | unique = md5(locale).hexdigest()
54 | project_branch = {'branch': ENV_BRANCH[env][PROJECT]}
55 | vendor_branch = {'branch': ENV_BRANCH[env][VENDOR]}
56 |
57 | commands = [
58 | (CHDIR, here),
59 | (EXEC, GIT_PULL % project_branch),
60 | (EXEC, GIT_SUBMODULE),
61 | ]
62 |
63 | # Checkout the locale repo into locale/ if the URL is known
64 | if LOCALE_REPO_URL and not os.path.exists(os.path.join(locale, '.svn')):
65 | commands += [
66 | (EXEC, SVN_CO % {'url': LOCALE_REPO_URL}),
67 | (EXEC, COMPILE_MO % {'localedir': locale, 'unique': unique}),
68 | ]
69 |
70 | # Update locale dir if applicable
71 | if os.path.exists(os.path.join(locale, '.svn')):
72 | commands += [
73 | (CHDIR, locale),
74 | (EXEC, SVN_UP),
75 | (CHDIR, here),
76 | (EXEC, COMPILE_MO % {'localedir': locale, 'unique': unique}),
77 | ]
78 | elif os.path.exists(os.path.join(locale, '.git')):
79 | commands += [
80 | (CHDIR, locale),
81 | (EXEC, GIT_PULL % 'master'),
82 | (CHDIR, here),
83 | ]
84 |
85 | commands += [
86 | (CHDIR, os.path.join(here, 'vendor')),
87 | (EXEC, GIT_PULL % vendor_branch),
88 | (EXEC, GIT_SUBMODULE),
89 | (CHDIR, os.path.join(here)),
90 | (EXEC, 'python2.6 vendor/src/schematic/schematic migrations/'),
91 | (EXEC, 'python2.6 manage.py compress_assets'),
92 | ]
93 |
94 | for cmd, cmd_args in commands:
95 | if CHDIR == cmd:
96 | if debug:
97 | sys.stdout.write("cd %s\n" % cmd_args)
98 | os.chdir(cmd_args)
99 | elif EXEC == cmd:
100 | if debug:
101 | sys.stdout.write("%s\n" % cmd_args)
102 | if not 0 == os.system(cmd_args):
103 | error_updating = True
104 | break
105 | else:
106 | raise Exception("Unknown type of command %s" % cmd)
107 |
108 | if error_updating:
109 | sys.stderr.write("There was an error while updating. Please try again "
110 | "later. Aborting.\n")
111 |
112 |
113 | def main():
114 | """ Handels command line args. """
115 | debug = False
116 | usage = dedent("""\
117 | %prog [options]
118 | Updates a server's sources, vendor libraries, packages CSS/JS
119 | assets, migrates the database, and other nifty deployment tasks.
120 | """.rstrip())
121 |
122 | options = OptionParser(usage=usage)
123 | e_help = "Type of environment. One of (%s) Example: update_site.py \
124 | -e stage" % '|'.join(ENV_BRANCH.keys())
125 | options.add_option("-e", "--environment", help=e_help)
126 | options.add_option("-v", "--verbose",
127 | help="Echo actions before taking them.",
128 | action="store_true", dest="verbose")
129 | (opts, _) = options.parse_args()
130 |
131 | if opts.verbose:
132 | debug = True
133 | if opts.environment in ENV_BRANCH.keys():
134 | update_site(opts.environment, debug)
135 | else:
136 | sys.stderr.write("Invalid environment!\n")
137 | options.print_help(sys.stderr)
138 | sys.exit(1)
139 |
140 |
141 | if __name__ == '__main__':
142 | main()
143 |
--------------------------------------------------------------------------------
/lib/product_details_json/languages.json:
--------------------------------------------------------------------------------
1 | {"af":{"English":"Afrikaans","native":"Afrikaans"},"ak":{"English":"Akan","native":"Akan"},"ast":{"English":"Asturian","native":"Asturianu"},"ar":{"English":"Arabic","native":"\u0639\u0631\u0628\u064a"},"as":{"English":"Assamese","native":"\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be"},"be":{"English":"Belarusian","native":"\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"},"bg":{"English":"Bulgarian","native":"\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"},"bn-BD":{"English":"Bengali (Bangladesh)","native":"\u09ac\u09be\u0982\u09b2\u09be (\u09ac\u09be\u0982\u09b2\u09be\u09a6\u09c7\u09b6)"},"bn-IN":{"English":"Bengali (India)","native":"\u09ac\u09be\u0982\u09b2\u09be (\u09ad\u09be\u09b0\u09a4)"},"br":{"English":"Breton","native":"Brezhoneg"},"ca":{"English":"Catalan","native":"catal\u00e0"},"ca-valencia":{"English":"Catalan (Valencian)","native":"catal\u00e0 (valenci\u00e0)"},"cs":{"English":"Czech","native":"\u010ce\u0161tina"},"cy":{"English":"Welsh","native":"Cymraeg"},"da":{"English":"Danish","native":"Dansk"},"de":{"English":"German","native":"Deutsch"},"de-AT":{"English":"German (Austria)","native":"Deutsch (\u00d6sterreich)"},"de-CH":{"English":"German (Switzerland)","native":"Deutsch (Schweiz)"},"de-DE":{"English":"German (Germany)","native":"Deutsch (Deutschland)"},"dsb":{"English":"Lower Sorbian","native":"Dolnoserb\u0161\u0107ina"},"el":{"English":"Greek","native":"\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"},"en-AU":{"English":"English (Australian)","native":"English (Australian)"},"en-CA":{"English":"English (Canadian)","native":"English (Canadian)"},"en-GB":{"English":"English (British)","native":"English (British)"},"en-NZ":{"English":"English (New Zealand)","native":"English (New Zealand)"},"en-US":{"English":"English (US)","native":"English (US)"},"en-ZA":{"English":"English (South African)","native":"English (South African)"},"eo":{"English":"Esperanto","native":"Esperanto"},"es":{"English":"Spanish","native":"Espa\u00f1ol"},"es-AR":{"English":"Spanish (Argentina)","native":"Espa\u00f1ol (de Argentina)"},"es-CL":{"English":"Spanish (Chile)","native":"Espa\u00f1ol (de Chile)"},"es-ES":{"English":"Spanish (Spain)","native":"Espa\u00f1ol (de Espa\u00f1a)"},"es-MX":{"English":"Spanish (Mexico)","native":"Espa\u00f1ol (de M\u00e9xico)"},"et":{"English":"Estonian","native":"Eesti keel"},"eu":{"English":"Basque","native":"Euskara"},"fa":{"English":"Persian","native":"\u0641\u0627\u0631\u0633\u06cc"},"fi":{"English":"Finnish","native":"suomi"},"fj-FJ":{"English":"Fijian","native":"Vosa vaka-Viti"},"fr":{"English":"French","native":"Fran\u00e7ais"},"fur-IT":{"English":"Friulian","native":"Furlan"},"fy-NL":{"English":"Frisian","native":"Frysk"},"ga":{"English":"Irish","native":"Gaeilge"},"ga-IE":{"English":"Irish (Ireland)","native":"Gaeilge (\u00c9ire)"},"gd":{"English":"Gaelic (Scotland)","native":"G\u00e0idhlig"},"gl":{"English":"Galician","native":"Galego"},"gu-IN":{"English":"Gujarati","native":"\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0"},"he":{"English":"Hebrew","native":"\u05e2\u05d1\u05e8\u05d9\u05ea"},"hi":{"English":"Hindi","native":"\u0939\u093f\u0928\u094d\u0926\u0940"},"hi-IN":{"English":"Hindi (India)","native":"\u0939\u093f\u0928\u094d\u0926\u0940 (\u092d\u093e\u0930\u0924)"},"hr":{"English":"Croatian","native":"Hrvatski"},"hsb":{"English":"Upper Sorbian","native":"Hornjoserbsce"},"hu":{"English":"Hungarian","native":"Magyar"},"hy-AM":{"English":"Armenian","native":"\u0540\u0561\u0575\u0565\u0580\u0565\u0576"},"id":{"English":"Indonesian","native":"Bahasa Indonesia"},"is":{"English":"Icelandic","native":"\u00edslenska"},"it":{"English":"Italian","native":"Italiano"},"ja":{"English":"Japanese","native":"\u65e5\u672c\u8a9e"},"ja-JP-mac":{"English":"Japanese","native":"\u65e5\u672c\u8a9e"},"ka":{"English":"Georgian","native":"\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8"},"kk":{"English":"Kazakh","native":"\u049a\u0430\u0437\u0430\u049b"},"kn":{"English":"Kannada","native":"\u0c95\u0ca8\u0ccd\u0ca8\u0ca1"},"ko":{"English":"Korean","native":"\ud55c\uad6d\uc5b4"},"ku":{"English":"Kurdish","native":"Kurd\u00ee"},"la":{"English":"Latin","native":"Latina"},"lg":{"English":"Luganda","native":"Luganda"},"lt":{"English":"Lithuanian","native":"lietuvi\u0173 kalba"},"lv":{"English":"Latvian","native":"Latvie\u0161u"},"mai":{"English":"Maithili","native":"\u092e\u0948\u0925\u093f\u0932\u0940 \u09ae\u09c8\u09a5\u09bf\u09b2\u09c0"},"mg":{"English":"Malagasy","native":"Malagasy"},"mi":{"English":"Maori (Aotearoa)","native":"M\u0101ori (Aotearoa)"},"mk":{"English":"Macedonian","native":"\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438"},"ml":{"English":"Malayalam","native":"\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02"},"mn":{"English":"Mongolian","native":"\u041c\u043e\u043d\u0433\u043e\u043b"},"mr":{"English":"Marathi","native":"\u092e\u0930\u093e\u0920\u0940"},"nb-NO":{"English":"Norwegian (Bokm\u00e5l)","native":"Norsk bokm\u00e5l"},"ne-NP":{"English":"Nepali","native":"\u0928\u0947\u092a\u093e\u0932\u0940"},"nn-NO":{"English":"Norwegian (Nynorsk)","native":"Norsk nynorsk"},"nl":{"English":"Dutch","native":"Nederlands"},"nr":{"English":"Ndebele, South","native":"isiNdebele"},"nso":{"English":"Northern Sotho","native":"Sepedi"},"oc":{"English":"Occitan (Lengadocian)","native":"occitan (lengadocian)"},"or":{"English":"Oriya","native":"\u0b13\u0b21\u0b3c\u0b3f\u0b06"},"pa-IN":{"English":"Punjabi","native":"\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40"},"pl":{"English":"Polish","native":"Polski"},"pt-BR":{"English":"Portuguese (Brazilian)","native":"Portugu\u00eas (do Brasil)"},"pt-PT":{"English":"Portuguese (Portugal)","native":"Portugu\u00eas (Europeu)"},"ro":{"English":"Romanian","native":"rom\u00e2n\u0103"},"rm":{"English":"Romansh","native":"rumantsch"},"ru":{"English":"Russian","native":"\u0420\u0443\u0441\u0441\u043a\u0438\u0439"},"rw":{"English":"Kinyarwanda","native":"Ikinyarwanda"},"si":{"English":"Sinhala","native":"\u0dc3\u0dd2\u0d82\u0dc4\u0dbd"},"sk":{"English":"Slovak","native":"sloven\u010dina"},"sl":{"English":"Slovenian","native":"slovensko"},"son":{"English":"Songhai","native":"So\u014bay"},"sq":{"English":"Albanian","native":"Shqip"},"sr":{"English":"Serbian","native":"\u0421\u0440\u043f\u0441\u043a\u0438"},"sr-Latn":{"English":"Serbian","native":"Srpski"},"ss":{"English":"Siswati","native":"siSwati"},"st":{"English":"Southern Sotho","native":"Sesotho"},"sv-SE":{"English":"Swedish","native":"Svenska"},"ta":{"English":"Tamil","native":"\u0ba4\u0bae\u0bbf\u0bb4\u0bcd"},"ta-IN":{"English":"Tamil (India)","native":"\u0ba4\u0bae\u0bbf\u0bb4\u0bcd (\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe)"},"ta-LK":{"English":"Tamil (Sri Lanka)","native":"\u0ba4\u0bae\u0bbf\u0bb4\u0bcd (\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8)"},"te":{"English":"Telugu","native":"\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41"},"th":{"English":"Thai","native":"\u0e44\u0e17\u0e22"},"tn":{"English":"Tswana","native":"Setswana"},"tr":{"English":"Turkish","native":"T\u00fcrk\u00e7e"},"ts":{"English":"Tsonga","native":"Xitsonga"},"tt-RU":{"English":"Tatar","native":"Tatar\u00e7a"},"uk":{"English":"Ukrainian","native":"\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"},"ur":{"English":"Urdu","native":"\u0627\u064f\u0631\u062f\u0648"},"ve":{"English":"Venda","native":"Tshiven\u1e13a"},"vi":{"English":"Vietnamese","native":"Ti\u1ebfng Vi\u1ec7t"},"wo":{"English":"Wolof","native":"Wolof"},"xh":{"English":"Xhosa","native":"isiXhosa"},"zh-CN":{"English":"Chinese (Simplified)","native":"\u4e2d\u6587 (\u7b80\u4f53)"},"zh-TW":{"English":"Chinese (Traditional)","native":"\u6b63\u9ad4\u4e2d\u6587 (\u7e41\u9ad4)"},"zu":{"English":"Zulu","native":"isiZulu"}}
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # playdoh documentation build configuration file, created by
4 | # sphinx-quickstart on Tue Jan 4 15:11:09 2011.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage']
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'a playdoh-based project'
44 | copyright = u'2011, the authors'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '1.0'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '1.0'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = ['_build']
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_module_names = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 |
90 | # -- Options for HTML output ---------------------------------------------------
91 |
92 | # The theme to use for HTML and HTML Help pages. See the documentation for
93 | # a list of builtin themes.
94 | html_theme = 'default'
95 |
96 | # Theme options are theme-specific and customize the look and feel of a theme
97 | # further. For a list of options available for each theme, see the
98 | # documentation.
99 | #html_theme_options = {}
100 |
101 | # Add any paths that contain custom themes here, relative to this directory.
102 | #html_theme_path = []
103 |
104 | # The name for this set of Sphinx documents. If None, it defaults to
105 | # " v documentation".
106 | #html_title = None
107 |
108 | # A shorter title for the navigation bar. Default is the same as html_title.
109 | #html_short_title = None
110 |
111 | # The name of an image file (relative to this directory) to place at the top
112 | # of the sidebar.
113 | #html_logo = None
114 |
115 | # The name of an image file (within the static path) to use as favicon of the
116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
117 | # pixels large.
118 | #html_favicon = None
119 |
120 | # Add any paths that contain custom static files (such as style sheets) here,
121 | # relative to this directory. They are copied after the builtin static files,
122 | # so a file named "default.css" will overwrite the builtin "default.css".
123 | html_static_path = ['_static']
124 |
125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
126 | # using the given strftime format.
127 | #html_last_updated_fmt = '%b %d, %Y'
128 |
129 | # If true, SmartyPants will be used to convert quotes and dashes to
130 | # typographically correct entities.
131 | #html_use_smartypants = True
132 |
133 | # Custom sidebar templates, maps document names to template names.
134 | #html_sidebars = {}
135 |
136 | # Additional templates that should be rendered to pages, maps page names to
137 | # template names.
138 | #html_additional_pages = {}
139 |
140 | # If false, no module index is generated.
141 | #html_domain_indices = True
142 |
143 | # If false, no index is generated.
144 | #html_use_index = True
145 |
146 | # If true, the index is split into individual pages for each letter.
147 | #html_split_index = False
148 |
149 | # If true, links to the reST sources are added to the pages.
150 | #html_show_sourcelink = True
151 |
152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
153 | #html_show_sphinx = True
154 |
155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
156 | #html_show_copyright = True
157 |
158 | # If true, an OpenSearch description file will be output, and all pages will
159 | # contain a tag referring to it. The value of this option must be the
160 | # base URL from which the finished HTML is served.
161 | #html_use_opensearch = ''
162 |
163 | # This is the file name suffix for HTML files (e.g. ".xhtml").
164 | #html_file_suffix = None
165 |
166 | # Output file base name for HTML help builder.
167 | htmlhelp_basename = 'playdohdoc'
168 |
169 |
170 | # -- Options for LaTeX output --------------------------------------------------
171 |
172 | # The paper size ('letter' or 'a4').
173 | #latex_paper_size = 'letter'
174 |
175 | # The font size ('10pt', '11pt' or '12pt').
176 | #latex_font_size = '10pt'
177 |
178 | # Grouping the document tree into LaTeX files. List of tuples
179 | # (source start file, target name, title, author, documentclass [howto/manual]).
180 | latex_documents = [
181 | ('index', 'playdoh.tex', u'playdoh Documentation',
182 | u'Mozilla', 'manual'),
183 | ]
184 |
185 | # The name of an image file (relative to this directory) to place at the top of
186 | # the title page.
187 | #latex_logo = None
188 |
189 | # For "manual" documents, if this is true, then toplevel headings are parts,
190 | # not chapters.
191 | #latex_use_parts = False
192 |
193 | # If true, show page references after internal links.
194 | #latex_show_pagerefs = False
195 |
196 | # If true, show URL addresses after external links.
197 | #latex_show_urls = False
198 |
199 | # Additional stuff for the LaTeX preamble.
200 | #latex_preamble = ''
201 |
202 | # Documents to append as an appendix to all manuals.
203 | #latex_appendices = []
204 |
205 | # If false, no module index is generated.
206 | #latex_domain_indices = True
207 |
208 |
209 | # -- Options for manual page output --------------------------------------------
210 |
211 | # One entry per manual page. List of tuples
212 | # (source start file, name, description, authors, manual section).
213 | man_pages = [
214 | ('index', 'a-playdoh-app', u"a-playdoh-app's Documentation",
215 | [u'the authors'], 1)
216 | ]
217 |
218 |
219 | # Example configuration for intersphinx: refer to the Python standard library.
220 | intersphinx_mapping = {'http://docs.python.org/': None}
221 |
--------------------------------------------------------------------------------
/lib/product_details_json/thunderbird_primary_builds.json:
--------------------------------------------------------------------------------
1 | {"af":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"ar":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"be":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.2},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.7}}},"bg":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"bn-BD":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"ca":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"cs":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"da":{"3.0.11":{"Windows":{"filesize":8.9},"OS X":{"filesize":19.8},"Linux":{"filesize":10.8}},"3.1.7":{"Windows":{"filesize":9.3},"OS X":{"filesize":21},"Linux":{"filesize":11.3}}},"de":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"el":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"en-GB":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"en-US":{"3.0.11":{"Windows":{"filesize":8.6},"OS X":{"filesize":19.5},"Linux":{"filesize":10.5}},"3.1.7":{"Windows":{"filesize":9},"OS X":{"filesize":20.6},"Linux":{"filesize":11}}},"es-AR":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"es-ES":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.2},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.8},"OS X":{"filesize":20.4},"Linux":{"filesize":10.7}}},"et":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"eu":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"fi":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"fr":{"3.0.11":{"Windows":{"filesize":8.7},"OS X":{"filesize":19.5},"Linux":{"filesize":10.5}},"3.1.7":{"Windows":{"filesize":9.1},"OS X":{"filesize":20.7},"Linux":{"filesize":11}}},"fy-NL":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"ga-IE":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"gd":{"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"gl":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"he":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.2},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"hu":{"3.0.11":{"Windows":{"filesize":9},"OS X":{"filesize":19.9},"Linux":{"filesize":10.9}},"3.1.7":{"Windows":{"filesize":9.4},"OS X":{"filesize":21.1},"Linux":{"filesize":11.4}}},"id":{"3.0.11":{"Windows":{"filesize":8.6},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.5},"Linux":{"filesize":10.9}}},"is":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"it":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.2},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.8},"OS X":{"filesize":20.4},"Linux":{"filesize":10.7}}},"ja":{"3.0.11":{"Windows":{"filesize":8.7},"OS X":{"filesize":19.5},"Linux":{"filesize":10.5}},"3.1.7":{"Windows":{"filesize":9},"OS X":{"filesize":20.7},"Linux":{"filesize":11}}},"ka":{"3.0.11":{"Windows":{"filesize":8.7},"OS X":{"filesize":19.5},"Linux":{"filesize":10.5}}},"ko":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.2},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"lt":{"3.0.11":{"Windows":{"filesize":8.8},"OS X":{"filesize":19.6},"Linux":{"filesize":10.6}},"3.1.7":{"Windows":{"filesize":9.2},"OS X":{"filesize":20.8},"Linux":{"filesize":11.1}}},"nb-NO":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"nl":{"3.0.11":{"Windows":{"filesize":9.3},"OS X":{"filesize":20.1},"Linux":{"filesize":11}},"3.1.7":{"Windows":{"filesize":9.7},"OS X":{"filesize":21.3},"Linux":{"filesize":11.6}}},"nn-NO":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"pa-IN":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"pl":{"3.0.11":{"Windows":{"filesize":9.3},"OS X":{"filesize":20.2},"Linux":{"filesize":11.3}},"3.1.7":{"Windows":{"filesize":9.7},"OS X":{"filesize":21.4},"Linux":{"filesize":11.8}}},"pt-BR":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"pt-PT":{"3.0.11":{"Windows":{"filesize":8.7},"OS X":{"filesize":19.4},"Linux":{"filesize":10.4}},"3.1.7":{"Windows":{"filesize":9},"OS X":{"filesize":20.6},"Linux":{"filesize":11}}},"ro":{"3.0.11":{"Windows":{"filesize":8.9},"OS X":{"filesize":19.8},"Linux":{"filesize":10.8}},"3.1.7":{"Windows":{"filesize":9.3},"OS X":{"filesize":21},"Linux":{"filesize":11.3}}},"ru":{"3.0.11":{"Windows":{"filesize":8.9},"OS X":{"filesize":19.7},"Linux":{"filesize":10.7}},"3.1.7":{"Windows":{"filesize":9.3},"OS X":{"filesize":20.9},"Linux":{"filesize":11.2}}},"si":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"sk":{"3.0.11":{"Windows":{"filesize":9},"OS X":{"filesize":19.8},"Linux":{"filesize":10.8}},"3.1.7":{"Windows":{"filesize":9.3},"OS X":{"filesize":21},"Linux":{"filesize":11.4}}},"sl":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"sq":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"sr":{"3.0.11":{"Windows":{"filesize":9.6},"OS X":{"filesize":20.9},"Linux":{"filesize":12.1}},"3.1.7":{"Windows":{"filesize":10},"OS X":{"filesize":22.1},"Linux":{"filesize":12.6}}},"sv-SE":{"3.0.11":{"Windows":{"filesize":8.7},"OS X":{"filesize":19.6},"Linux":{"filesize":10.6}},"3.1.7":{"Windows":{"filesize":9.1},"OS X":{"filesize":20.8},"Linux":{"filesize":11.1}}},"ta-LK":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}}},"tr":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"uk":{"3.0.11":{"Windows":{"filesize":8.9},"OS X":{"filesize":19.7},"Linux":{"filesize":10.7}},"3.1.7":{"Windows":{"filesize":9.3},"OS X":{"filesize":20.8},"Linux":{"filesize":11.2}}},"vi":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.3}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.5},"Linux":{"filesize":10.8}}},"zh-CN":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.2},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}},"zh-TW":{"3.0.11":{"Windows":{"filesize":8.5},"OS X":{"filesize":19.3},"Linux":{"filesize":10.2}},"3.1.7":{"Windows":{"filesize":8.9},"OS X":{"filesize":20.4},"Linux":{"filesize":10.8}}}}
--------------------------------------------------------------------------------
/settings/base.py-back:
--------------------------------------------------------------------------------
1 | # Django settings file for a project based on the playdoh template.
2 |
3 | import os
4 |
5 | from django.utils.functional import lazy
6 |
7 | # Make file paths relative to settings.
8 | ROOT = os.path.dirname(os.path.abspath(__file__))
9 | path = lambda *a: os.path.join(ROOT, *a)
10 |
11 | ROOT_PACKAGE = os.path.basename(ROOT)
12 |
13 | # Is this a dev instance?
14 | DEV = False
15 |
16 | DEBUG = False
17 | TEMPLATE_DEBUG = DEBUG
18 |
19 | ADMINS = ()
20 | MANAGERS = ADMINS
21 |
22 | DATABASES = {} # See settings_local.
23 |
24 | # Site ID is used by Django's Sites framework.
25 | SITE_ID = 1
26 |
27 |
28 | ## Internationalization.
29 |
30 | # Local time zone for this installation. Choices can be found here:
31 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
32 | # although not all choices may be available on all operating systems.
33 | # On Unix systems, a value of None will cause Django to use the same
34 | # timezone as the operating system.
35 | # If running in a Windows environment this must be set to the same as your
36 | # system time zone.
37 | TIME_ZONE = 'America/Los_Angeles'
38 |
39 | # If you set this to False, Django will make some optimizations so as not
40 | # to load the internationalization machinery.
41 | USE_I18N = True
42 |
43 | # If you set this to False, Django will not format dates, numbers and
44 | # calendars according to the current locale
45 | USE_L10N = True
46 |
47 | # Gettext text domain
48 | TEXT_DOMAIN = 'messages'
49 | STANDALONE_DOMAINS = [TEXT_DOMAIN, 'javascript']
50 | TOWER_KEYWORDS = {'_lazy': None}
51 | TOWER_ADD_HEADERS = True
52 |
53 | # Language code for this installation. All choices can be found here:
54 | # http://www.i18nguy.com/unicode/language-identifiers.html
55 | LANGUAGE_CODE = 'en-US'
56 |
57 | ## Accepted locales
58 |
59 | # On dev instances, the list of accepted locales defaults to the contents of
60 | # the `locale` directory. A localizer can add their locale in the l10n
61 | # repository (copy of which is checked out into `locale`) in order to start
62 | # testing the localization on the dev server.
63 | try:
64 | DEV_LANGUAGES = [
65 | loc.replace('_', '-') for loc in os.listdir(path('locale'))
66 | if os.path.isdir(path('locale', loc)) and loc != 'templates'
67 | ]
68 | except OSError:
69 | DEV_LANGUAGES = ('en-US',)
70 |
71 | # On stage/prod, the list of accepted locales is manually maintained. Only
72 | # locales whose localizers have signed off on their work should be listed here.
73 | PROD_LANGUAGES = (
74 | 'en-US',
75 | )
76 |
77 | def lazy_lang_url_map():
78 | from django.conf import settings
79 | langs = settings.DEV_LANGUAGES if settings.DEV else settings.PROD_LANGUAGES
80 | return dict([(i.lower(), i) for i in langs])
81 |
82 | LANGUAGE_URL_MAP = lazy(lazy_lang_url_map, dict)()
83 |
84 | # Override Django's built-in with our native names
85 | def lazy_langs():
86 | from django.conf import settings
87 | from product_details import product_details
88 | langs = DEV_LANGUAGES if settings.DEV else PROD_LANGUAGES
89 | return dict([(lang.lower(), product_details.languages[lang]['native'])
90 | for lang in langs])
91 |
92 | # Where to store product details etc.
93 | PROD_DETAILS_DIR = path('lib/product_details_json')
94 |
95 | LANGUAGES = lazy(lazy_langs, dict)()
96 |
97 | # Paths that don't require a locale code in the URL.
98 | SUPPORTED_NONLOCALES = ['media']
99 |
100 |
101 | ## Media and templates.
102 |
103 | # Absolute path to the directory that holds media.
104 | # Example: "/home/media/media.lawrence.com/"
105 | MEDIA_ROOT = path('media')
106 |
107 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a
108 | # trailing slash if there is a path component (optional in other cases).
109 | # Examples: "http://media.lawrence.com", "http://example.com/media/"
110 | MEDIA_URL = '/media/'
111 |
112 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
113 | # trailing slash.
114 | # Examples: "http://foo.com/media/", "/media/".
115 | ADMIN_MEDIA_PREFIX = '/admin-media/'
116 |
117 | # Make this unique, and don't share it with anybody.
118 | SECRET_KEY = '1iz#v0m55@h26^m6hxk3a7at*h$qj_2a$juu1#nv50548j(x1v'
119 |
120 | # List of callables that know how to import templates from various sources.
121 | TEMPLATE_LOADERS = (
122 | 'django.template.loaders.filesystem.Loader',
123 | 'django.template.loaders.app_directories.Loader',
124 | # 'django.template.loaders.eggs.Loader',
125 | )
126 |
127 | TEMPLATE_CONTEXT_PROCESSORS = (
128 | 'django.contrib.auth.context_processors.auth',
129 | 'django.core.context_processors.debug',
130 | 'django.core.context_processors.media',
131 | 'django.core.context_processors.request',
132 | 'django.core.context_processors.csrf',
133 | 'django.contrib.messages.context_processors.messages',
134 |
135 | 'commons.context_processors.i18n',
136 | #'jingo_minify.helpers.build_ids',
137 | )
138 |
139 | TEMPLATE_DIRS = (
140 | path('templates'),
141 | )
142 |
143 | def JINJA_CONFIG():
144 | import jinja2
145 | from django.conf import settings
146 | # from caching.base import cache
147 | config = {'extensions': ['tower.template.i18n', 'jinja2.ext.do',
148 | 'jinja2.ext.with_', 'jinja2.ext.loopcontrols'],
149 | 'finalize': lambda x: x if x is not None else ''}
150 | # if 'memcached' in cache.scheme and not settings.DEBUG:
151 | # We're passing the _cache object directly to jinja because
152 | # Django can't store binary directly; it enforces unicode on it.
153 | # Details: http://jinja.pocoo.org/2/documentation/api#bytecode-cache
154 | # and in the errors you get when you try it the other way.
155 | # bc = jinja2.MemcachedBytecodeCache(cache._cache,
156 | # "%sj2:" % settings.CACHE_PREFIX)
157 | # config['cache_size'] = -1 # Never clear the cache
158 | # config['bytecode_cache'] = bc
159 | return config
160 |
161 | # Bundles is a dictionary of two dictionaries, css and js, which list css files
162 | # and js files that can be bundled together by the minify app.
163 | MINIFY_BUNDLES = {
164 | 'css': {
165 | 'common_css': (
166 | 'css/dll/main.css',
167 | ),
168 | },
169 | 'js': {
170 | 'common_js': (
171 | 'js/libs/jquery-1.6.2.js',
172 | 'js/dll/nav.js',
173 | ),
174 | 'detail_js': (
175 | 'js/dll/detail.js',
176 | ),
177 | }
178 | }
179 |
180 |
181 | ## Middlewares, apps, URL configs.
182 |
183 | MIDDLEWARE_CLASSES = (
184 | 'commons.middleware.LocaleURLMiddleware',
185 | 'django.middleware.common.CommonMiddleware',
186 | 'django.contrib.sessions.middleware.SessionMiddleware',
187 | 'django.middleware.csrf.CsrfViewMiddleware',
188 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
189 | 'django.contrib.messages.middleware.MessageMiddleware',
190 |
191 | 'commonware.middleware.FrameOptionsHeader',
192 | )
193 |
194 | ROOT_URLCONF = '%s.urls' % ROOT_PACKAGE
195 |
196 | INSTALLED_APPS = (
197 | # Local apps
198 | 'commons', # Content common to most playdoh-based apps.
199 | 'jingo_minify',
200 | 'tower', # for ./manage.py extract (L10n)
201 |
202 | # DLL Specific
203 | 'dll',
204 | 'users',
205 |
206 | # We need this so the jsi18n view will pick up our locale directory.
207 | ROOT_PACKAGE,
208 |
209 | # Third-party apps
210 | 'commonware.response.cookies',
211 | 'djcelery',
212 | 'django_nose',
213 |
214 | # Django contrib apps
215 | 'django.contrib.auth',
216 | 'django_sha2', # Load after auth to monkey-patch it.
217 |
218 | 'django.contrib.contenttypes',
219 | 'django.contrib.sessions',
220 | # 'django.contrib.sites',
221 | # 'django.contrib.messages',
222 | # Uncomment the next line to enable the admin:
223 | # 'django.contrib.admin',
224 | # Uncomment the next line to enable admin documentation:
225 | # 'django.contrib.admindocs',
226 |
227 | # L10n
228 | 'product_details',
229 |
230 | )
231 |
232 | # Tells the extract script what files to look for L10n in and what function
233 | # handles the extraction. The Tower library expects this.
234 | DOMAIN_METHODS = {
235 | 'messages': [
236 | ('apps/**.py',
237 | 'tower.management.commands.extract.extract_tower_python'),
238 | ('**/templates/**.html',
239 | 'tower.management.commands.extract.extract_tower_template'),
240 | ],
241 |
242 | ## Use this if you have localizable HTML files:
243 | #'lhtml': [
244 | # ('**/templates/**.lhtml',
245 | # 'tower.management.commands.extract.extract_tower_template'),
246 | #],
247 |
248 | ## Use this if you have localizable JS files:
249 | #'javascript': [
250 | # Make sure that this won't pull in strings from external libraries you
251 | # may use.
252 | # ('media/js/**.js', 'javascript'),
253 | #],
254 | }
255 |
256 | # Path to Java. Used for compress_assets.
257 | JAVA_BIN = '/usr/bin/java'
258 |
259 | ## Auth
260 | PWD_ALGORITHM = 'sha512' # recommended: 'bcrypt'
261 | HMAC_KEYS = { # for bcrypt only
262 | #'2011-01-01': 'cheesecake',
263 | }
264 |
265 | ## Tests
266 | TEST_RUNNER = 'test_utils.runner.RadicalTestSuiteRunner'
267 |
268 | ## Celery
269 | BROKER_HOST = 'localhost'
270 | BROKER_PORT = 5672
271 | BROKER_USER = 'playdoh'
272 | BROKER_PASSWORD = 'playdoh'
273 | BROKER_VHOST = 'playdoh'
274 | BROKER_CONNECTION_TIMEOUT = 0.1
275 | CELERY_RESULT_BACKEND = 'amqp'
276 | CELERY_IGNORE_RESULT = True
277 |
278 | AUTH_PROFILE_MODULE = 'users.UserProfile'
279 | LOGIN_URL = '/users/login/'
280 | LOGOUT_REDIRECT_URL = '/'
281 | LOGIN_REDIRECT_URL = '/'
282 |
283 | try:
284 | ## LDAP
285 | import ldap
286 |
287 | AUTHENTICATION_BACKENDS = (
288 | 'users.email_auth_backend.EmailOrUsernameModelBackend',
289 | 'users.auth.backends.MozillaLDAPBackend',
290 | 'django.contrib.auth.backends.ModelBackend',
291 | )
292 |
293 | # these must be set in settings/local.py!
294 | AUTH_LDAP_SERVER_URI = ''
295 | AUTH_LDAP_BIND_DN = ''
296 | AUTH_LDAP_BIND_PASSWORD = ''
297 |
298 | AUTH_LDAP_START_TLS = True
299 | AUTH_LDAP_USER_ATTR_MAP = {
300 | "first_name": "givenName",
301 | "last_name": "sn",
302 | "email": "mail",
303 | }
304 | from django_auth_ldap.config import LDAPSearch
305 | AUTH_LDAP_USER_SEARCH = LDAPSearch(
306 | "dc=mozilla",
307 | ldap.SCOPE_SUBTREE,
308 | "mail=%(user)s"
309 | )
310 |
311 | except ImportError:
312 | AUTHENTICATION_BACKENDS = (
313 | 'users.email_auth_backend.EmailOrUsernameModelBackend',
314 | 'django.contrib.auth.backends.ModelBackend',
315 | )
316 |
--------------------------------------------------------------------------------
/apps/users/tests.py:
--------------------------------------------------------------------------------
1 | # ***** BEGIN LICENSE BLOCK *****
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 | #
4 | # The contents of this file are subject to the Mozilla Public License Version
5 | # 1.1 (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | # http://www.mozilla.org/MPL/
8 | #
9 | # Software distributed under the License is distributed on an "AS IS" basis,
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 | # for the specific language governing rights and limitations under the
12 | # License.
13 | #
14 | # The Original Code is Mozilla Sheriff Duty.
15 | #
16 | # The Initial Developer of the Original Code is Mozilla Corporation.
17 | # Portions created by the Initial Developer are Copyright (C) 2011
18 | # the Initial Developer. All Rights Reserved.
19 | #
20 | # Contributor(s):
21 | #
22 | # Alternatively, the contents of this file may be used under the terms of
23 | # either the GNU General Public License Version 2 or later (the "GPL"), or
24 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 | # in which case the provisions of the GPL or the LGPL are applicable instead
26 | # of those above. If you wish to allow use of your version of this file only
27 | # under the terms of either the GPL or the LGPL, and not to allow others to
28 | # use your version of this file under the terms of the MPL, indicate your
29 | # decision by deleting the provisions above and replace them with the notice
30 | # and other provisions required by the GPL or the LGPL. If you do not delete
31 | # the provisions above, a recipient may use your version of this file under
32 | # the terms of any one of the MPL, the GPL or the LGPL.
33 | #
34 | # ***** END LICENSE BLOCK *****
35 |
36 | import re
37 | from urlparse import urlparse
38 | import datetime
39 | from django.test import TestCase
40 | from django.conf import settings
41 | from django.core.urlresolvers import reverse
42 | from django.contrib.auth.models import User
43 | from django.contrib.auth import REDIRECT_FIELD_NAME
44 | from nose.tools import eq_, ok_
45 | from commons.urlresolvers import reverse
46 |
47 | try:
48 | import ldap
49 | from users.auth.backends import MozillaLDAPBackend
50 | except ImportError:
51 | MozillaLDAPBackend = None
52 |
53 |
54 | class UsersTest(TestCase):
55 |
56 | def test_login(self):
57 | self.client.get('/')
58 | url = reverse('users.login')
59 |
60 | response = self.client.get(url)
61 | eq_(response.status_code, 200)
62 |
63 | mortal = User.objects.create(
64 | username='mortal',
65 | first_name='Mortal',
66 | last_name='Joe'
67 | )
68 | mortal.set_password('secret')
69 | mortal.save()
70 |
71 | response = self.client.post(url, {'username': 'mortal',
72 | 'password': 'wrong'})
73 | eq_(response.status_code, 200)
74 | ok_('errorlist' in response.content)
75 |
76 | response = self.client.post(url, {'username': 'mortal',
77 | 'password': 'secret'})
78 | eq_(response.status_code, 302)
79 | path = urlparse(response['location']).path
80 | eq_(path, settings.LOGIN_REDIRECT_URL)
81 |
82 | response = self.client.get('/')
83 | eq_(response.status_code, 200)
84 | ok_('Mortal' in response.content)
85 |
86 | url = reverse('users.logout')
87 | response = self.client.get(url)
88 | eq_(response.status_code, 302)
89 | path = urlparse(response['location']).path
90 | eq_(path, settings.LOGOUT_REDIRECT_URL)
91 |
92 | response = self.client.get('/')
93 | eq_(response.status_code, 200)
94 | ok_('Mortal' not in response.content)
95 |
96 | def _get_all_inputs(self, html):
97 | _input_regex = re.compile('', re.M | re.DOTALL)
98 | _attrs_regex = re.compile('(\w+)="([^"]+)"')
99 | all_attrs = {}
100 | for input in _input_regex.findall(html):
101 | attrs = dict(_attrs_regex.findall(input))
102 | all_attrs[attrs.get('name', attrs.get('id', ''))] = attrs
103 | return all_attrs
104 |
105 | def test_login_next_redirect(self):
106 | url = reverse('users.login')
107 | response = self.client.get(url, {'next': '/foo/bar'})
108 | eq_(response.status_code, 200)
109 | attrs = self._get_all_inputs(response.content)
110 | ok_(attrs[REDIRECT_FIELD_NAME])
111 | eq_(attrs[REDIRECT_FIELD_NAME]['value'], '/foo/bar')
112 |
113 | mortal = User.objects.create_user(
114 | 'mortal', 'mortal', password='secret'
115 | )
116 | mortal.set_password('secret')
117 | mortal.save()
118 |
119 | response = self.client.post(url, {'username': 'mortal',
120 | 'password': 'secret',
121 | 'next': '/foo/bar'})
122 | eq_(response.status_code, 302)
123 | path = urlparse(response['location']).path
124 | eq_(path, '/foo/bar')
125 |
126 | def test_login_failure(self):
127 | url = reverse('users.login')
128 | mortal = User.objects.create(
129 | username='mortal',
130 | first_name='Mortal',
131 | last_name='Joe',
132 | email='mortal@mozilla.com',
133 | )
134 | mortal.set_password('secret')
135 | mortal.save()
136 |
137 | response = self.client.post(url, {'username': 'mortal',
138 | 'password': 'xxx'})
139 | eq_(response.status_code, 200)
140 | ok_('errorlist' in response.content)
141 |
142 | response = self.client.post(url, {'username': 'xxx',
143 | 'password': 'secret'})
144 | eq_(response.status_code, 200)
145 | ok_('errorlist' in response.content)
146 |
147 | def test_login_rememberme(self):
148 | url = reverse('users.login')
149 | mortal = User.objects.create(
150 | username='mortal',
151 | first_name='Mortal',
152 | last_name='Joe'
153 | )
154 | mortal.set_password('secret')
155 | mortal.save()
156 |
157 | response = self.client.post(url, {'username': 'mortal',
158 | 'password': 'secret',
159 | 'rememberme': 'yes'})
160 | eq_(response.status_code, 302)
161 | expires = self.client.cookies['sessionid']['expires']
162 | date = expires.split()[1]
163 | then = datetime.datetime.strptime(date, '%d-%b-%Y')
164 | today = datetime.datetime.today()
165 | days = settings.SESSION_COOKIE_AGE / 24 / 3600
166 | eq_((then - today).days + 1, days)
167 |
168 | def test_login_by_email(self):
169 | url = reverse('users.login')
170 |
171 | mortal = User.objects.create(
172 | username='mortal',
173 | email='mortal@hotmail.com',
174 | first_name='Mortal',
175 | last_name='Joe'
176 | )
177 | mortal.set_password('secret')
178 | mortal.save()
179 |
180 | response = self.client.post(url, {'username': 'Mortal@hotmail.com',
181 | 'password': 'secret'})
182 | eq_(response.status_code, 302)
183 |
184 | response = self.client.get('/')
185 | eq_(response.status_code, 200)
186 | ok_('Mortal' in response.content)
187 |
188 | def test_changing_your_username(self):
189 | url = reverse('users.settings')
190 | response = self.client.get(url)
191 | eq_(response.status_code, 302)
192 | path = urlparse(response['location']).path
193 | eq_(path, settings.LOGIN_URL)
194 |
195 | mortal = User.objects.create(
196 | username='mortal',
197 | email='mortal@hotmail.com',
198 | first_name='Mortal',
199 | last_name='Joe'
200 | )
201 | mortal.set_password('secret')
202 | mortal.save()
203 | assert self.client.login(username='mortal', password='secret')
204 |
205 | url = reverse('users.settings')
206 | response = self.client.get(url)
207 | eq_(response.status_code, 200)
208 |
209 | ok_('value="%s"' % mortal.username in response.content)
210 |
211 | User.objects.create_user(
212 | 'maxpower',
213 | 'maxpower@mozilla.com',
214 | password='secret',
215 | )
216 |
217 | response = self.client.post(url, {'username':' Maxpower '})
218 | eq_(response.status_code, 200)
219 | ok_('errorlist' in response.content)
220 |
221 | response = self.client.post(url, {'username':'homer '})
222 | eq_(response.status_code, 302)
223 |
224 | ok_(User.objects.get(username='homer'))
225 | ok_(not User.objects.filter(username='mortal').exists())
226 |
227 | # stupid but I should be able to save my own username twice
228 | response = self.client.post(url, {'username':'homer'})
229 | ok_(User.objects.get(username='homer'))
230 |
231 | response = self.client.post(url, {'username':'Homer'})
232 | ok_(User.objects.get(username='Homer'))
233 |
234 | def test_mozilla_ldap_backend_basic(self):
235 | if MozillaLDAPBackend is None:
236 | return
237 | back = MozillaLDAPBackend()
238 | class LDAPUser:
239 | def __init__(self, attrs):
240 | self.attrs = attrs
241 | ldap_user = LDAPUser({'mail':['mail@peterbe.com']})
242 | user, created = back.get_or_create_user('peter', ldap_user)
243 | ok_(created)
244 | ok_(user)
245 | eq_(user.username, 'peter')
246 |
247 | peppe = User.objects.create_user(
248 | 'peppe',
249 | 'mail@peterbe.com',
250 | )
251 | user, created = back.get_or_create_user('peter', ldap_user)
252 | ok_(not created)
253 | eq_(user, peppe)
254 |
255 | username = back.ldap_to_django_username('mail@peterbe.com')
256 | eq_(username, 'peppe')
257 | username = back.ldap_to_django_username('lois@peterbe.com')
258 | eq_(username, 'lois')
259 |
260 | def test_login_username_form_field(self):
261 | url = reverse('users.login')
262 | response = self.client.get(url)
263 | eq_(response.status_code, 200)
264 | html = response.content.split('