aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/tools
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/tools')
-rw-r--r--mediagoblin/tools/exif.py3
-rw-r--r--mediagoblin/tools/pagination.py15
-rw-r--r--mediagoblin/tools/request.py12
-rw-r--r--mediagoblin/tools/response.py55
-rw-r--r--mediagoblin/tools/routing.py66
-rw-r--r--mediagoblin/tools/template.py35
-rw-r--r--mediagoblin/tools/translate.py42
-rw-r--r--mediagoblin/tools/url.py15
8 files changed, 162 insertions, 81 deletions
diff --git a/mediagoblin/tools/exif.py b/mediagoblin/tools/exif.py
index 543484c9..4a1afb0f 100644
--- a/mediagoblin/tools/exif.py
+++ b/mediagoblin/tools/exif.py
@@ -73,7 +73,7 @@ def extract_exif(filename):
try:
image = open(filename)
- exif_tags = process_file(image)
+ exif_tags = process_file(image, details=False)
except IOError:
raise BadMediaFail(_('Could not read the image file.'))
@@ -97,7 +97,6 @@ def clean_exif(exif):
for key, value in exif.items():
if not key in disabled_tags:
clean_exif[key] = _ifd_tag_to_dict(value)
-
return clean_exif
diff --git a/mediagoblin/tools/pagination.py b/mediagoblin/tools/pagination.py
index 50e59070..d0f08c94 100644
--- a/mediagoblin/tools/pagination.py
+++ b/mediagoblin/tools/pagination.py
@@ -25,7 +25,7 @@ PAGINATION_DEFAULT_PER_PAGE = 30
class Pagination(object):
"""
- Pagination class for mongodb queries.
+ Pagination class for database queries.
Initialization through __init__(self, cursor, page=1, per_page=2),
get actual data slice through __call__().
@@ -40,8 +40,8 @@ class Pagination(object):
- page: requested page
- per_page: number of objects per page
- cursor: db cursor
- - jump_to_id: ObjectId, sets the page to the page containing the
- object with _id == jump_to_id.
+ - jump_to_id: object id, sets the page to the page containing the
+ object with id == jump_to_id.
"""
self.page = page
self.per_page = per_page
@@ -53,7 +53,7 @@ class Pagination(object):
cursor = copy.copy(self.cursor)
for (doc, increment) in izip(cursor, count(0)):
- if doc._id == jump_to_id:
+ if doc.id == jump_to_id:
self.page = 1 + int(floor(increment / self.per_page))
self.active_id = jump_to_id
@@ -63,8 +63,11 @@ class Pagination(object):
"""
Returns slice of objects for the requested page
"""
- return self.cursor.skip(
- (self.page - 1) * self.per_page).limit(self.per_page)
+ # TODO, return None for out of index so templates can
+ # distinguish between empty galleries and out-of-bound pages???
+ return self.cursor.slice(
+ (self.page - 1) * self.per_page,
+ self.page * self.per_page)
@property
def pages(self):
diff --git a/mediagoblin/tools/request.py b/mediagoblin/tools/request.py
index ae372c92..f7311fac 100644
--- a/mediagoblin/tools/request.py
+++ b/mediagoblin/tools/request.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
-from mediagoblin.db.util import ObjectId, InvalidId
+from mediagoblin.db.models import User
_log = logging.getLogger(__name__)
@@ -29,17 +29,11 @@ def setup_user_in_request(request):
request.user = None
return
- try:
- oid = ObjectId(request.session['user_id'])
- except InvalidId:
- user = None
- else:
- user = request.db.User.find_one({'_id': oid})
+ request.user = User.query.get(request.session['user_id'])
- if not user:
+ if not request.user:
# Something's wrong... this user doesn't exist? Invalidate
# this session.
_log.warn("Killing session for user id %r", request.session['user_id'])
request.session.invalidate()
- request.user = user
diff --git a/mediagoblin/tools/response.py b/mediagoblin/tools/response.py
index 6d14b8b7..80df1f5a 100644
--- a/mediagoblin/tools/response.py
+++ b/mediagoblin/tools/response.py
@@ -14,9 +14,15 @@
# 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 webob import Response, exc
+import werkzeug.utils
+from werkzeug.wrappers import Response as wz_Response
from mediagoblin.tools.template import render_template
-from mediagoblin.tools.translate import fake_ugettext_passthrough as _
+from mediagoblin.tools.translate import (lazy_pass_to_ugettext as _,
+ pass_to_ugettext)
+
+class Response(wz_Response):
+ """Set default response mimetype to HTML, otherwise we get text/plain"""
+ default_mimetype = u'text/html'
def render_to_response(request, template, context, status=200):
@@ -41,6 +47,7 @@ def render_error(request, status=500, title=_('Oops!'),
def render_403(request):
"""Render a standard 403 page"""
+ _ = pass_to_ugettext
title = _('Operation not allowed')
err_msg = _("Sorry Dave, I can't let you do that!</p><p>You have tried "
" to perform a function that you are not allowed to. Have you "
@@ -49,20 +56,46 @@ def render_403(request):
def render_404(request):
"""Render a standard 404 page."""
+ _ = pass_to_ugettext
err_msg = _("There doesn't seem to be a page at this address. Sorry!</p>"
"<p>If you're sure the address is correct, maybe the page "
"you're looking for has been moved or deleted.")
return render_error(request, 404, err_msg=err_msg)
+
+def render_http_exception(request, exc, description):
+ """Return Response() given a werkzeug.HTTPException
+
+ :param exc: werkzeug.HTTPException or subclass thereof
+ :description: message describing the error."""
+ # If we were passed the HTTPException stock description on
+ # exceptions where we have localized ones, use those:
+ stock_desc = (description == exc.__class__.description)
+
+ if stock_desc and exc.code == 403:
+ return render_403(request)
+ elif stock_desc and exc.code == 404:
+ return render_404(request)
+
+ return render_error(request, title=exc.args[0],
+ err_msg=description,
+ status=exc.code)
+
+
def redirect(request, *args, **kwargs):
- """Returns a HTTPFound(), takes a request and then urlgen params"""
+ """Redirects to an URL, using urlgen params or location string
+
+ :param querystring: querystring to be appended to the URL
+ :param location: If the location keyword is given, redirect to the URL
+ """
+ querystring = kwargs.pop('querystring', None)
- querystring = None
- if kwargs.get('querystring'):
- querystring = kwargs.get('querystring')
- del kwargs['querystring']
+ # Redirect to URL if given by "location=..."
+ if 'location' in kwargs:
+ location = kwargs.pop('location')
+ else:
+ location = request.urlgen(*args, **kwargs)
- return exc.HTTPFound(
- location=''.join([
- request.urlgen(*args, **kwargs),
- querystring if querystring else '']))
+ if querystring:
+ location += querystring
+ return werkzeug.utils.redirect(location)
diff --git a/mediagoblin/tools/routing.py b/mediagoblin/tools/routing.py
new file mode 100644
index 00000000..791cd1e6
--- /dev/null
+++ b/mediagoblin/tools/routing.py
@@ -0,0 +1,66 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 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 logging
+
+from werkzeug.routing import Map, Rule
+from mediagoblin.tools.common import import_component
+
+
+_log = logging.getLogger(__name__)
+
+url_map = Map()
+
+
+class MGRoute(Rule):
+ def __init__(self, endpoint, url, controller):
+ Rule.__init__(self, url, endpoint=endpoint)
+ self.gmg_controller = controller
+
+ def empty(self):
+ new_rule = Rule.empty(self)
+ new_rule.gmg_controller = self.gmg_controller
+ return new_rule
+
+
+def endpoint_to_controller(rule):
+ endpoint = rule.endpoint
+ view_func = rule.gmg_controller
+
+ _log.debug('endpoint: {0} view_func: {1}'.format(endpoint, view_func))
+
+ # import the endpoint, or if it's already a callable, call that
+ if isinstance(view_func, basestring):
+ view_func = import_component(view_func)
+ rule.gmg_controller = view_func
+
+ return view_func
+
+
+def add_route(endpoint, url, controller):
+ """
+ Add a route to the url mapping
+ """
+ url_map.add(MGRoute(endpoint, url, controller))
+
+
+def mount(mountpoint, routes):
+ """
+ Mount a bunch of routes to this mountpoint
+ """
+ for endpoint, url, controller in routes:
+ url = "%s/%s" % (mountpoint.rstrip('/'), url.lstrip('/'))
+ add_route(endpoint, url, controller)
diff --git a/mediagoblin/tools/template.py b/mediagoblin/tools/template.py
index 6f603bab..d9c6e654 100644
--- a/mediagoblin/tools/template.py
+++ b/mediagoblin/tools/template.py
@@ -17,6 +17,8 @@
from math import ceil
import jinja2
from babel.localedata import exists
+from werkzeug.urls import url_quote_plus
+
from mediagoblin import mg_globals
from mediagoblin import messages
from mediagoblin.tools import common
@@ -57,11 +59,11 @@ def get_jinja_env(template_loader, locale):
# ... construct a grid of thumbnails or other media
# ... have access to the global and app config
template_env.globals['fetch_messages'] = messages.fetch_messages
- template_env.globals['gridify_list'] = gridify_list
- template_env.globals['gridify_cursor'] = gridify_cursor
template_env.globals['app_config'] = mg_globals.app_config
template_env.globals['global_config'] = mg_globals.global_config
+ template_env.filters['urlencode'] = url_quote_plus
+
if exists(locale):
SETUP_JINJA_ENVS[locale] = template_env
@@ -96,32 +98,3 @@ def render_template(request, template_path, context):
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)
diff --git a/mediagoblin/tools/translate.py b/mediagoblin/tools/translate.py
index 28b7aec0..96144538 100644
--- a/mediagoblin/tools/translate.py
+++ b/mediagoblin/tools/translate.py
@@ -27,18 +27,19 @@ from mediagoblin import mg_globals
# Translation tools
###################
-
+AVAILABLE_LOCALES = None
TRANSLATIONS_PATH = pkg_resources.resource_filename(
'mediagoblin', 'i18n')
-def get_available_locales():
- """Return a list of locales for which we have translations"""
- locales=[]
+def set_available_locales():
+ """Set available locales for which we have translations"""
+ global AVAILABLE_LOCALES
+ locales=['en', 'en_US'] # these are available without translations
for locale in localedata.list():
if gettext.find('mediagoblin', TRANSLATIONS_PATH, [locale]):
locales.append(locale)
- return locales
+ AVAILABLE_LOCALES = locales
def locale_to_lower_upper(locale):
@@ -68,28 +69,26 @@ def locale_to_lower_lower(locale):
def get_locale_from_request(request):
"""
- Figure out what target language is most appropriate based on the
- request
+ Return most appropriate language based on prefs/request request
"""
- request_form = request.args or request.form
+ request_args = (request.args, request.form)[request.method=='POST']
- if request_form.has_key('lang'):
- # User explicitely demanded a language
- target_lang = request_form['lang']
+ if request_args.has_key('lang'):
+ # User explicitely demanded a language, normalize lower_uppercase
+ target_lang = locale_to_lower_upper(request_args['lang'])
elif 'target_lang' in request.session:
# TODO: Uh, ohh, this is never ever set anywhere?
target_lang = request.session['target_lang']
else:
- # Pull the first acceptable language or English
- # This picks your favorite browser lingo, falling back to 'en'
+ # Pull the most acceptable language based on browser preferences
+ # This returns one of AVAILABLE_LOCALES which is aready case-normalized.
+ # Note: in our tests request.accept_languages is None, so we need
+ # to explicitely fallback to en here.
+ target_lang = request.accept_languages.best_match(AVAILABLE_LOCALES) \
+ or "en_US"
- # TODO: We need a list of available locales, and match with the list
- # of accepted locales, and serve the best available locale rather than
- # the most preferred, or fall back to 'en' immediately.
- target_lang = request.accept_languages.best_match(
- mg_globals.available_locales) or 'en'
- return locale_to_lower_upper(target_lang)
+ return target_lang
SETUP_GETTEXTS = {}
@@ -130,7 +129,10 @@ def lazy_pass_to_ugettext(*args, **kwargs):
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.
+ used as a string. For example, in:
+ def func(self, message=_('Hello boys and girls'))
+
+ you would want to use the lazy version for _.
"""
return LazyProxy(pass_to_ugettext, *args, **kwargs)
diff --git a/mediagoblin/tools/url.py b/mediagoblin/tools/url.py
index 7477173a..8604ad5f 100644
--- a/mediagoblin/tools/url.py
+++ b/mediagoblin/tools/url.py
@@ -15,7 +15,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
-import translitcodec
+# This import *is* used; see word.encode('tranlit/long') below.
+from unicodedata import normalize
+
+try:
+ import translitcodec
+ USING_TRANSLITCODEC = True
+except ImportError:
+ USING_TRANSLITCODEC = False
_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
@@ -27,7 +34,11 @@ def slugify(text, delim=u'-'):
"""
result = []
for word in _punct_re.split(text.lower()):
- word = word.encode('translit/long')
+ if USING_TRANSLITCODEC:
+ word = word.encode('translit/long')
+ else:
+ word = normalize('NFKD', word).encode('ascii', 'ignore')
+
if word:
result.append(word)
return unicode(delim.join(result))