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