1# ************************************************************* 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20# ************************************************************* 21 22# XScript implementation for python 23import uno 24import unohelper 25import sys 26import os 27import imp 28import time 29import ast 30 31try: 32 unicode 33except NameError: 34 unicode = str 35 36class LogLevel: 37 NONE = 0 # production level 38 ERROR = 1 # for script developers 39 DEBUG = 2 # for script framework developers 40 41PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL" 42PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT" 43 44# Configuration ---------------------------------------------------- 45LogLevel.use = LogLevel.NONE 46if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR": 47 LogLevel.use = LogLevel.ERROR 48elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG": 49 LogLevel.use = LogLevel.DEBUG 50 51# True, writes to stdout (difficult on windows) 52# False, writes to user/Scripts/python/log.txt 53LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0" 54 55ENABLE_EDIT_DIALOG=False # offers a minimal editor for editing. 56#------------------------------------------------------------------- 57 58def encfile(uni): 59 if sys.version_info[0] > 2: 60 return uni 61 else: 62 return uni.encode( sys.getfilesystemencoding()) 63 64def lastException2String(): 65 (excType,excInstance,excTraceback) = sys.exc_info() 66 ret = str(excType) + ": "+str(excInstance) + "\n" + \ 67 uno._uno_extract_printable_stacktrace( excTraceback ) 68 return ret 69 70def logLevel2String( level ): 71 ret = " NONE" 72 if level == LogLevel.ERROR: 73 ret = "ERROR" 74 elif level >= LogLevel.DEBUG: 75 ret = "DEBUG" 76 return ret 77 78def getLogTarget(): 79 ret = sys.stdout 80 if not LOG_STDOUT: 81 try: 82 pathSubst = uno.getComponentContext().ServiceManager.createInstance( 83 "com.sun.star.util.PathSubstitution" ) 84 userInstallation = pathSubst.getSubstituteVariableValue( "user" ) 85 if len( userInstallation ) > 0: 86 systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" ) 87 ret = open( systemPath , "a" ) 88 except Exception as e: 89 print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delagating log to stdout\n") 90 return ret 91 92class Logger(LogLevel): 93 def __init__(self , target ): 94 self.target = target 95 96 def isDebugLevel( self ): 97 return self.use >= self.DEBUG 98 99 def debug( self, msg ): 100 if self.isDebugLevel(): 101 self.log( self.DEBUG, msg ) 102 103 def isErrorLevel( self ): 104 return self.use >= self.ERROR 105 106 def error( self, msg ): 107 if self.isErrorLevel(): 108 self.log( self.ERROR, msg ) 109 110 def log( self, level, msg ): 111 if self.use >= level: 112 try: 113 self.target.write( 114 time.asctime() + 115 " [" + 116 logLevel2String( level ) + 117 "] " + 118 encfile(msg) + 119 "\n" ) 120 self.target.flush() 121 except Exception as e: 122 print("Error during writing to stdout: " +lastException2String() + "\n") 123 124log = Logger( getLogTarget() ) 125 126log.debug( "pythonscript loading" ) 127 128#from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider 129from com.sun.star.uno import RuntimeException 130from com.sun.star.lang import XServiceInfo 131from com.sun.star.io import IOException, XInputStream 132from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command 133from com.sun.star.task import XInteractionHandler 134from com.sun.star.beans import XPropertySet, Property 135from com.sun.star.container import XNameContainer 136from com.sun.star.xml.sax import XDocumentHandler, InputSource 137from com.sun.star.uno import Exception as UnoException 138from com.sun.star.script import XInvocation 139from com.sun.star.awt import XActionListener 140 141from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException 142from com.sun.star.script.browse import XBrowseNode 143from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT 144from com.sun.star.util import XModifyListener 145 146LANGUAGENAME = "Python" 147GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT" 148CALLABLE_CONTAINER_NAME = "g_exportedScripts" 149 150# pythonloader looks for a static g_ImplementationHelper variable 151g_ImplementationHelper = unohelper.ImplementationHelper() 152g_implName = "org.openoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME 153 154 155 156BLOCK_SIZE = 65536 157def readTextFromStream( inputStream ): 158 # read the file 159 code = uno.ByteSequence( b"" ) 160 while True: 161 read,out = inputStream.readBytes( None , BLOCK_SIZE ) 162 code = code + out 163 if read < BLOCK_SIZE: 164 break 165 if sys.version_info[0] > 2: 166 return str( code.value, 'utf-8' ) 167 else: 168 return code.value 169 170def toIniName( str ): 171 # TODO: what is the official way to get to know whether i am on the windows platform ? 172 if( hasattr(sys , "dllhandle") ): 173 return str + ".ini" 174 return str + "rc" 175 176class EmptyInputStream( unohelper.Base, XInputStream ): 177 def __init__( self ): 178 self.alive = 1 179 180 def closeInput(self): 181 pass 182 183 def readBytes( self, seq, n ): 184 return 0, "" 185 186 def readSomeBytes( self, seq, n ): 187 log.debug("readSomeBytes(" + str(seq) + "," + str(n) + ")") 188 return 0, "" 189 190 def skipBytes( self, n ): 191 pass 192 193 def available( self ): 194 return 0 195 196 197""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk 198 scriptURI is the system independent uri 199""" 200class MyUriHelper: 201 202 def __init__( self, ctx, location ): 203 self.s_UriMap = \ 204 { "share" : "vnd.sun.star.expand:${$OOO_BASE_DIR/program/" + toIniName( "bootstrap") + "::BaseInstallation}/share/Scripts/python" , \ 205 "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \ 206 "user" : "vnd.sun.star.expand:${$OOO_BASE_DIR/program/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \ 207 "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" } 208 self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx) 209 if location.startswith( "vnd.sun.star.tdoc" ): 210 self.m_baseUri = location + "/Scripts/python" 211 self.m_scriptUriLocation = "document" 212 else: 213 self.m_baseUri = expandUri( self.s_UriMap[location] ) 214 self.m_scriptUriLocation = location 215 log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation ) 216 217 def getRootStorageURI( self ): 218 return self.m_baseUri 219 220 def getStorageURI( self, scriptURI ): 221 return self.scriptURI2StorageUri(scriptURI) 222 223 def getScriptURI( self, storageURI ): 224 return self.storageURI2ScriptUri(storageURI) 225 226 def storageURI2ScriptUri( self, storageURI ): 227 if not storageURI.startswith( self.m_baseUri ): 228 message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'" 229 log.debug( message ) 230 raise RuntimeException( message ) 231 232 ret = "vnd.sun.star.script:" + \ 233 storageURI[len(self.m_baseUri)+1:].replace("/","|") + \ 234 "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation 235 log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret ) 236 return ret 237 238 def scriptURI2StorageUri( self, scriptURI ): 239 try: 240 myUri = self.m_uriRefFac.parse(scriptURI) 241 ret = self.m_baseUri + "/" + myUri.getName().replace( "|", "/" ) 242 log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret ) 243 return ret 244 except UnoException as e: 245 log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message) 246 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " +e.getMessage(), None ) 247 except Exception as e: 248 log.error( "error during converting scriptURI="+scriptURI + ": " + str(e)) 249 raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), None ) 250 251 252class ModuleEntry: 253 def __init__( self, lastRead, module ): 254 self.lastRead = lastRead 255 self.module = module 256 257def hasChanged( oldDate, newDate ): 258 return newDate.Year > oldDate.Year or \ 259 newDate.Month > oldDate.Month or \ 260 newDate.Day > oldDate.Day or \ 261 newDate.Hours > oldDate.Hours or \ 262 newDate.Minutes > oldDate.Minutes or \ 263 newDate.Seconds > oldDate.Seconds or \ 264 newDate.HundredthSeconds > oldDate.HundredthSeconds 265 266def ensureSourceState( code ): 267 if not code.endswith( "\n" ): 268 code = code + "\n" 269 code = code.replace( "\r", "" ) 270 return code 271 272 273def checkForPythonPathBesideScript( url ): 274 if url.startswith( "file:" ): 275 path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" ); 276 log.log( LogLevel.DEBUG, "checking for existence of " + path ) 277 if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path: 278 log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" ) 279 sys.path.append( path ) 280 281 path = unohelper.fileUrlToSystemPath( url+"/pythonpath" ); 282 log.log( LogLevel.DEBUG, "checking for existence of " + path ) 283 if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path: 284 log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" ) 285 sys.path.append( path ) 286 287 288class ScriptContext(unohelper.Base): 289 def __init__( self, ctx, doc, inv ): 290 self.ctx = ctx 291 self.doc = doc 292 self.inv = inv 293 294 # XScriptContext 295 def getDocument(self): 296 if self.doc: 297 return self.doc 298 return self.getDesktop().getCurrentComponent() 299 300 def getDesktop(self): 301 return self.ctx.ServiceManager.createInstanceWithContext( 302 "com.sun.star.frame.Desktop", self.ctx ) 303 304 def getComponentContext(self): 305 return self.ctx 306 307 def getInvocationContext(self): 308 return self.inv 309 310#---------------------------------- 311# Global Module Administration 312# does not fit together with script 313# engine lifetime management 314#---------------------------------- 315#g_scriptContext = ScriptContext( uno.getComponentContext(), None ) 316#g_modules = {} 317#def getModuleByUrl( url, sfa ): 318# entry = g_modules.get(url) 319# load = True 320# lastRead = sfa.getDateTimeModified( url ) 321# if entry: 322# if hasChanged( entry.lastRead, lastRead ): 323# log.debug("file " + url + " has changed, reloading") 324# else: 325# load = False 326# 327# if load: 328# log.debug( "opening >" + url + "<" ) 329# 330# code = readTextFromStream( sfa.openFileRead( url ) ) 331 332 # execute the module 333# entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") ) 334# entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext 335# entry.module.__file__ = url 336# exec code in entry.module.__dict__ 337# g_modules[ url ] = entry 338# log.debug( "mapped " + url + " to " + str( entry.module ) ) 339# return entry.module 340 341class ProviderContext: 342 def __init__( self, storageType, sfa, uriHelper, scriptContext ): 343 self.storageType = storageType 344 self.sfa = sfa 345 self.uriHelper = uriHelper 346 self.scriptContext = scriptContext 347 self.modules = {} 348 self.rootUrl = None 349 self.mapPackageName2Path = None 350 351 def getTransientPartFromUrl( self, url ): 352 rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1) 353 return rest[0:rest.find("/")] 354 355 def getPackageNameFromUrl( self, url ): 356 rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1) 357 start = rest.find("/") +1 358 return rest[start:rest.find("/",start)] 359 360 361 def removePackageByUrl( self, url ): 362 items = list(self.mapPackageName2Path.items()) 363 for i in items: 364 if url in i[1].pathes: 365 self.mapPackageName2Path.pop(i[0]) 366 break 367 368 def addPackageByUrl( self, url ): 369 packageName = self.getPackageNameFromUrl( url ) 370 transientPart = self.getTransientPartFromUrl( url ) 371 log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl ) 372 if packageName in self.mapPackageName2Path: 373 package = self.mapPackageName2Path[ packageName ] 374 package.pathes = package.pathes + (url, ) 375 else: 376 package = Package( (url,), transientPart) 377 self.mapPackageName2Path[ packageName ] = package 378 379 def isUrlInPackage( self, url ): 380 values = list(self.mapPackageName2Path.values()) 381 for i in values: 382# print "checking " + url + " in " + str(i.pathes) 383 if url in i.pathes: 384 return True 385# print "false" 386 return False 387 388 def setPackageAttributes( self, mapPackageName2Path, rootUrl ): 389 self.mapPackageName2Path = mapPackageName2Path 390 self.rootUrl = rootUrl 391 392 def getPersistentUrlFromStorageUrl( self, url ): 393 # package name is the second directory 394 ret = url 395 if self.rootUrl: 396 pos = len( self.rootUrl) +1 397 ret = url[0:pos]+url[url.find("/",pos)+1:len(url)] 398 log.debug( "getPersistentUrlFromStorageUrl " + url + " -> "+ ret) 399 return ret 400 401 def getStorageUrlFromPersistentUrl( self, url): 402 ret = url 403 if self.rootUrl: 404 pos = len(self.rootUrl)+1 405 packageName = url[pos:url.find("/",pos+1)] 406 package = self.mapPackageName2Path[ packageName ] 407 ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)] 408 log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret) 409 return ret 410 411 def getFuncsByUrl( self, url ): 412 src = readTextFromStream( self.sfa.openFileRead( url ) ) 413 checkForPythonPathBesideScript( url[0:url.rfind('/')] ) 414 src = ensureSourceState( src ) 415 416 allFuncs = [] 417 g_exportedScripts = [] 418 419 a = ast.parse(src, url) 420 421 if isinstance(a, ast.Module): 422 for node in a.body: 423 if isinstance(node, ast.FunctionDef): 424 allFuncs.append(node.name) 425 elif isinstance(node, ast.Assign): 426 is_exported = False 427 for subnode in node.targets: 428 if isinstance(subnode, ast.Name) and \ 429 subnode.id == "g_exportedScripts": 430 is_exported = True 431 break 432 if is_exported: 433 value_node = node.value 434 if isinstance(value_node, ast.List) or \ 435 isinstance(value_node, ast.Tuple): 436 for elt in value_node.elts: 437 if isinstance(elt, ast.Str): 438 g_exportedScripts.append(elt.s) 439 elif isinstance(elt, ast.Name): 440 g_exportedScripts.append(elt.id) 441 elif isinstance(value_node, ast.Str): 442 g_exportedScripts.append(value_node.s) 443 elif isinstance(value_node, ast.Name): 444 g_exportedScripts.append(value_node.id) 445 return g_exportedScripts 446 return allFuncs 447 448 def getModuleByUrl( self, url ): 449 entry = self.modules.get(url) 450 load = True 451 lastRead = self.sfa.getDateTimeModified( url ) 452 if entry: 453 if hasChanged( entry.lastRead, lastRead ): 454 log.debug( "file " + url + " has changed, reloading" ) 455 else: 456 load = False 457 458 if load: 459 log.debug( "opening >" + url + "<" ) 460 461 src = readTextFromStream( self.sfa.openFileRead( url ) ) 462 checkForPythonPathBesideScript( url[0:url.rfind('/')] ) 463 src = ensureSourceState( src ) 464 465 # execute the module 466 entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") ) 467 entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext 468 469 code = None 470 if url.startswith( "file:" ): 471 code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" ) 472 else: 473 code = compile( src, url, "exec" ) 474 exec(code, entry.module.__dict__) 475 entry.module.__file__ = url 476 self.modules[ url ] = entry 477 log.debug( "mapped " + url + " to " + str( entry.module ) ) 478 return entry.module 479 480#-------------------------------------------------- 481def isScript( candidate ): 482 ret = False 483 if isinstance( candidate, type(isScript) ): 484 ret = True 485 return ret 486 487#------------------------------------------------------- 488class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ): 489 def __init__( self, provCtx, uri, fileName, funcName ): 490 self.fileName = fileName 491 self.funcName = funcName 492 self.provCtx = provCtx 493 self.uri = uri 494 495 def getName( self ): 496 return self.funcName 497 498 def getChildNodes(self): 499 return () 500 501 def hasChildNodes(self): 502 return False 503 504 def getType( self): 505 return SCRIPT 506 507 def getPropertyValue( self, name ): 508 ret = None 509 try: 510 if name == "URI": 511 ret = self.provCtx.uriHelper.getScriptURI( 512 self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) ) 513 elif name == "Editable" and ENABLE_EDIT_DIALOG: 514 ret = not self.provCtx.sfa.isReadOnly( self.uri ) 515 516 log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) ) 517 except Exception as e: 518 log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String()) 519 raise 520 521 return ret 522 def setPropertyValue( self, name, value ): 523 log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) ) 524 def getPropertySetInfo( self ): 525 log.debug( "ScriptBrowseNode.getPropertySetInfo called " ) 526 return None 527 528 def getIntrospection( self ): 529 return None 530 531 def invoke( self, name, params, outparamindex, outparams ): 532 if name == "Editable": 533 servicename = "com.sun.star.awt.DialogProvider" 534 ctx = self.provCtx.scriptContext.getComponentContext() 535 dlgprov = ctx.ServiceManager.createInstanceWithContext( 536 servicename, ctx ) 537 538 self.editor = dlgprov.createDialog( 539 "vnd.sun.star.script:" + 540 "ScriptBindingLibrary.MacroEditor?location=application") 541 542 code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri)) 543 code = ensureSourceState( code ) 544 self.editor.getControl("EditorTextField").setText(code) 545 546 self.editor.getControl("RunButton").setActionCommand("Run") 547 self.editor.getControl("RunButton").addActionListener(self) 548 self.editor.getControl("SaveButton").setActionCommand("Save") 549 self.editor.getControl("SaveButton").addActionListener(self) 550 551 self.editor.execute() 552 553 return None, (), () 554 555 def actionPerformed( self, event ): 556 try: 557 if event.ActionCommand == "Run": 558 code = self.editor.getControl("EditorTextField").getText() 559 code = ensureSourceState( code ) 560 mod = imp.new_module("ooo_script_framework") 561 mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext 562 exec(code, mod.__dict__) 563 values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None ) 564 if not values: 565 values = list(mod.__dict__.values()) 566 567 for i in values: 568 if isScript( i ): 569 i() 570 break 571 572 elif event.ActionCommand == "Save": 573 toWrite = uno.ByteSequence( 574 str( 575 self.editor.getControl("EditorTextField").getText().encode( 576 sys.getdefaultencoding())) ) 577 copyUrl = self.uri + ".orig" 578 self.provCtx.sfa.move( self.uri, copyUrl ) 579 out = self.provCtx.sfa.openFileWrite( self.uri ) 580 out.writeBytes( toWrite ) 581 out.close() 582 self.provCtx.sfa.kill( copyUrl ) 583# log.debug("Save is not implemented yet") 584# text = self.editor.getControl("EditorTextField").getText() 585# log.debug("Would save: " + text) 586 except Exception as e: 587 # TODO: add an error box here ! 588 log.error( lastException2String() ) 589 590 591 def setValue( self, name, value ): 592 return None 593 594 def getValue( self, name ): 595 return None 596 597 def hasMethod( self, name ): 598 return False 599 600 def hasProperty( self, name ): 601 return False 602 603 604#------------------------------------------------------- 605class FileBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ): 606 def __init__( self, provCtx, uri , name ): 607 self.provCtx = provCtx 608 self.uri = uri 609 self.name = name 610 self.funcnames = None 611 612 def getName( self ): 613 return self.name 614 615 def getChildNodes(self): 616 ret = () 617 try: 618 self.funcnames = self.provCtx.getFuncsByUrl( self.uri ) 619 620 scriptNodeList = [] 621 for i in self.funcnames: 622 scriptNodeList.append( 623 ScriptBrowseNode( 624 self.provCtx, self.uri, self.name, i )) 625 ret = tuple( scriptNodeList ) 626 log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri ) 627 except Exception as e: 628 text = lastException2String() 629 log.error( "Error while evaluating " + self.uri + ":" + text ) 630 raise 631 return ret 632 633 def hasChildNodes(self): 634 try: 635 return len(self.getChildNodes()) > 0 636 except Exception as e: 637 return False 638 639 def getType( self): 640 return CONTAINER 641 642 # XPropertySet 643 644 def getPropertyValue( self, name ): 645 ret = None 646 try: 647 if name == "Editable": 648 ret = not self.provCtx.sfa.isReadOnly( self.uri ) 649 elif name == "Deletable": 650 ret = not self.provCtx.sfa.isReadOnly( self.uri ) 651 elif name == "Renamable": 652 ret = not self.provCtx.sfa.isReadOnly( self.uri ) 653 654 log.debug("uri is " + self.uri) 655 log.debug( "FileBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) ) 656 except Exception as e: 657 log.error( "FileBrowseNode.getPropertyValue error " + lastException2String()) 658 raise 659 660 return ret 661 662 def setPropertyValue( self, name, value ): 663 log.debug( "FileBrowseNode.setPropertyValue called " + name + "=" +str(value ) ) 664 665 def getPropertySetInfo( self ): 666 log.debug( "FileBrowseNode.getPropertySetInfo called " ) 667 return None 668 669 # XInvocation 670 671 def getIntrospection( self ): 672 log.debug( "FileBrowseNode.getIntrospection() called" ) 673 return None 674 675 def invoke( self, name, params, outparamindex, outparams ): 676 log.debug("FileBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams)) 677 if name == "Editable": 678 log.debug("Editable not implemented") 679 elif name == "Deletable": 680 self.provCtx.sfa.kill( self.uri ) 681 return True, (), () 682 elif name == "Renamable": 683 try: 684 newUrl = self.uri[0:self.uri.rfind("/")+1] + params[0] + ".py" 685 self.provCtx.sfa.move( self.uri, newUrl ) 686 self.uri = newUrl 687 self.name = params[0] 688 return self, (), () 689 except Exception as e: 690 log.error( "FileBrowseNode invoke rename error " + lastException2String()) 691 raise 692 return None, (), () 693 694 def setValue( self, name, value ): 695 return None 696 697 def getValue( self, name ): 698 log.debug( "FileBrowseNode.getValue() called" ) 699 return None 700 701 def hasMethod( self, name ): 702 return False 703 704 def hasProperty( self, name ): 705 return False 706 707 708class DirBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ): 709 def __init__( self, provCtx, name, rootUrl, depth ): 710 self.provCtx = provCtx 711 self.name = name 712 self.rootUrl = rootUrl 713 self.depth = depth 714 log.debug( "DirBrowseNode constructor for " + name + "," + rootUrl ) 715 716 def getName( self ): 717 return self.name 718 719 def getChildNodes( self ): 720 try: 721 log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl ) 722 contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True ) 723 browseNodeList = [] 724 for i in contents: 725 if i.endswith( ".py" ): 726 log.debug( "adding filenode " + i ) 727 browseNodeList.append( 728 FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) ) 729 elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"): 730 log.debug( "adding DirBrowseNode " + i ) 731 browseNodeList.append( 732 DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)], i, self.depth + 1 ) ) 733 return tuple( browseNodeList ) 734 except Exception as e: 735 text = lastException2String() 736 log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl) 737 log.error( text) 738 return () 739 740 def hasChildNodes( self ): 741 return True 742 743 def getType( self ): 744 return CONTAINER 745 746 # XScriptProvider 747 748 def getScript( self, uri ): 749 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 750 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) 751 752 # XPropertySet 753 754 def getPropertyValue( self, name ): 755 ret = None 756 try: 757 if name == "Creatable": 758 ret = True 759 elif name == "Deletable": 760 ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl ) 761 elif name == "Renamable": 762 ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl ) 763 764 log.debug("rootUrl is " + self.rootUrl) 765 log.debug( "DirBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) ) 766 except Exception as e: 767 log.error( "DirBrowseNode.getPropertyValue error " + lastException2String()) 768 raise 769 770 return ret 771 772 def setPropertyValue( self, name, value ): 773 log.debug( "DirBrowseNode.setPropertyValue called " + name + "=" +str(value ) ) 774 775 def getPropertySetInfo( self ): 776 log.debug( "DirBrowseNode.getPropertySetInfo called " ) 777 return None 778 779 # XInvocation 780 781 def getIntrospection( self ): 782 log.debug( "DirBrowseNode.getIntrospection() called" ) 783 return None 784 785 def invoke( self, name, params, outparamindex, outparams ): 786 log.debug("DirBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams)) 787 if name == "Creatable": 788 if self.depth == 0: 789 log.debug( "depth is 0" ) 790 subFolderUrl = self.rootUrl + "/" + params[0] 791 self.provCtx.sfa.createFolder( subFolderUrl ) 792 log.debug( "created folder!" ) 793 childNode = DirBrowseNode( self.provCtx, subFolderUrl[subFolderUrl.rfind("/")+1:len(subFolderUrl)], subFolderUrl, self.depth + 1 ) 794 log.debug( "created childNode" ) 795 return childNode, (), () 796 else: 797 log.debug( "depth is " + str(self.depth) ) 798 scriptUrl = self.rootUrl + "/" + params[0] + ".py" 799 log.debug( "scriptUrl=" + scriptUrl ) 800 try: 801 self.provCtx.sfa.writeFile( scriptUrl, EmptyInputStream() ) 802 except Exception as e: 803 log.error( "Creatable error: " + lastException2String()) 804 raise 805 childNode = FileBrowseNode( self.provCtx, scriptUrl, params[0] ) 806 log.debug( "returning" ) 807 return childNode, (), () 808 elif name == "Deletable": 809 self.provCtx.sfa.kill( self.rootUrl ) 810 return True, (), () 811 elif name == "Renamable": 812 newUrl = self.rootUrl[0:self.rootUrl.rfind("/")+1] + params[0] 813 self.provCtx.sfa.move( self.rootUrl, newUrl ) 814 self.rootUrl = newUrl 815 self.name = params[0] 816 return self, (), () 817 return None, (), () 818 819 def setValue( self, name, value ): 820 return None 821 822 def getValue( self, name ): 823 log.debug( "DirBrowseNode.getValue() called" ) 824 return None 825 826 def hasMethod( self, name ): 827 return False 828 829 def hasProperty( self, name ): 830 return False 831 832 833class ManifestHandler( XDocumentHandler, unohelper.Base ): 834 def __init__( self, rootUrl ): 835 self.rootUrl = rootUrl 836 837 def startDocument( self ): 838 self.urlList = [] 839 840 def endDocument( self ): 841 pass 842 843 def startElement( self , name, attlist): 844 if name == "manifest:file-entry": 845 if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script": 846 self.urlList.append( 847 self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) ) 848 849 def endElement( self, name ): 850 pass 851 852 def characters ( self, chars ): 853 pass 854 855 def ignoreableWhitespace( self, chars ): 856 pass 857 858 def setDocumentLocator( self, locator ): 859 pass 860 861def isPyFileInPath( sfa, path ): 862 ret = False 863 contents = sfa.getFolderContents( path, True ) 864 for i in contents: 865 if sfa.isFolder(i): 866 ret = isPyFileInPath(sfa,i) 867 else: 868 if i.endswith(".py"): 869 ret = True 870 if ret: 871 break 872 return ret 873 874# extracts META-INF directory from 875def getPathesFromPackage( rootUrl, sfa ): 876 ret = () 877 try: 878 fileUrl = rootUrl + "/META-INF/manifest.xml" 879 inputStream = sfa.openFileRead( fileUrl ) 880 parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" ) 881 handler = ManifestHandler( rootUrl ) 882 parser.setDocumentHandler( handler ) 883 parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) ) 884 for i in tuple(handler.urlList): 885 if not isPyFileInPath( sfa, i ): 886 handler.urlList.remove(i) 887 ret = tuple( handler.urlList ) 888 except UnoException as e: 889 text = lastException2String() 890 log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text ) 891 pass 892 return ret 893 894 895class Package: 896 def __init__( self, pathes, transientPathElement ): 897 self.pathes = pathes 898 self.transientPathElement = transientPathElement 899 900class DummyInteractionHandler( unohelper.Base, XInteractionHandler ): 901 def __init__( self ): 902 pass 903 def handle( self, event): 904 log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) ) 905 906class DummyProgressHandler( unohelper.Base, XProgressHandler ): 907 def __init__( self ): 908 pass 909 910 def push( self,status ): 911 log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) ) 912 def update( self,status ): 913 log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) ) 914 def pop( self ): 915 log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) ) 916 917class CommandEnvironment(unohelper.Base, XCommandEnvironment): 918 def __init__( self ): 919 self.progressHandler = DummyProgressHandler() 920 self.interactionHandler = DummyInteractionHandler() 921 def getInteractionHandler( self ): 922 return self.interactionHandler 923 def getProgressHandler( self ): 924 return self.progressHandler 925 926#maybe useful for debugging purposes 927#class ModifyListener( unohelper.Base, XModifyListener ): 928# def __init__( self ): 929# pass 930# def modified( self, event ): 931# log.debug( "pythonscript: ModifyListener.modified " + str( event ) ) 932# def disposing( self, event ): 933# log.debug( "pythonscript: ModifyListener.disposing " + str( event ) ) 934 935def getModelFromDocUrl(ctx, url): 936 """Get document model from document url.""" 937 doc = None 938 args = ("Local", "Office") 939 ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext( 940 "com.sun.star.ucb.UniversalContentBroker", args, ctx) 941 identifier = ucb.createContentIdentifier(url) 942 content = ucb.queryContent(identifier) 943 p = Property() 944 p.Name = "DocumentModel" 945 p.Handle = -1 946 947 c = Command() 948 c.Handle = -1 949 c.Name = "getPropertyValues" 950 c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,)) 951 952 env = CommandEnvironment() 953 try: 954 ret = content.execute(c, 0, env) 955 doc = ret.getObject(1, None) 956 except Exception as e: 957 log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url) 958 return doc 959 960def mapStorageType2PackageContext( storageType ): 961 ret = storageType 962 if( storageType == "share:uno_packages" ): 963 ret = "shared" 964 if( storageType == "user:uno_packages" ): 965 ret = "user" 966 return ret 967 968def getPackageName2PathMap( sfa, storageType ): 969 ret = {} 970 packageManagerFactory = uno.getComponentContext().getValueByName( 971 "/singletons/com.sun.star.deployment.thePackageManagerFactory" ) 972 packageManager = packageManagerFactory.getPackageManager( 973 mapStorageType2PackageContext(storageType)) 974# packageManager.addModifyListener( ModifyListener() ) 975 log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" ) 976 packages = packageManager.getDeployedPackages( 977 packageManager.createAbortChannel(), CommandEnvironment( ) ) 978 log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" ) 979 980 for i in packages: 981 log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" ) 982 transientPathElement = penultimateElement( i.URL ) 983 j = expandUri( i.URL ) 984 pathes = getPathesFromPackage( j, sfa ) 985 if len( pathes ) > 0: 986 # map package name to url, we need this later 987 log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) ) 988 ret[ lastElement( j ) ] = Package( pathes, transientPathElement ) 989 return ret 990 991def penultimateElement( aStr ): 992 lastSlash = aStr.rindex("/") 993 penultimateSlash = aStr.rindex("/",0,lastSlash-1) 994 return aStr[ penultimateSlash+1:lastSlash ] 995 996def lastElement( aStr): 997 return aStr[ aStr.rfind( "/" )+1:len(aStr)] 998 999class PackageBrowseNode( unohelper.Base, XBrowseNode ): 1000 def __init__( self, provCtx, name, rootUrl ): 1001 self.provCtx = provCtx 1002 self.name = name 1003 self.rootUrl = rootUrl 1004 1005 def getName( self ): 1006 return self.name 1007 1008 def getChildNodes( self ): 1009 items = list(self.provCtx.mapPackageName2Path.items()) 1010 browseNodeList = [] 1011 for i in items: 1012 if len( i[1].pathes ) == 1: 1013 browseNodeList.append( 1014 DirBrowseNode( self.provCtx, i[0], i[1].pathes[0], 0 )) 1015 else: 1016 for j in i[1].pathes: 1017 browseNodeList.append( 1018 DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j, 0 ) ) 1019 return tuple( browseNodeList ) 1020 1021 def hasChildNodes( self ): 1022 return len( self.mapPackageName2Path ) > 0 1023 1024 def getType( self ): 1025 return CONTAINER 1026 1027 def getScript( self, uri ): 1028 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 1029 raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 ) 1030 1031 1032 1033 1034class PythonScript( unohelper.Base, XScript ): 1035 def __init__( self, func, mod ): 1036 self.func = func 1037 self.mod = mod 1038 def invoke(self, args, out, outindex ): 1039 log.debug( "PythonScript.invoke " + str( args ) ) 1040 try: 1041 ret = self.func( *args ) 1042 except UnoException as e: 1043 # UNO Exception continue to fly ... 1044 text = lastException2String() 1045 complete = "Error during invoking function " + \ 1046 str(self.func.__name__) + " in module " + \ 1047 self.mod.__file__ + " (" + text + ")" 1048 log.debug( complete ) 1049 # some people may beat me up for modifying the exception text, 1050 # but otherwise office just shows 1051 # the type name and message text with no more information, 1052 # this is really bad for most users. 1053 e.Message = e.Message + " (" + complete + ")" 1054 raise 1055 except Exception as e: 1056 # General python exception are converted to uno RuntimeException 1057 text = lastException2String() 1058 complete = "Error during invoking function " + \ 1059 str(self.func.__name__) + " in module " + \ 1060 self.mod.__file__ + " (" + text + ")" 1061 log.debug( complete ) 1062 raise RuntimeException( complete , self ) 1063 log.debug( "PythonScript.invoke ret = " + str( ret ) ) 1064 return ret, (), () 1065 1066def expandUri( uri ): 1067 if uri.startswith( "vnd.sun.star.expand:" ): 1068 uri = uri.replace( "vnd.sun.star.expand:", "",1) 1069 uri = uno.getComponentContext().getByName( 1070 "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri ) 1071 if uri.startswith( "file:" ): 1072 uri = uno.absolutize("",uri) # necessary to get rid of .. in uri 1073 return uri 1074 1075#-------------------------------------------------------------- 1076class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer, XPropertySet, XInvocation): 1077 def __init__( self, ctx, *args ): 1078 if log.isDebugLevel(): 1079 mystr = "" 1080 for i in args: 1081 if len(mystr) > 0: 1082 mystr = mystr +"," 1083 mystr = mystr + str(i) 1084 log.debug( "Entering PythonScriptProvider.ctor with args " + mystr ) 1085 1086 doc = None 1087 inv = None 1088 storageType = "" 1089 1090 if isinstance(args[0],unicode ): 1091 storageType = args[0] 1092 if storageType.startswith( "vnd.sun.star.tdoc" ): 1093 doc = getModelFromDocUrl(ctx, storageType) 1094 else: 1095 inv = args[0] 1096 try: 1097 doc = inv.ScriptContainer 1098 content = ctx.getServiceManager().createInstanceWithContext( 1099 "com.sun.star.frame.TransientDocumentsDocumentContentFactory", 1100 ctx).createDocumentContent(doc) 1101 storageType = content.getIdentifier().getContentIdentifier() 1102 except Exception as e: 1103 text = lastException2String() 1104 log.error( text ) 1105 1106 isPackage = storageType.endswith( ":uno_packages" ) 1107 1108 try: 1109# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext( 1110# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx) 1111 urlHelper = MyUriHelper( ctx, storageType ) 1112 log.debug( "got urlHelper " + str( urlHelper ) ) 1113 1114 rootUrl = expandUri( urlHelper.getRootStorageURI() ) 1115 log.debug( storageType + " transformed to " + rootUrl ) 1116 1117 ucbService = "com.sun.star.ucb.SimpleFileAccess" 1118 sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx ) 1119 if not sfa: 1120 log.debug("PythonScriptProvider couldn't instantiate " +ucbService) 1121 raise RuntimeException( 1122 "PythonScriptProvider couldn't instantiate " +ucbService, self) 1123 self.provCtx = ProviderContext( 1124 storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) ) 1125 if isPackage: 1126 mapPackageName2Path = getPackageName2PathMap( sfa, storageType ) 1127 self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl ) 1128 self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl ) 1129 else: 1130 self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl, 0 ) 1131 1132 except Exception as e: 1133 text = lastException2String() 1134 log.debug( "PythonScriptProvider could not be instantiated because of : " + text ) 1135 raise e 1136 1137 def getName( self ): 1138 return self.dirBrowseNode.getName() 1139 1140 def getChildNodes( self ): 1141 return self.dirBrowseNode.getChildNodes() 1142 1143 def hasChildNodes( self ): 1144 return self.dirBrowseNode.hasChildNodes() 1145 1146 def getType( self ): 1147 return self.dirBrowseNode.getType() 1148 1149 def getScript( self, uri ): 1150 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 1151 1152 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) 1153 1154 def getScript( self, scriptUri ): 1155 try: 1156 log.debug( "getScript " + scriptUri + " invoked") 1157 1158 storageUri = self.provCtx.getStorageUrlFromPersistentUrl( 1159 self.provCtx.uriHelper.getStorageURI(scriptUri) ); 1160 log.debug( "getScript: storageUri = " + storageUri) 1161 fileUri = storageUri[0:storageUri.find( "$" )] 1162 funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)] 1163 1164 mod = self.provCtx.getModuleByUrl( fileUri ) 1165 log.debug( " got mod " + str(mod) ) 1166 1167 func = mod.__dict__[ funcName ] 1168 1169 log.debug( "got func " + str( func ) ) 1170 return PythonScript( func, mod ) 1171 except Exception as e: 1172 text = lastException2String() 1173 log.error( text ) 1174 raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 ) 1175 1176 # XPropertySet 1177 1178 def getPropertyValue( self, name ): 1179 return self.dirBrowseNode.getPropertyValue( name ) 1180 1181 def setPropertyValue( self, name, value ): 1182 return self.dirBrowseNode.setPropertyValue( name, value ) 1183 1184 def getPropertySetInfo( self ): 1185 return self.dirBrowseNode.getPropertySetInfo() 1186 1187 # XInvocation 1188 1189 def getIntrospection( self ): 1190 return self.dirBrowseNode.getIntrospection() 1191 1192 def invoke( self, name, params, outparamindex, outparams ): 1193 return self.dirBrowseNode.invoke( name, params, outparamindex, outparams) 1194 1195 def setValue( self, name, value ): 1196 return self.dirBrowseNode.setValue( name, value ) 1197 1198 def getValue( self, name ): 1199 return self.dirBrowseNode.getValue( name ) 1200 1201 def hasMethod( self, name ): 1202 return self.dirBrowseNode.hasMethod( name ) 1203 1204 def hasProperty( self, name ): 1205 return self.dirBrowseNode.hasProperty( name ) 1206 1207 # XServiceInfo 1208 def getSupportedServices( self ): 1209 return g_ImplementationHelper.getSupportedServices(g_implName) 1210 1211 def supportsService( self, ServiceName ): 1212 return g_ImplementationHelper.supportsService( g_implName, ServiceName ) 1213 1214 def getImplementationName(self): 1215 return g_implName 1216 1217 def getByName( self, name ): 1218 log.debug( "getByName called" + str( name )) 1219 return None 1220 1221 1222 def getElementNames( self ): 1223 log.debug( "getElementNames called") 1224 return () 1225 1226 def hasByName( self, name ): 1227 try: 1228 log.debug( "hasByName called " + str( name )) 1229 uri = expandUri(name) 1230 ret = self.provCtx.isUrlInPackage( uri ) 1231 log.debug( "hasByName " + uri + " " +str( ret ) ) 1232 return ret 1233 except Exception as e: 1234 text = lastException2String() 1235 log.debug( "Error in hasByName:" + text ) 1236 return False 1237 1238 def removeByName( self, name ): 1239 log.debug( "removeByName called" + str( name )) 1240 uri = expandUri( name ) 1241 if self.provCtx.isUrlInPackage( uri ): 1242 self.provCtx.removePackageByUrl( uri ) 1243 else: 1244 log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" ) 1245 raise NoSuchElementException( uri + "is not in package" , self ) 1246 log.debug( "removeByName called" + str( uri ) + " successful" ) 1247 1248 def insertByName( self, name, value ): 1249 log.debug( "insertByName called " + str( name ) + " " + str( value )) 1250 uri = expandUri( name ) 1251 if isPyFileInPath( self.provCtx.sfa, uri ): 1252 self.provCtx.addPackageByUrl( uri ) 1253 else: 1254 # package is no python package ... 1255 log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" ) 1256 raise IllegalArgumentException( uri + " does not contain .py files", self, 1 ) 1257 log.debug( "insertByName called " + str( uri ) + " successful" ) 1258 1259 def replaceByName( self, name, value ): 1260 log.debug( "replaceByName called " + str( name ) + " " + str( value )) 1261 removeByName( name ) 1262 insertByName( name ) 1263 log.debug( "replaceByName called" + str( uri ) + " successful" ) 1264 1265 def getElementType( self ): 1266 log.debug( "getElementType called" ) 1267 return uno.getTypeByName( "void" ) 1268 1269 def hasElements( self ): 1270 log.debug( "hasElements got called") 1271 return False 1272 1273g_ImplementationHelper.addImplementation( \ 1274 PythonScriptProvider,g_implName, \ 1275 ("com.sun.star.script.provider.LanguageScriptProvider", 1276 "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),) 1277 1278 1279log.debug( "pythonscript finished initializing" ) 1280