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.

277 lines
11 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 xml.dom import minidom as minidom
  27. from openerp import models, fields, api, exceptions
  28. from openerp.tools.translate import _
  29. _logger = logging.getLogger(__name__)
  30. PAGE_PREFIX_PARAMETER = 'help_online_page_prefix'
  31. TEMPLATE_PREFIX_PARAMETER = 'help_online_template_prefix'
  32. AUTOBACKUP_PARAMETER = 'help_online_autobackup_path'
  33. HELP_ONLINE_SNIPPET_IMAGE_PATH = '/help_online/static/src/'\
  34. 'img/snippet/snippet_thumbs.png'
  35. class ExportHelpWizard(models.TransientModel):
  36. _name = "export.help.wizard"
  37. _description = 'Export Help Online'
  38. data = fields.Binary('XML', readonly=True)
  39. export_filename = fields.Char('Export XML Filename', size=128)
  40. def _manage_images_on_page(self, page_node, data_node):
  41. """
  42. - Extract images from page and generate a xml node
  43. - Replace db id in url with xml id
  44. """
  45. def substitute_id_by_xml_id(img_elem):
  46. new_src = False
  47. attach_id = False
  48. img_src = img_elem.get('src')
  49. if 'id=' in img_src:
  50. id_pos = img_src.index('id=') + 3
  51. attach_id = img_elem.get('src')[id_pos:]
  52. new_src = img_src.replace(attach_id, xml_id)
  53. else:
  54. fragments = img_src.split('ir.attachment/')
  55. attach_id, trail = fragments[1].split('_', 1)
  56. new_src = "/website/image/ir.attachment/%s|%s" % \
  57. (xml_id, trail)
  58. return new_src, attach_id
  59. i_img = 0
  60. img_model = 'ir.attachment'
  61. for img_elem in page_node.iter('img'):
  62. if img_model in img_elem.get('src'):
  63. i_img += 1
  64. xml_id = "%s_img_%s" % \
  65. (page_node.attrib['name'], str(i_img).rjust(2, '0'))
  66. new_src, attach_id = substitute_id_by_xml_id(img_elem)
  67. if not attach_id:
  68. continue
  69. image = self.env[img_model].browse(int(attach_id))
  70. if not image:
  71. continue
  72. img_elem.attrib['src'] = new_src
  73. img_node = ET.SubElement(data_node,
  74. 'record',
  75. attrib={'id': xml_id,
  76. 'model': img_model})
  77. field_node = ET.SubElement(img_node,
  78. 'field',
  79. attrib={'name': 'datas'})
  80. field_node.text = str(image.datas)
  81. field_node = ET.SubElement(img_node,
  82. 'field',
  83. attrib={'name': 'datas_fname'})
  84. field_node.text = image.datas_fname
  85. field_node = ET.SubElement(img_node,
  86. 'field',
  87. attrib={'name': 'name'})
  88. field_node.text = image.name
  89. field_node = ET.SubElement(img_node,
  90. 'field',
  91. attrib={'name': 'res_model'})
  92. field_node.text = image.res_model
  93. field_node = ET.SubElement(img_node,
  94. 'field',
  95. attrib={'name': 'mimetype'})
  96. field_node.text = image.mimetype
  97. data_node.append(img_node)
  98. def _clean_href_urls(self, page_node, page_prefix, template_prefix):
  99. """
  100. Remove host address for href urls
  101. """
  102. for a_elem in page_node.iter('a'):
  103. if not a_elem.get('href'):
  104. continue
  105. href = a_elem.get('href')
  106. if not href.startswith('http'):
  107. continue
  108. page_url = '/page/%s' % page_prefix
  109. template_url = '/page/%s' % template_prefix
  110. if page_url not in href and template_url not in href:
  111. continue
  112. elif page_url in href and template_url not in href:
  113. pass
  114. elif page_url not in href and template_url in href:
  115. page_url = template_url
  116. else:
  117. if page_prefix in template_prefix:
  118. page_url = template_url
  119. else:
  120. pass
  121. if page_url:
  122. trail = href.split(page_url, 1)[1]
  123. a_elem.attrib['href'] = page_url + trail
  124. def _generate_snippet_from_template(self, page_node,
  125. template_id, template_prefix):
  126. """
  127. Generate a website snippet from a template
  128. """
  129. page = copy.deepcopy(page_node)
  130. snippet = ET.Element('template')
  131. snippet.attrib['id'] = template_id + '_snippet'
  132. snippet.attrib['inherit_id'] = 'website.snippets'
  133. snippet.attrib['name'] = page_node.attrib['name']
  134. xpath = ET.SubElement(snippet,
  135. 'xpath',
  136. attrib={'expr': "//div[@id='snippet_structure']",
  137. 'position': 'inside'})
  138. main_div = ET.SubElement(xpath,
  139. 'div')
  140. thumbnail = ET.SubElement(main_div,
  141. 'div',
  142. attrib={'class': 'oe_snippet_thumbnail'})
  143. ET.SubElement(thumbnail,
  144. 'img',
  145. attrib={'class': 'oe_snippet_thumbnail_img',
  146. 'src': HELP_ONLINE_SNIPPET_IMAGE_PATH})
  147. span = ET.SubElement(thumbnail,
  148. 'span',
  149. attrib={'class': 'oe_snippet_thumbnail_title'})
  150. span.text = page_node.attrib['name'].replace(template_prefix, '')
  151. body = ET.SubElement(main_div,
  152. 'section',
  153. attrib={'class': 'oe_snippet_body '
  154. 'mt_simple_snippet'})
  155. template = page.find(".//div[@id='wrap']")
  156. for node in template.getchildren():
  157. body.append(node)
  158. return snippet
  159. def _get_qweb_views_data(self):
  160. parameter_model = self.env['ir.config_parameter']
  161. page_prefix = parameter_model.get_param(PAGE_PREFIX_PARAMETER,
  162. False)
  163. template_prefix = parameter_model.get_param(TEMPLATE_PREFIX_PARAMETER,
  164. False)
  165. if not page_prefix or not template_prefix:
  166. return False
  167. domain = [('type', '=', 'qweb'),
  168. ('page', '=', True),
  169. '|',
  170. ('name', 'like', '%s%%' % page_prefix),
  171. ('name', 'like', '%s%%' % template_prefix)]
  172. view_data_list = self.env['ir.ui.view'].search_read(domain,
  173. ['arch', 'name'],
  174. order='name')
  175. xml_to_export = ET.Element('openerp')
  176. data_node = ET.SubElement(xml_to_export, 'data')
  177. for view_data in view_data_list:
  178. parser = ET.XMLParser(remove_blank_text=True)
  179. root = ET.XML(view_data['arch'], parser=parser)
  180. root.tag = 'template'
  181. template_id = root.attrib.pop('t-name')
  182. root.attrib['name'] = view_data['name'].replace('website.', '')
  183. root.attrib['id'] = template_id
  184. root.attrib['page'] = 'True'
  185. self._manage_images_on_page(root, data_node)
  186. self._clean_href_urls(root, page_prefix, template_prefix)
  187. data_node.append(root)
  188. if root.attrib['name'].startswith(template_prefix):
  189. snippet = self._generate_snippet_from_template(root,
  190. template_id,
  191. template_prefix)
  192. data_node.append(snippet)
  193. if len(view_data_list) > 0:
  194. rough_string = ET.tostring(xml_to_export, encoding='utf-8',
  195. xml_declaration=True)
  196. reparsed = minidom.parseString(rough_string)
  197. return reparsed.toprettyxml(indent=" ", encoding='utf-8')
  198. else:
  199. return False
  200. @api.multi
  201. def export_help(self):
  202. """
  203. Export all Qweb views related to help online in a Odoo
  204. data XML file
  205. """
  206. xml_data = self._get_qweb_views_data()
  207. if not xml_data:
  208. raise exceptions.Warning(_('No data to export !'))
  209. out = base64.encodestring(xml_data)
  210. self.write({'data': out,
  211. 'export_filename': 'help_online_data.xml'})
  212. return {
  213. 'name': 'Help Online Export',
  214. 'type': 'ir.actions.act_window',
  215. 'res_model': self._name,
  216. 'view_mode': 'form',
  217. 'view_type': 'form',
  218. 'res_id': self.id,
  219. 'views': [(False, 'form')],
  220. 'target': 'new',
  221. }
  222. @api.model
  223. def auto_backup(self):
  224. """
  225. Export data to a file on home directory of user
  226. """
  227. parameter_model = self.env['ir.config_parameter']
  228. autobackup_path = parameter_model.get_param(AUTOBACKUP_PARAMETER,
  229. False)
  230. if autobackup_path:
  231. xml_data = self._get_qweb_views_data()
  232. try:
  233. timestr = time.strftime("%Y%m%d-%H%M%S")
  234. filename = '%s/help_online_backup-%s.xml' % (autobackup_path,
  235. timestr)
  236. backup_file = open(filename,
  237. 'w')
  238. backup_file.write(xml_data)
  239. backup_file.close
  240. except:
  241. _logger.warning(_('Unable to write autobackup file '
  242. 'in given directory: %s'
  243. % autobackup_path))