Moved common, translation, template, and url code out of util.py and into tools/[file].py

This commit is contained in:
Aaron Williamson 2011-10-01 09:31:42 -04:00
parent 9122a9d047
commit ae3bc7fabf
20 changed files with 636 additions and 303 deletions

View File

@ -21,6 +21,7 @@ import routes
from webob import Request, exc from webob import Request, exc
from mediagoblin import routing, util, middleware from mediagoblin import routing, util, middleware
from mediagoblin.tools import translate, template
from mediagoblin.mg_globals import setup_globals from mediagoblin.mg_globals import setup_globals
from mediagoblin.init.celery import setup_celery_from_config from mediagoblin.init.celery import setup_celery_from_config
from mediagoblin.init import (get_jinja_loader, get_staticdirector, from mediagoblin.init import (get_jinja_loader, get_staticdirector,
@ -123,9 +124,9 @@ class MediaGoblinApp(object):
# Attach self as request.app # Attach self as request.app
# Also attach a few utilities from request.app for convenience? # Also attach a few utilities from request.app for convenience?
request.app = self request.app = self
request.locale = util.get_locale_from_request(request) request.locale = translate.get_locale_from_request(request)
request.template_env = util.get_jinja_env( request.template_env = template.get_jinja_env(
self.template_loader, request.locale) self.template_loader, request.locale)
request.db = self.db request.db = self.db
request.staticdirect = self.staticdirector request.staticdirect = self.staticdirector

View File

@ -17,7 +17,7 @@
import wtforms import wtforms
import re import re
from mediagoblin.util import fake_ugettext_passthrough as _ from mediagoblin.tools.translate import fake_ugettext_passthrough as _
class RegistrationForm(wtforms.Form): class RegistrationForm(wtforms.Form):

View File

@ -19,7 +19,8 @@ import random
import bcrypt import bcrypt
from mediagoblin.util import send_email, render_template from mediagoblin.util import send_email
from mediagoblin.tools.template import render_template
from mediagoblin import mg_globals from mediagoblin import mg_globals

View File

@ -22,7 +22,7 @@ from webob import exc
from mediagoblin import messages from mediagoblin import messages
from mediagoblin import mg_globals from mediagoblin import mg_globals
from mediagoblin.util import render_to_response, redirect, render_404 from mediagoblin.util import render_to_response, redirect, render_404
from mediagoblin.util import pass_to_ugettext as _ from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.db.util import ObjectId, InvalidId from mediagoblin.db.util import ObjectId, InvalidId
from mediagoblin.auth import lib as auth_lib from mediagoblin.auth import lib as auth_lib
from mediagoblin.auth import forms as auth_forms from mediagoblin.auth import forms as auth_forms

View File

@ -25,7 +25,7 @@ from mediagoblin.db import migrations
from mediagoblin.db.util import ASCENDING, DESCENDING, ObjectId from mediagoblin.db.util import ASCENDING, DESCENDING, ObjectId
from mediagoblin.util import Pagination from mediagoblin.util import Pagination
from mediagoblin.util import DISPLAY_IMAGE_FETCHING_ORDER from mediagoblin.util import DISPLAY_IMAGE_FETCHING_ORDER
from mediagoblin.tools import url
################### ###################
# Custom validators # Custom validators
@ -242,7 +242,7 @@ class MediaEntry(Document):
pass pass
def generate_slug(self): def generate_slug(self):
self['slug'] = util.slugify(self['title']) self['slug'] = url.slugify(self['title'])
duplicate = mg_globals.database.media_entries.find_one( duplicate = mg_globals.database.media_entries.find_one(
{'slug': self['slug']}) {'slug': self['slug']})

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from mediagoblin.util import lazy_pass_to_ugettext as _ from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
class BaseProcessingFail(Exception): class BaseProcessingFail(Exception):
""" """

View File

@ -18,7 +18,7 @@
import wtforms import wtforms
from mediagoblin.util import tag_length_validator from mediagoblin.util import tag_length_validator
from mediagoblin.util import fake_ugettext_passthrough as _ from mediagoblin.tools.translate import fake_ugettext_passthrough as _
class SubmitStartForm(wtforms.Form): class SubmitStartForm(wtforms.Form):

View File

@ -25,7 +25,7 @@ from mediagoblin.db.util import ObjectId
from mediagoblin.util import ( from mediagoblin.util import (
render_to_response, redirect, cleaned_markdown_conversion, \ render_to_response, redirect, cleaned_markdown_conversion, \
convert_to_tag_list_of_dicts) convert_to_tag_list_of_dicts)
from mediagoblin.util import pass_to_ugettext as _ from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.decorators import require_active_login from mediagoblin.decorators import require_active_login
from mediagoblin.submit import forms as submit_forms, security from mediagoblin.submit import forms as submit_forms, security
from mediagoblin.process_media import process_media, mark_entry_failed from mediagoblin.process_media import process_media, mark_entry_failed

View File

@ -22,7 +22,7 @@ from nose.tools import assert_equal
from mediagoblin.auth import lib as auth_lib from mediagoblin.auth import lib as auth_lib
from mediagoblin.tests.tools import setup_fresh_app from mediagoblin.tests.tools import setup_fresh_app
from mediagoblin import mg_globals from mediagoblin import mg_globals
from mediagoblin import util from mediagoblin.tools import template
######################## ########################
@ -76,16 +76,16 @@ def test_register_views(test_app):
test_app.get('/auth/register/') test_app.get('/auth/register/')
# Make sure it rendered with the appropriate template # Make sure it rendered with the appropriate template
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/auth/register.html') 'mediagoblin/auth/register.html')
# Try to register without providing anything, should error # Try to register without providing anything, should error
# -------------------------------------------------------- # --------------------------------------------------------
util.clear_test_template_context() template.clear_test_template_context()
test_app.post( test_app.post(
'/auth/register/', {}) '/auth/register/', {})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
form = context['register_form'] form = context['register_form']
assert form.username.errors == [u'This field is required.'] assert form.username.errors == [u'This field is required.']
assert form.password.errors == [u'This field is required.'] assert form.password.errors == [u'This field is required.']
@ -96,14 +96,14 @@ def test_register_views(test_app):
# -------------------------------------------------------- # --------------------------------------------------------
## too short ## too short
util.clear_test_template_context() template.clear_test_template_context()
test_app.post( test_app.post(
'/auth/register/', { '/auth/register/', {
'username': 'l', 'username': 'l',
'password': 'o', 'password': 'o',
'confirm_password': 'o', 'confirm_password': 'o',
'email': 'l'}) 'email': 'l'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
form = context['register_form'] form = context['register_form']
assert form.username.errors == [ assert form.username.errors == [
@ -112,12 +112,12 @@ def test_register_views(test_app):
u'Field must be between 6 and 30 characters long.'] u'Field must be between 6 and 30 characters long.']
## bad form ## bad form
util.clear_test_template_context() template.clear_test_template_context()
test_app.post( test_app.post(
'/auth/register/', { '/auth/register/', {
'username': '@_@', 'username': '@_@',
'email': 'lollerskates'}) 'email': 'lollerskates'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
form = context['register_form'] form = context['register_form']
assert form.username.errors == [ assert form.username.errors == [
@ -126,12 +126,12 @@ def test_register_views(test_app):
u'Invalid email address.'] u'Invalid email address.']
## mismatching passwords ## mismatching passwords
util.clear_test_template_context() template.clear_test_template_context()
test_app.post( test_app.post(
'/auth/register/', { '/auth/register/', {
'password': 'herpderp', 'password': 'herpderp',
'confirm_password': 'derpherp'}) 'confirm_password': 'derpherp'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
form = context['register_form'] form = context['register_form']
assert form.password.errors == [ assert form.password.errors == [
@ -142,7 +142,7 @@ def test_register_views(test_app):
# Successful register # Successful register
# ------------------- # -------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/register/', { '/auth/register/', {
'username': 'happygirl', 'username': 'happygirl',
@ -155,7 +155,7 @@ def test_register_views(test_app):
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/u/happygirl/') '/u/happygirl/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/user_pages/user.html') 'mediagoblin/user_pages/user.html')
## Make sure user is in place ## Make sure user is in place
@ -166,15 +166,15 @@ def test_register_views(test_app):
assert new_user['email_verified'] == False assert new_user['email_verified'] == False
## Make sure user is logged in ## Make sure user is logged in
request = util.TEMPLATE_TEST_CONTEXT[ request = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html']['request'] 'mediagoblin/user_pages/user.html']['request']
assert request.session['user_id'] == unicode(new_user['_id']) assert request.session['user_id'] == unicode(new_user['_id'])
## Make sure we get email confirmation, and try verifying ## Make sure we get email confirmation, and try verifying
assert len(util.EMAIL_TEST_INBOX) == 1 assert len(template.EMAIL_TEST_INBOX) == 1
message = util.EMAIL_TEST_INBOX.pop() message = template.EMAIL_TEST_INBOX.pop()
assert message['To'] == 'happygrrl@example.org' assert message['To'] == 'happygrrl@example.org'
email_context = util.TEMPLATE_TEST_CONTEXT[ email_context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/auth/verification_email.txt'] 'mediagoblin/auth/verification_email.txt']
assert email_context['verification_url'] in message.get_payload(decode=True) assert email_context['verification_url'] in message.get_payload(decode=True)
@ -190,12 +190,12 @@ def test_register_views(test_app):
new_user['verification_key']] new_user['verification_key']]
## Try verifying with bs verification key, shouldn't work ## Try verifying with bs verification key, shouldn't work
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.get( response = test_app.get(
"/auth/verify_email/?userid=%s&token=total_bs" % unicode( "/auth/verify_email/?userid=%s&token=total_bs" % unicode(
new_user['_id'])) new_user['_id']))
response.follow() response.follow()
context = util.TEMPLATE_TEST_CONTEXT[ context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html'] 'mediagoblin/user_pages/user.html']
# assert context['verification_successful'] == True # assert context['verification_successful'] == True
# TODO: Would be good to test messages here when we can do so... # TODO: Would be good to test messages here when we can do so...
@ -206,10 +206,10 @@ def test_register_views(test_app):
assert new_user['email_verified'] == False assert new_user['email_verified'] == False
## Verify the email activation works ## Verify the email activation works
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.get("%s?%s" % (path, get_params)) response = test_app.get("%s?%s" % (path, get_params))
response.follow() response.follow()
context = util.TEMPLATE_TEST_CONTEXT[ context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html'] 'mediagoblin/user_pages/user.html']
# assert context['verification_successful'] == True # assert context['verification_successful'] == True
# TODO: Would be good to test messages here when we can do so... # TODO: Would be good to test messages here when we can do so...
@ -222,7 +222,7 @@ def test_register_views(test_app):
# Uniqueness checks # Uniqueness checks
# ----------------- # -----------------
## We shouldn't be able to register with that user twice ## We shouldn't be able to register with that user twice
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/register/', { '/auth/register/', {
'username': 'happygirl', 'username': 'happygirl',
@ -230,7 +230,7 @@ def test_register_views(test_app):
'confirm_password': 'iamsohappy2', 'confirm_password': 'iamsohappy2',
'email': 'happygrrl2@example.org'}) 'email': 'happygrrl2@example.org'})
context = util.TEMPLATE_TEST_CONTEXT[ context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/auth/register.html'] 'mediagoblin/auth/register.html']
form = context['register_form'] form = context['register_form']
assert form.username.errors == [ assert form.username.errors == [
@ -240,7 +240,7 @@ def test_register_views(test_app):
### Oops, forgot the password ### Oops, forgot the password
# ------------------- # -------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/forgot_password/', '/auth/forgot_password/',
{'username': 'happygirl'}) {'username': 'happygirl'})
@ -250,14 +250,14 @@ def test_register_views(test_app):
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/auth/forgot_password/email_sent/') '/auth/forgot_password/email_sent/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/auth/fp_email_sent.html') 'mediagoblin/auth/fp_email_sent.html')
## Make sure link to change password is sent by email ## Make sure link to change password is sent by email
assert len(util.EMAIL_TEST_INBOX) == 1 assert len(template.EMAIL_TEST_INBOX) == 1
message = util.EMAIL_TEST_INBOX.pop() message = template.EMAIL_TEST_INBOX.pop()
assert message['To'] == 'happygrrl@example.org' assert message['To'] == 'happygrrl@example.org'
email_context = util.TEMPLATE_TEST_CONTEXT[ email_context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/auth/fp_verification_email.txt'] 'mediagoblin/auth/fp_verification_email.txt']
#TODO - change the name of verification_url to something forgot-password-ish #TODO - change the name of verification_url to something forgot-password-ish
assert email_context['verification_url'] in message.get_payload(decode=True) assert email_context['verification_url'] in message.get_payload(decode=True)
@ -277,14 +277,14 @@ def test_register_views(test_app):
assert (new_user['fp_token_expire'] - datetime.datetime.now()).days == 9 assert (new_user['fp_token_expire'] - datetime.datetime.now()).days == 9
## Try using a bs password-changing verification key, shouldn't work ## Try using a bs password-changing verification key, shouldn't work
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.get( response = test_app.get(
"/auth/forgot_password/verify/?userid=%s&token=total_bs" % unicode( "/auth/forgot_password/verify/?userid=%s&token=total_bs" % unicode(
new_user['_id']), status=400) new_user['_id']), status=400)
assert response.status == '400 Bad Request' assert response.status == '400 Bad Request'
## Try using an expired token to change password, shouldn't work ## Try using an expired token to change password, shouldn't work
util.clear_test_template_context() template.clear_test_template_context()
real_token_expiration = new_user['fp_token_expire'] real_token_expiration = new_user['fp_token_expire']
new_user['fp_token_expire'] = datetime.datetime.now() new_user['fp_token_expire'] = datetime.datetime.now()
new_user.save() new_user.save()
@ -294,12 +294,12 @@ def test_register_views(test_app):
new_user.save() new_user.save()
## Verify step 1 of password-change works -- can see form to change password ## Verify step 1 of password-change works -- can see form to change password
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.get("%s?%s" % (path, get_params)) response = test_app.get("%s?%s" % (path, get_params))
assert util.TEMPLATE_TEST_CONTEXT.has_key('mediagoblin/auth/change_fp.html') assert template.TEMPLATE_TEST_CONTEXT.has_key('mediagoblin/auth/change_fp.html')
## Verify step 2.1 of password-change works -- report success to user ## Verify step 2.1 of password-change works -- report success to user
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/forgot_password/verify/', { '/auth/forgot_password/verify/', {
'userid': parsed_get_params['userid'], 'userid': parsed_get_params['userid'],
@ -307,11 +307,11 @@ def test_register_views(test_app):
'confirm_password': 'iamveryveryhappy', 'confirm_password': 'iamveryveryhappy',
'token': parsed_get_params['token']}) 'token': parsed_get_params['token']})
response.follow() response.follow()
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/auth/fp_changed_success.html') 'mediagoblin/auth/fp_changed_success.html')
## Verify step 2.2 of password-change works -- login w/ new password success ## Verify step 2.2 of password-change works -- login w/ new password success
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'username': u'happygirl', 'username': u'happygirl',
@ -322,7 +322,7 @@ def test_register_views(test_app):
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/') '/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/root.html') 'mediagoblin/root.html')
@ -341,61 +341,61 @@ def test_authentication_views(test_app):
# Get login # Get login
# --------- # ---------
test_app.get('/auth/login/') test_app.get('/auth/login/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/auth/login.html') 'mediagoblin/auth/login.html')
# Failed login - blank form # Failed login - blank form
# ------------------------- # -------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post('/auth/login/') response = test_app.post('/auth/login/')
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
form = context['login_form'] form = context['login_form']
assert form.username.errors == [u'This field is required.'] assert form.username.errors == [u'This field is required.']
assert form.password.errors == [u'This field is required.'] assert form.password.errors == [u'This field is required.']
# Failed login - blank user # Failed login - blank user
# ------------------------- # -------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'password': u'toast'}) 'password': u'toast'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
form = context['login_form'] form = context['login_form']
assert form.username.errors == [u'This field is required.'] assert form.username.errors == [u'This field is required.']
# Failed login - blank password # Failed login - blank password
# ----------------------------- # -----------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'username': u'chris'}) 'username': u'chris'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
form = context['login_form'] form = context['login_form']
assert form.password.errors == [u'This field is required.'] assert form.password.errors == [u'This field is required.']
# Failed login - bad user # Failed login - bad user
# ----------------------- # -----------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'username': u'steve', 'username': u'steve',
'password': 'toast'}) 'password': 'toast'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
assert context['login_failed'] assert context['login_failed']
# Failed login - bad password # Failed login - bad password
# --------------------------- # ---------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'username': u'chris', 'username': u'chris',
'password': 'jam'}) 'password': 'jam'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
assert context['login_failed'] assert context['login_failed']
# Successful login # Successful login
# ---------------- # ----------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'username': u'chris', 'username': u'chris',
@ -406,17 +406,17 @@ def test_authentication_views(test_app):
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/') '/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/root.html') 'mediagoblin/root.html')
# Make sure user is in the session # Make sure user is in the session
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session session = context['request'].session
assert session['user_id'] == unicode(test_user['_id']) assert session['user_id'] == unicode(test_user['_id'])
# Successful logout # Successful logout
# ----------------- # -----------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.get('/auth/logout/') response = test_app.get('/auth/logout/')
# Should be redirected to index page # Should be redirected to index page
@ -424,17 +424,17 @@ def test_authentication_views(test_app):
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/') '/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/root.html') 'mediagoblin/root.html')
# Make sure the user is not in the session # Make sure the user is not in the session
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session session = context['request'].session
assert session.has_key('user_id') == False assert session.has_key('user_id') == False
# User is redirected to custom URL if POST['next'] is set # User is redirected to custom URL if POST['next'] is set
# ------------------------------------------------------- # -------------------------------------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = test_app.post( response = test_app.post(
'/auth/login/', { '/auth/login/', {
'username': u'chris', 'username': u'chris',

View File

@ -16,7 +16,7 @@
from mediagoblin.messages import fetch_messages, add_message from mediagoblin.messages import fetch_messages, add_message
from mediagoblin.tests.tools import setup_fresh_app from mediagoblin.tests.tools import setup_fresh_app
from mediagoblin import util from mediagoblin.tools import template
@setup_fresh_app @setup_fresh_app
@ -28,7 +28,7 @@ def test_messages(test_app):
""" """
# Aquire a request object # Aquire a request object
test_app.get('/') test_app.get('/')
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
request = context['request'] request = context['request']
# The message queue should be empty # The message queue should be empty

View File

@ -22,7 +22,7 @@ from nose.tools import assert_equal, assert_true, assert_false
from mediagoblin.auth import lib as auth_lib from mediagoblin.auth import lib as auth_lib
from mediagoblin.tests.tools import setup_fresh_app, get_test_app from mediagoblin.tests.tools import setup_fresh_app, get_test_app
from mediagoblin import mg_globals from mediagoblin import mg_globals
from mediagoblin import util from mediagoblin.tools import template, common
GOOD_JPG = pkg_resources.resource_filename( GOOD_JPG = pkg_resources.resource_filename(
'mediagoblin.tests', 'test_submission/good.jpg') 'mediagoblin.tests', 'test_submission/good.jpg')
@ -63,20 +63,20 @@ class TestSubmission:
def test_missing_fields(self): def test_missing_fields(self):
# Test blank form # Test blank form
# --------------- # ---------------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', {}) '/submit/', {})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
form = context['submit_form'] form = context['submit_form']
assert form.file.errors == [u'You must provide a file.'] assert form.file.errors == [u'You must provide a file.']
# Test blank file # Test blank file
# --------------- # ---------------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'test title'}) 'title': 'test title'})
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
form = context['submit_form'] form = context['submit_form']
assert form.file.errors == [u'You must provide a file.'] assert form.file.errors == [u'You must provide a file.']
@ -84,7 +84,7 @@ class TestSubmission:
def test_normal_uploads(self): def test_normal_uploads(self):
# Test JPG # Test JPG
# -------- # --------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Normal upload 1' 'title': 'Normal upload 1'
@ -96,12 +96,12 @@ class TestSubmission:
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/u/chris/') '/u/chris/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/user_pages/user.html') 'mediagoblin/user_pages/user.html')
# Test PNG # Test PNG
# -------- # --------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Normal upload 2' 'title': 'Normal upload 2'
@ -112,13 +112,13 @@ class TestSubmission:
assert_equal( assert_equal(
urlparse.urlsplit(response.location)[2], urlparse.urlsplit(response.location)[2],
'/u/chris/') '/u/chris/')
assert util.TEMPLATE_TEST_CONTEXT.has_key( assert template.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/user_pages/user.html') 'mediagoblin/user_pages/user.html')
def test_tags(self): def test_tags(self):
# Good tag string # Good tag string
# -------- # --------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Balanced Goblin', 'title': 'Balanced Goblin',
@ -128,7 +128,7 @@ class TestSubmission:
# New media entry with correct tags should be created # New media entry with correct tags should be created
response.follow() response.follow()
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html']
request = context['request'] request = context['request']
media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0]
assert_equal(media['tags'], assert_equal(media['tags'],
@ -137,7 +137,7 @@ class TestSubmission:
# Test tags that are too long # Test tags that are too long
# --------------- # ---------------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Balanced Goblin', 'title': 'Balanced Goblin',
@ -146,14 +146,14 @@ class TestSubmission:
'file', GOOD_JPG)]) 'file', GOOD_JPG)])
# Too long error should be raised # Too long error should be raised
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
form = context['submit_form'] form = context['submit_form']
assert form.tags.errors == [ assert form.tags.errors == [
u'Tags must be shorter than 50 characters. Tags that are too long'\ u'Tags must be shorter than 50 characters. Tags that are too long'\
': ffffffffffffffffffffffffffuuuuuuuuuuuuuuuuuuuuuuuuuu'] ': ffffffffffffffffffffffffffuuuuuuuuuuuuuuuuuuuuuuuuuu']
def test_delete(self): def test_delete(self):
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Balanced Goblin', 'title': 'Balanced Goblin',
@ -163,7 +163,7 @@ class TestSubmission:
# Post image # Post image
response.follow() response.follow()
request = util.TEMPLATE_TEST_CONTEXT[ request = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html']['request'] 'mediagoblin/user_pages/user.html']['request']
media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0]
@ -183,7 +183,7 @@ class TestSubmission:
response.follow() response.follow()
request = util.TEMPLATE_TEST_CONTEXT[ request = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html']['request'] 'mediagoblin/user_pages/user.html']['request']
media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0]
@ -202,7 +202,7 @@ class TestSubmission:
response.follow() response.follow()
request = util.TEMPLATE_TEST_CONTEXT[ request = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html']['request'] 'mediagoblin/user_pages/user.html']['request']
# Does media entry still exist? # Does media entry still exist?
@ -213,14 +213,14 @@ class TestSubmission:
def test_malicious_uploads(self): def test_malicious_uploads(self):
# Test non-suppoerted file with non-supported extension # Test non-suppoerted file with non-supported extension
# ----------------------------------------------------- # -----------------------------------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Malicious Upload 1' 'title': 'Malicious Upload 1'
}, upload_files=[( }, upload_files=[(
'file', EVIL_FILE)]) 'file', EVIL_FILE)])
context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
form = context['submit_form'] form = context['submit_form']
assert form.file.errors == ['The file doesn\'t seem to be an image!'] assert form.file.errors == ['The file doesn\'t seem to be an image!']
@ -230,7 +230,7 @@ class TestSubmission:
# Test non-supported file with .jpg extension # Test non-supported file with .jpg extension
# ------------------------------------------- # -------------------------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Malicious Upload 2' 'title': 'Malicious Upload 2'
@ -250,7 +250,7 @@ class TestSubmission:
# Test non-supported file with .png extension # Test non-supported file with .png extension
# ------------------------------------------- # -------------------------------------------
util.clear_test_template_context() template.clear_test_template_context()
response = self.test_app.post( response = self.test_app.post(
'/submit/', { '/submit/', {
'title': 'Malicious Upload 3' 'title': 'Malicious Upload 3'

View File

@ -17,7 +17,7 @@
import email import email
from mediagoblin import util from mediagoblin import util
from mediagoblin.tools import url, translate
util._activate_testing() util._activate_testing()
@ -71,38 +71,38 @@ I hope you like unit tests JUST AS MUCH AS I DO!"""
I hope you like unit tests JUST AS MUCH AS I DO!""" I hope you like unit tests JUST AS MUCH AS I DO!"""
def test_slugify(): def test_slugify():
assert util.slugify('a walk in the park') == 'a-walk-in-the-park' assert url.slugify('a walk in the park') == 'a-walk-in-the-park'
assert util.slugify('A Walk in the Park') == 'a-walk-in-the-park' assert url.slugify('A Walk in the Park') == 'a-walk-in-the-park'
assert util.slugify('a walk in the park') == 'a-walk-in-the-park' assert url.slugify('a walk in the park') == 'a-walk-in-the-park'
assert util.slugify('a walk in-the-park') == 'a-walk-in-the-park' assert url.slugify('a walk in-the-park') == 'a-walk-in-the-park'
assert util.slugify('a w@lk in the park?') == 'a-w-lk-in-the-park' assert url.slugify('a w@lk in the park?') == 'a-w-lk-in-the-park'
assert util.slugify(u'a walk in the par\u0107') == 'a-walk-in-the-parc' assert url.slugify(u'a walk in the par\u0107') == 'a-walk-in-the-parc'
assert util.slugify(u'\u00E0\u0042\u00E7\u010F\u00EB\u0066') == 'abcdef' assert url.slugify(u'\u00E0\u0042\u00E7\u010F\u00EB\u0066') == 'abcdef'
def test_locale_to_lower_upper(): def test_locale_to_lower_upper():
""" """
Test cc.i18n.util.locale_to_lower_upper() Test cc.i18n.util.locale_to_lower_upper()
""" """
assert util.locale_to_lower_upper('en') == 'en' assert translate.locale_to_lower_upper('en') == 'en'
assert util.locale_to_lower_upper('en_US') == 'en_US' assert translate.locale_to_lower_upper('en_US') == 'en_US'
assert util.locale_to_lower_upper('en-us') == 'en_US' assert translate.locale_to_lower_upper('en-us') == 'en_US'
# crazy renditions. Useful? # crazy renditions. Useful?
assert util.locale_to_lower_upper('en-US') == 'en_US' assert translate.locale_to_lower_upper('en-US') == 'en_US'
assert util.locale_to_lower_upper('en_us') == 'en_US' assert translate.locale_to_lower_upper('en_us') == 'en_US'
def test_locale_to_lower_lower(): def test_locale_to_lower_lower():
""" """
Test cc.i18n.util.locale_to_lower_lower() Test cc.i18n.util.locale_to_lower_lower()
""" """
assert util.locale_to_lower_lower('en') == 'en' assert translate.locale_to_lower_lower('en') == 'en'
assert util.locale_to_lower_lower('en_US') == 'en-us' assert translate.locale_to_lower_lower('en_US') == 'en-us'
assert util.locale_to_lower_lower('en-us') == 'en-us' assert translate.locale_to_lower_lower('en-us') == 'en-us'
# crazy renditions. Useful? # crazy renditions. Useful?
assert util.locale_to_lower_lower('en-US') == 'en-us' assert translate.locale_to_lower_lower('en-US') == 'en-us'
assert util.locale_to_lower_lower('en_us') == 'en-us' assert translate.locale_to_lower_lower('en_us') == 'en-us'
def test_html_cleaner(): def test_html_cleaner():

View File

View File

@ -0,0 +1,18 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 Free Software Foundation, Inc
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
global TESTS_ENABLED
TESTS_ENABLED = False

View File

@ -0,0 +1,114 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from math import ceil
import jinja2
from babel.localedata import exists
from babel.support import LazyProxy
from mediagoblin import mg_globals
from mediagoblin import messages
from mediagoblin.tools import common
from mediagoblin.tools.translate import setup_gettext
SETUP_JINJA_ENVS = {}
def get_jinja_env(template_loader, locale):
"""
Set up the Jinja environment,
(In the future we may have another system for providing theming;
for now this is good enough.)
"""
setup_gettext(locale)
# If we have a jinja environment set up with this locale, just
# return that one.
if SETUP_JINJA_ENVS.has_key(locale):
return SETUP_JINJA_ENVS[locale]
template_env = jinja2.Environment(
loader=template_loader, autoescape=True,
extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape'])
template_env.install_gettext_callables(
mg_globals.translations.ugettext,
mg_globals.translations.ungettext)
# All templates will know how to ...
# ... fetch all waiting messages and remove them from the queue
# ... construct a grid of thumbnails or other media
template_env.globals['fetch_messages'] = messages.fetch_messages
template_env.globals['gridify_list'] = gridify_list
template_env.globals['gridify_cursor'] = gridify_cursor
if exists(locale):
SETUP_JINJA_ENVS[locale] = template_env
return template_env
# We'll store context information here when doing unit tests
TEMPLATE_TEST_CONTEXT = {}
def render_template(request, template_path, context):
"""
Render a template with context.
Always inserts the request into the context, so you don't have to.
Also stores the context if we're doing unit tests. Helpful!
"""
template = request.template_env.get_template(
template_path)
context['request'] = request
rendered = template.render(context)
if common.TESTS_ENABLED:
TEMPLATE_TEST_CONTEXT[template_path] = context
return rendered
def clear_test_template_context():
global TEMPLATE_TEST_CONTEXT
TEMPLATE_TEST_CONTEXT = {}
def gridify_list(this_list, num_cols=5):
"""
Generates a list of lists where each sub-list's length depends on
the number of columns in the list
"""
grid = []
# Figure out how many rows we should have
num_rows = int(ceil(float(len(this_list)) / num_cols))
for row_num in range(num_rows):
slice_min = row_num * num_cols
slice_max = (row_num + 1) * num_cols
row = this_list[slice_min:slice_max]
grid.append(row)
return grid
def gridify_cursor(this_cursor, num_cols=5):
"""
Generates a list of lists where each sub-list's length depends on
the number of columns in the list
"""
return gridify_list(list(this_cursor), num_cols)

View File

@ -0,0 +1,167 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import gettext
import pkg_resources
from babel.localedata import exists
from babel.support import LazyProxy
from mediagoblin import mg_globals
###################
# Translation tools
###################
TRANSLATIONS_PATH = pkg_resources.resource_filename(
'mediagoblin', 'i18n')
def locale_to_lower_upper(locale):
"""
Take a locale, regardless of style, and format it like "en-us"
"""
if '-' in locale:
lang, country = locale.split('-', 1)
return '%s_%s' % (lang.lower(), country.upper())
elif '_' in locale:
lang, country = locale.split('_', 1)
return '%s_%s' % (lang.lower(), country.upper())
else:
return locale.lower()
def locale_to_lower_lower(locale):
"""
Take a locale, regardless of style, and format it like "en_US"
"""
if '_' in locale:
lang, country = locale.split('_', 1)
return '%s-%s' % (lang.lower(), country.lower())
else:
return locale.lower()
def get_locale_from_request(request):
"""
Figure out what target language is most appropriate based on the
request
"""
request_form = request.GET or request.POST
if request_form.has_key('lang'):
return locale_to_lower_upper(request_form['lang'])
accept_lang_matches = request.accept_language.best_matches()
# Your routing can explicitly specify a target language
matchdict = request.matchdict or {}
if matchdict.has_key('locale'):
target_lang = matchdict['locale']
elif request.session.has_key('target_lang'):
target_lang = request.session['target_lang']
# Pull the first acceptable language
elif accept_lang_matches:
target_lang = accept_lang_matches[0]
# Fall back to English
else:
target_lang = 'en'
return locale_to_lower_upper(target_lang)
SETUP_GETTEXTS = {}
def setup_gettext(locale):
"""
Setup the gettext instance based on this locale
"""
# Later on when we have plugins we may want to enable the
# multi-translations system they have so we can handle plugin
# translations too
# TODO: fallback nicely on translations from pt_PT to pt if not
# available, etc.
if SETUP_GETTEXTS.has_key(locale):
this_gettext = SETUP_GETTEXTS[locale]
else:
this_gettext = gettext.translation(
'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True)
if exists(locale):
SETUP_GETTEXTS[locale] = this_gettext
mg_globals.setup_globals(
translations=this_gettext)
# Force en to be setup before anything else so that
# mg_globals.translations is never None
setup_gettext('en')
def pass_to_ugettext(*args, **kwargs):
"""
Pass a translation on to the appropriate ugettext method.
The reason we can't have a global ugettext method is because
mg_globals gets swapped out by the application per-request.
"""
return mg_globals.translations.ugettext(
*args, **kwargs)
def lazy_pass_to_ugettext(*args, **kwargs):
"""
Lazily pass to ugettext.
This is useful if you have to define a translation on a module
level but you need it to not translate until the time that it's
used as a string.
"""
return LazyProxy(pass_to_ugettext, *args, **kwargs)
def pass_to_ngettext(*args, **kwargs):
"""
Pass a translation on to the appropriate ngettext method.
The reason we can't have a global ngettext method is because
mg_globals gets swapped out by the application per-request.
"""
return mg_globals.translations.ngettext(
*args, **kwargs)
def lazy_pass_to_ngettext(*args, **kwargs):
"""
Lazily pass to ngettext.
This is useful if you have to define a translation on a module
level but you need it to not translate until the time that it's
used as a string.
"""
return LazyProxy(pass_to_ngettext, *args, **kwargs)
def fake_ugettext_passthrough(string):
"""
Fake a ugettext call for extraction's sake ;)
In wtforms there's a separate way to define a method to translate
things... so we just need to mark up the text so that it can be
extracted, not so that it's actually run through gettext.
"""
return string

31
mediagoblin/tools/url.py Normal file
View File

@ -0,0 +1,31 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 Free Software Foundation, Inc
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import translitcodec
_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
def slugify(text, delim=u'-'):
"""
Generates an ASCII-only slug. Taken from http://flask.pocoo.org/snippets/5/
"""
result = []
for word in _punct_re.split(text.lower()):
word = word.encode('translit/long')
if word:
result.append(word)
return unicode(delim.join(result))

View File

@ -16,7 +16,7 @@
import wtforms import wtforms
from mediagoblin.util import fake_ugettext_passthrough as _ from mediagoblin.tools.translate import fake_ugettext_passthrough as _
class MediaCommentForm(wtforms.Form): class MediaCommentForm(wtforms.Form):

View File

@ -21,7 +21,7 @@ from mediagoblin.db.util import DESCENDING, ObjectId
from mediagoblin.util import ( from mediagoblin.util import (
Pagination, render_to_response, redirect, cleaned_markdown_conversion, Pagination, render_to_response, redirect, cleaned_markdown_conversion,
render_404, delete_media_files) render_404, delete_media_files)
from mediagoblin.util import pass_to_ugettext as _ from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.user_pages import forms as user_forms from mediagoblin.user_pages import forms as user_forms
from mediagoblin.decorators import (uses_pagination, get_user_media_entry, from mediagoblin.decorators import (uses_pagination, get_user_media_entry,

View File

@ -17,41 +17,42 @@
from __future__ import division from __future__ import division
from email.MIMEText import MIMEText from email.MIMEText import MIMEText
import gettext #import gettext
import pkg_resources #import pkg_resources
import smtplib import smtplib
import sys import sys
import re #import re
#import translitcodec
import urllib import urllib
from math import ceil, floor from math import ceil, floor
import copy import copy
import wtforms import wtforms
from babel.localedata import exists #from babel.localedata import exists
from babel.support import LazyProxy #from babel.support import LazyProxy
import jinja2 #import jinja2
import translitcodec
from webob import Response, exc from webob import Response, exc
from lxml.html.clean import Cleaner from lxml.html.clean import Cleaner
import markdown import markdown
from wtforms.form import Form from wtforms.form import Form
from mediagoblin import mg_globals from mediagoblin import mg_globals
from mediagoblin import messages #from mediagoblin import messages
from mediagoblin.db.util import ObjectId from mediagoblin.db.util import ObjectId
from mediagoblin.tools import url
from mediagoblin.tools import common
from mediagoblin.tools.template import TEMPLATE_TEST_CONTEXT, render_template
from itertools import izip, count from itertools import izip, count
DISPLAY_IMAGE_FETCHING_ORDER = [u'medium', u'original', u'thumb'] DISPLAY_IMAGE_FETCHING_ORDER = [u'medium', u'original', u'thumb']
TESTS_ENABLED = False
def _activate_testing(): def _activate_testing():
""" """
Call this to activate testing in util.py Call this to activate testing in util.py
""" """
global TESTS_ENABLED
TESTS_ENABLED = True
common.TESTS_ENABLED = True
def clear_test_buckets(): def clear_test_buckets():
""" """
@ -73,64 +74,64 @@ def clear_test_buckets():
clear_test_template_context() clear_test_template_context()
SETUP_JINJA_ENVS = {} # SETUP_JINJA_ENVS = {}
def get_jinja_env(template_loader, locale): # def get_jinja_env(template_loader, locale):
""" # """
Set up the Jinja environment, # Set up the Jinja environment,
(In the future we may have another system for providing theming; # (In the future we may have another system for providing theming;
for now this is good enough.) # for now this is good enough.)
""" # """
setup_gettext(locale) # setup_gettext(locale)
# If we have a jinja environment set up with this locale, just # # If we have a jinja environment set up with this locale, just
# return that one. # # return that one.
if SETUP_JINJA_ENVS.has_key(locale): # if SETUP_JINJA_ENVS.has_key(locale):
return SETUP_JINJA_ENVS[locale] # return SETUP_JINJA_ENVS[locale]
template_env = jinja2.Environment( # template_env = jinja2.Environment(
loader=template_loader, autoescape=True, # loader=template_loader, autoescape=True,
extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape']) # extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape'])
template_env.install_gettext_callables( # template_env.install_gettext_callables(
mg_globals.translations.ugettext, # mg_globals.translations.ugettext,
mg_globals.translations.ungettext) # mg_globals.translations.ungettext)
# All templates will know how to ... # # All templates will know how to ...
# ... fetch all waiting messages and remove them from the queue # # ... fetch all waiting messages and remove them from the queue
# ... construct a grid of thumbnails or other media # # ... construct a grid of thumbnails or other media
template_env.globals['fetch_messages'] = messages.fetch_messages # template_env.globals['fetch_messages'] = messages.fetch_messages
template_env.globals['gridify_list'] = gridify_list # template_env.globals['gridify_list'] = gridify_list
template_env.globals['gridify_cursor'] = gridify_cursor # template_env.globals['gridify_cursor'] = gridify_cursor
if exists(locale): # if exists(locale):
SETUP_JINJA_ENVS[locale] = template_env # SETUP_JINJA_ENVS[locale] = template_env
return template_env # return template_env
# We'll store context information here when doing unit tests # # We'll store context information here when doing unit tests
TEMPLATE_TEST_CONTEXT = {} # TEMPLATE_TEST_CONTEXT = {}
def render_template(request, template_path, context): # def render_template(request, template_path, context):
""" # """
Render a template with context. # Render a template with context.
Always inserts the request into the context, so you don't have to. # Always inserts the request into the context, so you don't have to.
Also stores the context if we're doing unit tests. Helpful! # Also stores the context if we're doing unit tests. Helpful!
""" # """
template = request.template_env.get_template( # template = request.template_env.get_template(
template_path) # template_path)
context['request'] = request # context['request'] = request
rendered = template.render(context) # rendered = template.render(context)
if TESTS_ENABLED: # if TESTS_ENABLED:
TEMPLATE_TEST_CONTEXT[template_path] = context # TEMPLATE_TEST_CONTEXT[template_path] = context
return rendered # return rendered
def clear_test_template_context(): def clear_test_template_context():
@ -195,18 +196,18 @@ def import_component(import_string):
func = getattr(module, func_name) func = getattr(module, func_name)
return func return func
_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+') # _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
def slugify(text, delim=u'-'): # def slugify(text, delim=u'-'):
""" # """
Generates an ASCII-only slug. Taken from http://flask.pocoo.org/snippets/5/ # Generates an ASCII-only slug. Taken from http://flask.pocoo.org/snippets/5/
""" # """
result = [] # result = []
for word in _punct_re.split(text.lower()): # for word in _punct_re.split(text.lower()):
word = word.encode('translit/long') # word = word.encode('translit/long')
if word: # if word:
result.append(word) # result.append(word)
return unicode(delim.join(result)) # return unicode(delim.join(result))
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### Special email test stuff begins HERE ### Special email test stuff begins HERE
@ -274,7 +275,7 @@ def send_email(from_addr, to_addrs, subject, message_body):
- subject: subject of the email - subject: subject of the email
- message_body: email body text - message_body: email body text
""" """
if TESTS_ENABLED or mg_globals.app_config['email_debug_mode']: if common.TESTS_ENABLED or mg_globals.app_config['email_debug_mode']:
mhost = FakeMhost() mhost = FakeMhost()
elif not mg_globals.app_config['email_debug_mode']: elif not mg_globals.app_config['email_debug_mode']:
mhost = smtplib.SMTP( mhost = smtplib.SMTP(
@ -296,7 +297,7 @@ def send_email(from_addr, to_addrs, subject, message_body):
message['From'] = from_addr message['From'] = from_addr
message['To'] = ', '.join(to_addrs) message['To'] = ', '.join(to_addrs)
if TESTS_ENABLED: if common.TESTS_ENABLED:
EMAIL_TEST_INBOX.append(message) EMAIL_TEST_INBOX.append(message)
if mg_globals.app_config['email_debug_mode']: if mg_globals.app_config['email_debug_mode']:
@ -310,67 +311,67 @@ def send_email(from_addr, to_addrs, subject, message_body):
return mhost.sendmail(from_addr, to_addrs, message.as_string()) return mhost.sendmail(from_addr, to_addrs, message.as_string())
################### # ###################
# Translation tools # # Translation tools
################### # ###################
TRANSLATIONS_PATH = pkg_resources.resource_filename( # TRANSLATIONS_PATH = pkg_resources.resource_filename(
'mediagoblin', 'i18n') # 'mediagoblin', 'i18n')
def locale_to_lower_upper(locale): # def locale_to_lower_upper(locale):
""" # """
Take a locale, regardless of style, and format it like "en-us" # Take a locale, regardless of style, and format it like "en-us"
""" # """
if '-' in locale: # if '-' in locale:
lang, country = locale.split('-', 1) # lang, country = locale.split('-', 1)
return '%s_%s' % (lang.lower(), country.upper()) # return '%s_%s' % (lang.lower(), country.upper())
elif '_' in locale: # elif '_' in locale:
lang, country = locale.split('_', 1) # lang, country = locale.split('_', 1)
return '%s_%s' % (lang.lower(), country.upper()) # return '%s_%s' % (lang.lower(), country.upper())
else: # else:
return locale.lower() # return locale.lower()
def locale_to_lower_lower(locale): # def locale_to_lower_lower(locale):
""" # """
Take a locale, regardless of style, and format it like "en_US" # Take a locale, regardless of style, and format it like "en_US"
""" # """
if '_' in locale: # if '_' in locale:
lang, country = locale.split('_', 1) # lang, country = locale.split('_', 1)
return '%s-%s' % (lang.lower(), country.lower()) # return '%s-%s' % (lang.lower(), country.lower())
else: # else:
return locale.lower() # return locale.lower()
def get_locale_from_request(request): # def get_locale_from_request(request):
""" # """
Figure out what target language is most appropriate based on the # Figure out what target language is most appropriate based on the
request # request
""" # """
request_form = request.GET or request.POST # request_form = request.GET or request.POST
if request_form.has_key('lang'): # if request_form.has_key('lang'):
return locale_to_lower_upper(request_form['lang']) # return locale_to_lower_upper(request_form['lang'])
accept_lang_matches = request.accept_language.best_matches() # accept_lang_matches = request.accept_language.best_matches()
# Your routing can explicitly specify a target language # # Your routing can explicitly specify a target language
matchdict = request.matchdict or {} # matchdict = request.matchdict or {}
if matchdict.has_key('locale'): # if matchdict.has_key('locale'):
target_lang = matchdict['locale'] # target_lang = matchdict['locale']
elif request.session.has_key('target_lang'): # elif request.session.has_key('target_lang'):
target_lang = request.session['target_lang'] # target_lang = request.session['target_lang']
# Pull the first acceptable language # # Pull the first acceptable language
elif accept_lang_matches: # elif accept_lang_matches:
target_lang = accept_lang_matches[0] # target_lang = accept_lang_matches[0]
# Fall back to English # # Fall back to English
else: # else:
target_lang = 'en' # target_lang = 'en'
return locale_to_lower_upper(target_lang) # return locale_to_lower_upper(target_lang)
# A super strict version of the lxml.html cleaner class # A super strict version of the lxml.html cleaner class
@ -424,7 +425,7 @@ def convert_to_tag_list_of_dicts(tag_string):
if tag.strip() and tag.strip() not in [t['name'] for t in taglist]: if tag.strip() and tag.strip() not in [t['name'] for t in taglist]:
taglist.append({'name': tag.strip(), taglist.append({'name': tag.strip(),
'slug': slugify(tag.strip())}) 'slug': url.slugify(tag.strip())})
return taglist return taglist
@ -472,88 +473,88 @@ def cleaned_markdown_conversion(text):
return clean_html(MARKDOWN_INSTANCE.convert(text)) return clean_html(MARKDOWN_INSTANCE.convert(text))
SETUP_GETTEXTS = {} # SETUP_GETTEXTS = {}
def setup_gettext(locale): # def setup_gettext(locale):
""" # """
Setup the gettext instance based on this locale # Setup the gettext instance based on this locale
""" # """
# Later on when we have plugins we may want to enable the # # Later on when we have plugins we may want to enable the
# multi-translations system they have so we can handle plugin # # multi-translations system they have so we can handle plugin
# translations too # # translations too
# TODO: fallback nicely on translations from pt_PT to pt if not # # TODO: fallback nicely on translations from pt_PT to pt if not
# available, etc. # # available, etc.
if SETUP_GETTEXTS.has_key(locale): # if SETUP_GETTEXTS.has_key(locale):
this_gettext = SETUP_GETTEXTS[locale] # this_gettext = SETUP_GETTEXTS[locale]
else: # else:
this_gettext = gettext.translation( # this_gettext = gettext.translation(
'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True) # 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True)
if exists(locale): # if exists(locale):
SETUP_GETTEXTS[locale] = this_gettext # SETUP_GETTEXTS[locale] = this_gettext
mg_globals.setup_globals( # mg_globals.setup_globals(
translations=this_gettext) # translations=this_gettext)
# Force en to be setup before anything else so that # # Force en to be setup before anything else so that
# mg_globals.translations is never None # # mg_globals.translations is never None
setup_gettext('en') # setup_gettext('en')
def pass_to_ugettext(*args, **kwargs): # def pass_to_ugettext(*args, **kwargs):
""" # """
Pass a translation on to the appropriate ugettext method. # Pass a translation on to the appropriate ugettext method.
The reason we can't have a global ugettext method is because # The reason we can't have a global ugettext method is because
mg_globals gets swapped out by the application per-request. # mg_globals gets swapped out by the application per-request.
""" # """
return mg_globals.translations.ugettext( # return mg_globals.translations.ugettext(
*args, **kwargs) # *args, **kwargs)
def lazy_pass_to_ugettext(*args, **kwargs): # def lazy_pass_to_ugettext(*args, **kwargs):
""" # """
Lazily pass to ugettext. # Lazily pass to ugettext.
This is useful if you have to define a translation on a module # This is useful if you have to define a translation on a module
level but you need it to not translate until the time that it's # level but you need it to not translate until the time that it's
used as a string. # used as a string.
""" # """
return LazyProxy(pass_to_ugettext, *args, **kwargs) # return LazyProxy(pass_to_ugettext, *args, **kwargs)
def pass_to_ngettext(*args, **kwargs): # def pass_to_ngettext(*args, **kwargs):
""" # """
Pass a translation on to the appropriate ngettext method. # Pass a translation on to the appropriate ngettext method.
The reason we can't have a global ngettext method is because # The reason we can't have a global ngettext method is because
mg_globals gets swapped out by the application per-request. # mg_globals gets swapped out by the application per-request.
""" # """
return mg_globals.translations.ngettext( # return mg_globals.translations.ngettext(
*args, **kwargs) # *args, **kwargs)
def lazy_pass_to_ngettext(*args, **kwargs): # def lazy_pass_to_ngettext(*args, **kwargs):
""" # """
Lazily pass to ngettext. # Lazily pass to ngettext.
This is useful if you have to define a translation on a module # This is useful if you have to define a translation on a module
level but you need it to not translate until the time that it's # level but you need it to not translate until the time that it's
used as a string. # used as a string.
""" # """
return LazyProxy(pass_to_ngettext, *args, **kwargs) # return LazyProxy(pass_to_ngettext, *args, **kwargs)
def fake_ugettext_passthrough(string): # def fake_ugettext_passthrough(string):
""" # """
Fake a ugettext call for extraction's sake ;) # Fake a ugettext call for extraction's sake ;)
In wtforms there's a separate way to define a method to translate # In wtforms there's a separate way to define a method to translate
things... so we just need to mark up the text so that it can be # things... so we just need to mark up the text so that it can be
extracted, not so that it's actually run through gettext. # extracted, not so that it's actually run through gettext.
""" # """
return string # return string
PAGINATION_DEFAULT_PER_PAGE = 30 PAGINATION_DEFAULT_PER_PAGE = 30
@ -646,33 +647,33 @@ class Pagination(object):
request.path_info, request.GET, page_no) request.path_info, request.GET, page_no)
def gridify_list(this_list, num_cols=5): # def gridify_list(this_list, num_cols=5):
""" # """
Generates a list of lists where each sub-list's length depends on # Generates a list of lists where each sub-list's length depends on
the number of columns in the list # the number of columns in the list
""" # """
grid = [] # grid = []
# Figure out how many rows we should have # # Figure out how many rows we should have
num_rows = int(ceil(float(len(this_list)) / num_cols)) # num_rows = int(ceil(float(len(this_list)) / num_cols))
for row_num in range(num_rows): # for row_num in range(num_rows):
slice_min = row_num * num_cols # slice_min = row_num * num_cols
slice_max = (row_num + 1) * num_cols # slice_max = (row_num + 1) * num_cols
row = this_list[slice_min:slice_max] # row = this_list[slice_min:slice_max]
grid.append(row) # grid.append(row)
return grid # return grid
def gridify_cursor(this_cursor, num_cols=5): # def gridify_cursor(this_cursor, num_cols=5):
""" # """
Generates a list of lists where each sub-list's length depends on # Generates a list of lists where each sub-list's length depends on
the number of columns in the list # the number of columns in the list
""" # """
return gridify_list(list(this_cursor), num_cols) # return gridify_list(list(this_cursor), num_cols)
def render_404(request): def render_404(request):