X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fwebsite%2Fcontrollers%2Fmain.py;h=fc8dca6d07f40c95b107450a83d9549aeb8579ea;hb=828b7aef2f66fbc15ed28e39e7adc9068e9c4fa7;hp=76907faec3cd5229e2dbe103d190639fc60a8eab;hpb=69e6a7a80876f01d7fd12d95053d5b333a41d747;p=odoo%2Fodoo.git diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 76907fa..fc8dca6 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- import cStringIO -import contextlib -import hashlib +from itertools import islice import json import logging -import os -import datetime +import math +import re from sys import maxint -import werkzeug -import werkzeug.exceptions import werkzeug.utils import werkzeug.wrappers from PIL import Image @@ -25,6 +22,24 @@ logger = logging.getLogger(__name__) # Completely arbitrary limits MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768) +LOC_PER_SITEMAP = 45000 + +import time + +from functools import wraps + +def timeit(f): + @wraps(f) + def wrapper(*args, **kw): + ts = time.time() + result = f(*args, **kw) + te = time.time() + + print 'func:%r args:[%r, %r] took: %2.4f sec' % \ + (f.__name__, args, kw, te-ts) + return result + return wrapper + class Website(openerp.addons.web.controllers.main.Home): @@ -48,7 +63,7 @@ class Website(openerp.addons.web.controllers.main.Home): # TODO: can't we just put auth=public, ... in web client ? return super(Website, self).web_login(*args, **kw) - @http.route('/page/', type='http', auth="public", website=True, multilang=True) + @http.route('/page/', type='http', auth="public", website=True, multilang=True) def page(self, page, **opt): values = { 'path': page, @@ -68,25 +83,44 @@ class Website(openerp.addons.web.controllers.main.Home): return request.render(page, values) - @http.route(['/robots.txt'], type='http', auth="public", website=True) + @http.route(['/robots.txt'], type='http', auth="public") def robots(self): return request.render('website.robots', {'url_root': request.httprequest.url_root}, mimetype='text/plain') - @http.route('/sitemap', type='http', auth='public', website=True, multilang=True) - def sitemap(self): - return request.render('website.sitemap', { - 'pages': request.website.enumerate_pages() - }) - + @timeit @http.route('/sitemap.xml', type='http', auth="public", website=True) - def sitemap_xml(self): + def sitemap_xml_index(self): + count = 0 + for loc in request.website.enumerate_pages(): + count += 1 + if count <= LOC_PER_SITEMAP: + return self.__sitemap_xml(0) + # Sitemaps must be split in several smaller files with a sitemap index + values = { + 'pages': range(int(math.ceil(float(count) / LOC_PER_SITEMAP))), + 'url_root': request.httprequest.url_root + } + headers = { + 'Content-Type': 'application/xml;charset=utf-8', + } + return request.render('website.sitemap_index_xml', values, headers=headers) + + @http.route('/sitemap-.xml', type='http', auth="public", website=True) + def sitemap_xml(self, page): + return self.__sitemap_xml(page) + + @timeit + def __sitemap_xml(self, index=0): + start = index * LOC_PER_SITEMAP + locs = islice(request.website.enumerate_pages(), start , start + LOC_PER_SITEMAP) values = { - 'pages': request.website.enumerate_pages() + 'locs': locs, + 'url_root': request.httprequest.url_root.rstrip('/') } headers = { 'Content-Type': 'application/xml;charset=utf-8', } - return request.render('website.sitemap_xml', values, headers=headers) + return request.render('website.sitemap_xml', values, headers=headers).flatten() #------------------------------------------------------ # Edit @@ -101,7 +135,9 @@ class Website(openerp.addons.web.controllers.main.Home): 'url': "/page/" + xml_id, 'parent_id': id, }, context=request.context) - url = "/page/" + xml_id + # Reverse action in order to allow shortcut for /page/ + url = "/page/" + re.sub(r"^website\.", '', xml_id) + if noredirect: return werkzeug.wrappers.Response(url, mimetype='text/plain') return werkzeug.utils.redirect(url) @@ -247,37 +283,47 @@ class Website(openerp.addons.web.controllers.main.Home): return True @http.route('/website/attach', type='http', auth='user', methods=['POST'], website=True) - def attach(self, func, upload): + def attach(self, func, upload=None, url=None): + Attachments = request.registry['ir.attachment'] - url = message = None - try: - image_data = upload.read() - image = Image.open(cStringIO.StringIO(image_data)) - w, h = image.size - if w*h > 42e6: # Nokia Lumia 1020 photo resolution - raise ValueError( - u"Image size excessive, uploaded images must be smaller " - u"than 42 million pixel") - - Attachments = request.registry['ir.attachment'] + website_url = message = None + if not upload: + website_url = url + name = url.split("/").pop() attachment_id = Attachments.create(request.cr, request.uid, { - 'name': upload.filename, - 'datas': image_data.encode('base64'), - 'datas_fname': upload.filename, + 'name':name, + 'type': 'url', + 'url': url, 'res_model': 'ir.ui.view', }, request.context) - - [attachment] = Attachments.read( - request.cr, request.uid, [attachment_id], ['website_url'], - context=request.context) - url = attachment['website_url'] - except Exception, e: - logger.exception("Failed to upload image to attachment") - message = unicode(e) + else: + try: + image_data = upload.read() + image = Image.open(cStringIO.StringIO(image_data)) + w, h = image.size + if w*h > 42e6: # Nokia Lumia 1020 photo resolution + raise ValueError( + u"Image size excessive, uploaded images must be smaller " + u"than 42 million pixel") + + attachment_id = Attachments.create(request.cr, request.uid, { + 'name': upload.filename, + 'datas': image_data.encode('base64'), + 'datas_fname': upload.filename, + 'res_model': 'ir.ui.view', + }, request.context) + + [attachment] = Attachments.read( + request.cr, request.uid, [attachment_id], ['website_url'], + context=request.context) + website_url = attachment['website_url'] + except Exception, e: + logger.exception("Failed to upload image to attachment") + message = unicode(e) return """""" % (func, json.dumps(url), json.dumps(message)) + """ % (func, json.dumps(website_url), json.dumps(message)) @http.route(['/website/publish'], type='json', auth="public", website=True) def publish(self, id, object): @@ -288,8 +334,6 @@ class Website(openerp.addons.web.controllers.main.Home): values = {} if 'website_published' in _object._all_columns: values['website_published'] = not obj.website_published - if 'website_published_datetime' in _object._all_columns and values.get('website_published'): - values['website_published_datetime'] = fields.datetime.now() _object.write(request.cr, request.uid, [_id], values, context=request.context) @@ -299,18 +343,12 @@ class Website(openerp.addons.web.controllers.main.Home): #------------------------------------------------------ # Helpers #------------------------------------------------------ - @http.route(['/website/kanban/'], type='http', auth="public", methods=['POST'], website=True) + @http.route(['/website/kanban'], type='http', auth="public", methods=['POST'], website=True) def kanban(self, **post): return request.website.kanban_col(**post) def placeholder(self, response): - # file_open may return a StringIO. StringIO can be closed but are - # not context managers in Python 2 though that is fixed in 3 - with contextlib.closing(openerp.tools.misc.file_open( - os.path.join('web', 'static', 'src', 'img', 'placeholder.png'), - mode='rb')) as f: - response.data = f.read() - return response.make_conditional(request.httprequest) + return request.registry['website']._image_placeholder(response) @http.route([ '/website/image', @@ -334,73 +372,10 @@ class Website(openerp.addons.web.controllers.main.Home): The requested field is assumed to be base64-encoded image data in all cases. """ - Model = request.registry[model] - response = werkzeug.wrappers.Response() + return request.registry['website']._image( + request.cr, request.uid, model, id, field, response) - id = int(id) - - ids = Model.search(request.cr, request.uid, - [('id', '=', id)], context=request.context) \ - or Model.search(request.cr, openerp.SUPERUSER_ID, - [('id', '=', id), ('website_published', '=', True)], context=request.context) - - if not ids: - return self.placeholder(response) - - presized = '%s_big' % field - concurrency = '__last_update' - [record] = Model.read(request.cr, openerp.SUPERUSER_ID, [id], - [concurrency, field, presized], - context=request.context) - - if concurrency in record: - server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT - try: - response.last_modified = datetime.datetime.strptime( - record[concurrency], server_format + '.%f') - except ValueError: - # just in case we have a timestamp without microseconds - response.last_modified = datetime.datetime.strptime( - record[concurrency], server_format) - - # Field does not exist on model or field set to False - if not record.get(field): - # FIXME: maybe a field which does not exist should be a 404? - return self.placeholder(response) - - response.set_etag(hashlib.sha1(record[field]).hexdigest()) - response.make_conditional(request.httprequest) - - # conditional request match - if response.status_code == 304: - return response - - data = (record.get(presized) or record[field]).decode('base64') - - image = Image.open(cStringIO.StringIO(data)) - response.mimetype = Image.MIME[image.format] - - # record provides a pre-resized version of the base field, use that - # directly - if record.get(presized): - response.set_data(data) - return response - - fit = int(max_width), int(max_height) - w, h = image.size - max_w, max_h = fit - - if w < max_w and h < max_h: - response.set_data(data) - else: - image.thumbnail(fit, Image.ANTIALIAS) - image.save(response.stream, image.format) - # invalidate content-length computed by make_conditional as - # writing to response.stream does not do it (as of werkzeug 0.9.3) - del response.headers['Content-Length'] - - return response #------------------------------------------------------ # Server actions @@ -438,4 +413,3 @@ class Website(openerp.addons.web.controllers.main.Home): return res return request.redirect('/') -# vim:et: