1 # -*- coding: utf-8 -*-
11 import werkzeug.exceptions
13 import werkzeug.wrappers
17 from openerp.addons.website import website
18 from openerp.addons.web import http
19 from openerp.addons.web.http import request
21 logger = logging.getLogger(__name__)
24 def auth_method_public():
25 registry = openerp.modules.registry.RegistryManager.get(request.db)
26 if not request.session.uid:
27 request.uid = registry['website'].get_public_user().id
29 request.uid = request.session.uid
30 http.auth_methods['public'] = auth_method_public
33 # PIL images have a type flag, but no MIME. Reverse type flag to MIME.
34 PIL_MIME_MAPPING = {'PNG': 'image/png', 'JPEG': 'image/jpeg', 'GIF': 'image/gif', }
35 # Completely arbitrary limits
36 MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
37 class Website(openerp.addons.web.controllers.main.Home):
38 @website.route('/', type='http', auth="admin")
39 def index(self, **kw):
40 return self.page("website.homepage")
42 @http.route('/admin', type='http', auth="none")
43 def admin(self, *args, **kw):
44 return super(Website, self).index(*args, **kw)
46 # FIXME: auth, if /pagenew known anybody can create new empty page
47 @website.route('/pagenew/<path:path>', type='http', auth="admin")
48 def pagenew(self, path, noredirect=NOPE):
50 module, idname = path.split('.', 1)
54 xid = "%s.%s" % (module, idname)
56 request.cr.execute('SAVEPOINT pagenew')
57 imd = request.registry['ir.model.data']
58 view = request.registry['ir.ui.view']
59 view_model, view_id = imd.get_object_reference(
60 request.cr, request.uid, 'website', 'default_page')
61 newview_id = view.copy(
62 request.cr, request.uid, view_id, context=request.context)
63 newview = view.browse(
64 request.cr, request.uid, newview_id, context=request.context)
66 'arch': newview.arch.replace("website.default_page", xid),
67 'name': "page/%s" % xid,
70 # Fuck it, we're doing it live
72 imd.create(request.cr, request.uid, {
75 'model': 'ir.ui.view',
78 }, context=request.context)
79 except psycopg2.IntegrityError:
80 request.cr.execute('ROLLBACK TO SAVEPOINT pagenew')
82 request.cr.execute('RELEASE SAVEPOINT pagenew')
83 url = "/page/%s" % path
84 if noredirect is not NOPE:
85 return werkzeug.wrappers.Response(url, mimetype='text/plain')
86 return werkzeug.utils.redirect(url)
88 @website.route('/website/theme_change', type='http', auth="admin")
89 def theme_change(self, theme_id=False, **kwargs):
90 imd = request.registry['ir.model.data']
91 view = request.registry['ir.ui.view']
93 view_model, view_option_id = imd.get_object_reference(
94 request.cr, request.uid, 'website', 'theme')
96 request.cr, request.uid, [('inherit_id', '=', view_option_id)],
97 context=request.context)
98 view.write(request.cr, request.uid, views, {'inherit_id': False},
99 context=request.context)
102 module, xml_id = theme_id.split('.')
103 view_model, view_id = imd.get_object_reference(
104 request.cr, request.uid, module, xml_id)
105 view.write(request.cr, request.uid, [view_id],
106 {'inherit_id': view_option_id}, context=request.context)
108 return request.website.render('website.themes', {'theme_changed': True})
110 @website.route('/page/<path:path>', type='http', auth="admin")
111 def page(self, path, **kwargs):
116 html = request.website.render(path, values)
118 html = request.website.render('website.404', values)
121 @website.route('/website/customize_template_toggle', type='json', auth='admin') # FIXME: auth
122 def customize_template_set(self, view_id):
123 view_obj = request.registry.get("ir.ui.view")
124 view = view_obj.browse(request.cr, request.uid, int(view_id),
125 context=request.context)
129 value = view.inherit_option_id and view.inherit_option_id.id or False
130 view_obj.write(request.cr, request.uid, [view_id], {
132 }, context=request.context)
135 @website.route('/website/customize_template_get', type='json', auth='admin') # FIXME: auth
136 def customize_template_get(self, xml_id, optional=True):
137 imd = request.registry['ir.model.data']
138 view_model, view_theme_id = imd.get_object_reference(
139 request.cr, request.uid, 'website', 'theme')
141 view = request.registry.get("ir.ui.view")
142 views = view._views_get(request.cr, request.uid, xml_id, request.context)
146 if v.inherit_option_id and v.inherit_option_id.id != view_theme_id or not optional:
147 if v.inherit_option_id.id not in done:
149 'name': v.inherit_option_id.name,
154 done[v.inherit_option_id.id] = True
159 'active': v.inherit_id.id == v.inherit_option_id.id
163 # # FIXME: auth, anybody can upload an attachment if URL known/found
164 @website.route('/website/attach', type='http', auth='admin')
165 def attach(self, func, upload):
166 req = request.httprequest
167 if req.method != 'POST':
168 return werkzeug.exceptions.MethodNotAllowed(valid_methods=['POST'])
172 attachment_id = request.registry['ir.attachment'].create(request.cr, request.uid, {
173 'name': upload.filename,
174 'datas': base64.encodestring(upload.read()),
175 'datas_fname': upload.filename,
176 'res_model': 'ir.ui.view',
178 # FIXME: auth=user... no good.
179 url = '/website/attachment/%d' % attachment_id
181 logger.exception("Failed to upload image to attachment")
184 return """<script type='text/javascript'>
185 window.parent['%s'](%s, %s);
186 </script>""" % (func, json.dumps(url), json.dumps(message))
188 @website.route('/website/attachment/<int:id>', type='http', auth="admin")
189 def attachment(self, id):
190 # TODO: provide actual thumbnails?
191 # FIXME: can't use Binary.image because auth=user and website attachments need to be public
192 attachment = request.registry['ir.attachment'].browse(
193 request.cr, request.uid, id, request.context)
195 buf = cStringIO.StringIO(base64.decodestring(attachment.datas))
197 image = Image.open(buf)
198 mime = PIL_MIME_MAPPING[image.format]
201 resized = w > MAX_IMAGE_WIDTH or h > MAX_IMAGE_HEIGHT
203 # If saving unnecessary, just send the image buffer, don't go through
204 # Image.save() (especially as it breaks animated gifs)
207 return werkzeug.wrappers.Response(buf, status=200, mimetype=mime)
209 image.thumbnail(IMAGE_LIMITS, Image.ANTIALIAS)
210 response = werkzeug.wrappers.Response(status=200, mimetype=mime)
211 image.save(response.stream, image.format)
214 @website.route('/website/image', type='http', auth="public")
215 def image(self, model, id, field, **kw):
216 last_update = '__last_update'
217 Model = request.registry[model]
218 headers = [('Content-Type', 'image/png')]
219 etag = request.httprequest.headers.get('If-None-Match')
220 hashed_session = hashlib.md5(request.session_id).hexdigest()
221 retag = hashed_session
224 date = Model.read(request.cr, request.uid, [id], [last_update], request.context)[0].get(last_update)
225 if hashlib.md5(date).hexdigest() == etag:
226 return werkzeug.wrappers.Response(status=304)
228 res = Model.read(request.cr, request.uid, [id], [last_update, field], request.context)[0]
229 retag = hashlib.md5(res.get(last_update)).hexdigest()
230 image_base64 = res.get(field)
233 resize = kw.get('resize').split(',')
234 if len(resize) == 2 and int(resize[0]) and int(resize[1]):
235 width = int(resize[0])
236 height = int(resize[1])
237 # resize maximum 500*500
242 image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
244 image_data = base64.b64decode(image_base64)
246 image_data = open(os.path.join(http.addons_manifest['web']['addons_path'], 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
248 headers.append(('ETag', retag))
249 headers.append(('Content-Length', len(image_data)))
251 ncache = int(kw.get('cache'))
252 headers.append(('Cache-Control', 'no-cache' if ncache == 0 else 'max-age=%s' % (ncache)))
255 return request.make_response(image_data, headers)
257 @website.route(['/website/publish/'], type='http', auth="public")
258 def publish(self, **post):
259 _id = int(post['id'])
260 _object = request.registry[post['object']]
262 obj = _object.browse(request.cr, request.uid, _id)
263 _object.write(request.cr, request.uid, [_id],
264 {'website_published': not obj.website_published},
265 context=request.context)
266 obj = _object.browse(request.cr, request.uid, _id)
268 return obj.website_published and "1" or "0"
270 @website.route(['/website/kanban/'], type='http', auth="public")
271 def kanban(self, **post):
272 return request.website.kanban_col(**post)
274 # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: