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