Xml description refactory

This commit is contained in:
Mauricio Baeza 2019-11-26 23:32:39 -06:00
parent 43f8bf5a88
commit 71d1c281d6
6 changed files with 224 additions and 72 deletions

2
.gitignore vendored
View File

@ -12,4 +12,6 @@ source/source/
.env/ .env/
virtual/ virtual/
source/VERSION

View File

@ -15,9 +15,9 @@ For Python 3.6+
This extension have a cost of maintenance of 1 euro every year. This extension have a cost of maintenance of 1 euro every year.
BCH: `1RPLWHJW34p7pMQV1ft4x7eWhAYw69Dsb` BCH: `qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d`
BTC: `3Fe4JuADrAK8Qs7GDAxbSXR8E54avwZJLW` BTC: `3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV`
PayPal :( donate ATT elmau DOT net PayPal :( donate ATT elmau DOT net

View File

@ -1 +1 @@
0.10.0 0.11.0

View File

@ -633,5 +633,8 @@ DATA = {
} }
with open('VERSION', 'w') as f:
f.write(VERSION)
# ~ LICENSE_ACCEPT_BY = 'user' # or admin # ~ LICENSE_ACCEPT_BY = 'user' # or admin
# ~ LICENSE_SUPPRESS_ON_UPDATE = True # ~ LICENSE_SUPPRESS_ON_UPDATE = True

View File

@ -34,6 +34,7 @@ import shlex
import shutil import shutil
import socket import socket
import subprocess import subprocess
import ssl
import sys import sys
import tempfile import tempfile
import threading import threading
@ -41,13 +42,9 @@ import time
import traceback import traceback
import zipfile import zipfile
# ~ from collections import OrderedDict
# ~ from collections.abc import MutableMapping
from functools import wraps from functools import wraps
from operator import itemgetter
from pathlib import Path, PurePath from pathlib import Path, PurePath
from pprint import pprint from pprint import pprint
from enum import Enum, IntEnum
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError from urllib.error import URLError, HTTPError
from string import Template from string import Template
@ -96,15 +93,6 @@ from com.sun.star.awt.grid import XGridDataListener
from com.sun.star.awt.grid import XGridSelectionListener from com.sun.star.awt.grid import XGridSelectionListener
class FontSlant(IntEnum):
NONE = 0
OBLIQUE = 1
ITALIC = 2
DONTKNOW = 3
REVERSE_OBLIQUE = 4
REVERSE_ITALIC = 5
try: try:
from fernet import Fernet, InvalidToken from fernet import Fernet, InvalidToken
except ImportError: except ImportError:
@ -124,7 +112,6 @@ KEY = {
SEPARATION = 5 SEPARATION = 5
MSG_LANG = { MSG_LANG = {
'es': { 'es': {
'OK': 'Aceptar', 'OK': 'Aceptar',
@ -135,35 +122,36 @@ MSG_LANG = {
} }
} }
OS = platform.system() OS = platform.system()
USER = getpass.getuser() USER = getpass.getuser()
PC = platform.node() PC = platform.node()
DESKTOP = os.environ.get('DESKTOP_SESSION', '') DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path)) INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
IS_WIN = OS == 'Windows' IS_WIN = OS == 'Windows'
LOG_NAME = 'ZAZ' LOG_NAME = 'ZAZ'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
PYTHON = 'python' PYTHON = 'python'
if IS_WIN: if IS_WIN:
PYTHON = 'python.exe' PYTHON = 'python.exe'
CALC = 'calc' CALC = 'calc'
WRITER = 'writer' WRITER = 'writer'
OBJ_CELL = 'ScCellObj' OBJ_CELL = 'ScCellObj'
OBJ_RANGE = 'ScCellRangeObj' OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj' OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES) OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
TEXT_RANGE = 'SwXTextRange'
TEXT_RANGES = 'SwXTextRanges'
TEXT_TYPE_RANGES = (TEXT_RANGE, TEXT_RANGES)
TYPE_DOC = { TYPE_DOC = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument', 'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument', 'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument', 'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument', 'draw': 'com.sun.star.drawing.DrawingDocument',
# ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'base': 'com.sun.star.sdb.DocumentDataSource', 'base': 'com.sun.star.sdb.DocumentDataSource',
'math': 'com.sun.star.formula.FormulaProperties', 'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE', 'basic': 'com.sun.star.script.BasicIDE',
@ -202,19 +190,16 @@ MENUS_WRITER = {
'windows': '.uno:WindowList', 'windows': '.uno:WindowList',
'help': '.uno:HelpMenu', 'help': '.uno:HelpMenu',
} }
MENUS_APP = { MENUS_APP = {
'main': MENUS_MAIN, 'main': MENUS_MAIN,
'calc': MENUS_CALC, 'calc': MENUS_CALC,
'writer': MENUS_WRITER, 'writer': MENUS_WRITER,
} }
EXT = { EXT = {
'pdf': 'pdf', 'pdf': 'pdf',
} }
FILE_NAME_DEBUG = 'debug.odt' FILE_NAME_DEBUG = 'debug.odt'
FILE_NAME_CONFIG = 'zaz-{}.json' FILE_NAME_CONFIG = 'zaz-{}.json'
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s' LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
@ -284,6 +269,12 @@ def mri(obj):
return return
def inspect(obj):
zaz = create_instance('net.elmau.zaz.inspect')
zaz.inspect(obj)
return
def catch_exception(f): def catch_exception(f):
@wraps(f) @wraps(f)
def func(*args, **kwargs): def func(*args, **kwargs):
@ -459,12 +450,14 @@ def call_dispatch(url, args=()):
return return
def get_temp_file(): def get_temp_file(only_name=False):
delete = True delete = True
if IS_WIN: if IS_WIN:
delete = False delete = False
return tempfile.NamedTemporaryFile(delete=delete) tmp = tempfile.NamedTemporaryFile(delete=delete)
if only_name:
tmp = tmp.name
return tmp
def _path_url(path): def _path_url(path):
if path.startswith('file://'): if path.startswith('file://'):
@ -506,6 +499,11 @@ def dict_to_property(values, uno_any=False):
return ps return ps
def dict_to_named(values):
ps = tuple([NamedValue(n, v) for n, v in values.items()])
return ps
def property_to_dict(values): def property_to_dict(values):
d = {i.Name: i.Value for i in values} d = {i.Name: i.Value for i in values}
return d return d
@ -1008,7 +1006,9 @@ class LOCalc(LODocument):
def __getitem__(self, index): def __getitem__(self, index):
if isinstance(index, str): if isinstance(index, str):
index = [s.Name for s in self._sheets if s.CodeName == index][0] or index code_name = [s.Name for s in self._sheets if s.CodeName == index]
if code_name:
index = code_name[0]
return LOCalcSheet(self._sheets[index], self) return LOCalcSheet(self._sheets[index], self)
def __setitem__(self, key, value): def __setitem__(self, key, value):
@ -1376,7 +1376,11 @@ class LOWriter(LODocument):
@property @property
def selection(self): def selection(self):
sel = self.obj.getCurrentSelection() sel = self.obj.getCurrentSelection()
return LOTextRange(sel[0]) if sel.ImplementationName == TEXT_RANGES:
return LOTextRange(sel[0])
elif sel.ImplementationName == TEXT_RANGE:
return LOTextRange(sel)
return sel
def write(self, data, cursor=None): def write(self, data, cursor=None):
cursor = cursor or self.selection.cursor.getEnd() cursor = cursor or self.selection.cursor.getEnd()
@ -1653,7 +1657,7 @@ class LODrawImpress(LODocument):
def insert_image(self, path, **kwargs): def insert_image(self, path, **kwargs):
w = kwargs.get('width', 3000) w = kwargs.get('width', 3000)
h = kwargs.get('Height', 1000) h = kwargs.get('Height', 3000)
x = kwargs.get('X', 1000) x = kwargs.get('X', 1000)
y = kwargs.get('Y', 1000) y = kwargs.get('Y', 1000)
@ -1710,6 +1714,9 @@ class LOCellRange(object):
def __getitem__(self, index): def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc) return LOCellRange(self.obj[index], self.doc)
def __contains__(self, item):
return item.in_range(self)
def _init_values(self): def _init_values(self):
self._type_obj = self.obj.ImplementationName self._type_obj = self.obj.ImplementationName
self._type_content = EMPTY self._type_content = EMPTY
@ -1759,7 +1766,7 @@ class LOCellRange(object):
self.obj.setFormula(data) self.obj.setFormula(data)
else: else:
self.obj.setString(data) self.obj.setString(data)
elif isinstance(data, (int, float)): elif isinstance(data, (int, float, bool)):
self.obj.setValue(data) self.obj.setValue(data)
elif isinstance(data, datetime.datetime): elif isinstance(data, datetime.datetime):
d = data.toordinal() d = data.toordinal()
@ -1777,8 +1784,6 @@ class LOCellRange(object):
return self.obj.getDataArray() return self.obj.getDataArray()
@data.setter @data.setter
def data(self, values): def data(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setDataArray(values) self.obj.setDataArray(values)
@property @property
@ -1786,8 +1791,6 @@ class LOCellRange(object):
return self.obj.getFormulaArray() return self.obj.getFormulaArray()
@formula.setter @formula.setter
def formula(self, values): def formula(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setFormulaArray(values) self.obj.setFormulaArray(values)
@property @property
@ -1812,13 +1815,19 @@ class LOCellRange(object):
cursor.collapseToSize(cols, rows) cursor.collapseToSize(cols, rows)
return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc) return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
def copy_from(self, rango): def copy_from(self, rango, formula=False):
data = rango data = rango
if isinstance(rango, LOCellRange): if isinstance(rango, LOCellRange):
data = rango.data if formula:
data = rango.formula
else:
data = rango.data
rows = len(data) rows = len(data)
cols = len(data[0]) cols = len(data[0])
self.to_size(rows, cols).data = data if formula:
self.to_size(rows, cols).formula = data
else:
self.to_size(rows, cols).data = data
return return
def copy_to(self, cell, formula=False): def copy_to(self, cell, formula=False):
@ -1833,6 +1842,33 @@ class LOCellRange(object):
self.sheet.obj.copyRange(self.address, source.range_address) self.sheet.obj.copyRange(self.address, source.range_address)
return return
def transpose(self, formula=False):
data = self.data
if formula:
data = self.formula
data = tuple(zip(*data))
self.clear(1023)
self[0,0].copy_from(data, formula=formula)
return
def transpose2(self):
# ~ 'Flags': 'A',
# ~ 'FormulaCommand': 0,
# ~ 'SkipEmptyCells': False,
# ~ 'AsLink': False,
# ~ 'MoveMode': 4,
args = {
'Transpose': True,
}
args = dict_to_property(args)
self.select()
copy()
self.clear(1023)
self[0,0].select()
call_dispatch('.uno:InsertContents', args)
set_clipboard('')
return
def offset(self, row=1, col=0): def offset(self, row=1, col=0):
ra = self.obj.getRangeAddress() ra = self.obj.getRangeAddress()
col = ra.EndColumn + col col = ra.EndColumn + col
@ -1935,6 +1971,10 @@ class LOCellRange(object):
def auto_format(self, value): def auto_format(self, value):
self.obj.autoFormat(value) self.obj.autoFormat(value)
def auto_width(self):
self.obj.Columns.OptimalWidth = True
return
def insert_image(self, path, **kwargs): def insert_image(self, path, **kwargs):
s = self.obj.Size s = self.obj.Size
w = kwargs.get('width', s.Width) w = kwargs.get('width', s.Width)
@ -2056,6 +2096,22 @@ class LOCellRange(object):
found = self.obj.replaceAll(descriptor) found = self.obj.replaceAll(descriptor)
return found return found
@property
def validation(self):
return self.obj.Validation
@validation.setter
def validation(self, values):
is_list = False
current = self.validation
for k, v in values.items():
if k == 'Type' and v == 6:
is_list = True
if k == 'Formula1' and is_list:
if isinstance(v, (tuple, list)):
v = ';'.join(['"{}"'.format(i) for i in v])
setattr(current, k, v)
self.obj.Validation = current
class EventsListenerBase(unohelper.Base, XEventListener): class EventsListenerBase(unohelper.Base, XEventListener):
@ -2178,7 +2234,10 @@ class EventsItem(EventsListenerBase, XItemListener):
pass pass
def itemStateChanged(self, event): def itemStateChanged(self, event):
pass event_name = '{}_item_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsItemRoadmap(EventsItem): class EventsItemRoadmap(EventsItem):
@ -2447,23 +2506,39 @@ class UnoBaseObject(object):
return self._model.Width return self._model.Width
@width.setter @width.setter
def width(self, value): def width(self, value):
if hasattr(self.obj, 'PosSize'): self.model.Width = value
self._set_possize('Width', value)
else: @property
self._model.Width = value def ps_width(self):
return self._get_possize('Width')
@ps_width.setter
def ps_width(self, value):
self._set_possize('Width', value)
@property @property
def height(self): def height(self):
if hasattr(self._model, 'Height'): return self.model.Height
return self._model.Height
ps = self.obj.getPosSize()
return ps.Height
@height.setter @height.setter
def height(self, value): def height(self, value):
if hasattr(self.obj, 'PosSize'): self.model.Height = value
self._set_possize('Height', value)
else: @property
self._model.Height = value def ps_height(self):
return self._get_possize('Height')
@ps_height.setter
def ps_height(self, value):
self._set_possize('Height', value)
@property
def size(self):
ps = self.obj.getPosSize()
return (ps.Width, ps.Height)
@size.setter
def size(self, value):
ps = self.obj.getPosSize()
ps.Width = value[0]
ps.Height = value[1]
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, SIZE)
@property @property
def tag(self): def tag(self):
@ -2526,8 +2601,12 @@ class UnoBaseObject(object):
def move(self, origin, x=0, y=5): def move(self, origin, x=0, y=5):
if x: if x:
self.x = origin.x + origin.width + x self.x = origin.x + origin.width + x
else:
self.x = origin.x
if y: if y:
self.y = origin.y + origin.height + y self.y = origin.y + origin.height + y
else:
self.y = origin.y
return return
def possize(self, origin): def possize(self, origin):
@ -2771,7 +2850,6 @@ class UnoGrid(UnoBaseObject):
def sort(self, column, asc=True): def sort(self, column, asc=True):
self._gdm.sortByColumn(column, asc) self._gdm.sortByColumn(column, asc)
# ~ self._data.sort(key=itemgetter(column), reverse=not asc)
self.update_row_heading() self.update_row_heading()
return return
@ -3022,7 +3100,6 @@ def get_control_model(control):
return services[control] return services[control]
@catch_exception
def add_listeners(events, control, name=''): def add_listeners(events, control, name=''):
listeners = { listeners = {
'addActionListener': EventsButton, 'addActionListener': EventsButton,
@ -3891,7 +3968,6 @@ class LOWindow(object):
return properties return properties
@catch_exception
def add_control(self, properties): def add_control(self, properties):
tipo = properties.pop('Type').lower() tipo = properties.pop('Type').lower()
root = properties.pop('Root', '') root = properties.pop('Root', '')
@ -4207,8 +4283,12 @@ def json_loads(data):
def get_path_extension(id): def get_path_extension(id):
path = ''
pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider') pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
path = _path_system(pip.getPackageLocation(id)) try:
path = _path_system(pip.getPackageLocation(id))
except Exception as e:
error(e)
return path return path
@ -4405,23 +4485,29 @@ def popen(command, stdin=None):
yield (e.errno, e.strerror) yield (e.errno, e.strerror)
def url_open(url, options={}, json=False): def url_open(url, options={}, verify=True, json=False):
data = '' data = ''
err = ''
req = Request(url) req = Request(url)
try: try:
response = urlopen(req) if verify:
# ~ response.info() response = urlopen(req)
else:
context = ssl._create_unverified_context()
response = urlopen(req, context=context)
except HTTPError as e: except HTTPError as e:
error(e) error(e)
err = str(e)
except URLError as e: except URLError as e:
error(e.reason) error(e.reason)
err = str(e.reason)
else: else:
if json: if json:
data = json_loads(response.read()) data = json_loads(response.read())
else: else:
data = response.read() data = response.read()
return data return data, err
def run(command, wait=False): def run(command, wait=False):
@ -4475,7 +4561,7 @@ def _zippwd(source, target, pwd):
# ~ Export ok # ~ Export ok
def zip(source, target='', mode='w', pwd=''): def zip_files(source, target='', mode='w', pwd=''):
if pwd: if pwd:
return _zippwd(source, target, pwd) return _zippwd(source, target, pwd)
@ -5398,8 +5484,6 @@ class LIBOServer(object):
return instance return instance
# ~ controls = { # ~ controls = {
# ~ 'CheckBox': 'com.sun.star.awt.UnoControlCheckBoxModel', # ~ 'CheckBox': 'com.sun.star.awt.UnoControlCheckBoxModel',
# ~ 'ComboBox': 'com.sun.star.awt.UnoControlComboBoxModel', # ~ 'ComboBox': 'com.sun.star.awt.UnoControlComboBoxModel',

View File

@ -53,18 +53,23 @@ class LiboXML(object):
'help': 'application/vnd.sun.star.help', 'help': 'application/vnd.sun.star.help',
'component': 'application/vnd.sun.star.uno-components', 'component': 'application/vnd.sun.star.uno-components',
} }
NAME_SPACES = { NS_MANIFEST = {
'manifest_version': '1.2', 'manifest_version': '1.2',
'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
'xmlns:loext': 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0', 'xmlns:loext': 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0',
} }
NS_DESCRIPTION = {
'xmlns': 'http://openoffice.org/extensions/description/2006',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'xmlns:d': 'http://openoffice.org/extensions/description/2006',
}
def __init__(self): def __init__(self):
self._manifest = None self._manifest = None
self._paths = [] self._paths = []
def _save_path(self, attr): def _save_path(self, attr):
self._paths.append(attr['{{{}}}full-path'.format(self.NAME_SPACES['manifest'])]) self._paths.append(attr['{{{}}}full-path'.format(self.NS_MANIFEST['manifest'])])
return return
def _clean(self, name, nodes): def _clean(self, name, nodes):
@ -86,17 +91,17 @@ class LiboXML(object):
def new_manifest(self, data): def new_manifest(self, data):
attr = { attr = {
'manifest:version': self.NAME_SPACES['manifest_version'], 'manifest:version': self.NS_MANIFEST['manifest_version'],
'xmlns:manifest': self.NAME_SPACES['manifest'], 'xmlns:manifest': self.NS_MANIFEST['manifest'],
'xmlns:loext': self.NAME_SPACES['xmlns:loext'], 'xmlns:loext': self.NS_MANIFEST['xmlns:loext'],
} }
self._manifest = ET.Element('manifest:manifest', attr) self._manifest = ET.Element('manifest:manifest', attr)
return self.add_data_manifest(data) return self.add_data_manifest(data)
def parse_manifest(self, data): def parse_manifest(self, data):
ET.register_namespace('manifest', self.NAME_SPACES['manifest']) ET.register_namespace('manifest', self.NS_MANIFEST['manifest'])
self._manifest = ET.fromstring(data) self._manifest = ET.fromstring(data)
data = {'xmlns:loext': self.NAME_SPACES['xmlns:loext']} data = {'xmlns:loext': self.NS_MANIFEST['xmlns:loext']}
self._manifest.attrib.update(**data) self._manifest.attrib.update(**data)
self._clean('manifest', self._manifest) self._clean('manifest', self._manifest)
return return
@ -116,6 +121,60 @@ class LiboXML(object):
ET.SubElement(self._manifest, node_name, attr) ET.SubElement(self._manifest, node_name, attr)
return self._get_xml(self._manifest) return self._get_xml(self._manifest)
def new_description(self, data):
doc = ET.Element('description', self.NS_DESCRIPTION)
key = 'identifier'
ET.SubElement(doc, key, data[key])
key = 'version'
ET.SubElement(doc, key, data[key])
key = 'display-name'
node = ET.SubElement(doc, key)
for k, v in data[key].items():
sn = ET.SubElement(node, 'name', {'lang': k})
sn.text = v
node = ET.SubElement(doc, 'extension-description')
for k in data[key].keys():
attr = {
'lang': k,
'xlink:href': f'description/desc_{k}.txt',
}
ET.SubElement(node, 'src', attr)
key = 'icon'
node = ET.SubElement(doc, key)
attr = {'xlink:href': f"images/{data[key]}"}
ET.SubElement(node, 'default', attr)
key = 'publisher'
node = ET.SubElement(doc, key)
for k, v in data[key].items():
attr = {
'xlink:href': v['link'],
'lang': k,
}
sn = ET.SubElement(node, 'name', attr)
sn.text = v['text']
key = 'display-name'
node = ET.SubElement(doc, 'registration')
attr = {
'accept-by': 'user',
'suppress-on-update': 'true',
}
node = ET.SubElement(node, 'simple-license', attr)
for k in data[key].keys():
attr = {
'xlink:href': f"{DIRS['registration']}/license_{k}.txt",
'lang': k
}
ET.SubElement(node, 'license-text', attr)
return self._get_xml(doc)
def _get_xml(self, doc): def _get_xml(self, doc):
xml = parseString(ET.tostring(doc, encoding='utf-8')) xml = parseString(ET.tostring(doc, encoding='utf-8'))
return xml.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8') return xml.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')
@ -325,6 +384,13 @@ def _update_files():
data = xml.new_manifest(DATA['manifest']) data = xml.new_manifest(DATA['manifest'])
_save(path, data) _save(path, data)
path = _join(path_source, FILES['description'])
data = xml.new_description(DATA['description'])
_save(path, data)
path = _join(path_source, DIRS['office']) path = _join(path_source, DIRS['office'])
_mkdir(path) _mkdir(path)
path = _join(path_source, DIRS['office'], FILES['shortcut']) path = _join(path_source, DIRS['office'], FILES['shortcut'])
@ -333,9 +399,6 @@ def _update_files():
path = _join(path_source, FILES['addons']) path = _join(path_source, FILES['addons'])
_save(path, DATA['addons']) _save(path, DATA['addons'])
path = _join(path_source, FILES['description'])
_save(path, DATA['description'])
if TYPE_EXTENSION == 3: if TYPE_EXTENSION == 3:
path = _join(path_source, FILES['addin']) path = _join(path_source, FILES['addin'])
_save(path, DATA['addin']) _save(path, DATA['addin'])