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.

299 lines
12 KiB

  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Authors: Cédric Pigeon
  5. # Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as published
  9. # by the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. import logging
  22. import base64
  23. import time
  24. import copy
  25. from lxml import etree as ET
  26. from openerp import models, fields, api, exceptions
  27. from openerp.tools.translate import _
  28. _logger = logging.getLogger(__name__)
  29. PAGE_PREFIX_PARAMETER = 'help_online_page_prefix'
  30. TEMPLATE_PREFIX_PARAMETER = 'help_online_template_prefix'
  31. AUTOBACKUP_PARAMETER = 'help_online_autobackup_path'
  32. HELP_ONLINE_SNIPPET_IMAGE_PATH = '/help_online/static/src/'\
  33. 'img/snippet/snippet_thumbs.png'
  34. class ExportHelpWizard(models.TransientModel):
  35. _name = "export.help.wizard"
  36. _description = 'Export Help Online'
  37. data = fields.Binary('XML', readonly=True)
  38. export_filename = fields.Char('Export XML Filename', size=128)
  39. def _manage_images_on_page(self, page_node, data_node, images_reference):
  40. """
  41. - Extract images from page and generate a xml node
  42. - Replace db id in url with xml id
  43. """
  44. def get_attach_id(images_reference,
  45. img_model, img_src, generated_xml_id=False):
  46. attach_id = False
  47. if 'id=' in img_src:
  48. id_pos = img_src.index('id=') + 3
  49. attach_id = img_src[id_pos:]
  50. else:
  51. fragments = img_src.split('ir.attachment/')
  52. attach_id, _ = fragments[1].split('_', 1)
  53. if attach_id in images_reference:
  54. xml_id = images_reference[attach_id]
  55. else:
  56. ir_data = self.env['ir.model.data'].search(
  57. [('model', '=', img_model),
  58. ('res_id', '=', attach_id)])
  59. xml_id = generated_xml_id
  60. if ir_data:
  61. xml_id = ir_data[0].name
  62. images_reference[attach_id] = xml_id
  63. return attach_id, xml_id
  64. def substitute_id_by_xml_id(img_src, attach_id, xml_id):
  65. new_src = False
  66. if 'id=' in img_src:
  67. new_src = img_src.replace(attach_id, xml_id)
  68. else:
  69. fragments = img_src.split('ir.attachment/')
  70. _, trail = fragments[1].split('_', 1)
  71. new_src = "/website/image/ir.attachment/%s|%s" % \
  72. (xml_id, trail)
  73. return new_src
  74. i_img = 0
  75. img_model = 'ir.attachment'
  76. for img_elem in page_node.iter('img'):
  77. img_src = img_elem.get('src')
  78. if img_model in img_src:
  79. i_img += 1
  80. generated_xml_id = "%s_img_%s" % \
  81. (page_node.attrib['name'], str(i_img).rjust(2, '0'))
  82. attach_id, xml_id = get_attach_id(images_reference,
  83. img_model,
  84. img_src,
  85. generated_xml_id)
  86. new_src = substitute_id_by_xml_id(img_src, attach_id, xml_id)
  87. if not attach_id:
  88. continue
  89. image = self.env[img_model].browse(int(attach_id))
  90. if not image:
  91. continue
  92. img_elem.attrib['src'] = new_src
  93. img_node = ET.SubElement(data_node,
  94. 'record',
  95. attrib={'id': xml_id,
  96. 'model': img_model})
  97. field_node = ET.SubElement(img_node,
  98. 'field',
  99. attrib={'name': 'datas'})
  100. field_node.text = str(image.datas)
  101. field_node = ET.SubElement(img_node,
  102. 'field',
  103. attrib={'name': 'datas_fname'})
  104. field_node.text = image.datas_fname
  105. field_node = ET.SubElement(img_node,
  106. 'field',
  107. attrib={'name': 'name'})
  108. field_node.text = image.name
  109. field_node = ET.SubElement(img_node,
  110. 'field',
  111. attrib={'name': 'res_model'})
  112. field_node.text = image.res_model
  113. field_node = ET.SubElement(img_node,
  114. 'field',
  115. attrib={'name': 'mimetype'})
  116. field_node.text = image.mimetype
  117. data_node.append(img_node)
  118. def _clean_href_urls(self, page_node, page_prefix, template_prefix):
  119. """
  120. Remove host address for href urls
  121. """
  122. for a_elem in page_node.iter('a'):
  123. if not a_elem.get('href'):
  124. continue
  125. href = a_elem.get('href')
  126. if not href.startswith('http'):
  127. continue
  128. page_url = '/page/%s' % page_prefix
  129. template_url = '/page/%s' % template_prefix
  130. if page_url not in href and template_url not in href:
  131. continue
  132. elif page_url in href and template_url not in href:
  133. pass
  134. elif page_url not in href and template_url in href:
  135. page_url = template_url
  136. else:
  137. if page_prefix in template_prefix:
  138. page_url = template_url
  139. else:
  140. pass
  141. if page_url:
  142. trail = href.split(page_url, 1)[1]
  143. a_elem.attrib['href'] = page_url + trail
  144. def _generate_snippet_from_template(self, page_node,
  145. template_id, template_prefix):
  146. """
  147. Generate a website snippet from a template
  148. """
  149. page = copy.deepcopy(page_node)
  150. snippet = ET.Element('template')
  151. snippet.attrib['id'] = template_id + '_snippet'
  152. snippet.attrib['inherit_id'] = 'website.snippets'
  153. snippet.attrib['name'] = page_node.attrib['name']
  154. xpath = ET.SubElement(snippet,
  155. 'xpath',
  156. attrib={'expr': "//div[@id='snippet_structure']",
  157. 'position': 'inside'})
  158. main_div = ET.SubElement(xpath,
  159. 'div')
  160. thumbnail = ET.SubElement(main_div,
  161. 'div',
  162. attrib={'class': 'oe_snippet_thumbnail'})
  163. ET.SubElement(thumbnail,
  164. 'img',
  165. attrib={'class': 'oe_snippet_thumbnail_img',
  166. 'src': HELP_ONLINE_SNIPPET_IMAGE_PATH})
  167. span = ET.SubElement(thumbnail,
  168. 'span',
  169. attrib={'class': 'oe_snippet_thumbnail_title'})
  170. span.text = page_node.attrib['name'].replace(template_prefix, '')
  171. body = ET.SubElement(main_div,
  172. 'section',
  173. attrib={'class': 'oe_snippet_body '
  174. 'mt_simple_snippet'})
  175. template = page.find(".//div[@id='wrap']")
  176. for node in template.getchildren():
  177. body.append(node)
  178. return snippet
  179. def _get_qweb_views_data(self):
  180. parameter_model = self.env['ir.config_parameter']
  181. page_prefix = parameter_model.get_param(PAGE_PREFIX_PARAMETER,
  182. False)
  183. template_prefix = parameter_model.get_param(TEMPLATE_PREFIX_PARAMETER,
  184. False)
  185. if not page_prefix or not template_prefix:
  186. return False
  187. domain = [('type', '=', 'qweb'),
  188. ('page', '=', True),
  189. '|',
  190. ('name', 'like', '%s%%' % page_prefix),
  191. ('name', 'like', '%s%%' % template_prefix)]
  192. view_data_list = self.env['ir.ui.view'].search_read(domain,
  193. ['arch', 'name'],
  194. order='name')
  195. xml_to_export = ET.Element('openerp')
  196. data_node = ET.SubElement(xml_to_export, 'data')
  197. images_reference = {}
  198. for view_data in view_data_list:
  199. parser = ET.XMLParser(remove_blank_text=True)
  200. root = ET.XML(view_data['arch'], parser=parser)
  201. root.tag = 'template'
  202. template_id = root.attrib.pop('t-name')
  203. root.attrib['name'] = view_data['name'].replace('website.', '')
  204. root.attrib['id'] = template_id
  205. root.attrib['page'] = 'True'
  206. self._manage_images_on_page(root, data_node, images_reference)
  207. self._clean_href_urls(root, page_prefix, template_prefix)
  208. data_node.append(root)
  209. if root.attrib['name'].startswith(template_prefix):
  210. snippet = self._generate_snippet_from_template(root,
  211. template_id,
  212. template_prefix)
  213. data_node.append(snippet)
  214. if len(view_data_list) > 0:
  215. return ET.tostring(xml_to_export, encoding='utf-8',
  216. xml_declaration=True,
  217. pretty_print=True)
  218. else:
  219. return False
  220. @api.multi
  221. def export_help(self):
  222. """
  223. Export all Qweb views related to help online in a Odoo
  224. data XML file
  225. """
  226. xml_data = self._get_qweb_views_data()
  227. if not xml_data:
  228. raise exceptions.Warning(_('No data to export !'))
  229. out = base64.encodestring(xml_data)
  230. self.write({'data': out,
  231. 'export_filename': 'help_online_data.xml'})
  232. return {
  233. 'name': _('Export Help'),
  234. 'type': 'ir.actions.act_window',
  235. 'res_model': self._name,
  236. 'view_mode': 'form',
  237. 'view_type': 'form',
  238. 'res_id': self.id,
  239. 'views': [(False, 'form')],
  240. 'target': 'new',
  241. }
  242. @api.model
  243. def auto_backup(self):
  244. """
  245. Export data to a file on home directory of user
  246. """
  247. parameter_model = self.env['ir.config_parameter']
  248. autobackup_path = parameter_model.get_param(AUTOBACKUP_PARAMETER,
  249. False)
  250. if autobackup_path:
  251. xml_data = self._get_qweb_views_data()
  252. try:
  253. timestr = time.strftime("%Y%m%d-%H%M%S")
  254. filename = '%s/help_online_backup-%s.xml' % (autobackup_path,
  255. timestr)
  256. backup_file = open(filename,
  257. 'w')
  258. backup_file.write(xml_data)
  259. backup_file.close
  260. except:
  261. _logger.warning(_('Unable to write autobackup file '
  262. 'in given directory: %s'
  263. % autobackup_path))