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 newUri = self.parent.rootUrl + "/" + params[0] + ".py" 688 self.provCtx.sfa.move( self.uri(), newUri ) 689 self.name = params[0] 690 return self, (), () 691 except Exception as e: 692 log.error( "FileBrowseNode.invoke error " + lastException2String() ) 693 raise 694 return None, (), () 695 696 def setValue( self, name, value ): 697 return None 698 699 def getValue( self, name ): 700 log.debug( "FileBrowseNode.getValue() called" ) 701 return None 702 703 def hasMethod( self, name ): 704 return False 705 706 def hasProperty( self, name ): 707 return False 708 709 710class DirBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ): 711 def __init__( self, provCtx, name, rootUrl, depth ): 712 self.provCtx = provCtx 713 self.name = name 714 self.rootUrl = rootUrl 715 self.depth = depth 716 log.debug( "DirBrowseNode constructor for " + name + "," + rootUrl ) 717 718 def getName( self ): 719 return self.name 720 721 def getChildNodes( self ): 722 try: 723 log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl ) 724 contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True ) 725 browseNodeList = [] 726 for i in contents: 727 if i.endswith( ".py" ): 728 log.debug( "adding filenode " + i ) 729 browseNodeList.append( 730 FileBrowseNode( self.provCtx, self, i[i.rfind("/")+1:len(i)-3] ) ) 731 elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"): 732 log.debug( "adding DirBrowseNode " + i ) 733 browseNodeList.append( 734 DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)], i, self.depth + 1 ) ) 735 return tuple( browseNodeList ) 736 except Exception as e: 737 text = lastException2String() 738 log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl) 739 log.error( text) 740 return () 741 742 def hasChildNodes( self ): 743 return True 744 745 def getType( self ): 746 return CONTAINER 747 748 # XScriptProvider 749 750 def getScript( self, uri ): 751 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 752 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) 753 754 # XPropertySet 755 756 def getPropertyValue( self, name ): 757 ret = None 758 try: 759 if name == "Creatable": 760 ret = True 761 elif name == "Deletable": 762 ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl ) 763 elif name == "Renamable": 764 ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl ) 765 766 log.debug( "DirBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) ) 767 except Exception as e: 768 log.error( "DirBrowseNode.getPropertyValue error " + lastException2String()) 769 raise 770 771 return ret 772 773 def setPropertyValue( self, name, value ): 774 log.debug( "DirBrowseNode.setPropertyValue called " + name + "=" +str(value ) ) 775 776 def getPropertySetInfo( self ): 777 log.debug( "DirBrowseNode.getPropertySetInfo called " ) 778 return None 779 780 # XInvocation 781 782 def getIntrospection( self ): 783 log.debug( "DirBrowseNode.getIntrospection() called" ) 784 return None 785 786 def invoke( self, name, params, outparamindex, outparams ): 787 log.debug("DirBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams)) 788 try: 789 if name == "Creatable": 790 if self.depth == 0: 791 subFolderUrl = self.rootUrl + "/" + params[0] 792 self.provCtx.sfa.createFolder( subFolderUrl ) 793 childNode = DirBrowseNode( self.provCtx, subFolderUrl[subFolderUrl.rfind("/")+1:len(subFolderUrl)], subFolderUrl, self.depth + 1 ) 794 return childNode, (), () 795 else: 796 scriptUrl = self.rootUrl + "/" + params[0] + ".py" 797 # Creates an empty file 798 self.provCtx.sfa.writeFile( scriptUrl, EmptyInputStream() ) 799 childNode = FileBrowseNode( self.provCtx, self, params[0] ) 800 return childNode, (), () 801 elif name == "Deletable": 802 self.provCtx.sfa.kill( self.rootUrl ) 803 return True, (), () 804 elif name == "Renamable": 805 newUrl = self.rootUrl[0:self.rootUrl.rfind("/")+1] + params[0] 806 self.provCtx.sfa.move( self.rootUrl, newUrl ) 807 self.rootUrl = newUrl 808 self.name = params[0] 809 return self, (), () 810 except Exception as e: 811 log.error( "DirBrowseNode.invoke error: " + lastException2String()) 812 raise 813 return None, (), () 814 815 def setValue( self, name, value ): 816 return None 817 818 def getValue( self, name ): 819 log.debug( "DirBrowseNode.getValue() called" ) 820 return None 821 822 def hasMethod( self, name ): 823 return False 824 825 def hasProperty( self, name ): 826 return False 827 828 829class ManifestHandler( XDocumentHandler, unohelper.Base ): 830 def __init__( self, rootUrl ): 831 self.rootUrl = rootUrl 832 833 def startDocument( self ): 834 self.urlList = [] 835 836 def endDocument( self ): 837 pass 838 839 def startElement( self , name, attlist): 840 if name == "manifest:file-entry": 841 if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script": 842 self.urlList.append( 843 self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) ) 844 845 def endElement( self, name ): 846 pass 847 848 def characters ( self, chars ): 849 pass 850 851 def ignoreableWhitespace( self, chars ): 852 pass 853 854 def setDocumentLocator( self, locator ): 855 pass 856 857def isPyFileInPath( sfa, path ): 858 ret = False 859 contents = sfa.getFolderContents( path, True ) 860 for i in contents: 861 if sfa.isFolder(i): 862 ret = isPyFileInPath(sfa,i) 863 else: 864 if i.endswith(".py"): 865 ret = True 866 if ret: 867 break 868 return ret 869 870# extracts META-INF directory from 871def getPathesFromPackage( rootUrl, sfa ): 872 ret = () 873 try: 874 fileUrl = rootUrl + "/META-INF/manifest.xml" 875 inputStream = sfa.openFileRead( fileUrl ) 876 parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" ) 877 handler = ManifestHandler( rootUrl ) 878 parser.setDocumentHandler( handler ) 879 parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) ) 880 for i in tuple(handler.urlList): 881 if not isPyFileInPath( sfa, i ): 882 handler.urlList.remove(i) 883 ret = tuple( handler.urlList ) 884 except UnoException as e: 885 text = lastException2String() 886 log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text ) 887 pass 888 return ret 889 890 891class Package: 892 def __init__( self, pathes, transientPathElement ): 893 self.pathes = pathes 894 self.transientPathElement = transientPathElement 895 896class DummyInteractionHandler( unohelper.Base, XInteractionHandler ): 897 def __init__( self ): 898 pass 899 def handle( self, event): 900 log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) ) 901 902class DummyProgressHandler( unohelper.Base, XProgressHandler ): 903 def __init__( self ): 904 pass 905 906 def push( self,status ): 907 log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) ) 908 def update( self,status ): 909 log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) ) 910 def pop( self ): 911 log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) ) 912 913class CommandEnvironment(unohelper.Base, XCommandEnvironment): 914 def __init__( self ): 915 self.progressHandler = DummyProgressHandler() 916 self.interactionHandler = DummyInteractionHandler() 917 def getInteractionHandler( self ): 918 return self.interactionHandler 919 def getProgressHandler( self ): 920 return self.progressHandler 921 922#maybe useful for debugging purposes 923#class ModifyListener( unohelper.Base, XModifyListener ): 924# def __init__( self ): 925# pass 926# def modified( self, event ): 927# log.debug( "pythonscript: ModifyListener.modified " + str( event ) ) 928# def disposing( self, event ): 929# log.debug( "pythonscript: ModifyListener.disposing " + str( event ) ) 930 931def getModelFromDocUrl(ctx, url): 932 """Get document model from document url.""" 933 doc = None 934 args = ("Local", "Office") 935 ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext( 936 "com.sun.star.ucb.UniversalContentBroker", args, ctx) 937 identifier = ucb.createContentIdentifier(url) 938 content = ucb.queryContent(identifier) 939 p = Property() 940 p.Name = "DocumentModel" 941 p.Handle = -1 942 943 c = Command() 944 c.Handle = -1 945 c.Name = "getPropertyValues" 946 c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,)) 947 948 env = CommandEnvironment() 949 try: 950 ret = content.execute(c, 0, env) 951 doc = ret.getObject(1, None) 952 except Exception as e: 953 log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url) 954 return doc 955 956def mapStorageType2PackageContext( storageType ): 957 ret = storageType 958 if( storageType == "share:uno_packages" ): 959 ret = "shared" 960 if( storageType == "user:uno_packages" ): 961 ret = "user" 962 return ret 963 964def getPackageName2PathMap( sfa, storageType ): 965 ret = {} 966 packageManagerFactory = uno.getComponentContext().getValueByName( 967 "/singletons/com.sun.star.deployment.thePackageManagerFactory" ) 968 packageManager = packageManagerFactory.getPackageManager( 969 mapStorageType2PackageContext(storageType)) 970# packageManager.addModifyListener( ModifyListener() ) 971 log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" ) 972 packages = packageManager.getDeployedPackages( 973 packageManager.createAbortChannel(), CommandEnvironment( ) ) 974 log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" ) 975 976 for i in packages: 977 log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" ) 978 transientPathElement = penultimateElement( i.URL ) 979 j = expandUri( i.URL ) 980 pathes = getPathesFromPackage( j, sfa ) 981 if len( pathes ) > 0: 982 # map package name to url, we need this later 983 log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) ) 984 ret[ lastElement( j ) ] = Package( pathes, transientPathElement ) 985 return ret 986 987def penultimateElement( aStr ): 988 lastSlash = aStr.rindex("/") 989 penultimateSlash = aStr.rindex("/",0,lastSlash-1) 990 return aStr[ penultimateSlash+1:lastSlash ] 991 992def lastElement( aStr): 993 return aStr[ aStr.rfind( "/" )+1:len(aStr)] 994 995class PackageBrowseNode( unohelper.Base, XBrowseNode ): 996 def __init__( self, provCtx, name, rootUrl ): 997 self.provCtx = provCtx 998 self.name = name 999 self.rootUrl = rootUrl 1000 1001 def getName( self ): 1002 return self.name 1003 1004 def getChildNodes( self ): 1005 items = list(self.provCtx.mapPackageName2Path.items()) 1006 browseNodeList = [] 1007 for i in items: 1008 if len( i[1].pathes ) == 1: 1009 browseNodeList.append( 1010 DirBrowseNode( self.provCtx, i[0], i[1].pathes[0], 0 )) 1011 else: 1012 for j in i[1].pathes: 1013 browseNodeList.append( 1014 DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j, 0 ) ) 1015 return tuple( browseNodeList ) 1016 1017 def hasChildNodes( self ): 1018 return len( self.mapPackageName2Path ) > 0 1019 1020 def getType( self ): 1021 return CONTAINER 1022 1023 def getScript( self, uri ): 1024 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 1025 raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 ) 1026 1027 1028 1029 1030class PythonScript( unohelper.Base, XScript ): 1031 def __init__( self, func, mod ): 1032 self.func = func 1033 self.mod = mod 1034 def invoke(self, args, out, outindex ): 1035 log.debug( "PythonScript.invoke " + str( args ) ) 1036 try: 1037 ret = self.func( *args ) 1038 except UnoException as e: 1039 # UNO Exception continue to fly ... 1040 text = lastException2String() 1041 complete = "Error during invoking function " + \ 1042 str(self.func.__name__) + " in module " + \ 1043 self.mod.__file__ + " (" + text + ")" 1044 log.debug( complete ) 1045 # some people may beat me up for modifying the exception text, 1046 # but otherwise office just shows 1047 # the type name and message text with no more information, 1048 # this is really bad for most users. 1049 e.Message = e.Message + " (" + complete + ")" 1050 raise 1051 except Exception as e: 1052 # General python exception are converted to uno RuntimeException 1053 text = lastException2String() 1054 complete = "Error during invoking function " + \ 1055 str(self.func.__name__) + " in module " + \ 1056 self.mod.__file__ + " (" + text + ")" 1057 log.debug( complete ) 1058 raise RuntimeException( complete , self ) 1059 log.debug( "PythonScript.invoke ret = " + str( ret ) ) 1060 return ret, (), () 1061 1062def expandUri( uri ): 1063 if uri.startswith( "vnd.sun.star.expand:" ): 1064 uri = uri.replace( "vnd.sun.star.expand:", "",1) 1065 uri = uno.getComponentContext().getByName( 1066 "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri ) 1067 if uri.startswith( "file:" ): 1068 uri = uno.absolutize("",uri) # necessary to get rid of .. in uri 1069 return uri 1070 1071#-------------------------------------------------------------- 1072class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer, XPropertySet, XInvocation): 1073 def __init__( self, ctx, *args ): 1074 if log.isDebugLevel(): 1075 mystr = "" 1076 for i in args: 1077 if len(mystr) > 0: 1078 mystr = mystr +"," 1079 mystr = mystr + str(i) 1080 log.debug( "Entering PythonScriptProvider.ctor with args " + mystr ) 1081 1082 doc = None 1083 inv = None 1084 storageType = "" 1085 1086 if isinstance(args[0],unicode ): 1087 storageType = args[0] 1088 if storageType.startswith( "vnd.sun.star.tdoc" ): 1089 doc = getModelFromDocUrl(ctx, storageType) 1090 else: 1091 inv = args[0] 1092 try: 1093 doc = inv.ScriptContainer 1094 content = ctx.getServiceManager().createInstanceWithContext( 1095 "com.sun.star.frame.TransientDocumentsDocumentContentFactory", 1096 ctx).createDocumentContent(doc) 1097 storageType = content.getIdentifier().getContentIdentifier() 1098 except Exception as e: 1099 text = lastException2String() 1100 log.error( text ) 1101 1102 isPackage = storageType.endswith( ":uno_packages" ) 1103 1104 try: 1105# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext( 1106# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx) 1107 urlHelper = MyUriHelper( ctx, storageType ) 1108 log.debug( "got urlHelper " + str( urlHelper ) ) 1109 1110 rootUrl = expandUri( urlHelper.getRootStorageURI() ) 1111 log.debug( storageType + " transformed to " + rootUrl ) 1112 1113 ucbService = "com.sun.star.ucb.SimpleFileAccess" 1114 sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx ) 1115 if not sfa: 1116 log.debug("PythonScriptProvider couldn't instantiate " +ucbService) 1117 raise RuntimeException( 1118 "PythonScriptProvider couldn't instantiate " +ucbService, self) 1119 self.provCtx = ProviderContext( 1120 storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) ) 1121 if isPackage: 1122 mapPackageName2Path = getPackageName2PathMap( sfa, storageType ) 1123 self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl ) 1124 self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl ) 1125 else: 1126 self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl, 0 ) 1127 1128 except Exception as e: 1129 text = lastException2String() 1130 log.debug( "PythonScriptProvider could not be instantiated because of : " + text ) 1131 raise e 1132 1133 def getName( self ): 1134 return self.dirBrowseNode.getName() 1135 1136 def getChildNodes( self ): 1137 return self.dirBrowseNode.getChildNodes() 1138 1139 def hasChildNodes( self ): 1140 return self.dirBrowseNode.hasChildNodes() 1141 1142 def getType( self ): 1143 return self.dirBrowseNode.getType() 1144 1145 def getScript( self, uri ): 1146 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 1147 1148 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) 1149 1150 def getScript( self, scriptUri ): 1151 try: 1152 log.debug( "getScript " + scriptUri + " invoked") 1153 1154 storageUri = self.provCtx.getStorageUrlFromPersistentUrl( 1155 self.provCtx.uriHelper.getStorageURI(scriptUri) ); 1156 log.debug( "getScript: storageUri = " + storageUri) 1157 fileUri = storageUri[0:storageUri.find( "$" )] 1158 funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)] 1159 1160 mod = self.provCtx.getModuleByUrl( fileUri ) 1161 log.debug( " got mod " + str(mod) ) 1162 1163 func = mod.__dict__[ funcName ] 1164 1165 log.debug( "got func " + str( func ) ) 1166 return PythonScript( func, mod ) 1167 except Exception as e: 1168 text = lastException2String() 1169 log.error( text ) 1170 raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 ) 1171 1172 # XPropertySet 1173 1174 def getPropertyValue( self, name ): 1175 return self.dirBrowseNode.getPropertyValue( name ) 1176 1177 def setPropertyValue( self, name, value ): 1178 return self.dirBrowseNode.setPropertyValue( name, value ) 1179 1180 def getPropertySetInfo( self ): 1181 return self.dirBrowseNode.getPropertySetInfo() 1182 1183 # XInvocation 1184 1185 def getIntrospection( self ): 1186 return self.dirBrowseNode.getIntrospection() 1187 1188 def invoke( self, name, params, outparamindex, outparams ): 1189 return self.dirBrowseNode.invoke( name, params, outparamindex, outparams) 1190 1191 def setValue( self, name, value ): 1192 return self.dirBrowseNode.setValue( name, value ) 1193 1194 def getValue( self, name ): 1195 return self.dirBrowseNode.getValue( name ) 1196 1197 def hasMethod( self, name ): 1198 return self.dirBrowseNode.hasMethod( name ) 1199 1200 def hasProperty( self, name ): 1201 return self.dirBrowseNode.hasProperty( name ) 1202 1203 # XServiceInfo 1204 def getSupportedServices( self ): 1205 return g_ImplementationHelper.getSupportedServices(g_implName) 1206 1207 def supportsService( self, ServiceName ): 1208 return g_ImplementationHelper.supportsService( g_implName, ServiceName ) 1209 1210 def getImplementationName(self): 1211 return g_implName 1212 1213 def getByName( self, name ): 1214 log.debug( "getByName called" + str( name )) 1215 return None 1216 1217 1218 def getElementNames( self ): 1219 log.debug( "getElementNames called") 1220 return () 1221 1222 def hasByName( self, name ): 1223 try: 1224 log.debug( "hasByName called " + str( name )) 1225 uri = expandUri(name) 1226 ret = self.provCtx.isUrlInPackage( uri ) 1227 log.debug( "hasByName " + uri + " " +str( ret ) ) 1228 return ret 1229 except Exception as e: 1230 text = lastException2String() 1231 log.debug( "Error in hasByName:" + text ) 1232 return False 1233 1234 def removeByName( self, name ): 1235 log.debug( "removeByName called" + str( name )) 1236 uri = expandUri( name ) 1237 if self.provCtx.isUrlInPackage( uri ): 1238 self.provCtx.removePackageByUrl( uri ) 1239 else: 1240 log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" ) 1241 raise NoSuchElementException( uri + "is not in package" , self ) 1242 log.debug( "removeByName called" + str( uri ) + " successful" ) 1243 1244 def insertByName( self, name, value ): 1245 log.debug( "insertByName called " + str( name ) + " " + str( value )) 1246 uri = expandUri( name ) 1247 if isPyFileInPath( self.provCtx.sfa, uri ): 1248 self.provCtx.addPackageByUrl( uri ) 1249 else: 1250 # package is no python package ... 1251 log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" ) 1252 raise IllegalArgumentException( uri + " does not contain .py files", self, 1 ) 1253 log.debug( "insertByName called " + str( uri ) + " successful" ) 1254 1255 def replaceByName( self, name, value ): 1256 log.debug( "replaceByName called " + str( name ) + " " + str( value )) 1257 removeByName( name ) 1258 insertByName( name ) 1259 log.debug( "replaceByName called" + str( uri ) + " successful" ) 1260 1261 def getElementType( self ): 1262 log.debug( "getElementType called" ) 1263 return uno.getTypeByName( "void" ) 1264 1265 def hasElements( self ): 1266 log.debug( "hasElements got called") 1267 return False 1268 1269g_ImplementationHelper.addImplementation( \ 1270 PythonScriptProvider,g_implName, \ 1271 ("com.sun.star.script.provider.LanguageScriptProvider", 1272 "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),) 1273 1274 1275log.debug( "pythonscript finished initializing" ) 1276