You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
6.2 KiB

7 years ago
  1. # -*- coding: utf-8 -*-
  2. ###################################################################################
  3. #
  4. # Copyright (C) 2017 MuK IT GmbH
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Affero General Public License as
  8. # published by the Free Software Foundation, either version 3 of the
  9. # License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Affero General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Affero General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. ###################################################################################
  20. import os
  21. import sys
  22. import json
  23. import uuid
  24. import base64
  25. import urllib
  26. import urllib2
  27. import logging
  28. import tempfile
  29. import urlparse
  30. import cStringIO
  31. import mimetypes
  32. import collections
  33. import werkzeug.exceptions
  34. from contextlib import closing
  35. from odoo import _
  36. from odoo import tools
  37. from odoo import http
  38. from odoo.http import request
  39. from odoo.http import Response
  40. _logger = logging.getLogger(__name__)
  41. try:
  42. import requests
  43. except ImportError:
  44. _logger.warn('Cannot `import requests`.')
  45. try:
  46. from cachetools import TTLCache
  47. pdf_cache = TTLCache(maxsize=25, ttl=1200)
  48. except ImportError:
  49. _logger.warn('Cannot `import cachetools`.')
  50. try:
  51. import pdfconv
  52. except ImportError:
  53. _logger.warn('Cannot `import pdfconv`.')
  54. class ExportAttachmentController(http.Controller):
  55. @http.route('/web/export/pdf/check/filename', auth="user", type='http')
  56. def check_filename(self, filename, **kw):
  57. return self.check_mimetype(mimetypes.guess_type(urllib.pathname2url(filename))[0])
  58. @http.route('/web/export/pdf/check/mimetype', auth="user", type='http')
  59. def check_mimetype(self, mimetype, **kw):
  60. if mimetype in ['application/msword',
  61. 'application/ms-word',
  62. 'application/vnd.ms-word.document.macroEnabled.12',
  63. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  64. 'application/vnd.oasis.opendocument.text',
  65. 'application/vnd.mspowerpoint',
  66. 'application/vnd.ms-powerpoint',
  67. 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
  68. 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  69. 'application/vnd.oasis.opendocument.presentation',
  70. 'application/vnd.msexcel',
  71. 'application/vnd.ms-excel',
  72. 'application/vnd.ms-excel.sheet.macroEnabled.12',
  73. 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
  74. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  75. 'application/vnd.oasis.opendocument.spreadsheet']:
  76. return Response(json.dumps(True), mimetype='application/json;charset=utf-8')
  77. else:
  78. return Response(json.dumps(False), mimetype='application/json;charset=utf-8')
  79. @http.route('/web/export/pdf', auth="user", type='http')
  80. def export(self, url, filename=None, force_compute=False, **kw):
  81. try:
  82. response = pdf_cache[url] if pdf_cache and not force_compute else None
  83. except KeyError:
  84. response = None
  85. if not response:
  86. return self._get_response(url, filename)
  87. return response
  88. def _get_response(self, url, filename):
  89. if not bool(urlparse.urlparse(url).netloc):
  90. method, params = self._get_route(url)
  91. response = method(**params)
  92. if not response.status_code == 200:
  93. return self._make_error_response(response.status_code,response.description if hasattr(response, 'description') else _("Unknown Error"))
  94. else:
  95. content_type = response.headers['content-type']
  96. data = response.data
  97. else:
  98. try:
  99. response = requests.get(url)
  100. content_type = response.headers['content-type']
  101. data = response.content
  102. except requests.exceptions.RequestException as exception:
  103. return self._make_error_response(exception.response.status_code, exception.response.reason or _("Unknown Error"))
  104. try:
  105. response = self._make_pdf_response(pdfconv.converter.convert_binary2pdf(data, content_type, None, format='binary'), filename or uuid.uuid4())
  106. pdf_cache[url] = response
  107. return response
  108. except KeyError:
  109. return werkzeug.exceptions.UnsupportedMediaType(_("The file couldn't be converted. Unsupported mine type."))
  110. except (ImportError, IOError, WindowsError) as error:
  111. _logger.error(error)
  112. return werkzeug.exceptions.InternalServerError(_("An error occurred during the process. Please contact your system administrator."))
  113. def _get_route(self, url):
  114. url_parts = url.split('?')
  115. path = url_parts[0]
  116. query_string = url_parts[1] if len(url_parts) > 1 else None
  117. router = request.httprequest.app.get_db_router(request.db).bind('')
  118. match = router.match(path, query_args=query_string)
  119. method = router.match(path, query_args=query_string)[0]
  120. params = dict(urlparse.parse_qsl(query_string))
  121. if len(match) > 1:
  122. params.update(match[1])
  123. return method, params
  124. def _make_error_response(self, status, message):
  125. exception = werkzeug.exceptions.HTTPException()
  126. exception.code = status
  127. exception.description = message
  128. return exception
  129. def _make_pdf_response(self, file, filename):
  130. headers = [('Content-Type', 'application/pdf'),
  131. ('Content-Disposition', 'attachment;filename={};'.format(filename)),
  132. ('Content-Length', len(file))]
  133. return request.make_response(file, headers)