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 path = "%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", path),
67 'name': "page/%s" % path,
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.webcontext.render('website.themes', {'theme_changed': True})
110 @website.route('/page/<path:path>', type='http', auth="admin")
111 def page(self, path, **kwargs):
112 request.webcontext['path'] = path
114 html = request.webcontext.render(path)
116 html = request.webcontext.render('website.404')
119 @website.route('/website/customize_template_toggle', type='json', auth='admin') # FIXME: auth
120 def customize_template_set(self, view_id):
121 view_obj = request.registry.get("ir.ui.view")
122 view = view_obj.browse(request.cr, request.uid, int(view_id),
123 context=request.context)
127 value = view.inherit_option_id and view.inherit_option_id.id or False
128 view_obj.write(request.cr, request.uid, [view_id], {
130 }, context=request.context)
133 @website.route('/website/customize_template_get', type='json', auth='admin') # FIXME: auth
134 def customize_template_get(self, xml_id, optional=True):
135 imd = request.registry['ir.model.data']
136 view_model, view_theme_id = imd.get_object_reference(
137 request.cr, request.uid, 'website', 'theme')
139 view = request.registry.get("ir.ui.view")
140 views = view._views_get(request.cr, request.uid, xml_id, request.context)
144 if v.inherit_option_id and v.inherit_option_id.id != view_theme_id or not optional:
145 if v.inherit_option_id.id not in done:
147 'name': v.inherit_option_id.name,
152 done[v.inherit_option_id.id] = True
157 'active': v.inherit_id.id == v.inherit_option_id.id
161 # # FIXME: auth, anybody can upload an attachment if URL known/found
162 @website.route('/website/attach', type='http', auth='admin')
163 def attach(self, func, upload):
164 req = request.httprequest
165 if req.method != 'POST':
166 return werkzeug.exceptions.MethodNotAllowed(valid_methods=['POST'])
170 attachment_id = request.registry['ir.attachment'].create(request.cr, request.uid, {
171 'name': upload.filename,
172 'datas': base64.encodestring(upload.read()),
173 'datas_fname': upload.filename,
174 'res_model': 'ir.ui.view',
176 # FIXME: auth=user... no good.
177 url = '/website/attachment/%d' % attachment_id
179 logger.exception("Failed to upload image to attachment")
182 return """<script type='text/javascript'>
183 window.parent['%s'](%s, %s);
184 </script>""" % (func, json.dumps(url), json.dumps(message))
186 @website.route('/website/attachment/<int:id>', type='http', auth="admin")
187 def attachment(self, id):
188 # TODO: provide actual thumbnails?
189 # FIXME: can't use Binary.image because auth=user and website attachments need to be public
190 attachment = request.registry['ir.attachment'].browse(
191 request.cr, request.uid, id, request.context)
193 buf = cStringIO.StringIO(base64.decodestring(attachment.datas))
195 image = Image.open(buf)
196 mime = PIL_MIME_MAPPING[image.format]
199 resized = w > MAX_IMAGE_WIDTH or h > MAX_IMAGE_HEIGHT
201 # If saving unnecessary, just send the image buffer, don't go through
202 # Image.save() (especially as it breaks animated gifs)
205 return werkzeug.wrappers.Response(buf, status=200, mimetype=mime)
207 image.thumbnail(IMAGE_LIMITS, Image.ANTIALIAS)
208 response = werkzeug.wrappers.Response(status=200, mimetype=mime)
209 image.save(response.stream, image.format)
212 @website.route('/website/image', type='http', auth="public")
213 def image(self, model, id, field, **kw):
214 last_update = '__last_update'
215 Model = request.registry[model]
216 headers = [('Content-Type', 'image/png')]
217 etag = request.httprequest.headers.get('If-None-Match')
218 hashed_session = hashlib.md5(request.session_id).hexdigest()
219 retag = hashed_session
222 date = Model.read(request.cr, request.uid, [id], [last_update], request.context)[0].get(last_update)
223 if hashlib.md5(date).hexdigest() == etag:
224 return werkzeug.wrappers.Response(status=304)
226 res = Model.read(request.cr, request.uid, [id], [last_update, field], request.context)[0]
227 retag = hashlib.md5(res.get(last_update)).hexdigest()
228 image_base64 = res.get(field)
231 resize = kw.get('resize').split(',')
232 if len(resize) == 2 and int(resize[0]) and int(resize[1]):
233 width = int(resize[0])
234 height = int(resize[1])
235 # resize maximum 500*500
240 image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
242 image_data = base64.b64decode(image_base64)
244 image_data = open(os.path.join(http.addons_manifest['web']['addons_path'], 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
246 headers.append(('ETag', retag))
247 headers.append(('Content-Length', len(image_data)))
249 ncache = int(kw.get('cache'))
250 headers.append(('Cache-Control', 'no-cache' if ncache == 0 else 'max-age=%s' % (ncache)))
253 return request.make_response(image_data, headers)
255 @website.route(['/website/publish/'], type='http', auth="public")
256 def publish(self, **post):
257 _id = int(post['id'])
258 _object = request.registry[post['object']]
260 obj = _object.browse(request.cr, request.uid, _id)
261 _object.write(request.cr, request.uid, [_id],
262 {'website_published': not obj.website_published},
263 context=request.context)
264 obj = _object.browse(request.cr, request.uid, _id)
266 return obj.website_published and "1" or "0"
268 @website.route(['/website/kanban/'], type='http', auth="public")
269 def kanban(self, **post):
270 return request.registry['website'].kanban_col(**post)
272 # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: