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.

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