diff options
Diffstat (limited to 'mediagoblin/tools')
-rw-r--r-- | mediagoblin/tools/exif.py | 3 | ||||
-rw-r--r-- | mediagoblin/tools/pagination.py | 15 | ||||
-rw-r--r-- | mediagoblin/tools/request.py | 12 | ||||
-rw-r--r-- | mediagoblin/tools/response.py | 55 | ||||
-rw-r--r-- | mediagoblin/tools/routing.py | 66 | ||||
-rw-r--r-- | mediagoblin/tools/template.py | 35 | ||||
-rw-r--r-- | mediagoblin/tools/translate.py | 42 | ||||
-rw-r--r-- | mediagoblin/tools/url.py | 15 |
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)) |