def download_attachment(self, attachment_id): # Check if this is a valid attachment id attachment = request.env['ir.attachment'].sudo().search_read([ ('id', '=', int(attachment_id)) ], ["name", "datas", "mimetype", "res_model", "res_id", "type", "url"]) if attachment: attachment = attachment[0] else: return redirect(self.orders_page) # Check if the user has bought the associated product res_model = attachment['res_model'] res_id = attachment['res_id'] purchased_products = request.env[ 'account.move.line'].get_digital_purchases() if res_model == 'product.product': if res_id not in purchased_products: return redirect(self.orders_page) # Also check for attachments in the product templates elif res_model == 'product.template': template_ids = request.env['product.product'].sudo().browse( purchased_products).mapped('product_tmpl_id').ids if res_id not in template_ids: return redirect(self.orders_page) else: return redirect(self.orders_page) # The client has bought the product, otherwise it would have been blocked by now if attachment["type"] == "url": if attachment["url"]: return redirect(attachment["url"]) else: return request.not_found() elif attachment["datas"]: data = io.BytesIO(base64.standard_b64decode(attachment["datas"])) # we follow what is done in ir_http's binary_content for the extension management extension = os.path.splitext(attachment["name"] or '')[1] extension = extension if extension else mimetypes.guess_extension( attachment["mimetype"] or '') filename = attachment['name'] filename = filename if os.path.splitext( filename)[1] else filename + extension return http.send_file(data, filename=filename, as_attachment=True) else: return request.not_found()
def _response_by_status(self, status, headers, content): if status == 304: return werkzeug.wrappers.Response(status=status, headers=headers) elif status == 301: return werkzeug.utils.redirect(content, code=301) elif status != 200: return request.not_found()
def view(self, db, token, action, id, view='calendar'): registry = registry_get(db) with registry.cursor() as cr: # Since we are in auth=none, create an env with SUPERUSER_ID env = Environment(cr, SUPERUSER_ID, {}) attendee = env['calendar.attendee'].search([('access_token', '=', token), ('event_id', '=', int(id))]) if not attendee: return request.not_found() timezone = attendee.partner_id.tz lang = attendee.partner_id.lang or 'en_US' event = env['calendar.event'].with_context(tz=timezone, lang=lang).browse(int(id)) # If user is internal and logged, redirect to form view of event # otherwise, display the simplifyed web page with event informations if request.session.uid and request.env['res.users'].browse(request.session.uid).user_has_groups('base.group_user'): return werkzeug.utils.redirect('/web?db=%s#id=%s&view_type=form&model=calendar.event' % (db, id)) # NOTE : we don't use request.render() since: # - we need a template rendering which is not lazy, to render before cursor closing # - we need to display the template in the language of the user (not possible with # request.render()) response_content = env['ir.ui.view'].with_context(lang=lang).render_template( 'calendar.invitation_page_anonymous', { 'event': event, 'attendee': attendee, }) return request.make_response(response_content, headers=[('Content-Type', 'text/html')])
def download_document(self, file, filename): if not file: return request.not_found() else: return request.make_response( file, [('Content-Type', 'application/octet-stream'), ('Content-Disposition', content_disposition(filename))])
def view_meeting(self, token, id, **kwargs): attendee = request.env['calendar.attendee'].sudo().search([ ('access_token', '=', token), ('event_id', '=', int(id)) ]) if not attendee: return request.not_found() timezone = attendee.partner_id.tz lang = attendee.partner_id.lang or get_lang(request.env).code event = request.env['calendar.event'].with_context( tz=timezone, lang=lang).sudo().browse(int(id)) # If user is internal and logged, redirect to form view of event # otherwise, display the simplifyed web page with event informations if request.session.uid and request.env['res.users'].browse( request.session.uid).user_has_groups('base.group_user'): return werkzeug.utils.redirect( '/web?db=%s#id=%s&view_type=form&model=calendar.event' % (request.env.cr.dbname, id)) # NOTE : we don't use request.render() since: # - we need a template rendering which is not lazy, to render before cursor closing # - we need to display the template in the language of the user (not possible with # request.render()) response_content = request.env['ir.ui.view'].with_context( lang=lang)._render_template('calendar.invitation_page_anonymous', { 'event': event, 'attendee': attendee, }) return request.make_response(response_content, headers=[('Content-Type', 'text/html')])
def action_submit_rating(self, token, **kwargs): rate = int(kwargs.get('rate')) assert rate in (1, 3, 5), "Incorrect rating" rating = request.env['helpdesk.ticket'].sudo().search([('access_token', '=', token)]) if not rating: return request.not_found() feedback = (kwargs.get('feedback')) rating.rating_last_value = float(rate) if feedback: message = plaintext2html(feedback) post_values = { 'res_model': 'helpdesk.ticket', 'res_id': rating.id, 'message': feedback, 'send_after_commit': False, 'attachment_ids': False, # will be added afterward } message = _message_post_helper(**post_values) lang = rating.partner_id.lang or get_lang(request.env).code return request.env['ir.ui.view'].with_context( lang=lang)._render_template( 'helpdesk_basic.rating_external_page_view', { 'web_base_url': request.env['ir.config_parameter'].sudo().get_param( 'web.base.url'), 'rating_value': rating, })
def download_document(self, model, field, id, filename=None, **kw): """ :param str filename: field holding the file's name, if any :returns: :class:`werkzeug.wrappers.Response` """ Model = request.registry[model] cr, uid, context = request.cr, request.uid, request.context fields = [field] res = Model.read(cr, uid, [int(id)], fields, context)[0] # filecontent = base64.b64decode(res.get(field) or '') filecontent = res.get(field) print(filecontent) if not filecontent: return request.not_found() else: if not filename: filename = '%s_%s' % (model.replace('.', '_'), id) headers = [ ('Content-Type', 'application/xml'), ('Content-Disposition', content_disposition(filename)), ('charset', 'utf-8'), ] return request.make_response(filecontent, headers=headers, cookies=None)
def content_common(self, xmlid=None, model='ir.attachment', id=None, field='datas', filename=None, filename_field='datas_fname', mimetype=None, download=None, access_token=None): obj = None if xmlid: obj = request.env.ref(xmlid, False) elif id and model in request.env.registry: obj = request.env[model].browse(int(id)) if not obj or not obj.exists() or field not in obj: return request.not_found() try: last_update = obj['__last_update'] except AccessError: return wrappers.Response(status=403, headers=[]) status, headers, content = None, [], None content = obj.with_context({'stream': True})[field] or b'' if not filename: if filename_field in obj: filename = obj[filename_field] else: filename = "%s-%s-%s" % (obj._name, obj.id, field) mimetype = 'mimetype' in obj and obj.mimetype or False if not mimetype and filename: mimetype = mimetypes.guess_type(filename)[0] headers += [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')] etag = bool(request) and request.httprequest.headers.get('If-None-Match') retag = '"%s"' % hashlib.md5(pycompat.to_text(content).encode('utf-8')).hexdigest() status = status or (304 if etag == retag else 200) headers.append(('ETag', retag)) if download: headers.append(('Content-Disposition', http.content_disposition(filename))) return wrappers.Response(content, headers=headers, direct_passthrough=True, status=status)
def download_attachment(self, attachment_id): # Check if this is a valid attachment id attachment = request.env['ir.attachment'].sudo().search_read([ ('id', '=', int(attachment_id)) ], [ "name", "datas", "file_type", "res_model", "res_id", "type", "url" ]) if attachment: attachment = attachment[0] else: return redirect(self.orders_page) # Check if the user has bought the associated product res_model = attachment['res_model'] res_id = attachment['res_id'] purchased_products = request.env[ 'account.invoice.line'].get_digital_purchases() if res_model == 'product.product': if res_id not in purchased_products: return redirect(self.orders_page) # Also check for attachments in the product templates elif res_model == 'product.template': template_ids = request.env['product.product'].sudo().browse( purchased_products).mapped('product_tmpl_id').ids if res_id not in template_ids: return redirect(self.orders_page) else: return redirect(self.orders_page) # The client has bought the product, otherwise it would have been blocked by now if attachment["type"] == "url": if attachment["url"]: return redirect(attachment["url"]) else: return request.not_found() elif attachment["datas"]: data = io.BytesIO(base64.standard_b64decode(attachment["datas"])) return http.send_file(data, filename=attachment['name'], as_attachment=True) else: return request.not_found()
def partners_detail(self, partner_id, **post): _, partner_id = unslug(partner_id) if partner_id: partner_sudo = request.env['res.partner'].sudo().browse(partner_id) is_website_publisher = request.env['res.users'].has_group( 'website.group_website_publisher') if partner_sudo.exists() and (partner_sudo.website_published or is_website_publisher): values = { 'main_object': partner_sudo, 'partner': partner_sudo, 'edit_page': False } return request.render("website_partner.partner_page", values) return request.not_found()
def index(self, **kw): homepage = request.website.homepage_id if homepage and (homepage.sudo().is_visible or request.env.user.has_group('base.group_user')) and homepage.url != '/': return request.env['ir.http'].reroute(homepage.url) website_page = request.env['ir.http']._serve_page() if website_page: return website_page else: top_menu = request.website.menu_id first_menu = top_menu and top_menu.child_id and top_menu.child_id.filtered(lambda menu: menu.is_visible) if first_menu and first_menu[0].url not in ('/', '') and (not (first_menu[0].url.startswith(('/?', '/#', ' ')))): return request.redirect(first_menu[0].url) raise request.not_found()
def index(self, **kw): homepage = request.website.homepage_id if homepage and homepage.url != '/': return request.env['ir.http'].reroute(homepage.url) website_page = request.env['ir.http']._serve_page() if website_page: return website_page else: top_menu = request.website.sudo().menu_id first_menu = top_menu and top_menu.child_id and top_menu.child_id[0] if first_menu and first_menu.url != '/' and ( not (first_menu.url.startswith(('/?', '/#')))): return request.redirect(first_menu.url) raise request.not_found()
def action_open_rating(self, token, rate, **kwargs): assert rate in (1, 3, 5), "Incorrect rating" rating = request.env['helpdesk.ticket'].sudo().search([('access_token', '=', token)]) if not rating: return request.not_found() rate_names = { 5: _("satisfied"), 3: _("not satisfied"), 1: _("highly dissatisfied") } lang = rating.partner_id.lang or get_lang(request.env).code return request.env['ir.ui.view'].with_context( lang=lang)._render_template( 'helpdesk_basic.rating_external_page_submit', { 'token': token, 'rate_names': rate_names, 'rate': rate })
def submit_rating(self, token, rate, **kwargs): rating = request.env['rating.rating'].sudo().search([('access_token', '=', token)]) if not rating: return request.not_found() record_sudo = request.env[rating.res_model].sudo().browse( rating.res_id) record_sudo.rating_apply(rate, token=token, feedback=kwargs.get('feedback')) lang = rating.partner_id.lang or 'en_US' return request.env['ir.ui.view'].with_context( lang=lang).render_template( 'rating.rating_external_page_view', { 'web_base_url': request.env['ir.config_parameter'].sudo().get_param( 'web.base.url'), 'rating': rating, })
def open_rating(self, token, rate, **kwargs): assert rate in (1, 5, 10), "Incorrect rating" rating = request.env['rating.rating'].sudo().search([('access_token', '=', token)]) if not rating: return request.not_found() rate_names = { 5: _("not satisfied"), 1: _("highly dissatisfied"), 10: _("satisfied") } rating.write({'rating': rate, 'consumed': True}) lang = rating.partner_id.lang or 'en_US' return request.env['ir.ui.view'].with_context( lang=lang).render_template( 'rating.rating_external_page_submit', { 'rating': rating, 'token': token, 'rate_name': rate_names[rate], 'rate': rate })
def download_document(self,model,field,id,filename=None, **kw): """ Download link for files stored as binary fields. :param str model: name of the model to fetch the binary from :param str field: binary field :param str id: id of the record from which to fetch the binary :param str filename: field holding the file's name, if any :returns: :class:`werkzeug.wrappers.Response` """ Model = request.registry[model] cr, uid, context = request.cr, request.uid, request.context fields = [field] res = Model.read(cr, uid, [int(id)], fields, context)[0] filecontent = base64.b64decode(res.get(field) or '') if not filecontent: return request.not_found() else: if not filename: filename = '%s_%s' % (model.replace('.', '_'), id) return request.make_response(filecontent, [('Content-Type', 'application/pdf'), ('Content-Disposition', content_disposition(filename))])
def content_image(self, xmlid=None, model='ir.attachment', id=None, field='datas', filename_field='datas_fname', unique=None, filename=None, mimetype=None, download=None, width=0, height=0, crop=False, access_token=None): status, headers, content = binary_content(xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field, download=download, mimetype=mimetype, default_mimetype='image/png', access_token=access_token) if status == 304: return werkzeug.wrappers.Response(status=304, headers=headers) elif status == 301: return werkzeug.utils.redirect(content, code=301) elif status != 200 and download: return request.not_found() height = int(height or 0) width = int(width or 0) if crop and (width or height): content = crop_image(content, type='center', size=(width, height), ratio=(1, 1)) elif content and (width or height): # resize maximum 500*500 content = flectra.tools.image_resize_image(base64_source=content, size=(width or None, height or None), encoding='base64', filetype='PNG') # resize force png as filetype headers = self.force_contenttype(headers, contenttype='image/png') if content: image_base64 = base64.b64decode(content) else: image_base64 = self.placeholder(image='placeholder.png') # could return (contenttype, content) in master headers = self.force_contenttype(headers, contenttype='image/png') headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status_code = status return response status, headers, content = binary_content(xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field, download=download, mimetype=mimetype, default_mimetype='image/png') if status == 304: return werkzeug.wrappers.Response(status=304, headers=headers) elif status == 301: return werkzeug.utils.redirect(content, code=301) elif status != 200 and download: return request.not_found() if content and (width or height): content = flectra.tools.image_resize_image(base64_source=content, size=(width or None, height or None), encoding='base64', filetype='PNG') # resize force png as filetype headers = self.force_contenttype(headers, contenttype='image/png') if content: image_base64 = base64.b64decode(content) else: image_base64 = self.placeholder(image='placeholder.png') # could return (contenttype, content) in master headers = self.force_contenttype(headers, contenttype='image/png') headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status_code = status return response
def get_claim_report_user(self, employee_id, **post): if not request.env.user.has_group('fleet.fleet_group_manager'): return request.not_found() employee = request.env['hr.employee'].search([('id', '=', employee_id)], limit=1) partner_ids = (employee.user_id.partner_id | employee.sudo().address_home_id).ids if not employee or not partner_ids: return request.not_found() car_assignation_logs = request.env['fleet.vehicle.assignation.log'].search([('driver_id', 'in', partner_ids)]) doc_list = request.env['ir.attachment'].search([ ('res_model', '=', 'fleet.vehicle.assignation.log'), ('res_id', 'in', car_assignation_logs.ids)], order='create_date') writer = PdfFileWriter() font = "Helvetica" normal_font_size = 14 for document in doc_list: car_line_doc = request.env['fleet.vehicle.assignation.log'].browse(document.res_id) try: reader = PdfFileReader(io.BytesIO(base64.b64decode(document.datas)), strict=False, overwriteWarnings=False) except Exception: continue width = float(reader.getPage(0).mediaBox.getUpperRight_x()) height = float(reader.getPage(0).mediaBox.getUpperRight_y()) header = io.BytesIO() can = canvas.Canvas(header) can.setFont(font, normal_font_size) can.setFillColorRGB(1, 0, 0) car_name = car_line_doc.vehicle_id.display_name date_start = car_line_doc.date_start date_end = car_line_doc.date_end or '...' text_to_print = _( "%(car_name)s (driven from: %(date_start)s to %(date_end)s)", car_name=car_name, date_start=date_start, date_end=date_end ) can.drawCentredString(width / 2, height - normal_font_size, text_to_print) can.save() header_pdf = PdfFileReader(header, overwriteWarnings=False) for page_number in range(0, reader.getNumPages()): page = reader.getPage(page_number) page.mergePage(header_pdf.getPage(0)) writer.addPage(page) _buffer = io.BytesIO() writer.write(_buffer) merged_pdf = _buffer.getvalue() _buffer.close() pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(merged_pdf))] return request.make_response(merged_pdf, headers=pdfhttpheaders)
def sitemap_xml_index(self, **kwargs): current_website = request.website Attachment = request.env['ir.attachment'].sudo() View = request.env['ir.ui.view'].sudo() mimetype = 'application/xml;charset=utf-8' content = None def create_sitemap(url, content): return Attachment.create({ 'datas': base64.b64encode(content), 'mimetype': mimetype, 'type': 'binary', 'name': url, 'url': url, }) dom = [('url', '=', '/sitemap-%d.xml' % current_website.id), ('type', '=', 'binary')] sitemap = Attachment.search(dom, limit=1) if sitemap: # Check if stored version is still valid create_date = fields.Datetime.from_string(sitemap.create_date) delta = datetime.datetime.now() - create_date if delta < SITEMAP_CACHE_TIME: content = base64.b64decode(sitemap.datas) if not content: # Remove all sitemaps in ir.attachments as we're going to regenerated them dom = [('type', '=', 'binary'), '|', ('url', '=like', '/sitemap-%d-%%.xml' % current_website.id), ('url', '=', '/sitemap-%d.xml' % current_website.id)] sitemaps = Attachment.search(dom) sitemaps.unlink() pages = 0 locs = request.website.sudo( user=request.website.user_id.id).enumerate_pages() while True: values = { 'locs': islice(locs, 0, LOC_PER_SITEMAP), 'url_root': request.httprequest.url_root[:-1], } urls = View.render_template('website.sitemap_locs', values) if urls.strip(): content = View.render_template('website.sitemap_xml', {'content': urls}) pages += 1 last_sitemap = create_sitemap( '/sitemap-%d-%d.xml' % (current_website.id, pages), content) else: break if not pages: return request.not_found() elif pages == 1: # rename the -id-page.xml => -id.xml last_sitemap.write({ 'url': "/sitemap-%d.xml" % current_website.id, 'name': "/sitemap-%d.xml" % current_website.id, }) else: # TODO: in master/saas-15, move current_website_id in template directly pages_with_website = [ "%d-%d" % (current_website.id, p) for p in range(1, pages + 1) ] # Sitemaps must be split in several smaller files with a sitemap index content = View.render_template( 'website.sitemap_index_xml', { 'pages': pages_with_website, 'url_root': request.httprequest.url_root, }) create_sitemap('/sitemap-%d.xml' % current_website.id, content) return request.make_response(content, [('Content-Type', mimetype)])