diff --git a/CHANGELOG b/CHANGELOG
index 2332ccf..093ddeb 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+v 0.8.0 [13-oct-2019]
+ - Generate manifest
+ - Add support for execute dialogs in documents.
+
+
v 0.7.0 [27-sep-2019]
- Add support for InputBox and documents
diff --git a/VERSION b/VERSION
index faef31a..a3df0a6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.7.0
+0.8.0
diff --git a/source/conf.py.example b/source/conf.py.example
index 9e4f591..9be5aeb 100644
--- a/source/conf.py.example
+++ b/source/conf.py.example
@@ -205,6 +205,7 @@ PATHS = {
'regmerge': '/usr/lib/libreoffice/program/regmerge',
'soffice': ('soffice', PROGRAM, FILE_TEST),
'install': ('unopkg', 'add', '-v', '-f', '-s'),
+ 'profile': '/home/mau/.config/libreoffice/4/user',
}
@@ -427,20 +428,6 @@ FILE_ADDONS = f"""
"""
-NODE_ADDONS = '\n '
-if TYPE_EXTENSION > 1:
- NODE_ADDONS = f'\n '
-if TYPE_EXTENSION == 3:
- NODE_ADDONS += '\n '
-
-FILE_MANIFEST = f"""
-
-
- {NODE_ADDONS}
-
-"""
-
-
FILE_UPDATE = ''
if URL_XML_UPDATE:
FILE_UPDATE = f"""
@@ -625,9 +612,16 @@ FILE_SHORTCUTS = f"""
"""
+DATA_MANIFEST = [FILES['py'], f"Office/{FILES['shortcut']}", 'Addons.xcu']
+if TYPE_EXTENSION > 1:
+ DATA_MANIFEST.append(FILES['rdb'])
+if TYPE_EXTENSION == 3:
+ DATA_MANIFEST.append('CalcAddIn.xcu')
+
+
DATA = {
'py': FILE_PY,
- 'manifest': FILE_MANIFEST,
+ 'manifest': DATA_MANIFEST,
'description': FILE_DESCRIPTION,
'addons': FILE_ADDONS,
'update': FILE_UPDATE,
diff --git a/source/easymacro.py b/source/easymacro.py
index 5261700..218abbf 100644
--- a/source/easymacro.py
+++ b/source/easymacro.py
@@ -576,6 +576,10 @@ class LODocument(object):
def title(self, value):
self.obj.setTitle(value)
+ @property
+ def uid(self):
+ return self.obj.RuntimeUID
+
@property
def type(self):
return self._type_doc
@@ -2656,16 +2660,21 @@ class LODialog(object):
def _create(self, properties):
path = properties.pop('Path', '')
if path:
- dp = create_instance('com.sun.star.awt.DialogProvider2', True)
+ dp = create_instance('com.sun.star.awt.DialogProvider', True)
return dp.createDialog(_path_url(path))
- if 'Library' in properties:
- location = properties['Location']
+ if 'Location' in properties:
+ location = properties.get('Location', 'application')
+ library = properties.get('Library', 'Standard')
if location == 'user':
location = 'application'
- dp = create_instance('com.sun.star.awt.DialogProvider2', True)
+ dp = create_instance('com.sun.star.awt.DialogProvider', True)
path = 'vnd.sun.star.script:{}.{}?location={}'.format(
- properties['Library'], properties['Name'], location)
+ library, properties['Name'], location)
+ if location == 'document':
+ uid = get_document().uid
+ path = 'vnd.sun.star.tdoc:/{}/Dialogs/{}/{}.xml'.format(
+ uid, library, properties['Name'])
return dp.createDialog(path)
dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
@@ -2678,8 +2687,20 @@ class LODialog(object):
return dlg
- def _init_controls(self):
+ def _get_type_control(self, name):
+ types = {
+ 'stardiv.Toolkit.UnoFixedTextControl': 'label',
+ 'stardiv.Toolkit.UnoButtonControl': 'button',
+ 'stardiv.Toolkit.UnoEditControl': 'text',
+ }
+ return types[name]
+ def _init_controls(self):
+ for control in self.obj.getControls():
+ tipo = self._get_type_control(control.ImplementationName)
+ name = control.Model.Name
+ control = get_custom_class(tipo, control)
+ setattr(self, name, control)
return
@property
@@ -2699,23 +2720,8 @@ class LODialog(object):
self._connect_listeners()
def _connect_listeners(self):
-
- return
-
- def _add_listeners(self, control):
- if self.events is None:
- return
-
- listeners = {
- 'addActionListener': EventsButton,
- 'addMouseListener': EventsMouse,
- }
- for key, value in listeners.items():
- if hasattr(control.obj, key):
- if control.type == 'grid' and key == 'addMouseListener':
- control.obj.addMouseListener(EventsMouseGrid(self.events))
- continue
- getattr(control.obj, key)(listeners[key](self.events))
+ for control in self.obj.getControls():
+ add_listeners(self._events, control, control.Model.Name)
return
def open(self):
@@ -2740,23 +2746,6 @@ class LODialog(object):
}
return services[control]
- # ~ def _get_custom_class(self, tipo, obj):
- # ~ classes = {
- # ~ 'label': UnoLabel,
- # ~ 'button': UnoButton,
- # ~ 'text': UnoText,
- # ~ 'listbox': UnoListBox,
- # ~ 'grid': UnoGrid,
- # ~ 'link': UnoLink,
- # ~ 'tab': UnoTab,
- # ~ 'roadmap': UnoRoadmap,
- # ~ 'image': UnoImage,
- # ~ 'radio': UnoRadio,
- # ~ 'groupbox': UnoGroupBox,
- # ~ 'tree': UnoTree,
- # ~ }
- # ~ return classes[tipo](obj)
-
def _set_column_model(self, columns):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
column_model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
diff --git a/source/zaz.py b/source/zaz.py
index 5669ec1..867ac42 100644
--- a/source/zaz.py
+++ b/source/zaz.py
@@ -19,11 +19,16 @@
import argparse
import os
+import re
import sys
+import zipfile
+from datetime import datetime
from pathlib import Path
from shutil import copyfile
from subprocess import call
-import zipfile
+from xml.etree import ElementTree as ET
+from xml.dom.minidom import parseString
+
from conf import (
DATA,
@@ -37,6 +42,83 @@ from conf import (
log)
+class LiboXML(object):
+ TYPES = {
+ 'py': 'application/vnd.sun.star.uno-component;type=Python',
+ 'zip': 'application/binary',
+ 'xcu': 'application/vnd.sun.star.configuration-data',
+ 'rdb': 'application/vnd.sun.star.uno-typelibrary;type=RDB',
+ 'xcs': 'application/vnd.sun.star.configuration-schema',
+ 'help': 'application/vnd.sun.star.help',
+ }
+ NAME_SPACES = {
+ 'manifest_version': '1.2',
+ 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
+ 'xmlns:loext': 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0',
+ }
+
+ def __init__(self):
+ self._manifest = None
+ self._paths = []
+
+ def _save_path(self, attr):
+ self._paths.append(attr['{{{}}}full-path'.format(self.NAME_SPACES['manifest'])])
+ return
+
+ def _clean(self, name, nodes):
+ has_words = re.compile('\\w')
+
+ if not re.search(has_words, str(nodes.tail)):
+ nodes.tail = ''
+ if not re.search(has_words, str(nodes.text)):
+ nodes.text = ''
+
+ for node in nodes:
+ if name == 'manifest':
+ self._save_path(node.attrib)
+ if not re.search(has_words, str(node.tail)):
+ node.tail = ''
+ if not re.search(has_words, str(node.text)):
+ node.text = ''
+ return
+
+ def new_manifest(self, data):
+ attr = {
+ 'manifest:version': self.NAME_SPACES['manifest_version'],
+ 'xmlns:manifest': self.NAME_SPACES['manifest'],
+ 'xmlns:loext': self.NAME_SPACES['xmlns:loext'],
+ }
+ self._manifest = ET.Element('manifest:manifest', attr)
+ return self.add_data_manifest(data)
+
+ def parse_manifest(self, data):
+ ET.register_namespace('manifest', self.NAME_SPACES['manifest'])
+ self._manifest = ET.fromstring(data)
+ data = {'xmlns:loext': self.NAME_SPACES['xmlns:loext']}
+ self._manifest.attrib.update(**data)
+ self._clean('manifest', self._manifest)
+ return
+
+ def add_data_manifest(self, data):
+ node_name = 'manifest:file-entry'
+ attr = {
+ 'manifest:full-path': '',
+ 'manifest:media-type': '',
+ }
+ for path in data:
+ if path in self._paths:
+ continue
+ ext = path.split('.')[-1]
+ attr['manifest:full-path'] = path
+ attr['manifest:media-type'] = self.TYPES.get(ext, '')
+ ET.SubElement(self._manifest, node_name, attr)
+ return self._get_xml(self._manifest)
+
+ def _get_xml(self, doc):
+ xml = parseString(ET.tostring(doc, encoding='utf-8'))
+ return xml.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')
+
+
def _exists(path):
return os.path.exists(path)
@@ -222,8 +304,11 @@ def _update_files():
target = _join(path_source, 'pythonpath', source)
copyfile(source, target)
+ xml = LiboXML()
+
path = _join(path_source, DIRS['meta'], FILES['manifest'])
- _save(path, DATA['manifest'])
+ data = xml.new_manifest(DATA['manifest'])
+ _save(path, data)
path = _join(path_source, DIRS['office'])
_mkdir(path)
@@ -265,7 +350,96 @@ def _new():
return
+def _get_info_path(path):
+ path, filename = os.path.split(path)
+ name, extension = os.path.splitext(filename)
+ return (path, filename, name, extension)
+
+
+def _zip_embed(source, files):
+ PATH = 'Scripts/python/'
+ EASYMACRO = 'easymacro.'
+
+ p, f, name, e = _get_info_path(source)
+ now = datetime.now().strftime('_%Y%m%d_%H%M%S')
+ path_source = _join(p, name + now + e)
+ copyfile(source, path_source)
+ target = source
+
+ with zipfile.PyZipFile(EASYMACRO + 'zip', mode='w') as zf:
+ zf.writepy(EASYMACRO + 'py')
+
+ xml = LiboXML()
+
+ path_easymacro = PATH + EASYMACRO + 'zip'
+ names = [f[1] for f in files] + [path_easymacro]
+ nodes = []
+ with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as zt:
+ with zipfile.ZipFile(path_source, compression=zipfile.ZIP_DEFLATED) as zs:
+ for name in zs.namelist():
+ if FILES['manifest'] in name:
+ path_manifest = name
+ xml_manifest = zs.open(name).read()
+ elif name in names:
+ continue
+ else:
+ zt.writestr(name, zs.open(name).read())
+
+ data = []
+ for path, name in files:
+ data.append(name)
+ zt.write(path, name)
+
+ zt.write(EASYMACRO + 'zip', path_easymacro)
+ data.append(path_easymacro)
+
+ xml.parse_manifest(xml_manifest)
+ xml_manifest = xml.add_data_manifest(data)
+ zt.writestr(path_manifest, xml_manifest)
+
+ os.unlink(EASYMACRO + 'zip')
+ return
+
+
+def _embed(args):
+ PATH = 'Scripts/python'
+ PYTHONPATH = 'pythonpath'
+
+ doc = args.document
+ if not doc:
+ msg = '-d/--document Path file to embed is mandatory'
+ log.error(msg)
+ return
+ if not _exists(doc):
+ msg = 'Path file not exists'
+ log.error(msg)
+ return
+
+ files = []
+ if args.files:
+ files = args.files.split(',')
+ source = _join(PATHS['profile'], PATH)
+ content = os.listdir(source)
+ if PYTHONPATH in content:
+ content.remove(PYTHONPATH)
+
+ if files:
+ files = [(_join(source, f), _join(PATH, f)) for f in files if f in content]
+ else:
+ files = [(_join(source, f), _join(PATH, f)) for f in content]
+
+ _zip_embed(doc, files)
+
+ log.info('Embedded macros successfully...')
+ return
+
+
+
def main(args):
+ if args.embed:
+ _embed(args)
+ return
+
if args.new:
_new()
return
@@ -279,7 +453,7 @@ def main(args):
if args.install:
_install_and_test()
- log.info('Extension make sucesfully...')
+ log.info('Extension make successfully...')
return
@@ -290,6 +464,10 @@ def _process_command_line_arguments():
default=False, required=False)
parser.add_argument('-n', '--new', dest='new', action='store_true',
default=False, required=False)
+ parser.add_argument('-e', '--embed', dest='embed', action='store_true',
+ default=False, required=False)
+ parser.add_argument('-d', '--document', dest='document', default='')
+ parser.add_argument('-f', '--files', dest='files', default='')
return parser.parse_args()