def avatar(self, res_model, res_id, partner_id): headers = [('Content-Type', 'image/png')] status = 200 content = 'R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' # default image is one white pixel if res_model in request.env: try: # if the current user has access to the document, get the partner avatar as sudo() request.env[res_model].browse(res_id).check_access_rule('read') if partner_id in request.env[res_model].browse(res_id).sudo( ).exists().message_ids.mapped('author_id').ids: status, headers, _content = request.env['ir.http'].sudo( ).binary_content(model='res.partner', id=partner_id, field='image_128', default_mimetype='image/png') # binary content return an empty string and not a placeholder if obj[field] is False if _content != '': content = _content if status == 304: return werkzeug.wrappers.Response(status=304) except AccessError: pass image_base64 = base64.b64decode(content) headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status = str(status) return response
def survey_get_certification(self, survey_id, **kwargs): """ The certification document can be downloaded as long as the user has succeeded the certification """ survey = request.env['survey.survey'].sudo().search([ ('id', '=', survey_id), ('certificate', '=', True) ]) if not survey: # no certification found return werkzeug.utils.redirect("/") succeeded_attempt = request.env['survey.user_input'].sudo().search( [('partner_id', '=', request.env.user.partner_id.id), ('survey_id', '=', survey_id), ('quizz_passed', '=', True)], limit=1) if not succeeded_attempt: raise UserError(_("The user has not succeeded the certification")) report_sudo = request.env.ref('survey.certification_report').sudo() report = report_sudo.render_qweb_pdf([succeeded_attempt.id], data={'report_type': 'pdf'})[0] reporthttpheaders = [ ('Content-Type', 'application/pdf'), ('Content-Length', len(report)), ] reporthttpheaders.append( ('Content-Disposition', content_disposition('Certification.pdf'))) return request.make_response(report, headers=reporthttpheaders)
def _show_report(self, model, report_type, report_ref, download=False): if report_type not in ('html', 'pdf', 'text'): raise UserError(_("Invalid report type: %s") % report_type) report_sudo = request.env.ref(report_ref).sudo() if not isinstance(report_sudo, type(request.env['ir.actions.report'])): raise UserError( _("%s is not the reference of a report") % report_ref) method_name = 'render_qweb_%s' % (report_type) report = getattr(report_sudo, method_name)([model.id], data={ 'report_type': report_type })[0] reporthttpheaders = [ ('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'), ('Content-Length', len(report)), ] if report_type == 'pdf' and download: filename = "%s.pdf" % (re.sub('\W+', '-', model._get_report_base_filename())) reporthttpheaders.append( ('Content-Disposition', content_disposition(filename))) return request.make_response(report, headers=reporthttpheaders)
def base(self, data, token): params = json.loads(data) header, dashboard_data = operator.itemgetter('header', 'dashboard_data')(params) return request.make_response(self.from_data(dashboard_data), headers=[('Content-Disposition', content_disposition(self.filename(header))), ('Content-Type', self.content_type)], cookies={'fileToken': token})
def print_sale_details(self, date_start=False, date_stop=False, **kw): r = request.env['report.point_of_sale.report_saledetails'] pdf, _ = request.env.ref( 'point_of_sale.sale_details_report').with_context( date_start=date_start, date_stop=date_stop).render_qweb_pdf(r) pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] return request.make_response(pdf, headers=pdfhttpheaders)
def get_wishlist(self, count=False, **kw): values = request.env['product.wishlist'].with_context(display_default_code=False).current() if count: return request.make_response(json.dumps(values.mapped('product_id').ids)) if not len(values): return request.redirect("/shop") return request.render("website_sale_wishlist.product_wishlist", dict(wishes=values))
def download_catalogue(self, product_id): """In this function we are calling the report template of the corresponding product and downloads the catalogue in pdf format""" pdf, _ = request.env.ref('product_catalogue.action_report_product_catalog')\ .sudo().render_qweb_pdf([int(product_id)]) pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf)), ('Content-Disposition', 'catalogue' + '.pdf;')] return request.make_response(pdf, headers=pdfhttpheaders)
def make_event_ics_file(self, event, **kwargs): if not event or not event.registration_ids: return request.not_found() files = event._get_ics_file() content = files[event.id] return request.make_response(content, [ ('Content-Type', 'application/octet-stream'), ('Content-Length', len(content)), ('Content-Disposition', content_disposition('%s.ics' % event.name)) ])
def report(self, output_format, report_name, token, report_id=False, **kw): uid = request.session.uid domain = [('create_uid', '=', uid)] stock_traceability = request.env[ 'stock.traceability.report'].with_user(uid).search(domain, limit=1) line_data = json.loads(kw['data']) try: if output_format == 'pdf': response = request.make_response( stock_traceability.with_context( active_id=report_id).get_pdf(line_data), headers=[('Content-Type', 'application/pdf'), ('Content-Disposition', 'attachment; filename=' + 'stock_traceability' + '.pdf;')]) response.set_cookie('fileToken', token) return response except Exception as e: se = _serialize_exception(e) error = {'code': 200, 'message': 'Eagle Server Error', 'data': se} return request.make_response(html_escape(json.dumps(error)))
def attachment_add_create(self, name, file, **kwargs): attachment_id = request.env['ir.attachment'].create({ 'name': name, 'datas': base64.b64encode(file.read()), 'res_model': 'mail.compose.message', 'res_id': 0, 'mimetype':'video/mp4' }).id return request.make_response( data=json.dumps({"id": attachment_id}), headers=[('Content-Type', 'application/json')] )
def slide_download(self, slide, **kw): slide = slide.sudo() if slide.download_security == 'public' or (slide.download_security == 'user' and request.env.user and request.env.user != request.website.user_id): filecontent = base64.b64decode(slide.datas) disposition = 'attachment; filename=%s.pdf' % werkzeug.urls.url_quote(slide.name) return request.make_response( filecontent, [('Content-Type', 'application/pdf'), ('Content-Length', len(filecontent)), ('Content-Disposition', disposition)]) elif not request.session.uid and slide.download_security == 'user': return request.redirect('/web/login?redirect=/slides/slide/%s' % (slide.id)) return request.render("website.403")
def student_health_report_download_pdf(self, **post): """ after form filup parent/student get PDF of Health report containing student health information. ------------------------------------------- :param post: :return: """ cr, uid, context = request.cr, SUPERUSER_ID, request.context student_id = post.get('student_id') if student_id: pdf = request.registry['report'].get_pdf(cr, uid, [int(student_id)], 'edsys_paperless_registrations.student_health_report_templet', data=None, context=context) pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] return request.make_response(pdf, headers=pdfhttpheaders)
def livechat_lib(self, ext, **kwargs): # _get_asset return the bundle html code (script and link list) but we want to use the attachment content xmlid = 'im_livechat.external_lib' files, remains = request.env["ir.qweb"]._get_asset_content( xmlid, options=request.context) asset = AssetsBundle(xmlid, files) mock_attachment = getattr(asset, ext)() if isinstance( mock_attachment, list ): # suppose that CSS asset will not required to be split in pages mock_attachment = mock_attachment[0] # can't use /web/content directly because we don't have attachment ids (attachments must be created) status, headers, content = request.env['ir.http'].binary_content( id=mock_attachment.id, unique=asset.checksum) content_base64 = base64.b64decode(content) if content else '' headers.append(('Content-Length', len(content_base64))) return request.make_response(content_base64, headers)
def portal_my_picking_report(self, picking_id, access_token=None, **kw): """ Print delivery slip for customer, using either access rights or access token to be sure customer has access """ try: picking_sudo = self._stock_picking_check_access( picking_id, access_token=access_token) except exceptions.AccessError: return request.redirect('/my') # print report as sudo, since it require access to product, taxes, payment term etc.. and portal does not have those access rights. pdf = request.env.ref( 'stock.action_report_delivery').sudo().render_qweb_pdf( [picking_sudo.id])[0] pdfhttpheaders = [ ('Content-Type', 'application/pdf'), ('Content-Length', len(pdf)), ] return request.make_response(pdf, headers=pdfhttpheaders)
def base(self, data, token): params = json.loads(data) header, chart_data = operator.itemgetter('header', 'chart_data')(params) chart_data = json.loads(chart_data) chart_data['labels'].insert(0, 'Measure') columns_headers = chart_data['labels'] import_data = [] for dataset in chart_data['datasets']: dataset['data'].insert(0, dataset['label']) import_data.append(dataset['data']) return request.make_response( self.from_data(columns_headers, import_data), headers=[('Content-Disposition', content_disposition(self.filename(header))), ('Content-Type', self.content_type)], cookies={'fileToken': token})
def get_user_profile_avatar(self, user_id, field='image_256', width=0, height=0, crop=False, **post): if field not in ('image_128', 'image_256'): return werkzeug.exceptions.Forbidden() can_sudo = self._check_avatar_access(user_id, **post) if can_sudo: status, headers, image_base64 = request.env['ir.http'].sudo( ).binary_content(model='res.users', id=user_id, field=field, default_mimetype='image/png') else: status, headers, image_base64 = request.env[ 'ir.http'].binary_content(model='res.users', id=user_id, field=field, default_mimetype='image/png') if status == 301: return request.env['ir.http']._response_by_status( status, headers, image_base64) if status == 304: return werkzeug.wrappers.Response(status=304) if not image_base64: image_base64 = self._get_default_avatar() if not (width or height): width, height = tools.image_guess_size_from_field_name(field) image_base64 = tools.image_process(image_base64, size=(int(width), int(height)), crop=crop) content = base64.b64decode(image_base64) headers = http.set_safe_image_headers(headers, content) response = request.make_response(content, headers) response.status_code = status return response
def user_avatar(self, user_id=0, **post): status, headers, content = binary_content( model='res.users', id=user_id, field='image_medium', default_mimetype='image/png', env=request.env(user=SUPERUSER_ID)) if not content: img_path = modules.get_module_resource('web', 'static/src/img', 'placeholder.png') with open(img_path, 'rb') as f: image = f.read() content = base64.b64encode(image) if status == 304: return werkzeug.wrappers.Response(status=304) image_base64 = base64.b64decode(content) headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status = str(status) return response
def slide_get_image(self, slide_id, field='image_128', width=0, height=0, crop=False): # Protect infographics by limiting access to 256px (large) images if field not in ('image_128', 'image_256', 'image_512', 'image_1024', 'image_1920'): return werkzeug.exceptions.Forbidden() slide = request.env['slide.slide'].sudo().browse(slide_id).exists() if not slide: raise werkzeug.exceptions.NotFound() status, headers, image_base64 = request.env['ir.http'].sudo( ).binary_content(model='slide.slide', id=slide.id, field=field, default_mimetype='image/png') if status == 301: return request.env['ir.http']._response_by_status( status, headers, image_base64) if status == 304: return werkzeug.wrappers.Response(status=304) if not image_base64: image_base64 = self._get_default_avatar() if not (width or height): width, height = tools.image_guess_size_from_field_name(field) image_base64 = tools.image_process(image_base64, size=(int(width), int(height)), crop=crop) content = base64.b64decode(image_base64) headers = http.set_safe_image_headers(headers, content) response = request.make_response(content, headers) response.status_code = status return response
def report_routes(self, reportname, docids=None, converter=None, **data): if converter == 'xlsx': report = request.env['ir.actions.report']._get_report_from_name( reportname) context = dict(request.env.context) if docids: docids = [int(i) for i in docids.split(',')] if data.get('options'): data.update(json.loads(data.pop('options'))) if data.get('context'): # Ignore 'lang' here, because the context in data is the one # from the webclient *but* if the user explicitely wants to # change the lang, this mechanism overwrites it. data['context'] = json.loads(data['context']) if data['context'].get('lang'): del data['context']['lang'] context.update(data['context']) xlsx = report.with_context(context).render_xlsx( docids, data=data )[0] report_name = report.report_file if report.print_report_name and not len(docids) > 1: obj = request.env[report.model].browse(docids[0]) report_name = safe_eval(report.print_report_name, {'object': obj, 'time': time}) xlsxhttpheaders = [ ('Content-Type', 'application/vnd.openxmlformats-' 'officedocument.spreadsheetml.sheet'), ('Content-Length', len(xlsx)), ( 'Content-Disposition', content_disposition(report_name + '.xlsx') ) ] return request.make_response(xlsx, headers=xlsxhttpheaders) return super(ReportController, self).report_routes( reportname, docids, converter, **data )
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 get_lang(request.env).code 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 attachment_add(self, **kwargs): attachment = request.env['ir.attachment'].browse(int(kwargs.get('id', 0))) return request.make_response( data=json.dumps(attachment.read(['id', 'name', 'mimetype', 'file_size', 'access_token'])[0]), headers=[('Content-Type', 'application/json')] )
def attachment_add(self, name, file, res_model, res_id, access_token=None, **kwargs): """Process a file uploaded from the portal chatter and create the corresponding `ir.attachment`. The attachment will be created "pending" until the associated message is actually created, and it will be garbage collected otherwise. :param name: name of the file to save. :type name: string :param file: the file to save :type file: werkzeug.FileStorage :param res_model: name of the model of the original document. To check access rights only, it will not be saved here. :type res_model: string :param res_id: id of the original document. To check access rights only, it will not be saved here. :type res_id: int :param access_token: access_token of the original document. To check access rights only, it will not be saved here. :type access_token: string :return: attachment data {id, name, mimetype, file_size, access_token} :rtype: dict """ try: self._document_check_access(res_model, int(res_id), access_token=access_token) except (AccessError, MissingError) as e: raise UserError( _("The document does not exist or you do not have the rights to access it." )) IrAttachment = request.env['ir.attachment'] access_token = False # Avoid using sudo or creating access_token when not necessary: internal # users can create attachments, as opposed to public and portal users. if not request.env.user.has_group('base.group_user'): IrAttachment = IrAttachment.sudo().with_context( binary_field_real_user=IrAttachment.env.user) access_token = IrAttachment._generate_access_token() # At this point the related message does not exist yet, so we assign # those specific res_model and res_is. They will be correctly set # when the message is created: see `portal_chatter_post`, # or garbage collected otherwise: see `_garbage_collect_attachments`. attachment = IrAttachment.create({ 'name': name, 'datas': base64.b64encode(file.read()), 'res_model': 'mail.compose.message', 'res_id': 0, 'access_token': access_token, }) return request.make_response(data=json.dumps( attachment.read( ['id', 'name', 'mimetype', 'file_size', 'access_token'])[0]), headers=[('Content-Type', 'application/json')])
def test_ignore_args_kw(self, a, **kw): return request.make_response(json.dumps(dict(a=a, kw=kw)))
def export_xls(self, data, token): jdata = json.loads(data) workbook = xlwt.Workbook() worksheet = workbook.add_sheet(jdata['title']) header_bold = xlwt.easyxf( "font: bold on; pattern: pattern solid, fore_colour gray25;") header_plain = xlwt.easyxf( "pattern: pattern solid, fore_colour gray25;") bold = xlwt.easyxf("font: bold on;") measure_count = jdata['measure_count'] origin_count = jdata['origin_count'] # Step 1: writing col group headers col_group_headers = jdata['col_group_headers'] # x,y: current coordinates # carry: queue containing cell information when a cell has a >= 2 height # and the drawing code needs to add empty cells below x, y, carry = 1, 0, deque() for i, header_row in enumerate(col_group_headers): worksheet.write(i, 0, '', header_plain) for header in header_row: while (carry and carry[0]['x'] == x): cell = carry.popleft() for j in range(measure_count * (2 * origin_count - 1)): worksheet.write(y, x + j, '', header_plain) if cell['height'] > 1: carry.append({'x': x, 'height': cell['height'] - 1}) x = x + measure_count * (2 * origin_count - 1) for j in range(header['width']): worksheet.write(y, x + j, header['title'] if j == 0 else '', header_plain) if header['height'] > 1: carry.append({'x': x, 'height': header['height'] - 1}) x = x + header['width'] while (carry and carry[0]['x'] == x): cell = carry.popleft() for j in range(measure_count * (2 * origin_count - 1)): worksheet.write(y, x + j, '', header_plain) if cell['height'] > 1: carry.append({'x': x, 'height': cell['height'] - 1}) x = x + measure_count * (2 * origin_count - 1) x, y = 1, y + 1 # Step 2: writing measure headers measure_headers = jdata['measure_headers'] if measure_headers: worksheet.write(y, 0, '', header_plain) for measure in measure_headers: style = header_bold if measure['is_bold'] else header_plain worksheet.write(y, x, measure['title'], style) for i in range(1, 2 * origin_count - 1): worksheet.write(y, x + i, '', header_plain) x = x + (2 * origin_count - 1) x, y = 1, y + 1 # Step 3: writing origin headers origin_headers = jdata['origin_headers'] if origin_headers: worksheet.write(y, 0, '', header_plain) for origin in origin_headers: style = header_bold if origin['is_bold'] else header_plain worksheet.write(y, x, origin['title'], style) x = x + 1 y = y + 1 # Step 4: writing data x = 0 for row in jdata['rows']: worksheet.write(y, x, row['indent'] * ' ' + ustr(row['title']), header_plain) for cell in row['values']: x = x + 1 if cell.get('is_bold', False): worksheet.write(y, x, cell['value'], bold) else: worksheet.write(y, x, cell['value']) x, y = 0, y + 1 response = request.make_response(None, headers=[ ('Content-Type', 'application/vnd.ms-excel'), ('Content-Disposition', 'attachment; filename=table.xls') ], cookies={'fileToken': token}) workbook.save(response.stream) return response
def test_ignore_args_converter(self, a, b='youhou', **kw): return request.make_response(json.dumps(dict(a=a, b=b, kw=kw)))
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 = _("%s (driven from: %s to %s)") % ( car_name, date_start, 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 test_ignore_args_converter_nokw(self, a, b='youhou'): return request.make_response(json.dumps(dict(a=a, b=b)))
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.with_user( request.website.user_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)])
def test_company_context(self): return request.make_response( json.dumps(request.context.get('allowed_company_ids')))
def test_ignore_args_a(self, a): return request.make_response(json.dumps(dict(a=a, kw=None)))