diff --git a/help_online/__openerp__.py b/help_online/__openerp__.py
index c27f0e55..832d3be1 100644
--- a/help_online/__openerp__.py
+++ b/help_online/__openerp__.py
@@ -3,11 +3,19 @@
#
# Authors: Nemry Jonathan
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -45,9 +53,13 @@ help page.
The help pages are created and managed via the website Module.
""",
'data': [
+ 'data/help_auto_backup.xml', # must always be the first
'security/help_online_groups.xml',
'views/help_online_view.xml',
'views/website_help_online.xml',
+ 'views/ir_ui_view_view.xml',
+ 'views/export_help_wizard_view.xml',
+ 'data/ir_config_parameter_data.xml',
],
'qweb': [
'static/src/xml/help_online.xml',
diff --git a/help_online/data/help_auto_backup.xml b/help_online/data/help_auto_backup.xml
new file mode 100644
index 00000000..ac6dcd57
--- /dev/null
+++ b/help_online/data/help_auto_backup.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/help_online/data/ir_config_parameter_data.xml b/help_online/data/ir_config_parameter_data.xml
new file mode 100644
index 00000000..94f859fa
--- /dev/null
+++ b/help_online/data/ir_config_parameter_data.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ help_online_autobackup_path
+ False
+
+
+
+
+
+ help_online_page_prefix
+ help
+
+
+
+
+
+ help_online_template_prefix
+ help-template
+
+
+
+
\ No newline at end of file
diff --git a/help_online/models/__init__.py b/help_online/models/__init__.py
index fe7c6a0f..fd36eaa5 100644
--- a/help_online/models/__init__.py
+++ b/help_online/models/__init__.py
@@ -3,11 +3,19 @@
#
# Authors: Nemry Jonathan
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,3 +27,5 @@
#
##############################################################################
from . import help_online
+from . import export_help_wizard
+from . import ir_model
diff --git a/help_online/models/export_help_wizard.py b/help_online/models/export_help_wizard.py
new file mode 100644
index 00000000..c98ed837
--- /dev/null
+++ b/help_online/models/export_help_wizard.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Authors: Cédric Pigeon
+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+import logging
+import base64
+import time
+import copy
+
+from lxml import etree as ET
+from xml.dom import minidom as minidom
+from openerp import models, fields, api, exceptions
+from openerp.tools.translate import _
+
+_logger = logging.getLogger(__name__)
+
+PAGE_PREFIX_PARAMETER = 'help_online_page_prefix'
+TEMPLATE_PREFIX_PARAMETER = 'help_online_template_prefix'
+AUTOBACKUP_PARAMETER = 'help_online_autobackup_path'
+HELP_ONLINE_SNIPPET_IMAGE_PATH = '/help_online/static/src/'\
+ 'img/snippet/snippet_thumbs.png'
+
+
+class ExportHelpWizard(models.TransientModel):
+ _name = "export.help.wizard"
+ _description = 'Export Help Online'
+
+ data = fields.Binary('XML', readonly=True)
+ export_filename = fields.Char('Export XML Filename', size=128)
+
+ def _manage_images_on_page(self, page_node, data_node):
+ '''
+ - Extract images from page and generate a xml node
+ - Replace db id in url with xml id
+ '''
+
+ def substitute_id_by_xml_id():
+ new_src = False
+ attach_id = False
+ if 'id=' in img_src:
+ id_pos = img_src.index('id=') + 3
+ attach_id = img_elem.get('src')[id_pos:]
+ new_src = img_src.replace(attach_id, xml_id)
+ else:
+ fragments = img_src.split('ir.attachment/')
+ attach_id, trail = fragments[1].split('_', 1)
+ new_src = "/website/image/ir.attachment/%s|%s" % \
+ (xml_id, trail)
+ return new_src, attach_id
+
+ i_img = 0
+ img_model = 'ir.attachment'
+ for img_elem in page_node.iter('img'):
+ if img_model in img_elem.get('src'):
+ i_img += 1
+ xml_id = "%s_img_%s" % \
+ (page_node.attrib['name'], str(i_img).rjust(2, '0'))
+ img_src = img_elem.get('src')
+
+ new_src, attach_id = substitute_id_by_xml_id()
+
+ if not attach_id:
+ continue
+
+ image = self.env[img_model].browse(int(attach_id))
+ if not image:
+ continue
+
+ img_elem.attrib['src'] = new_src
+ img_node = ET.SubElement(data_node,
+ 'record',
+ attrib={'id': xml_id,
+ 'model': img_model})
+ field_node = ET.SubElement(img_node,
+ 'field',
+ attrib={'name': 'datas'})
+ field_node.text = str(image.datas)
+ field_node = ET.SubElement(img_node,
+ 'field',
+ attrib={'name': 'datas_fname'})
+ field_node.text = image.datas_fname
+ field_node = ET.SubElement(img_node,
+ 'field',
+ attrib={'name': 'name'})
+ field_node.text = image.name
+ field_node = ET.SubElement(img_node,
+ 'field',
+ attrib={'name': 'res_model'})
+ field_node.text = image.res_model
+ field_node = ET.SubElement(img_node,
+ 'field',
+ attrib={'name': 'mimetype'})
+ field_node.text = image.mimetype
+ data_node.append(img_node)
+
+ def _clean_href_urls(self, page_node, page_prefix, template_prefix):
+ '''
+ Remove host address for href urls
+ '''
+ for a_elem in page_node.iter('a'):
+ if not a_elem.get('href'):
+ continue
+ href = a_elem.get('href')
+ if not href.startswith('http:'):
+ continue
+ page_url = '/page/%s' % page_prefix
+ template_url = '/page/%s' % template_prefix
+ if not page_url in href and not template_url in href:
+ continue
+ elif page_url in href and not template_url in href:
+ pass
+ elif not page_url in href and template_url in href:
+ page_url = template_url
+ else:
+ if page_prefix in template_prefix:
+ page_url = template_url
+ else:
+ pass
+
+ if page_url:
+ trail = href.split(page_url, 1)[1]
+ a_elem.attrib['href'] = page_url + trail
+
+ def _generate_snippet_from_template(self, page_node,
+ template_id, template_prefix):
+ '''
+ Generate a website snippet from a template
+ '''
+ page = copy.deepcopy(page_node)
+ snippet = ET.Element('template')
+ snippet.attrib['id'] = template_id + '_snippet'
+ snippet.attrib['inherit_id'] = 'website.snippets'
+ snippet.attrib['name'] = page_node.attrib['name']
+
+ xpath = ET.SubElement(snippet,
+ 'xpath',
+ attrib={'expr': "//div[@id='snippet_structure']",
+ 'position': 'inside'})
+ main_div = ET.SubElement(xpath,
+ 'div')
+ thumbnail = ET.SubElement(main_div,
+ 'div',
+ attrib={'class': 'oe_snippet_thumbnail'})
+ img = ET.SubElement(thumbnail,
+ 'img',
+ attrib={'class': 'oe_snippet_thumbnail_img',
+ 'src': HELP_ONLINE_SNIPPET_IMAGE_PATH})
+ span = ET.SubElement(thumbnail,
+ 'span',
+ attrib={'class': 'oe_snippet_thumbnail_title'})
+ span.text = page_node.attrib['name'].replace(template_prefix, '')
+ body = ET.SubElement(main_div,
+ 'section',
+ attrib={'class': 'oe_snippet_body '\
+ 'mt_simple_snippet'})
+
+ template = page.find(".//div[@id='wrap']")
+
+ for node in template.getchildren():
+ body.append(node)
+
+ return snippet
+
+ def _get_qweb_views_data(self):
+ parameter_model = self.env['ir.config_parameter']
+ page_prefix = parameter_model.get_param(PAGE_PREFIX_PARAMETER,
+ False)
+ template_prefix = parameter_model.get_param(TEMPLATE_PREFIX_PARAMETER,
+ False)
+
+ if not page_prefix or not template_prefix:
+ return False
+
+ domain = [('type', '=', 'qweb'),
+ ('page', '=', True),
+ '|',
+ ('name', 'like', '%s%%' % page_prefix),
+ ('name', 'like', '%s%%' % template_prefix)]
+
+ view_data_list = self.env['ir.ui.view'].search_read(domain,
+ ['arch', 'name'],
+ order='name')
+ xml_to_export = ET.Element('openerp')
+ data_node = ET.SubElement(xml_to_export, 'data')
+
+ for view_data in view_data_list:
+ parser = ET.XMLParser(remove_blank_text=True)
+ root = ET.XML(view_data['arch'], parser=parser)
+
+ root.tag = 'template'
+ template_id = root.attrib.pop('t-name')
+ root.attrib['name'] = view_data['name'].replace('website.', '')
+ root.attrib['id'] = template_id
+ root.attrib['page'] = 'True'
+
+ self._manage_images_on_page(root, data_node)
+ self._clean_href_urls(root, page_prefix, template_prefix)
+ data_node.append(root)
+
+ if root.attrib['name'].startswith(template_prefix):
+ snippet = self._generate_snippet_from_template(root,
+ template_id,
+ template_prefix)
+ data_node.append(snippet)
+
+ if len(view_data_list) > 0:
+ rough_string = ET.tostring(xml_to_export, encoding='utf-8',
+ xml_declaration=True)
+ reparsed = minidom.parseString(rough_string)
+ return reparsed.toprettyxml(indent=" ", encoding='utf-8')
+ else:
+ return False
+
+ @api.multi
+ def export_help(self):
+ """
+ Export all Qweb views related to help online in a Odoo
+ data XML file
+ """
+ xml_data = self._get_qweb_views_data()
+ if not xml_data:
+ raise exceptions.Warning(_('No data to export !'))
+ out = base64.encodestring(xml_data)
+
+ self.write({'data': out,
+ 'export_filename': 'help_online_data.xml'})
+
+ return {
+ 'name': 'Help Online Export',
+ 'type': 'ir.actions.act_window',
+ 'res_model': self._name,
+ 'view_mode': 'form',
+ 'view_type': 'form',
+ 'res_id': self.id,
+ 'views': [(False, 'form')],
+ 'target': 'new',
+ }
+
+ @api.model
+ def auto_backup(self):
+ """
+ Export data to a file on home directory of user
+ """
+ parameter_model = self.env['ir.config_parameter']
+ autobackup_path = parameter_model.get_param(AUTOBACKUP_PARAMETER,
+ False)
+
+ if autobackup_path:
+ xml_data = self._get_qweb_views_data()
+ try:
+ timestr = time.strftime("%Y%m%d-%H%M%S")
+ filename = '%s/help_online_backup-%s.xml' % (autobackup_path,
+ timestr)
+ backup_file = open(filename,
+ 'w')
+ backup_file.write(xml_data)
+ backup_file.close
+ except:
+ _logger.warning(_('Unable to write autobackup file '\
+ 'in given directory: %s'
+ % autobackup_path))
diff --git a/help_online/models/help_online.py b/help_online/models/help_online.py
index 15e41a70..5a076cd6 100644
--- a/help_online/models/help_online.py
+++ b/help_online/models/help_online.py
@@ -3,11 +3,19 @@
#
# Authors: Laurent Mignon
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,15 +26,20 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm
+from openerp import models, exceptions
from openerp.tools.translate import _
-class HelpOnline(orm.TransientModel):
+class HelpOnline(models.TransientModel):
_name = 'help.online'
def _get_view_name(self, model, view_type, domain=None, context=None):
- name = 'help-%s' % model.replace('.', '-')
+ parameter_model = self.env['ir.config_parameter']
+ page_prefix = parameter_model.get_param('help_online_page_prefix',
+ False)
+ if not page_prefix:
+ raise exceptions.Warning(_('No page prefix parameter specified !'))
+ name = '%s-%s' % (page_prefix, model.replace('.', '-'))
return name
def page_exists(self, name):
@@ -40,7 +53,7 @@ class HelpOnline(orm.TransientModel):
ir_model = self.env['ir.model']
description = self.env[model]._description
res = ir_model.name_search(model, operator='=')
- if res:
+ if(res):
description = res[0][1]
name = self._get_view_name(model, view_type, domain, context)
if self.page_exists(name):
diff --git a/help_online/models/ir_model.py b/help_online/models/ir_model.py
new file mode 100644
index 00000000..8cbccf69
--- /dev/null
+++ b/help_online/models/ir_model.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Authors: Cédric Pigeon
+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+from openerp import models, api
+
+from lxml import etree as ET
+
+
+class ir_model_data(models.Model):
+ _inherit = 'ir.model.data'
+
+ @api.model
+ def _update(self, model, module, values, xml_id=False, store=True,
+ noupdate=False, mode='init', res_id=False):
+
+ if model == 'ir.ui.view':
+ parameter_model = self.env['ir.config_parameter']
+ page_prefix = parameter_model.get_param('help_online_page_prefix',
+ False)
+ if page_prefix and xml_id.startswith('website.%s' % page_prefix):
+ xml_str = self.manageImageReferences(values['arch'], module)
+ values['arch'] = xml_str
+
+ return super(ir_model_data, self)._update(model,
+ module,
+ values,
+ xml_id=xml_id,
+ store=store,
+ noupdate=noupdate,
+ mode=mode,
+ res_id=res_id)
+
+ def manageImageReferences(self, xml_str, module):
+ parser = ET.XMLParser(remove_blank_text=True)
+ root = ET.XML(xml_str, parser=parser)
+ img_model = 'ir.attachment'
+ for img_elem in root.iter('img'):
+ if img_model in img_elem.get('src'):
+ img_src = img_elem.get('src')
+ try:
+ if '/ir.attachment/' in img_src:
+ fragments = img_src.split('/ir.attachment/')
+ xml_id = fragments[1].split('|')[0]
+ img_src = img_src.replace("|", "_")
+ else:
+ id_pos = img_src.index('id=') + 3
+ xml_id = img_elem.get('src')[id_pos:]
+
+ img_id = self.get_object_reference(module,
+ xml_id)
+
+ img_elem.attrib['src'] = img_src.replace(xml_id,
+ str(img_id[1]))
+ except:
+ continue
+ return ET.tostring(root, encoding='utf-8', xml_declaration=False)
diff --git a/help_online/static/description/icon.png b/help_online/static/description/icon.png
new file mode 100644
index 00000000..ab8d478d
Binary files /dev/null and b/help_online/static/description/icon.png differ
diff --git a/help_online/static/description/index.html b/help_online/static/description/index.html
index e2edb627..7cab8f1b 100644
--- a/help_online/static/description/index.html
+++ b/help_online/static/description/index.html
@@ -12,5 +12,6 @@
The help pages are created and managed via the website Module.
+ If you want to export your work, you simply have to use the export wizard through the settings menu.
diff --git a/help_online/static/src/img/snippet/snippet_thumbs.png b/help_online/static/src/img/snippet/snippet_thumbs.png
new file mode 100644
index 00000000..4d5c2d89
Binary files /dev/null and b/help_online/static/src/img/snippet/snippet_thumbs.png differ
diff --git a/help_online/tests/__init__.py b/help_online/tests/__init__.py
new file mode 100644
index 00000000..6085ce18
--- /dev/null
+++ b/help_online/tests/__init__.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+from . import test_export_help_wizard
+
+fast_suite = [
+]
+
+checks = [
+ test_export_help_wizard,
+]
diff --git a/help_online/tests/data/help_test_data.xml b/help_online/tests/data/help_test_data.xml
new file mode 100644
index 00000000..3d939d21
--- /dev/null
+++ b/help_online/tests/data/help_test_data.xml
@@ -0,0 +1,78 @@
+
+
+
+
+ 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
+
+ image
+ test.png
+ test.png
+ ir.ui.view
+ image/png
+
+
+
\ No newline at end of file
diff --git a/help_online/tests/test_export_help_wizard.py b/help_online/tests/test_export_help_wizard.py
new file mode 100644
index 00000000..6494f121
--- /dev/null
+++ b/help_online/tests/test_export_help_wizard.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Authors: Cédric Pigeon
+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs.
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contact a Free Software
+# Service Company.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+import logging
+import base64
+from lxml import etree as ET
+
+from anybox.testing.openerp import SharedSetupTransactionCase
+
+_logger = logging.getLogger(__name__)
+
+
+class test_export_help_wizard(object):
+ _data_files = ('data/help_test_data.xml',)
+
+ _module_ns = 'help_online'
+
+ def createPage(self, pageName, imgXmlId=False):
+ imgId = False
+ if imgXmlId:
+ imgId = self.ref('%s.%s' % (self._module_ns, imgXmlId))
+
+ rootNode = ET.Element('t')
+ rootNode.attrib['name'] = pageName
+ rootNode.attrib['t-name'] = "website.%s" % pageName
+ tNode = ET.SubElement(rootNode,
+ 't',
+ attrib={'t-call': 'website.layout'})
+ structDivNode = ET.SubElement(tNode,
+ 'div',
+ attrib={'class': 'oe_structure oe_empty',
+ 'id': 'wrap'})
+ sectionNode = ET.SubElement(structDivNode,
+ 'section',
+ attrib={'class': 'mt16 mb16'})
+ containerNode = ET.SubElement(sectionNode,
+ 'div',
+ attrib={'class': 'container'})
+ rowNode = ET.SubElement(containerNode,
+ 'div',
+ attrib={'class': 'row'})
+ bodyDivNode = ET.SubElement(rowNode,
+ 'div',
+ attrib={'class': 'col-md-12 '\
+ 'text-center mt16 mb32'})
+ style = "font-family: 'Helvetica Neue', Helvetica,"\
+ " Arial, sans-serif; color: rgb(51, 51, 51);"\
+ " text-align: left;"
+ h2Node = ET.SubElement(bodyDivNode,
+ 'h2',
+ attrib={'style': style})
+ h2Node.text = "Test Sample Title"
+ if imgId:
+ imgDivNode = ET.SubElement(bodyDivNode,
+ 'div',
+ attrib={'style': 'text-align: left;'})
+ src = "/website/image?field=datas&"\
+ "model=ir.attachment&id=%s" % str(imgId)
+ imgNode = ET.SubElement(imgDivNode,
+ 'img',
+ attrib={'class': 'img-thumbnail',
+ 'src': src})
+ imgDivNode = ET.SubElement(bodyDivNode,
+ 'div',
+ attrib={'style': 'text-align: left;'})
+ src = "/website/image/ir.attachment/%s_ccc838d/datas" % str(imgId)
+ imgNode = ET.SubElement(imgDivNode,
+ 'img',
+ attrib={'class': 'img-thumbnail',
+ 'src': src})
+ arch = ET.tostring(rootNode, encoding='utf-8', xml_declaration=False)
+ vals = {
+ 'name': pageName,
+ 'type': 'qweb',
+ 'arch': arch,
+ 'page': True,
+ }
+ view_id = self.env['ir.ui.view'].create(vals)
+ return view_id.id
+
+ def setUp(self):
+ super(test_export_help_wizard, self).setUp()
+ self.pageName = False
+ self.imgXmlId = False
+ self.pageTemplate = False
+
+ def test_export_help(self):
+ '''
+ Export help data
+ '''
+ self.createPage(pageName=self.pageName, imgXmlId=self.imgXmlId)
+
+ wizardPool = self.env['export.help.wizard']
+ wizard = wizardPool.create({})
+ wizard.export_help()
+ xmlData = base64.decodestring(wizard.data)
+
+ parser = ET.XMLParser(remove_blank_text=True)
+ rootXml = ET.XML(xmlData, parser=parser)
+
+ xPath = ".//template[@id='website.%s']" % self.pageName
+ templateNodeList = rootXml.findall(xPath)
+ self.assertEqual(len(templateNodeList), 1)
+ self.assertNotIn("website.", templateNodeList[0].attrib['name'])
+
+ if self.imgXmlId:
+ xPath = ".//record[@id='%s_img_01']" % self.pageName
+ imgNodeList = rootXml.findall(xPath)
+ self.assertEqual(len(imgNodeList), 1)
+
+ for imgElem in templateNodeList[0].iter('img'):
+ imgSrc = imgElem.get('src')
+ if '/ir.attachment/' in imgSrc:
+ self.assertIn("/ir.attachment/%s_img_02|" \
+ % self.pageName, imgSrc)
+ else:
+ self.assertIn("id=%s_img_01" % self.pageName, imgSrc)
+
+ if self.pageTemplate:
+ xPath = ".//template[@id='website.%s_snippet']" % self.pageName
+ templateNodeList = rootXml.findall(xPath)
+ self.assertEqual(len(templateNodeList), 1)
+ self.assertNotIn("website.", templateNodeList[0].attrib['name'])
+
+
+class test_export_help_with_image(test_export_help_wizard,
+ SharedSetupTransactionCase):
+ def setUp(self):
+ super(test_export_help_with_image, self).setUp()
+ parameter_model = self.env['ir.config_parameter']
+ page_prefix = parameter_model.get_param('help_online_page_prefix')
+ self.pageName = '%stest-page' % page_prefix
+ self.imgXmlId = 'test_img_1'
+
+
+class test_export_help_template(test_export_help_wizard,
+ SharedSetupTransactionCase):
+ def setUp(self):
+ super(test_export_help_template, self).setUp()
+ parameter_model = self.env['ir.config_parameter']
+ template_prefix = parameter_model.get_param(
+ 'help_online_template_prefix')
+ self.pageName = '%stest-template' % template_prefix
+ self.pageTemplate = True
diff --git a/help_online/views/export_help_wizard_view.xml b/help_online/views/export_help_wizard_view.xml
new file mode 100644
index 00000000..7e5235df
--- /dev/null
+++ b/help_online/views/export_help_wizard_view.xml
@@ -0,0 +1,56 @@
+
+
+
+
+ export.help.wizard.view
+ export.help.wizard
+ form
+
+
+
+
+
+
+ Export Help
+ export.help.wizard
+
+ form
+ form
+ new
+ ir.actions.act_window
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/help_online/views/ir_ui_view_view.xml b/help_online/views/ir_ui_view_view.xml
new file mode 100644
index 00000000..85fc7783
--- /dev/null
+++ b/help_online/views/ir_ui_view_view.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ ir.ui.view search (help_online)
+
+ ir.ui.view
+
+
+
+
+
+
+
+
+ ir.ui.view form (help_online)
+
+ ir.ui.view
+
+
+
+
+
+
+
+
+