Implements the XCopyableBrowseNode interface on the Python script provider. Change-Id: I17c6916567518fd9f28d11724def4a0d98f53ed9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207737 Tested-by: Jenkins Reviewed-by: Neil Roberts <nroberts@libreoffice.org>
1364 lines
47 KiB
Python
1364 lines
47 KiB
Python
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
|
||
#
|
||
# This file is part of the LibreOffice project.
|
||
#
|
||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
#
|
||
# This file incorporates work covered by the following license notice:
|
||
#
|
||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||
# contributor license agreements. See the NOTICE file distributed
|
||
# with this work for additional information regarding copyright
|
||
# ownership. The ASF licenses this file to you under the Apache
|
||
# License, Version 2.0 (the "License"); you may not use this file
|
||
# except in compliance with the License. You may obtain a copy of
|
||
# the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
||
#
|
||
# XScript implementation for python
|
||
import uno
|
||
import unohelper
|
||
import sys
|
||
import os
|
||
import types
|
||
import time
|
||
import ast
|
||
import platform
|
||
import subprocess
|
||
import urllib.parse
|
||
import re
|
||
from com.sun.star.uri.RelativeUriExcessParentSegments import RETAIN
|
||
|
||
from com.sun.star.uno import RuntimeException
|
||
from com.sun.star.lang import IllegalArgumentException
|
||
from com.sun.star.container import NoSuchElementException
|
||
from com.sun.star.ucb import XCommandEnvironment, XProgressHandler, Command
|
||
from com.sun.star.task import XInteractionHandler
|
||
from com.sun.star.beans import XPropertySet, Property
|
||
from com.sun.star.container import XNameContainer
|
||
from com.sun.star.xml.sax import XDocumentHandler, InputSource
|
||
from com.sun.star.uno import Exception as UnoException
|
||
from com.sun.star.awt import XActionListener
|
||
|
||
from com.sun.star.script.provider import XScriptProvider, XScript, ScriptFrameworkErrorException
|
||
from com.sun.star.script.browse import XBrowseNode, XCreatableBrowseNode, XCopyableBrowseNode
|
||
from com.sun.star.script.browse import XEditableBrowseNode
|
||
from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER
|
||
from com.sun.star.system import SystemShellExecuteFlags
|
||
|
||
class LogLevel:
|
||
NONE = 0 # production level
|
||
ERROR = 1 # for script developers
|
||
DEBUG = 2 # for script framework developers
|
||
|
||
|
||
PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL"
|
||
PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT"
|
||
|
||
# Configuration ----------------------------------------------------
|
||
LogLevel.use = LogLevel.NONE
|
||
if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR":
|
||
LogLevel.use = LogLevel.ERROR
|
||
elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG":
|
||
LogLevel.use = LogLevel.DEBUG
|
||
|
||
# True, writes to stdout (difficult on windows)
|
||
# False, writes to user/Scripts/python/log.txt
|
||
LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0"
|
||
|
||
ENABLE_EDIT_DIALOG = False # offers a minimal editor for editing.
|
||
# -------------------------------------------------------------------
|
||
|
||
|
||
EXAMPLE_MACRO = """\
|
||
def exampleMacro():
|
||
# Add some text to the end of the document
|
||
text = XSCRIPTCONTEXT.getDocument().getText()
|
||
text.getEnd().setString("example text")
|
||
|
||
|
||
# lists the scripts, that shall be visible inside LibreOffice. Can be omitted,
|
||
# if all functions shall be visible
|
||
g_exportedScripts = (exampleMacro,)
|
||
"""
|
||
|
||
|
||
def encfile(uni):
|
||
return uni.encode(sys.getfilesystemencoding())
|
||
|
||
|
||
def lastException2String():
|
||
(excType, excInstance, excTraceback) = sys.exc_info()
|
||
ret = str(excType) + ": " + str(excInstance) + "\n" + uno._uno_extract_printable_stacktrace(excTraceback)
|
||
return ret
|
||
|
||
|
||
def logLevel2String(level):
|
||
ret = " NONE"
|
||
if level == LogLevel.ERROR:
|
||
ret = "ERROR"
|
||
elif level >= LogLevel.DEBUG:
|
||
ret = "DEBUG"
|
||
return ret
|
||
|
||
|
||
def getLogTarget():
|
||
ret = sys.stdout
|
||
if not LOG_STDOUT:
|
||
try:
|
||
pathSubst = uno.getComponentContext().ServiceManager.createInstance("com.sun.star.util.PathSubstitution")
|
||
userInstallation = pathSubst.getSubstituteVariableValue("user")
|
||
if len(userInstallation) > 0:
|
||
systemPath = uno.fileUrlToSystemPath(userInstallation + "/Scripts/python/log.txt")
|
||
ret = open(systemPath, "a")
|
||
except Exception:
|
||
print(
|
||
"Exception during creation of pythonscript logfile: "
|
||
+ lastException2String()
|
||
+ "\n, delegating log to stdout\n"
|
||
)
|
||
return ret
|
||
|
||
|
||
class Logger(LogLevel):
|
||
def __init__(self, target):
|
||
self.target = target
|
||
|
||
def isDebugLevel(self):
|
||
return self.use >= self.DEBUG
|
||
|
||
def debug(self, msg):
|
||
if self.isDebugLevel():
|
||
self.log(self.DEBUG, msg)
|
||
|
||
def isErrorLevel(self):
|
||
return self.use >= self.ERROR
|
||
|
||
def error(self, msg):
|
||
if self.isErrorLevel():
|
||
self.log(self.ERROR, msg)
|
||
|
||
def log(self, level, msg):
|
||
if self.use >= level:
|
||
try:
|
||
self.target.write(time.asctime() + " [" + logLevel2String(level) + "] " + msg + "\n")
|
||
self.target.flush()
|
||
except Exception:
|
||
print("Error during writing to stdout: " + lastException2String() + "\n")
|
||
|
||
|
||
log = Logger(getLogTarget())
|
||
|
||
log.debug("pythonscript loading")
|
||
|
||
LANGUAGENAME = "Python"
|
||
GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
|
||
CALLABLE_CONTAINER_NAME = "g_exportedScripts"
|
||
|
||
# pythonloader looks for a static g_ImplementationHelper variable
|
||
g_ImplementationHelper = unohelper.ImplementationHelper()
|
||
g_implName = "org.libreoffice.pyuno.LanguageScriptProviderFor" + LANGUAGENAME
|
||
|
||
|
||
BLOCK_SIZE = 65536
|
||
|
||
|
||
def readTextFromStream(inputStream):
|
||
# read the file
|
||
code = uno.ByteSequence(b"")
|
||
while True:
|
||
read, out = inputStream.readBytes(None, BLOCK_SIZE)
|
||
code = code + out
|
||
if read < BLOCK_SIZE:
|
||
break
|
||
return code.value
|
||
|
||
|
||
def toIniName(str):
|
||
if platform.system() == "Windows":
|
||
return str + ".ini"
|
||
else:
|
||
return str + "rc"
|
||
|
||
|
||
# Tries to check whether the default app for opening the given file looks like a Python interpreter
|
||
# that will execute the script. If there’s any doubt, returns True
|
||
def windowsDefaultAppMightExecute():
|
||
import ctypes
|
||
|
||
try:
|
||
assocQueryString = ctypes.windll.shlwapi.AssocQueryStringW
|
||
except (AttributeError, FileNotFoundError):
|
||
log.debug("Couldn’t find AssocQueryStringW function")
|
||
return True
|
||
|
||
flags = 0
|
||
assocStr = 2 # ASSOCSTR_EXECUTABLE
|
||
length = ctypes.c_uint16()
|
||
|
||
# First get the length of the string
|
||
if assocQueryString(flags, assocStr, ".py", None, None, ctypes.byref(length)) != 1: # S_FALSE
|
||
log.debug("AsscQueryStringW failed")
|
||
return True
|
||
|
||
exeBuf = ctypes.create_unicode_buffer(length.value)
|
||
|
||
if assocQueryString(flags, assocStr, ".py", None, exeBuf, ctypes.byref(length)) != 0: # S_OK
|
||
log.debug("AsscQueryStringW failed")
|
||
return True
|
||
|
||
if length.value < 2:
|
||
return True
|
||
|
||
exe = exeBuf[0:length.value - 1]
|
||
|
||
return re.search(r'(?:\\|^)py(?:thon\d*)?\.exe$', exe, re.IGNORECASE) is not None
|
||
|
||
|
||
def openExternalEditor(ctx, uri):
|
||
if platform.system() == 'Windows':
|
||
filePath = uno.fileUrlToSystemPath(uri)
|
||
|
||
# If it looks like the default windows app might be a Python interpreter then open the file
|
||
# in Notepad instead
|
||
if windowsDefaultAppMightExecute():
|
||
subprocess.call(('notepad', filePath))
|
||
else:
|
||
# Otherwise try the 'open' verb
|
||
os.startfile(filePath)
|
||
else:
|
||
shellExecute = ctx.ServiceManager.createInstanceWithContext(
|
||
"com.sun.star.system.SystemShellExecute", ctx)
|
||
shellExecute.execute(uri, "", SystemShellExecuteFlags.URIS_ONLY)
|
||
|
||
|
||
""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
|
||
scriptURI is the system independent uri
|
||
"""
|
||
|
||
|
||
class MyUriHelper:
|
||
|
||
def __init__(self, ctx, location):
|
||
self.ctx = ctx
|
||
self.s_UriMap = {
|
||
"share": "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python",
|
||
"share:uno_packages": "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages",
|
||
"user": "vnd.sun.star.expand:${$BRAND_INI_DIR/"
|
||
+ toIniName("bootstrap")
|
||
+ "::UserInstallation}/user/Scripts/python",
|
||
"user:uno_packages": "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages",
|
||
}
|
||
self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory", ctx)
|
||
if location.startswith("vnd.sun.star.tdoc"):
|
||
self.m_baseUri = location + "/Scripts/python"
|
||
self.m_scriptUriLocation = "document"
|
||
else:
|
||
self.m_baseUri = expandUri(self.s_UriMap[location])
|
||
self.m_scriptUriLocation = location
|
||
log.debug(
|
||
"initialized urihelper with baseUri=" + self.m_baseUri + ",m_scriptUriLocation=" + self.m_scriptUriLocation
|
||
)
|
||
|
||
def getRootStorageURI(self):
|
||
return self.m_baseUri
|
||
|
||
def getStorageURI(self, scriptURI):
|
||
return self.scriptURI2StorageUri(scriptURI)
|
||
|
||
def getScriptURI(self, storageURI):
|
||
return self.storageURI2ScriptUri(storageURI)
|
||
|
||
def storageURI2ScriptUri(self, storageURI):
|
||
if not storageURI.startswith(self.m_baseUri):
|
||
message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
|
||
log.debug(message)
|
||
raise RuntimeException(message, self.ctx)
|
||
|
||
ret = (
|
||
"vnd.sun.star.script:"
|
||
+ storageURI[len(self.m_baseUri) + 1 :].replace("/", "|")
|
||
+ "?language="
|
||
+ LANGUAGENAME
|
||
+ "&location="
|
||
+ self.m_scriptUriLocation
|
||
)
|
||
log.debug("converting storageURI=" + storageURI + " to scriptURI=" + ret)
|
||
return ret
|
||
|
||
def scriptURI2StorageUri(self, scriptURI):
|
||
try:
|
||
# base path to the python script location
|
||
sBaseUri = self.m_baseUri + "/"
|
||
xBaseUri = self.m_uriRefFac.parse(sBaseUri)
|
||
|
||
# path to the .py file + "$functionname, arguments, etc
|
||
xStorageUri = self.m_uriRefFac.parse(scriptURI)
|
||
# getName will apply url-decoding to the name, so encode back
|
||
sStorageUri = xStorageUri.getName().replace("%", "%25")
|
||
sStorageUri = sStorageUri.replace("|", "/")
|
||
|
||
# path to the .py file, relative to the base
|
||
funcNameStart = sStorageUri.find("$")
|
||
if funcNameStart != -1:
|
||
sFileUri = sStorageUri[0:funcNameStart]
|
||
sFuncName = sStorageUri[funcNameStart + 1 :]
|
||
else:
|
||
sFileUri = sStorageUri
|
||
|
||
xFileUri = self.m_uriRefFac.parse(sFileUri)
|
||
if not xFileUri:
|
||
message = "pythonscript: invalid relative uri '" + sFileUri + "'"
|
||
log.debug(message)
|
||
raise RuntimeException(message, self.ctx)
|
||
|
||
if not xFileUri.hasRelativePath():
|
||
message = "pythonscript: an absolute uri is invalid '" + sFileUri + "'"
|
||
log.debug(message)
|
||
raise RuntimeException(message, self.ctx)
|
||
|
||
# absolute path to the .py file
|
||
xAbsScriptUri = self.m_uriRefFac.makeAbsolute(xBaseUri, xFileUri, True, RETAIN)
|
||
sAbsScriptUri = xAbsScriptUri.getUriReference()
|
||
|
||
# ensure py file is under the base path
|
||
if not sAbsScriptUri.startswith(sBaseUri):
|
||
message = "pythonscript: storage uri '" + sAbsScriptUri + "' not in base uri '" + self.m_baseUri + "'"
|
||
log.debug(message)
|
||
raise RuntimeException(message, self.ctx)
|
||
|
||
ret = sAbsScriptUri
|
||
if funcNameStart != -1:
|
||
ret = ret + "$" + sFuncName
|
||
log.debug("converting scriptURI=" + scriptURI + " to storageURI=" + ret)
|
||
return ret
|
||
except UnoException as e:
|
||
log.error("error during converting scriptURI=" + scriptURI + ": " + e.Message)
|
||
raise RuntimeException("pythonscript:scriptURI2StorageUri: " + e.Message, self.ctx)
|
||
except Exception as e:
|
||
log.error("error during converting scriptURI=" + scriptURI + ": " + str(e))
|
||
raise RuntimeException("pythonscript:scriptURI2StorageUri: " + str(e), self.ctx)
|
||
|
||
|
||
class ModuleEntry:
|
||
def __init__(self, lastRead, module):
|
||
self.lastRead = lastRead
|
||
self.module = module
|
||
|
||
|
||
def hasChanged(oldDate, newDate):
|
||
return (
|
||
newDate.Year > oldDate.Year
|
||
or newDate.Month > oldDate.Month
|
||
or newDate.Day > oldDate.Day
|
||
or newDate.Hours > oldDate.Hours
|
||
or newDate.Minutes > oldDate.Minutes
|
||
or newDate.Seconds > oldDate.Seconds
|
||
or newDate.NanoSeconds > oldDate.NanoSeconds
|
||
)
|
||
|
||
|
||
def ensureSourceState(code):
|
||
if code.endswith(b"\n"):
|
||
code = code + b"\n"
|
||
code = code.replace(b"\r", b"")
|
||
return code
|
||
|
||
|
||
def checkForPythonPathBesideScript(url):
|
||
if url.startswith("file:"):
|
||
path = unohelper.fileUrlToSystemPath(url + "/pythonpath.zip")
|
||
log.log(LogLevel.DEBUG, "checking for existence of " + path)
|
||
if 1 == os.access(encfile(path), os.F_OK) and path not in sys.path:
|
||
log.log(LogLevel.DEBUG, "adding " + path + " to sys.path")
|
||
sys.path.append(path)
|
||
|
||
path = unohelper.fileUrlToSystemPath(url + "/pythonpath")
|
||
log.log(LogLevel.DEBUG, "checking for existence of " + path)
|
||
if 1 == os.access(encfile(path), os.F_OK) and path not in sys.path:
|
||
log.log(LogLevel.DEBUG, "adding " + path + " to sys.path")
|
||
sys.path.append(path)
|
||
|
||
|
||
class ScriptContext(unohelper.Base):
|
||
def __init__(self, ctx, doc, inv):
|
||
self.ctx = ctx
|
||
self.doc = doc
|
||
self.inv = inv
|
||
|
||
# XScriptContext
|
||
def getDocument(self):
|
||
if self.doc:
|
||
return self.doc
|
||
return self.getDesktop().getCurrentComponent()
|
||
|
||
def getDesktop(self):
|
||
return self.ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx)
|
||
|
||
def getComponentContext(self):
|
||
return self.ctx
|
||
|
||
def getInvocationContext(self):
|
||
return self.inv
|
||
|
||
|
||
# ----------------------------------
|
||
# Global Module Administration
|
||
# does not fit together with script
|
||
# engine lifetime management
|
||
# ----------------------------------
|
||
# g_scriptContext = ScriptContext( uno.getComponentContext(), None )
|
||
# g_modules = {}
|
||
# def getModuleByUrl( url, sfa ):
|
||
# entry = g_modules.get(url)
|
||
# load = True
|
||
# lastRead = sfa.getDateTimeModified( url )
|
||
# if entry:
|
||
# if hasChanged( entry.lastRead, lastRead ):
|
||
# log.debug("file " + url + " has changed, reloading")
|
||
# else:
|
||
# load = False
|
||
#
|
||
# if load:
|
||
# log.debug( "opening >" + url + "<" )
|
||
#
|
||
# code = readTextFromStream( sfa.openFileRead( url ) )
|
||
|
||
# execute the module
|
||
# entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
|
||
# entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
|
||
# entry.module.__file__ = url
|
||
# exec code in entry.module.__dict__
|
||
# g_modules[ url ] = entry
|
||
# log.debug( "mapped " + url + " to " + str( entry.module ) )
|
||
# return entry.module
|
||
|
||
|
||
class ProviderContext:
|
||
def __init__(self, storageType, sfa, uriHelper, scriptContext):
|
||
self.storageType = storageType
|
||
self.sfa = sfa
|
||
self.uriHelper = uriHelper
|
||
self.scriptContext = scriptContext
|
||
self.modules = {}
|
||
self.rootUrl = None
|
||
self.mapPackageName2Path = None
|
||
|
||
def getTransientPartFromUrl(self, url):
|
||
rest = url.replace(self.rootUrl, "", 1).replace("/", "", 1)
|
||
return rest[0 : rest.find("/")]
|
||
|
||
def getPackageNameFromUrl(self, url):
|
||
rest = url.replace(self.rootUrl, "", 1).replace("/", "", 1)
|
||
start = rest.find("/") + 1
|
||
return rest[start : rest.find("/", start)]
|
||
|
||
def removePackageByUrl(self, url):
|
||
items = self.mapPackageName2Path.items()
|
||
for i in items:
|
||
if url in i[1].paths:
|
||
self.mapPackageName2Path.pop(i[0])
|
||
break
|
||
|
||
def addPackageByUrl(self, url):
|
||
packageName = self.getPackageNameFromUrl(url)
|
||
transientPart = self.getTransientPartFromUrl(url)
|
||
log.debug(
|
||
"addPackageByUrl : " + packageName + ", " + transientPart + "(" + url + ")" + ", rootUrl=" + self.rootUrl
|
||
)
|
||
if packageName in self.mapPackageName2Path:
|
||
package = self.mapPackageName2Path[packageName]
|
||
package.paths = package.paths + (url,)
|
||
else:
|
||
package = Package((url,), transientPart)
|
||
self.mapPackageName2Path[packageName] = package
|
||
|
||
def isUrlInPackage(self, url):
|
||
values = self.mapPackageName2Path.values()
|
||
for i in values:
|
||
# print ("checking " + url + " in " + str(i.paths))
|
||
if url in i.paths:
|
||
return True
|
||
# print ("false")
|
||
return False
|
||
|
||
def setPackageAttributes(self, mapPackageName2Path, rootUrl):
|
||
self.mapPackageName2Path = mapPackageName2Path
|
||
self.rootUrl = rootUrl
|
||
|
||
def getPersistentUrlFromStorageUrl(self, url):
|
||
# package name is the second directory
|
||
ret = url
|
||
if self.rootUrl:
|
||
pos = len(self.rootUrl) + 1
|
||
ret = url[0:pos] + url[url.find("/", pos) + 1 : len(url)]
|
||
log.debug("getPersistentUrlFromStorageUrl " + url + " -> " + ret)
|
||
return ret
|
||
|
||
def getStorageUrlFromPersistentUrl(self, url):
|
||
ret = url
|
||
if self.rootUrl:
|
||
pos = len(self.rootUrl) + 1
|
||
packageName = url[pos : url.find("/", pos + 1)]
|
||
package = self.mapPackageName2Path[packageName]
|
||
ret = url[0:pos] + package.transientPathElement + "/" + url[pos : len(url)]
|
||
log.debug("getStorageUrlFromPersistentUrl " + url + " -> " + ret)
|
||
return ret
|
||
|
||
def getFuncsByUrl(self, url):
|
||
src = readTextFromStream(self.sfa.openFileRead(url))
|
||
checkForPythonPathBesideScript(url[0 : url.rfind("/")])
|
||
src = ensureSourceState(src)
|
||
|
||
try:
|
||
code = ast.parse(src)
|
||
except Exception:
|
||
log.isDebugLevel() and log.debug(
|
||
"pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String()
|
||
)
|
||
raise
|
||
|
||
allFuncs = []
|
||
|
||
if code is None:
|
||
return allFuncs
|
||
|
||
g_exportedScripts = []
|
||
for node in ast.iter_child_nodes(code):
|
||
if isinstance(node, ast.FunctionDef):
|
||
allFuncs.append(node.name)
|
||
elif isinstance(node, ast.Assign):
|
||
for target in node.targets:
|
||
try:
|
||
identifier = target.id
|
||
except AttributeError:
|
||
identifier = ""
|
||
pass
|
||
if identifier == "g_exportedScripts":
|
||
for value in node.value.elts:
|
||
g_exportedScripts.append(value.id)
|
||
return g_exportedScripts
|
||
|
||
return allFuncs
|
||
|
||
def getModuleByUrl(self, url):
|
||
entry = self.modules.get(url)
|
||
load = True
|
||
lastRead = self.sfa.getDateTimeModified(url)
|
||
if entry:
|
||
if hasChanged(entry.lastRead, lastRead):
|
||
log.debug("file " + url + " has changed, reloading")
|
||
else:
|
||
load = False
|
||
|
||
if load:
|
||
log.debug("opening >" + url + "<")
|
||
|
||
src = readTextFromStream(self.sfa.openFileRead(url))
|
||
checkForPythonPathBesideScript(url[0 : url.rfind("/")])
|
||
src = ensureSourceState(src)
|
||
|
||
# execute the module
|
||
entry = ModuleEntry(lastRead, types.ModuleType("ooo_script_framework"))
|
||
entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
|
||
|
||
code = None
|
||
if url.startswith("file:"):
|
||
code = compile(src, encfile(uno.fileUrlToSystemPath(url)), "exec")
|
||
else:
|
||
code = compile(src, url, "exec")
|
||
exec(code, entry.module.__dict__)
|
||
entry.module.__file__ = url
|
||
self.modules[url] = entry
|
||
log.debug("mapped " + url + " to " + str(entry.module))
|
||
return entry.module
|
||
|
||
|
||
# --------------------------------------------------
|
||
def isScript(candidate):
|
||
ret = False
|
||
if isinstance(candidate, type(isScript)):
|
||
ret = True
|
||
return ret
|
||
|
||
|
||
# -------------------------------------------------------
|
||
# A mixin class for nodes that have a uri property so that an editor can be used on it
|
||
class EditableNode(XActionListener, XEditableBrowseNode):
|
||
def _edit_with_dialog(self):
|
||
servicename = "com.sun.star.awt.DialogProvider"
|
||
ctx = self.provCtx.scriptContext.getComponentContext()
|
||
dlgprov = ctx.ServiceManager.createInstanceWithContext(servicename, ctx)
|
||
|
||
self.editor = dlgprov.createDialog(
|
||
"vnd.sun.star.script:" + "ScriptBindingLibrary.MacroEditor?location=application"
|
||
)
|
||
|
||
code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri))
|
||
code = ensureSourceState(code)
|
||
self.editor.getControl("EditorTextField").setText(code)
|
||
|
||
self.editor.getControl("RunButton").setActionCommand("Run")
|
||
self.editor.getControl("RunButton").addActionListener(self)
|
||
self.editor.getControl("SaveButton").setActionCommand("Save")
|
||
self.editor.getControl("SaveButton").addActionListener(self)
|
||
|
||
self.editor.execute()
|
||
|
||
def editNode(self):
|
||
if ENABLE_EDIT_DIALOG:
|
||
self._edit_with_dialog()
|
||
else:
|
||
openExternalEditor(self.provCtx.scriptContext.getComponentContext(), self.uri)
|
||
|
||
return True
|
||
|
||
def setValue(self, name, value):
|
||
return None
|
||
|
||
def getValue(self, name):
|
||
return None
|
||
|
||
def hasMethod(self, name):
|
||
return False
|
||
|
||
def hasProperty(self, name):
|
||
return False
|
||
|
||
def getIntrospection(self):
|
||
return None
|
||
|
||
def isEditableNode(self):
|
||
if self.provCtx.sfa.isReadOnly(self.uri):
|
||
return False
|
||
if urllib.parse.urlsplit(self.uri).scheme != 'file':
|
||
return False
|
||
return True
|
||
|
||
def actionPerformed(self, event):
|
||
try:
|
||
if event.ActionCommand == "Run":
|
||
code = self.editor.getControl("EditorTextField").getText()
|
||
code = code.encode(sys.getdefaultencoding())
|
||
code = ensureSourceState(code)
|
||
mod = types.ModuleType("ooo_script_framework")
|
||
mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
|
||
exec(code, mod.__dict__)
|
||
values = mod.__dict__.get(CALLABLE_CONTAINER_NAME, None)
|
||
if not values:
|
||
values = mod.__dict__.values()
|
||
|
||
for i in values:
|
||
if isScript(i):
|
||
i()
|
||
break
|
||
|
||
elif event.ActionCommand == "Save":
|
||
toWrite = uno.ByteSequence(
|
||
self.editor.getControl("EditorTextField").getText().encode(sys.getdefaultencoding())
|
||
)
|
||
copyUrl = self.uri + ".orig"
|
||
self.provCtx.sfa.move(self.uri, copyUrl)
|
||
out = self.provCtx.sfa.openFileWrite(self.uri)
|
||
out.writeBytes(toWrite)
|
||
out.close()
|
||
self.provCtx.sfa.kill(copyUrl)
|
||
# log.debug("Save is not implemented yet")
|
||
# text = self.editor.getControl("EditorTextField").getText()
|
||
# log.debug("Would save: " + text)
|
||
except Exception:
|
||
# TODO: add an error box here!
|
||
log.error(lastException2String())
|
||
|
||
|
||
# -------------------------------------------------------
|
||
class ScriptBrowseNode(unohelper.Base, XBrowseNode, XPropertySet, EditableNode):
|
||
def __init__(self, provCtx, uri, fileName, funcName):
|
||
self.fileName = fileName
|
||
self.funcName = funcName
|
||
self.provCtx = provCtx
|
||
self.uri = uri
|
||
|
||
def getName(self):
|
||
return self.funcName
|
||
|
||
def getChildNodes(self):
|
||
return ()
|
||
|
||
def hasChildNodes(self):
|
||
return False
|
||
|
||
def getType(self):
|
||
return SCRIPT
|
||
|
||
def getPropertyValue(self, name):
|
||
ret = None
|
||
try:
|
||
if name == "URI":
|
||
ret = self.provCtx.uriHelper.getScriptURI(
|
||
self.provCtx.getPersistentUrlFromStorageUrl(self.uri + "$" + self.funcName)
|
||
)
|
||
|
||
log.debug("ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret))
|
||
except Exception:
|
||
log.error("ScriptBrowseNode.getPropertyValue error " + lastException2String())
|
||
raise
|
||
|
||
return ret
|
||
|
||
def setPropertyValue(self, name, value):
|
||
log.debug("ScriptBrowseNode.setPropertyValue called " + name + "=" + str(value))
|
||
|
||
def getPropertySetInfo(self):
|
||
log.debug("ScriptBrowseNode.getPropertySetInfo called ")
|
||
return None
|
||
|
||
|
||
# -------------------------------------------------------
|
||
class FileBrowseNode(unohelper.Base, XBrowseNode, XCopyableBrowseNode, EditableNode):
|
||
def __init__(self, provCtx, uri, name):
|
||
self.provCtx = provCtx
|
||
self.uri = uri
|
||
self.name = name
|
||
self.funcnames = None
|
||
|
||
def getName(self):
|
||
return self.name
|
||
|
||
def getChildNodes(self):
|
||
ret = ()
|
||
try:
|
||
self.funcnames = self.provCtx.getFuncsByUrl(self.uri)
|
||
|
||
scriptNodeList = []
|
||
for i in self.funcnames:
|
||
scriptNodeList.append(ScriptBrowseNode(self.provCtx, self.uri, self.name, i))
|
||
ret = tuple(scriptNodeList)
|
||
log.debug("returning " + str(len(ret)) + " ScriptChildNodes on " + self.uri)
|
||
except Exception:
|
||
text = lastException2String()
|
||
log.error("Error while evaluating " + self.uri + ":" + text)
|
||
raise
|
||
return ret
|
||
|
||
def hasChildNodes(self):
|
||
try:
|
||
return len(self.getChildNodes()) > 0
|
||
except Exception:
|
||
return False
|
||
|
||
def getType(self):
|
||
return CONTAINER
|
||
|
||
def isCopyableNode(self):
|
||
return True
|
||
|
||
def getCopyDestinationUrl(self, xDest):
|
||
if isinstance(xDest, PythonScriptProvider):
|
||
xDest = xDest.dirBrowseNode
|
||
if not isinstance(xDest, DirBrowseNode):
|
||
return None
|
||
|
||
# Don’t allow copying into the same directory
|
||
if xDest.rootUrl == self.uri[0:self.uri.rfind('/')]:
|
||
return None
|
||
|
||
return xDest.rootUrl
|
||
|
||
def nodeCanBeCopiedTo(self, parentNode):
|
||
return self.getCopyDestinationUrl(parentNode) != None
|
||
|
||
def copyNode(self, parentNode):
|
||
dest_dir_url = self.getCopyDestinationUrl(parentNode)
|
||
if dest_dir_url is None:
|
||
raise IllegalArgumentException("Invalid parent node passed to copyNode", self, 0)
|
||
|
||
self.provCtx.sfa.createFolder(dest_dir_url)
|
||
|
||
target_url = f"{dest_dir_url}/{self.uri[self.uri.rfind('/') + 1:]}"
|
||
self.provCtx.sfa.copy(self.uri, target_url)
|
||
|
||
return FileBrowseNode(self.provCtx, target_url, self.name)
|
||
|
||
|
||
class DirBrowseNode(unohelper.Base, XBrowseNode, XCreatableBrowseNode):
|
||
def __init__(self, provCtx, name, rootUrl):
|
||
self.provCtx = provCtx
|
||
self.name = name
|
||
self.rootUrl = rootUrl
|
||
|
||
def getName(self):
|
||
return self.name
|
||
|
||
def getChildNodes(self):
|
||
try:
|
||
log.debug("DirBrowseNode.getChildNodes called for " + self.rootUrl)
|
||
contents = self.provCtx.sfa.getFolderContents(self.rootUrl, True)
|
||
browseNodeList = []
|
||
for i in contents:
|
||
if i.endswith(".py"):
|
||
log.debug("adding filenode " + i)
|
||
name = urllib.parse.unquote(i[i.rfind("/") + 1 : len(i) - 3])
|
||
browseNodeList.append(FileBrowseNode(self.provCtx, i, name))
|
||
elif self.provCtx.sfa.isFolder(i) and not (i.endswith("/pythonpath") or i.endswith("/__pycache__")):
|
||
log.debug("adding DirBrowseNode " + i)
|
||
browseNodeList.append(DirBrowseNode(self.provCtx, i[i.rfind("/") + 1 : len(i)], i))
|
||
return tuple(browseNodeList)
|
||
except Exception as e:
|
||
text = lastException2String()
|
||
log.error("DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
|
||
log.error(text)
|
||
return ()
|
||
|
||
def hasChildNodes(self):
|
||
return True
|
||
|
||
def getType(self):
|
||
return CONTAINER
|
||
|
||
def getScript(self, uri):
|
||
log.debug("DirBrowseNode getScript " + uri + " invoked")
|
||
raise IllegalArgumentException("DirBrowseNode couldn't instantiate script " + uri, self, 0)
|
||
|
||
def isCreatableNode(self):
|
||
return urllib.parse.urlsplit(self.rootUrl).scheme == 'file'
|
||
|
||
def createNode(self, scriptName):
|
||
uri = f"{self.rootUrl}/{scriptName}.py"
|
||
|
||
# Create the file with an example macro
|
||
try:
|
||
filePath = uno.fileUrlToSystemPath(uri)
|
||
|
||
os.makedirs(os.path.dirname(filePath), exist_ok=True)
|
||
|
||
with open(filePath, "x", encoding="utf-8") as f:
|
||
f.write(EXAMPLE_MACRO)
|
||
|
||
except OSError as e:
|
||
log.error(f"DirBrowseNode Creatable error {e}")
|
||
raise
|
||
|
||
except Exception:
|
||
log.error(f"DirBrowseNode Creatable error {lastException2String()}")
|
||
raise
|
||
|
||
return FileBrowseNode(self.provCtx, uri, scriptName)
|
||
|
||
|
||
class ManifestHandler(XDocumentHandler, unohelper.Base):
|
||
def __init__(self, rootUrl):
|
||
self.rootUrl = rootUrl
|
||
|
||
def startDocument(self):
|
||
self.urlList = []
|
||
|
||
def endDocument(self):
|
||
pass
|
||
|
||
def startElement(self, name, attlist):
|
||
if name == "manifest:file-entry":
|
||
if attlist.getValueByName("manifest:media-type") == "application/vnd.sun.star.framework-script":
|
||
self.urlList.append(self.rootUrl + "/" + attlist.getValueByName("manifest:full-path"))
|
||
|
||
def endElement(self, name):
|
||
pass
|
||
|
||
def characters(self, chars):
|
||
pass
|
||
|
||
def ignoreableWhitespace(self, chars):
|
||
pass
|
||
|
||
def setDocumentLocator(self, locator):
|
||
pass
|
||
|
||
|
||
def isPyFileInPath(sfa, path):
|
||
ret = False
|
||
contents = sfa.getFolderContents(path, True)
|
||
for i in contents:
|
||
if sfa.isFolder(i):
|
||
ret = isPyFileInPath(sfa, i)
|
||
else:
|
||
if i.endswith(".py"):
|
||
ret = True
|
||
if ret:
|
||
break
|
||
return ret
|
||
|
||
|
||
# extracts META-INF directory from
|
||
def getPathsFromPackage(rootUrl, sfa):
|
||
ret = ()
|
||
try:
|
||
fileUrl = rootUrl + "/META-INF/manifest.xml"
|
||
inputStream = sfa.openFileRead(fileUrl)
|
||
parser = uno.getComponentContext().ServiceManager.createInstance("com.sun.star.xml.sax.Parser")
|
||
handler = ManifestHandler(rootUrl)
|
||
parser.setDocumentHandler(handler)
|
||
parser.parseStream(InputSource(inputStream, "", fileUrl, fileUrl))
|
||
for i in tuple(handler.urlList):
|
||
if not isPyFileInPath(sfa, i):
|
||
handler.urlList.remove(i)
|
||
ret = tuple(handler.urlList)
|
||
except UnoException:
|
||
text = lastException2String()
|
||
log.debug("getPathsFromPackage " + fileUrl + " Exception: " + text)
|
||
pass
|
||
return ret
|
||
|
||
|
||
class Package:
|
||
def __init__(self, paths, transientPathElement):
|
||
self.paths = paths
|
||
self.transientPathElement = transientPathElement
|
||
|
||
|
||
class DummyInteractionHandler(unohelper.Base, XInteractionHandler):
|
||
def __init__(self):
|
||
pass
|
||
|
||
def handle(self, event):
|
||
log.debug("pythonscript: DummyInteractionHandler.handle " + str(event))
|
||
|
||
|
||
class DummyProgressHandler(unohelper.Base, XProgressHandler):
|
||
def __init__(self):
|
||
pass
|
||
|
||
def push(self, status):
|
||
log.debug("pythonscript: DummyProgressHandler.push " + str(status))
|
||
|
||
def update(self, status):
|
||
log.debug("pythonscript: DummyProgressHandler.update " + str(status))
|
||
|
||
def pop(self, event):
|
||
log.debug("pythonscript: DummyProgressHandler.push " + str(event))
|
||
|
||
|
||
class CommandEnvironment(unohelper.Base, XCommandEnvironment):
|
||
def __init__(self):
|
||
self.progressHandler = DummyProgressHandler()
|
||
self.interactionHandler = DummyInteractionHandler()
|
||
|
||
def getInteractionHandler(self):
|
||
return self.interactionHandler
|
||
|
||
def getProgressHandler(self):
|
||
return self.progressHandler
|
||
|
||
|
||
# maybe useful for debugging purposes
|
||
# class ModifyListener( unohelper.Base, XModifyListener ):
|
||
# def __init__( self ):
|
||
# pass
|
||
# def modified( self, event ):
|
||
# log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
|
||
# def disposing( self, event ):
|
||
# log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
|
||
|
||
|
||
def getModelFromDocUrl(ctx, url):
|
||
"""Get document model from document url."""
|
||
doc = None
|
||
args = ("Local", "Office")
|
||
ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
|
||
"com.sun.star.ucb.UniversalContentBroker", args, ctx
|
||
)
|
||
identifier = ucb.createContentIdentifier(url)
|
||
content = ucb.queryContent(identifier)
|
||
p = Property()
|
||
p.Name = "DocumentModel"
|
||
p.Handle = -1
|
||
|
||
c = Command()
|
||
c.Handle = -1
|
||
c.Name = "getPropertyValues"
|
||
c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
|
||
|
||
env = CommandEnvironment()
|
||
try:
|
||
ret = content.execute(c, 0, env)
|
||
doc = ret.getObject(1, None)
|
||
except Exception:
|
||
log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
|
||
return doc
|
||
|
||
|
||
def mapStorageType2PackageContext(storageType):
|
||
ret = storageType
|
||
if storageType == "share:uno_packages":
|
||
ret = "shared"
|
||
if storageType == "user:uno_packages":
|
||
ret = "user"
|
||
return ret
|
||
|
||
|
||
def getPackageName2PathMap(sfa, storageType):
|
||
ret = {}
|
||
|
||
ext_mgr = uno.getComponentContext().getValueByName("/singletons/com.sun.star.deployment.ExtensionManager")
|
||
|
||
log.debug("pythonscript: getPackageName2PathMap start getDeployedPackages")
|
||
packages = ext_mgr.getDeployedExtensions(
|
||
mapStorageType2PackageContext(storageType),
|
||
ext_mgr.createAbortChannel(),
|
||
CommandEnvironment(),
|
||
)
|
||
|
||
log.debug("pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages)) + ")")
|
||
|
||
for pkg in packages:
|
||
log.debug("inspecting package " + pkg.Name + "(" + pkg.Identifier.Value + ")") # type: ignore
|
||
transientPathElement = penultimateElement(pkg.URL) # type: ignore
|
||
exp_uri = expandUri(pkg.URL) # type: ignore
|
||
paths = getPathsFromPackage(exp_uri, sfa)
|
||
if len(paths) > 0:
|
||
# map package name to url, we need this later
|
||
log.debug("adding Package " + transientPathElement + " " + str(paths))
|
||
ret[lastElement(exp_uri)] = Package(paths, transientPathElement)
|
||
return ret
|
||
|
||
|
||
def penultimateElement(aStr):
|
||
lastSlash = aStr.rindex("/")
|
||
penultimateSlash = aStr.rindex("/", 0, lastSlash - 1)
|
||
return aStr[penultimateSlash + 1 : lastSlash]
|
||
|
||
|
||
def lastElement(aStr):
|
||
return aStr[aStr.rfind("/") + 1 : len(aStr)]
|
||
|
||
|
||
class PackageBrowseNode(unohelper.Base, XBrowseNode):
|
||
def __init__(self, provCtx, name, rootUrl):
|
||
self.provCtx = provCtx
|
||
self.name = name
|
||
self.rootUrl = rootUrl
|
||
|
||
def getName(self):
|
||
return self.name
|
||
|
||
def getChildNodes(self):
|
||
items = self.provCtx.mapPackageName2Path.items()
|
||
browseNodeList = []
|
||
for i in items:
|
||
if len(i[1].paths) == 1:
|
||
browseNodeList.append(DirBrowseNode(self.provCtx, i[0], i[1].paths[0]))
|
||
else:
|
||
for j in i[1].paths:
|
||
browseNodeList.append(DirBrowseNode(self.provCtx, i[0] + "." + lastElement(j), j))
|
||
return tuple(browseNodeList)
|
||
|
||
def hasChildNodes(self):
|
||
return len(self.provCtx.mapPackageName2Path) > 0
|
||
|
||
def getType(self):
|
||
return CONTAINER
|
||
|
||
def getScript(self, uri):
|
||
log.debug("PackageBrowseNode getScript " + uri + " invoked")
|
||
raise IllegalArgumentException("PackageBrowseNode couldn't instantiate script " + uri, self, 0)
|
||
|
||
|
||
class PythonScript(unohelper.Base, XScript):
|
||
def __init__(self, func, mod, args):
|
||
self.func = func
|
||
self.mod = mod
|
||
self.args = args
|
||
|
||
def invoke(self, args, out, outindex):
|
||
log.debug("PythonScript.invoke " + str(args))
|
||
try:
|
||
if self.args:
|
||
args += self.args
|
||
ret = self.func(*args)
|
||
except UnoException as e:
|
||
# UNO Exception continue to fly ...
|
||
text = lastException2String()
|
||
complete = (
|
||
"Error during invoking function "
|
||
+ str(self.func.__name__)
|
||
+ " in module "
|
||
+ self.mod.__file__
|
||
+ " ("
|
||
+ text
|
||
+ ")"
|
||
)
|
||
log.debug(complete)
|
||
# some people may beat me up for modifying the exception text,
|
||
# but otherwise office just shows
|
||
# the type name and message text with no more information,
|
||
# this is really bad for most users.
|
||
e.Message = e.Message + " (" + complete + ")"
|
||
raise
|
||
except Exception:
|
||
# General python exception are converted to uno RuntimeException
|
||
text = lastException2String()
|
||
complete = (
|
||
"Error during invoking function "
|
||
+ str(self.func.__name__)
|
||
+ " in module "
|
||
+ self.mod.__file__
|
||
+ " ("
|
||
+ text
|
||
+ ")"
|
||
)
|
||
log.debug(complete)
|
||
raise RuntimeException(complete, self)
|
||
log.debug("PythonScript.invoke ret = " + str(ret))
|
||
return ret, (), ()
|
||
|
||
|
||
def expandUri(uri):
|
||
if uri.startswith("vnd.sun.star.expand:"):
|
||
ctx = uno.getComponentContext()
|
||
fac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory", ctx)
|
||
ref = fac.parse(uri)
|
||
uri = ref.expand(ctx.getValueByName("/singletons/com.sun.star.util.theMacroExpander"))
|
||
if uri.startswith("file:"):
|
||
uri = uno.absolutize("", uri) # necessary to get rid of .. in uri
|
||
return uri
|
||
|
||
|
||
# --------------------------------------------------------------
|
||
class PythonScriptProvider(unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer,
|
||
XPropertySet, XCreatableBrowseNode, XEditableBrowseNode):
|
||
def __init__(self, ctx, *args):
|
||
if log.isDebugLevel():
|
||
mystr = ""
|
||
for i in args:
|
||
if len(mystr) > 0:
|
||
mystr = mystr + ","
|
||
mystr = mystr + str(i)
|
||
log.debug("Entering PythonScriptProvider.ctor" + mystr)
|
||
|
||
doc = None
|
||
inv = None
|
||
storageType = ""
|
||
|
||
if isinstance(args[0], str):
|
||
storageType = args[0]
|
||
if storageType.startswith("vnd.sun.star.tdoc"):
|
||
doc = getModelFromDocUrl(ctx, storageType)
|
||
else:
|
||
inv = args[0]
|
||
try:
|
||
doc = inv.ScriptContainer
|
||
content = (
|
||
ctx.getServiceManager()
|
||
.createInstanceWithContext("com.sun.star.frame.TransientDocumentsDocumentContentFactory", ctx)
|
||
.createDocumentContent(doc)
|
||
)
|
||
storageType = content.getIdentifier().getContentIdentifier()
|
||
except Exception:
|
||
text = lastException2String()
|
||
log.error(text)
|
||
|
||
isPackage = storageType.endswith(":uno_packages")
|
||
|
||
try:
|
||
# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
|
||
# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
|
||
urlHelper = MyUriHelper(ctx, storageType)
|
||
log.debug("got urlHelper " + str(urlHelper))
|
||
|
||
rootUrl = expandUri(urlHelper.getRootStorageURI())
|
||
log.debug(storageType + " transformed to " + rootUrl)
|
||
|
||
ucbService = "com.sun.star.ucb.SimpleFileAccess"
|
||
sfa = ctx.ServiceManager.createInstanceWithContext(ucbService, ctx)
|
||
if not sfa:
|
||
log.debug("PythonScriptProvider couldn't instantiate " + ucbService)
|
||
raise RuntimeException("PythonScriptProvider couldn't instantiate " + ucbService, self)
|
||
self.provCtx = ProviderContext(
|
||
storageType, sfa, urlHelper, ScriptContext(uno.getComponentContext(), doc, inv)
|
||
)
|
||
if isPackage:
|
||
mapPackageName2Path = getPackageName2PathMap(sfa, storageType)
|
||
self.provCtx.setPackageAttributes(mapPackageName2Path, rootUrl)
|
||
self.dirBrowseNode = PackageBrowseNode(self.provCtx, LANGUAGENAME, rootUrl)
|
||
else:
|
||
self.dirBrowseNode = DirBrowseNode(self.provCtx, LANGUAGENAME, rootUrl)
|
||
|
||
except Exception as e:
|
||
text = lastException2String()
|
||
log.debug("PythonScriptProvider could not be instantiated because of : " + text)
|
||
raise e
|
||
|
||
def getName(self):
|
||
return self.dirBrowseNode.getName()
|
||
|
||
def getChildNodes(self):
|
||
return self.dirBrowseNode.getChildNodes()
|
||
|
||
def hasChildNodes(self):
|
||
return self.dirBrowseNode.hasChildNodes()
|
||
|
||
def getType(self):
|
||
return self.dirBrowseNode.getType()
|
||
|
||
def getPropertyValue(self, name):
|
||
if hasattr(self.dirBrowseNode, "getPropertyValue"):
|
||
return self.dirBrowseNode.getPropertyValue(name)
|
||
|
||
def setPropertyValue(self, name, value):
|
||
if hasattr(self.dirBrowseNode, "setPropertyValue"):
|
||
return self.dirBrowseNode.setPropertyValue(name, value)
|
||
|
||
def getPropertySetInfo(self):
|
||
if hasattr(self.dirBrowseNode, "getPropertySetInfo"):
|
||
return self.dirBrowseNode.getPropertySetInfo()
|
||
|
||
def isEditableNode(self):
|
||
if hasattr(self.dirBrowseNode, "isEditableNode"):
|
||
return self.dirBrowseNode.isEditableNode()
|
||
else:
|
||
return False
|
||
|
||
def editNode(self):
|
||
if hasattr(self.dirBrowseNode, "editNode"):
|
||
return self.dirBrowseNode.editNode()
|
||
else:
|
||
return False
|
||
|
||
def isCreatableNode(self):
|
||
if hasattr(self.dirBrowseNode, "isCreatableNode"):
|
||
return self.dirBrowseNode.isCreatableNode()
|
||
else:
|
||
return False
|
||
|
||
def createNode(self, name):
|
||
if hasattr(self.dirBrowseNode, "createNode"):
|
||
return self.dirBrowseNode.createNode(name)
|
||
else:
|
||
return None
|
||
|
||
# retrieve function args in parenthesis
|
||
def getFunctionArguments(self, func_signature):
|
||
nOpenParenthesis = func_signature.find("(")
|
||
if -1 == nOpenParenthesis:
|
||
function_name = func_signature
|
||
arguments = None
|
||
else:
|
||
function_name = func_signature[0:nOpenParenthesis]
|
||
arg_part = func_signature[nOpenParenthesis + 1 : len(func_signature)]
|
||
nCloseParenthesis = arg_part.find(")")
|
||
if -1 == nCloseParenthesis:
|
||
raise IllegalArgumentException("PythonLoader: mismatch parenthesis " + func_signature, self, 0)
|
||
arguments = arg_part[0:nCloseParenthesis].strip()
|
||
if arguments == "":
|
||
arguments = None
|
||
else:
|
||
arguments = tuple([x.strip().strip('"') for x in arguments.split(",")])
|
||
return function_name, arguments
|
||
|
||
def getScript(self, scriptUri):
|
||
try:
|
||
log.debug("getScript " + scriptUri + " invoked")
|
||
|
||
storageUri = self.provCtx.getStorageUrlFromPersistentUrl(self.provCtx.uriHelper.getStorageURI(scriptUri))
|
||
log.debug("getScript: storageUri = " + storageUri)
|
||
fileUri = storageUri[0 : storageUri.find("$")]
|
||
funcName = storageUri[storageUri.find("$") + 1 : len(storageUri)]
|
||
|
||
# retrieve arguments in parenthesis
|
||
funcName, funcArgs = self.getFunctionArguments(funcName)
|
||
log.debug(" getScript : parsed funcname " + str(funcName))
|
||
log.debug(" getScript : func args " + str(funcArgs))
|
||
|
||
mod = self.provCtx.getModuleByUrl(fileUri)
|
||
log.debug(" got mod " + str(mod))
|
||
|
||
func = mod.__dict__[funcName]
|
||
|
||
log.debug("got func " + str(func))
|
||
return PythonScript(func, mod, funcArgs)
|
||
except Exception:
|
||
text = lastException2String()
|
||
log.error(text)
|
||
raise ScriptFrameworkErrorException(text, self, scriptUri, LANGUAGENAME, 0)
|
||
|
||
# XServiceInfo
|
||
def getSupportedServices(self):
|
||
return g_ImplementationHelper.getSupportedServices(g_implName)
|
||
|
||
def supportsService(self, ServiceName):
|
||
return g_ImplementationHelper.supportsService(g_implName, ServiceName)
|
||
|
||
def getImplementationName(self):
|
||
return g_implName
|
||
|
||
def getByName(self, name):
|
||
log.debug("getByName called" + str(name))
|
||
return None
|
||
|
||
def getElementNames(self):
|
||
log.debug("getElementNames called")
|
||
return ()
|
||
|
||
def hasByName(self, name):
|
||
try:
|
||
log.debug("hasByName called " + str(name))
|
||
uri = expandUri(name)
|
||
ret = self.provCtx.isUrlInPackage(uri)
|
||
log.debug("hasByName " + uri + " " + str(ret))
|
||
return ret
|
||
except Exception:
|
||
text = lastException2String()
|
||
log.debug("Error in hasByName:" + text)
|
||
return False
|
||
|
||
def removeByName(self, name):
|
||
log.debug("removeByName called" + str(name))
|
||
uri = expandUri(name)
|
||
if self.provCtx.isUrlInPackage(uri):
|
||
self.provCtx.removePackageByUrl(uri)
|
||
else:
|
||
log.debug("removeByName unknown uri " + str(name) + ", ignoring")
|
||
raise NoSuchElementException(uri + "is not in package", self)
|
||
log.debug("removeByName called" + str(uri) + " successful")
|
||
|
||
def insertByName(self, name, value):
|
||
log.debug("insertByName called " + str(name) + " " + str(value))
|
||
uri = expandUri(name)
|
||
if isPyFileInPath(self.provCtx.sfa, uri):
|
||
self.provCtx.addPackageByUrl(uri)
|
||
else:
|
||
# package is no python package ...
|
||
log.debug("insertByName: no python files in " + str(uri) + ", ignoring")
|
||
raise IllegalArgumentException(uri + " does not contain .py files", self, 1)
|
||
log.debug("insertByName called " + str(uri) + " successful")
|
||
|
||
def replaceByName(self, name, value):
|
||
log.debug("replaceByName called " + str(name) + " " + str(value))
|
||
uri = expandUri(name)
|
||
self.removeByName(name)
|
||
self.insertByName(name, value)
|
||
log.debug("replaceByName called" + str(uri) + " successful")
|
||
|
||
def getElementType(self):
|
||
log.debug("getElementType called")
|
||
return uno.getTypeByName("void")
|
||
|
||
def hasElements(self):
|
||
log.debug("hasElements got called")
|
||
return False
|
||
|
||
|
||
g_ImplementationHelper.addImplementation(
|
||
PythonScriptProvider,
|
||
g_implName,
|
||
(
|
||
"com.sun.star.script.provider.LanguageScriptProvider",
|
||
"com.sun.star.script.provider.ScriptProviderFor" + LANGUAGENAME,
|
||
),
|
||
)
|
||
|
||
|
||
log.debug("pythonscript finished initializing")
|
||
|
||
# vim: set shiftwidth=4 softtabstop=4 expandtab:
|