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