1 # -*- coding: utf-8 -*-
11 import werkzeug.exceptions
13 import werkzeug.wrappers
17 from openerp.addons.website.website import route
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 @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 @route('/pagenew/<path:path>', type='http', auth="admin")
47 def pagenew(self, path, noredirect=NOPE):
49 module, idname = path.split('.', 1)
53 path = "%s.%s" % (module, idname)
55 request.cr.execute('SAVEPOINT pagenew')
56 imd = request.registry['ir.model.data']
57 view = request.registry['ir.ui.view']
58 view_model, view_id = imd.get_object_reference(request.cr, request.uid, 'website', 'default_page')
59 newview_id = view.copy(request.cr, request.uid, view_id)
60 newview = view.browse(request.cr, request.uid, newview_id)
62 'arch': newview.arch.replace("website.default_page", path),
63 'name': "page/%s" % path,
66 # Fuck it, we're doing it live
68 imd.create(request.cr, request.uid, {
71 'model': 'ir.ui.view',
75 except psycopg2.IntegrityError:
76 request.cr.execute('ROLLBACK TO SAVEPOINT pagenew')
78 request.cr.execute('RELEASE SAVEPOINT pagenew')
79 url = "/page/%s" % path
80 if noredirect is not NOPE:
81 return werkzeug.wrappers.Response(url, mimetype='text/plain')
82 return werkzeug.utils.redirect(url)
84 @route('/website/theme_change', type='http', auth="admin")
85 def theme_change(self, theme_id=False, **kwargs):
86 imd = request.registry['ir.model.data']
87 view = request.registry['ir.ui.view']
89 view_model, view_option_id = imd.get_object_reference(request.cr, request.uid, 'website', 'theme')
90 views = view.search(request.cr, request.uid, [('inherit_id','=',view_option_id)])
91 view.write(request.cr, request.uid, views, {'inherit_id': False})
94 module, xml_id = theme_id.split('.')
95 view_model, view_id = imd.get_object_reference(request.cr, request.uid, module, xml_id)
96 view.write(request.cr, request.uid, [view_id], {'inherit_id':view_option_id})
98 request.webcontext['theme_changed'] = True
99 return request.webcontext.render('website.themes')
101 @route('/page/<path:path>', type='http', auth="admin")
102 def page(self, path, **kwargs):
103 request.webcontext['path'] = path
105 html = request.webcontext.render(path)
107 html = request.webcontext.render('website.404')
110 @route('/website/customize_template_toggle', type='json', auth='admin') # FIXME: auth
111 def customize_template_set(self, view_id):
112 view_obj = request.registry.get("ir.ui.view")
113 view = view_obj.browse(request.cr, request.uid, int(view_id), context=request.context)
117 value = view.inherit_option_id and view.inherit_option_id.id or False
118 view_obj.write(request.cr, request.uid, [view_id], {
120 }, context=request.context)
123 @route('/website/customize_template_get', type='json', auth='admin') # FIXME: auth
124 def customize_template_get(self, xml_id):
125 imd = request.registry['ir.model.data']
126 view_model, view_theme_id = imd.get_object_reference(request.cr, request.uid, 'website', 'theme')
128 view = request.registry.get("ir.ui.view")
129 views = view._views_get(request.cr, request.uid, xml_id, request.context)
133 if v.inherit_option_id and v.inherit_option_id.id<>view_theme_id:
134 if v.inherit_option_id.id not in done:
136 'name': v.inherit_option_id.name,
140 done[v.inherit_option_id.id] = True
145 'active': v.inherit_id.id == v.inherit_option_id.id
149 @route('/website/attach', type='http', auth='admin') # FIXME: auth
150 def attach(self, func, upload):
151 req = request.httprequest
152 if req.method != 'POST':
153 return werkzeug.exceptions.MethodNotAllowed(valid_methods=['POST'])
157 attachment_id = request.registry['ir.attachment'].create(request.cr, request.uid, {
158 'name': upload.filename,
159 'datas': base64.encodestring(upload.read()),
160 'datas_fname': upload.filename,
161 'res_model': 'ir.ui.view',
163 # FIXME: auth=user... no good.
164 url = '/website/attachment/%d' % attachment_id
166 logger.exception("Failed to upload image to attachment")
169 return """<script type='text/javascript'>
170 window.parent['%s'](%s, %s);
171 </script>""" % (func, json.dumps(url), json.dumps(message))
173 @route('/website/attachment/<int:id>', type='http', auth="admin")
174 def attachment(self, id):
175 # FIXME: can't use Binary.image because auth=user and website attachments need to be public
176 attachment = request.registry['ir.attachment'].browse(
177 request.cr, request.uid, id, request.context)
179 buf = cStringIO.StringIO(base64.decodestring(attachment.datas))
181 image = Image.open(buf)
182 mime = PIL_MIME_MAPPING[image.format]
185 resized = w > MAX_IMAGE_WIDTH or h > MAX_IMAGE_HEIGHT
187 # If saving unnecessary, just send the image buffer, don't go through
188 # Image.save() (especially as it breaks animated gifs)
191 return werkzeug.wrappers.Response(buf, status=200, mimetype=mime)
193 image.thumbnail(IMAGE_LIMITS, Image.ANTIALIAS)
194 response = werkzeug.wrappers.Response(status=200, mimetype=mime)
195 image.save(response.stream, image.format)
198 @route('/website/image', type='http', auth="public")
199 def image(self, model, id, field, **kw):
200 last_update = '__last_update'
201 Model = request.registry[model]
202 headers = [('Content-Type', 'image/png')]
203 etag = request.httprequest.headers.get('If-None-Match')
204 hashed_session = hashlib.md5(request.session_id).hexdigest()
205 retag = hashed_session
208 date = Model.read(request.cr, request.uid, [id], [last_update], request.context)[0].get(last_update)
209 if hashlib.md5(date).hexdigest() == etag:
210 return werkzeug.wrappers.Response(status=304)
212 res = Model.read(request.cr, request.uid, [id], [last_update, field], request.context)[0]
213 retag = hashlib.md5(res.get(last_update)).hexdigest()
214 image_base64 = res.get(field)
217 resize = kw.get('resize').split(',')
218 if len(resize) == 2 and int(resize[0]) and int(resize[1]):
219 width = int(resize[0])
220 height = int(resize[1])
221 # resize maximum 500*500
222 if width > 500: width = 500
223 if height > 500: height = 500
224 image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
226 image_data = base64.b64decode(image_base64)
228 image_data = open(os.path.join(http.addons_manifest['web']['addons_path'], 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
230 headers.append(('ETag', retag))
231 headers.append(('Content-Length', len(image_data)))
233 ncache = int(kw.get('cache'))
234 headers.append(('Cache-Control', 'no-cache' if ncache == 0 else 'max-age=%s' % (ncache)))
237 return request.make_response(image_data, headers)
239 @route(['/website/publish/'], type='http', auth="public")
240 def publish(self, **post):
241 _id = int(post['id'])
242 _object = request.registry[post['object']]
244 obj = _object.browse(request.cr, request.uid, _id)
245 _object.write(request.cr, request.uid, [_id], {'website_published': not obj.website_published})
246 obj = _object.browse(request.cr, request.uid, _id)
248 return obj.website_published and "1" or "0"
250 @route(['/website/kanban/'], type='http', auth="public")
251 def kanban(self, **post):
252 return request.registry['website'].kanban_col(**post)
254 # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: