summaryrefslogtreecommitdiffstats
path: root/scripting/source/pyprov
diff options
context:
space:
mode:
Diffstat (limited to 'scripting/source/pyprov')
-rw-r--r--scripting/source/pyprov/mailmerge.README18
-rw-r--r--scripting/source/pyprov/mailmerge.component28
-rw-r--r--scripting/source/pyprov/mailmerge.py533
-rw-r--r--scripting/source/pyprov/msgbox.py241
-rw-r--r--scripting/source/pyprov/pythonscript.py1146
-rw-r--r--scripting/source/pyprov/scriptproviderforpython.rdb28
6 files changed, 1994 insertions, 0 deletions
diff --git a/scripting/source/pyprov/mailmerge.README b/scripting/source/pyprov/mailmerge.README
new file mode 100644
index 0000000000..6b4fb5ba4f
--- /dev/null
+++ b/scripting/source/pyprov/mailmerge.README
@@ -0,0 +1,18 @@
+Easiest way I find to test this is to...
+
+1)
+
+a) install fakemail and run it
+b) tools->options->writer->mail merge email
+c) localhost 8025
+
+2)
+
+a) type some text into writer that will exercise utf-8, e.g. "Caolán's test"
+b) tools->mail merge wizard->next->email message->select address book
+c) create, add one user with your own email address, ok
+d) next, next, text, send merged document as email
+e) and test all of plain text, html and the various attachment options
+
+fake mail will dump the mail it gets into its pwd, if all that works, you can
+then try with your own normal mail server.
diff --git a/scripting/source/pyprov/mailmerge.component b/scripting/source/pyprov/mailmerge.component
new file mode 100644
index 0000000000..8a93253412
--- /dev/null
+++ b/scripting/source/pyprov/mailmerge.component
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.Python"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.pyuno.MailMessage">
+ <service name="com.sun.star.mail.MailMessage"/>
+ </implementation>
+ <implementation name="org.openoffice.pyuno.MailServiceProvider">
+ <service name="com.sun.star.mail.MailServiceProvider"/>
+ </implementation>
+</component>
diff --git a/scripting/source/pyprov/mailmerge.py b/scripting/source/pyprov/mailmerge.py
new file mode 100644
index 0000000000..3c781c52f2
--- /dev/null
+++ b/scripting/source/pyprov/mailmerge.py
@@ -0,0 +1,533 @@
+# Caolan McNamara caolanm@redhat.com
+# a simple email mailmerge component
+
+# manual installation for hackers, not necessary for users
+# cp mailmerge.py /usr/lib/libreoffice/program
+# cd /usr/lib/libreoffice/program
+# ./unopkg add --shared mailmerge.py
+# edit ~/.openoffice.org2/user/registry/data/org/openoffice/Office/Writer.xcu
+# and change EMailSupported to as follows...
+# <prop oor:name="EMailSupported" oor:type="xs:boolean">
+# <value>true</value>
+# </prop>
+
+import unohelper
+import uno
+import re
+import os
+import encodings.idna
+
+#to implement com::sun::star::mail::XMailServiceProvider
+#and
+#to implement com.sun.star.mail.XMailMessage
+
+from com.sun.star.mail import XMailServiceProvider
+from com.sun.star.mail import XMailService
+from com.sun.star.mail import XSmtpService
+from com.sun.star.mail import XConnectionListener
+from com.sun.star.mail import XAuthenticator
+from com.sun.star.mail import XMailMessage
+from com.sun.star.mail.MailServiceType import SMTP
+from com.sun.star.mail.MailServiceType import POP3
+from com.sun.star.mail.MailServiceType import IMAP
+from com.sun.star.uno import XCurrentContext
+from com.sun.star.lang import IllegalArgumentException
+from com.sun.star.lang import EventObject
+from com.sun.star.lang import XServiceInfo
+from com.sun.star.mail import SendMailMessageFailedException
+
+from email.mime.base import MIMEBase
+from email.message import Message
+from email.charset import Charset
+from email.charset import QP
+from email.encoders import encode_base64
+from email.header import Header
+from email.mime.multipart import MIMEMultipart
+from email.utils import formatdate
+from email.utils import parseaddr
+from socket import _GLOBAL_DEFAULT_TIMEOUT
+
+import sys, ssl, smtplib, imaplib, poplib
+dbg = False
+
+# pythonloader looks for a static g_ImplementationHelper variable
+g_ImplementationHelper = unohelper.ImplementationHelper()
+g_providerImplName = "org.openoffice.pyuno.MailServiceProvider"
+g_messageImplName = "org.openoffice.pyuno.MailMessage"
+
+def prepareTLSContext(xComponent, xContext, isTLSRequested):
+ xConfigProvider = xContext.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider")
+ prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
+ prop.Name = "nodepath"
+ prop.Value = "/org.openoffice.Office.Security/Net"
+ xSettings = xConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
+ (prop,))
+ isAllowedInsecure = xSettings.getByName("AllowInsecureProtocols")
+ tlscontext = None
+ if isTLSRequested:
+ if dbg:
+ print("SSL config: " + str(ssl.get_default_verify_paths()), file=sys.stderr)
+ tlscontext = ssl.create_default_context()
+ # SSLv2/v3 is already disabled by default.
+ # This check does not work, because OpenSSL 3 defines SSL_OP_NO_SSLv2
+ # as 0, so even though _ssl__SSLContext_impl() tries to set it,
+ # getting the value from SSL_CTX_get_options() doesn't lead to setting
+ # the python-level flag.
+ #assert (tlscontext.options & ssl.Options.OP_NO_SSLv2) != 0
+ assert (tlscontext.options & ssl.Options.OP_NO_SSLv3) != 0
+ if not(isAllowedInsecure):
+ if not(isTLSRequested):
+ if dbg:
+ print("mailmerge.py: insecure connection not allowed by configuration", file=sys.stderr)
+ raise IllegalArgumentException("insecure connection not allowed by configuration", xComponent, 1)
+ tlscontext.options |= ssl.Options.OP_NO_TLSv1 | ssl.Options.OP_NO_TLSv1_1
+ return tlscontext
+
+class PyMailSMTPService(unohelper.Base, XSmtpService):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+ self.listeners = []
+ self.supportedtypes = ('Insecure', 'Ssl')
+ self.server = None
+ self.connectioncontext = None
+ self.notify = EventObject(self)
+ if dbg:
+ print("PyMailSMTPService init", file=sys.stderr)
+ print("python version is: " + sys.version, file=sys.stderr)
+ def addConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailSMTPService addConnectionListener", file=sys.stderr)
+ self.listeners.append(xListener)
+ def removeConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailSMTPService removeConnectionListener", file=sys.stderr)
+ self.listeners.remove(xListener)
+ def getSupportedConnectionTypes(self):
+ if dbg:
+ print("PyMailSMTPService getSupportedConnectionTypes", file=sys.stderr)
+ return self.supportedtypes
+ def connect(self, xConnectionContext, xAuthenticator):
+ self.connectioncontext = xConnectionContext
+ if dbg:
+ print("PyMailSMTPService connect", file=sys.stderr)
+ server = xConnectionContext.getValueByName("ServerName").strip()
+ if dbg:
+ print("ServerName: " + server, file=sys.stderr)
+ port = int(xConnectionContext.getValueByName("Port"))
+ if dbg:
+ print("Port: " + str(port), file=sys.stderr)
+ tout = xConnectionContext.getValueByName("Timeout")
+ if dbg:
+ print(isinstance(tout,int), file=sys.stderr)
+ if not isinstance(tout,int):
+ tout = _GLOBAL_DEFAULT_TIMEOUT
+ if dbg:
+ print("Timeout: " + str(tout), file=sys.stderr)
+ connectiontype = xConnectionContext.getValueByName("ConnectionType")
+ if dbg:
+ print("ConnectionType: " + connectiontype, file=sys.stderr)
+ tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == 'SSL' or port == 465)
+ if port == 465:
+ self.server = smtplib.SMTP_SSL(server, port, timeout=tout, context=tlscontext)
+ else:
+ self.server = smtplib.SMTP(server, port,timeout=tout)
+
+ if dbg:
+ self.server.set_debuglevel(1)
+
+ if connectiontype.upper() == 'SSL' and port != 465:
+ # STRIPTLS: smtplib raises an exception if result is not 220
+ self.server.starttls(context=tlscontext)
+
+ user = xAuthenticator.getUserName()
+ password = xAuthenticator.getPassword()
+ if user != '':
+ if dbg:
+ print("Logging in, username of: " + user, file=sys.stderr)
+ self.server.login(user, password)
+
+ for listener in self.listeners:
+ listener.connected(self.notify)
+ def disconnect(self):
+ if dbg:
+ print("PyMailSMTPService disconnect", file=sys.stderr)
+ if self.server:
+ self.server.quit()
+ self.server = None
+ for listener in self.listeners:
+ listener.disconnected(self.notify)
+ def isConnected(self):
+ if dbg:
+ print("PyMailSMTPService isConnected", file=sys.stderr)
+ return self.server != None
+ def getCurrentConnectionContext(self):
+ if dbg:
+ print("PyMailSMTPService getCurrentConnectionContext", file=sys.stderr)
+ return self.connectioncontext
+ def sendMailMessage(self, xMailMessage):
+ COMMASPACE = ', '
+
+ if dbg:
+ print("PyMailSMTPService sendMailMessage", file=sys.stderr)
+ recipients = xMailMessage.getRecipients()
+ sendermail = xMailMessage.SenderAddress
+ sendername = xMailMessage.SenderName
+ subject = xMailMessage.Subject
+ ccrecipients = xMailMessage.getCcRecipients()
+ bccrecipients = xMailMessage.getBccRecipients()
+ if dbg:
+ print("PyMailSMTPService subject: " + subject, file=sys.stderr)
+ print("PyMailSMTPService from: " + sendername, file=sys.stderr)
+ print("PyMailSMTPService from: " + sendermail, file=sys.stderr)
+ print("PyMailSMTPService send to: %s" % (recipients,), file=sys.stderr)
+
+ attachments = xMailMessage.getAttachments()
+
+ textmsg = Message()
+
+ content = xMailMessage.Body
+ flavors = content.getTransferDataFlavors()
+ if dbg:
+ print("PyMailSMTPService flavors len: %d" % (len(flavors),), file=sys.stderr)
+
+ #Use first flavor that's sane for an email body
+ for flavor in flavors:
+ if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find('text/plain') != -1:
+ if dbg:
+ print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=sys.stderr)
+ textbody = content.getTransferData(flavor)
+
+ if len(textbody):
+ mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType)
+ if mimeEncoding.find('charset=UTF-8') == -1:
+ mimeEncoding = mimeEncoding + "; charset=UTF-8"
+ textmsg['Content-Type'] = mimeEncoding
+ textmsg['MIME-Version'] = '1.0'
+
+ try:
+ #it's a string, get it as utf-8 bytes
+ textbody = textbody.encode('utf-8')
+ except:
+ #it's a bytesequence, get raw bytes
+ textbody = textbody.value
+ textbody = textbody.decode('utf-8')
+ c = Charset('utf-8')
+ c.body_encoding = QP
+ textmsg.set_payload(textbody, c)
+
+ break
+
+ if (len(attachments)):
+ msg = MIMEMultipart()
+ msg.epilogue = ''
+ msg.attach(textmsg)
+ else:
+ msg = textmsg
+
+ hdr = Header(sendername, 'utf-8')
+ hdr.append('<'+sendermail+'>','us-ascii')
+ msg['Subject'] = subject
+ msg['From'] = hdr
+ msg['To'] = COMMASPACE.join(recipients)
+ if len(ccrecipients):
+ msg['Cc'] = COMMASPACE.join(ccrecipients)
+ if xMailMessage.ReplyToAddress != '':
+ msg['Reply-To'] = xMailMessage.ReplyToAddress
+
+ mailerstring = "LibreOffice via Caolan's mailmerge component"
+ try:
+ ctx = uno.getComponentContext()
+ aConfigProvider = ctx.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider")
+ prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
+ prop.Name = "nodepath"
+ prop.Value = "/org.openoffice.Setup/Product"
+ aSettings = aConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
+ (prop,))
+ mailerstring = aSettings.getByName("ooName") + " " + \
+ aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component"
+ except:
+ pass
+
+ msg['X-Mailer'] = mailerstring
+ msg['Date'] = formatdate(localtime=True)
+
+ for attachment in attachments:
+ content = attachment.Data
+ flavors = content.getTransferDataFlavors()
+ flavor = flavors[0]
+ ctype = flavor.MimeType
+ maintype, subtype = ctype.split('/', 1)
+ msgattachment = MIMEBase(maintype, subtype)
+ data = content.getTransferData(flavor)
+ msgattachment.set_payload(data.value)
+ encode_base64(msgattachment)
+ fname = attachment.ReadableName
+ try:
+ msgattachment.add_header('Content-Disposition', 'attachment', \
+ filename=fname)
+ except:
+ msgattachment.add_header('Content-Disposition', 'attachment', \
+ filename=('utf-8','',fname))
+ if dbg:
+ print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=sys.stderr)
+
+ msg.attach(msgattachment)
+
+ uniquer = {}
+ for key in recipients:
+ uniquer[key] = True
+ if len(ccrecipients):
+ for key in ccrecipients:
+ uniquer[key] = True
+ if len(bccrecipients):
+ for key in bccrecipients:
+ uniquer[key] = True
+ truerecipients = uniquer.keys()
+
+ if dbg:
+ print(("PyMailSMTPService recipients are: ", truerecipients), file=sys.stderr)
+
+ self.server.sendmail(sendermail, truerecipients, msg.as_string())
+
+class PyMailIMAPService(unohelper.Base, XMailService):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+ self.listeners = []
+ self.supportedtypes = ('Insecure', 'Ssl')
+ self.server = None
+ self.connectioncontext = None
+ self.notify = EventObject(self)
+ if dbg:
+ print("PyMailIMAPService init", file=sys.stderr)
+ def addConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailIMAPService addConnectionListener", file=sys.stderr)
+ self.listeners.append(xListener)
+ def removeConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailIMAPService removeConnectionListener", file=sys.stderr)
+ self.listeners.remove(xListener)
+ def getSupportedConnectionTypes(self):
+ if dbg:
+ print("PyMailIMAPService getSupportedConnectionTypes", file=sys.stderr)
+ return self.supportedtypes
+ def connect(self, xConnectionContext, xAuthenticator):
+ if dbg:
+ print("PyMailIMAPService connect", file=sys.stderr)
+
+ self.connectioncontext = xConnectionContext
+ server = xConnectionContext.getValueByName("ServerName")
+ if dbg:
+ print(server, file=sys.stderr)
+ port = int(xConnectionContext.getValueByName("Port"))
+ if dbg:
+ print(port, file=sys.stderr)
+ connectiontype = xConnectionContext.getValueByName("ConnectionType")
+ if dbg:
+ print(connectiontype, file=sys.stderr)
+ tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == 'SSL')
+ print("BEFORE", file=sys.stderr)
+ if connectiontype.upper() == 'SSL':
+ self.server = imaplib.IMAP4_SSL(server, port, ssl_context=tlscontext)
+ else:
+ self.server = imaplib.IMAP4(server, port)
+ print("AFTER", file=sys.stderr)
+
+ user = xAuthenticator.getUserName()
+ password = xAuthenticator.getPassword()
+ if user != '':
+ if dbg:
+ print("Logging in, username of: " + user, file=sys.stderr)
+ self.server.login(user, password)
+
+ for listener in self.listeners:
+ listener.connected(self.notify)
+ def disconnect(self):
+ if dbg:
+ print("PyMailIMAPService disconnect", file=sys.stderr)
+ if self.server:
+ self.server.logout()
+ self.server = None
+ for listener in self.listeners:
+ listener.disconnected(self.notify)
+ def isConnected(self):
+ if dbg:
+ print("PyMailIMAPService isConnected", file=sys.stderr)
+ return self.server != None
+ def getCurrentConnectionContext(self):
+ if dbg:
+ print("PyMailIMAPService getCurrentConnectionContext", file=sys.stderr)
+ return self.connectioncontext
+
+class PyMailPOP3Service(unohelper.Base, XMailService):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+ self.listeners = []
+ self.supportedtypes = ('Insecure', 'Ssl')
+ self.server = None
+ self.connectioncontext = None
+ self.notify = EventObject(self)
+ if dbg:
+ print("PyMailPOP3Service init", file=sys.stderr)
+ def addConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailPOP3Service addConnectionListener", file=sys.stderr)
+ self.listeners.append(xListener)
+ def removeConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailPOP3Service removeConnectionListener", file=sys.stderr)
+ self.listeners.remove(xListener)
+ def getSupportedConnectionTypes(self):
+ if dbg:
+ print("PyMailPOP3Service getSupportedConnectionTypes", file=sys.stderr)
+ return self.supportedtypes
+ def connect(self, xConnectionContext, xAuthenticator):
+ if dbg:
+ print("PyMailPOP3Service connect", file=sys.stderr)
+
+ self.connectioncontext = xConnectionContext
+ server = xConnectionContext.getValueByName("ServerName")
+ if dbg:
+ print(server, file=sys.stderr)
+ port = int(xConnectionContext.getValueByName("Port"))
+ if dbg:
+ print(port, file=sys.stderr)
+ connectiontype = xConnectionContext.getValueByName("ConnectionType")
+ if dbg:
+ print(connectiontype, file=sys.stderr)
+ tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == 'SSL')
+ print("BEFORE", file=sys.stderr)
+ if connectiontype.upper() == 'SSL':
+ self.server = poplib.POP3_SSL(server, port, context=tlscontext)
+ else:
+ tout = xConnectionContext.getValueByName("Timeout")
+ if dbg:
+ print(isinstance(tout,int), file=sys.stderr)
+ if not isinstance(tout,int):
+ tout = _GLOBAL_DEFAULT_TIMEOUT
+ if dbg:
+ print("Timeout: " + str(tout), file=sys.stderr)
+ self.server = poplib.POP3(server, port, timeout=tout)
+ print("AFTER", file=sys.stderr)
+
+ user = xAuthenticator.getUserName()
+ password = xAuthenticator.getPassword()
+ if dbg:
+ print("Logging in, username of: " + user, file=sys.stderr)
+ self.server.user(user)
+ self.server.pass_(password)
+
+ for listener in self.listeners:
+ listener.connected(self.notify)
+ def disconnect(self):
+ if dbg:
+ print("PyMailPOP3Service disconnect", file=sys.stderr)
+ if self.server:
+ self.server.quit()
+ self.server = None
+ for listener in self.listeners:
+ listener.disconnected(self.notify)
+ def isConnected(self):
+ if dbg:
+ print("PyMailPOP3Service isConnected", file=sys.stderr)
+ return self.server != None
+ def getCurrentConnectionContext(self):
+ if dbg:
+ print("PyMailPOP3Service getCurrentConnectionContext", file=sys.stderr)
+ return self.connectioncontext
+
+class PyMailServiceProvider(unohelper.Base, XMailServiceProvider, XServiceInfo):
+ def __init__( self, ctx ):
+ if dbg:
+ print("PyMailServiceProvider init", file=sys.stderr)
+ self.ctx = ctx
+ def create(self, aType):
+ if dbg:
+ print("PyMailServiceProvider create with", aType, file=sys.stderr)
+ if aType == SMTP:
+ return PyMailSMTPService(self.ctx);
+ elif aType == POP3:
+ return PyMailPOP3Service(self.ctx);
+ elif aType == IMAP:
+ return PyMailIMAPService(self.ctx);
+ else:
+ print("PyMailServiceProvider, unknown TYPE " + aType, file=sys.stderr)
+
+ def getImplementationName(self):
+ return g_providerImplName
+
+ def supportsService(self, ServiceName):
+ return g_ImplementationHelper.supportsService(g_providerImplName, ServiceName)
+
+ def getSupportedServiceNames(self):
+ return g_ImplementationHelper.getSupportedServiceNames(g_providerImplName)
+
+class PyMailMessage(unohelper.Base, XMailMessage):
+ def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ):
+ if dbg:
+ print("PyMailMessage init", file=sys.stderr)
+ self.ctx = ctx
+
+ self.recipients = [sTo]
+ self.ccrecipients = []
+ self.bccrecipients = []
+ self.aMailAttachments = []
+ if aMailAttachment != None:
+ self.aMailAttachments.append(aMailAttachment)
+
+ self.SenderName, self.SenderAddress = parseaddr(sFrom)
+ self.ReplyToAddress = sFrom
+ self.Subject = Subject
+ self.Body = Body
+ if dbg:
+ print("post PyMailMessage init", file=sys.stderr)
+ def addRecipient( self, recipient ):
+ if dbg:
+ print("PyMailMessage.addRecipient: " + recipient, file=sys.stderr)
+ self.recipients.append(recipient)
+ def addCcRecipient( self, ccrecipient ):
+ if dbg:
+ print("PyMailMessage.addCcRecipient: " + ccrecipient, file=sys.stderr)
+ self.ccrecipients.append(ccrecipient)
+ def addBccRecipient( self, bccrecipient ):
+ if dbg:
+ print("PyMailMessage.addBccRecipient: " + bccrecipient, file=sys.stderr)
+ self.bccrecipients.append(bccrecipient)
+ def getRecipients( self ):
+ if dbg:
+ print("PyMailMessage.getRecipients: " + str(self.recipients), file=sys.stderr)
+ return tuple(self.recipients)
+ def getCcRecipients( self ):
+ if dbg:
+ print("PyMailMessage.getCcRecipients: " + str(self.ccrecipients), file=sys.stderr)
+ return tuple(self.ccrecipients)
+ def getBccRecipients( self ):
+ if dbg:
+ print("PyMailMessage.getBccRecipients: " + str(self.bccrecipients), file=sys.stderr)
+ return tuple(self.bccrecipients)
+ def addAttachment( self, aMailAttachment ):
+ if dbg:
+ print("PyMailMessage.addAttachment", file=sys.stderr)
+ self.aMailAttachments.append(aMailAttachment)
+ def getAttachments( self ):
+ if dbg:
+ print("PyMailMessage.getAttachments", file=sys.stderr)
+ return tuple(self.aMailAttachments)
+
+ def getImplementationName(self):
+ return g_messageImplName
+
+ def supportsService(self, ServiceName):
+ return g_ImplementationHelper.supportsService(g_messageImplName, ServiceName)
+
+ def getSupportedServiceNames(self):
+ return g_ImplementationHelper.getSupportedServiceNames(g_messageImplName)
+
+g_ImplementationHelper.addImplementation( \
+ PyMailServiceProvider, g_providerImplName,
+ ("com.sun.star.mail.MailServiceProvider",),)
+g_ImplementationHelper.addImplementation( \
+ PyMailMessage, g_messageImplName,
+ ("com.sun.star.mail.MailMessage",),)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/scripting/source/pyprov/msgbox.py b/scripting/source/pyprov/msgbox.py
new file mode 100644
index 0000000000..f9c93df174
--- /dev/null
+++ b/scripting/source/pyprov/msgbox.py
@@ -0,0 +1,241 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# prepare Python environment - Add the path of this class
+from os import path
+from sys import modules
+from sys import path as syspath
+
+# pyUNO program itself
+import uno, unohelper
+
+# UNO GUI toolkit
+from com.sun.star.awt.WindowClass import TOP, SIMPLE
+from com.sun.star.awt.PushButtonType import STANDARD as standard
+from com.sun.star.awt.PushButtonType import OK as ok
+from com.sun.star.awt.PushButtonType import CANCEL as cancel
+from com.sun.star.awt.PushButtonType import HELP as help
+from com.sun.star.awt.TextAlign import CENTER as center
+from com.sun.star.awt.TextAlign import LEFT as left
+from com.sun.star.awt.TextAlign import RIGHT as right
+
+# used UNO listeners
+from com.sun.star.awt import XActionListener
+
+class MsgBox(unohelper.Base):
+ """Inspect UNO object, link to sdk and recursive calls"""
+
+ def __init__(self, aContext):
+ """acontext : a Valid UNO context
+ """
+
+ self.VERSION = '0.1'
+ self.ctx = aContext
+ self.smgr = aContext.ServiceManager
+ # UI Dialog object
+ self.dialog=None
+ # List of opened Listeners
+ self.lst_listeners={}
+ #UI parameters
+ self.ButtonSize = 50
+ self.boxSize = 200
+ self.lineHeight = 10
+ self.fromBroxSize = False
+ self.numberOfLines = -1
+
+ self.Buttons = []
+ self.Response = ''
+
+ return
+
+ #####################################################
+ # GUI definition #
+ #####################################################
+ def _createBox(self):
+ """Create the Box"""
+
+ # computes parameters of the message dialog
+ if self.numberOfLines == -1:
+ #calculate
+ numberOfLines = len(self.message.split(chr(10)))
+ else:
+ numberOfLines = self.numberOfLines
+
+ numberOfButtons = len(self.Buttons)
+ self.ButtonSpace = self.ButtonSize/2
+ if self.fromBroxSize:
+ # button size is calculated from boxsize
+ size = (2 * self.boxSize) / (3 * numberOfButtons + 1)
+ self.ButtonSize = size
+ self.ButtonSpace = self.ButtonSize/2
+ else:
+ # boxsize is calculated from buttonsize
+ self.boxSize = numberOfButtons * (self.ButtonSize +
+ self.ButtonSpace) + self.ButtonSpace
+
+ # create the dialog model and set the properties
+ dialog_model = self.smgr.createInstanceWithContext(
+ 'com.sun.star.awt.UnoControlDialogModel',
+ self.ctx)
+ dialog_model.PositionX = 50
+ dialog_model.Step = 1
+ dialog_model.TabIndex = 7
+ dialog_model.Width = self.boxSize#numberOfButtons * (self.ButtonSize +
+ # self.ButtonSpace) + 25
+ dialog_model.Height = 10 + self.lineHeight * numberOfLines + 10 + 12 + 10
+ dialog_model.PositionY = 63
+ dialog_model.Sizeable = True
+ dialog_model.Closeable = False
+
+ dialog = self.smgr.createInstanceWithContext(
+ 'com.sun.star.awt.UnoControlDialog', self.ctx)
+
+ # label Label0
+ label = dialog_model.createInstance(
+ 'com.sun.star.awt.UnoControlFixedTextModel')
+ label.PositionX = 10
+ label.TabIndex = 9
+ label.Width = dialog_model.Width - label.PositionX
+ label.Height = self.lineHeight* numberOfLines
+ label.PositionY = 10
+ label.Align = left
+ label.MultiLine = True
+ label.Label = self.message
+ dialog_model.insertByName('Label0', label)
+
+ nb = 0
+ for buttonName in self.Buttons:
+ nb +=1
+ button = dialog_model.createInstance(
+ 'com.sun.star.awt.UnoControlButtonModel')
+ button.PositionX = nb * self.ButtonSpace + (nb-1)* self.ButtonSize
+ button.TabIndex = 8
+ button.Height = 12
+ button.Width = self.ButtonSize
+ button.PositionY = 10 + label.Height + 10
+ button.PushButtonType = standard
+ if nb == 1:
+ button.DefaultButton = True
+ else:
+ button.DefaultButton = False
+ button.Label = buttonName
+ dialog_model.insertByName('Btn' + str(nb), button )
+
+ if not dialog.getModel():
+ dialog.setModel(dialog_model)
+
+ # UNO toolkit definition
+ toolkit = self.smgr.createInstanceWithContext('com.sun.star.awt.Toolkit', self.ctx)
+ a_rect = uno.createUnoStruct( 'com.sun.star.awt.Rectangle' )
+ a_rect.X = 50
+ dialog.setTitle ( self.title )
+ a_rect.Width = 270
+ a_rect.Height = 261
+ a_rect.Y = 63
+ win_descriptor = uno.createUnoStruct('com.sun.star.awt.WindowDescriptor')
+ win_descriptor.Type = TOP
+ win_descriptor.ParentIndex = -1
+ win_descriptor.Bounds = a_rect
+ peer = toolkit.createWindow( win_descriptor )
+ dialog.createPeer( toolkit, peer )
+
+ return dialog
+
+ def _addListeners(self):
+ """Add listeners to dialog"""
+ nb = 0
+ for buttonName in self.Buttons:
+ nb +=1
+ a_control = self.dialog.getControl('Btn'+str(nb))
+ the_listener = ButtonListener(self)
+ a_control.addActionListener(the_listener)
+ self.lst_listeners['Btn'+str(nb)] = the_listener
+ return
+
+ def _removeListeners(self):
+ """ remove listeners on exiting"""
+ nb = 0
+ for buttonName in self.Buttons:
+ nb +=1
+ a_control = self.dialog.getControl('Btn'+str(nb))
+ a_control.removeActionListener(self.lst_listeners['Btn'+str(nb)])
+ return
+
+ def show(self, message, decoration, title):
+ self.message = message
+ self.decoration = decoration
+ self.title = title
+ # Create GUI
+ self.dialog = self._createBox()
+ self._addListeners()
+ #execute the dialog --> blocking call
+ self.dialog.execute()
+ #end --> release listeners and dispose dialog
+ self._removeListeners()
+ self.dialog.dispose()
+ return self.Response
+
+ def addButton(self, caption):
+ self.Buttons.append(caption)
+ return
+
+ def renderFromBoxSize(self, size = 150):
+ self.boxSize = size
+ self.fromBroxSize = True
+ return
+
+ def renderFromButtonSize(self, size = 50):
+ self.ButtonSize = size
+ self.fromBroxSize = False
+ return
+
+class ButtonListener(unohelper.Base, XActionListener):
+ """Stops the MessageBox, sets the button label as returned value"""
+ def __init__(self, caller):
+ self.caller = caller
+
+ def disposing(self, eventObject):
+ pass
+
+ def actionPerformed(self, actionEvent):
+ button = actionEvent.Source
+ self.caller.Response = button.Model.Label
+ self.caller.dialog.endExecute()
+ return
+
+### TEST
+if __name__ == '__main__':
+ # get the uno component context from the PyUNO runtime
+ localContext = uno.getComponentContext()
+
+ # create the UnoUrlResolver
+ resolver = localContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", localContext )
+
+ # connect to the running office
+ # LibO has to be launched in listen mode as
+ # ./soffice "--accept=socket,host=localhost,port=2002;urp;"
+ ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
+ myBox = MsgBox(ctx)
+ myBox.addButton("Yes")
+ myBox.addButton("No")
+ myBox.addButton("May be")
+ myBox.renderFromBoxSize(150)
+ myBox.numberOflines = 2
+
+ print(myBox.show("A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message " + chr(10)+chr(10)+"Do you agree ?",0,"Dialog title"))
+
+ myBox = MsgBox(ctx)
+ myBox.addButton("oK")
+ myBox.renderFromButtonSize()
+ myBox.numberOflines = 2
+
+ print(myBox.show("A small message",0,"Dialog title"))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/scripting/source/pyprov/pythonscript.py b/scripting/source/pyprov/pythonscript.py
new file mode 100644
index 0000000000..00d96d9b0d
--- /dev/null
+++ b/scripting/source/pyprov/pythonscript.py
@@ -0,0 +1,1146 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+# XScript implementation for python
+import uno
+import unohelper
+import sys
+import os
+import types
+import time
+import ast
+import platform
+from com.sun.star.uri.RelativeUriExcessParentSegments import RETAIN
+from urllib.parse import unquote
+
+class LogLevel:
+ NONE = 0 # production level
+ ERROR = 1 # for script developers
+ DEBUG = 2 # for script framework developers
+
+PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL"
+PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT"
+
+# Configuration ----------------------------------------------------
+LogLevel.use = LogLevel.NONE
+if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR":
+ LogLevel.use = LogLevel.ERROR
+elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG":
+ LogLevel.use = LogLevel.DEBUG
+
+# True, writes to stdout (difficult on windows)
+# False, writes to user/Scripts/python/log.txt
+LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0"
+
+ENABLE_EDIT_DIALOG=False # offers a minimal editor for editing.
+#-------------------------------------------------------------------
+
+def encfile(uni):
+ return uni.encode( sys.getfilesystemencoding())
+
+def lastException2String():
+ (excType,excInstance,excTraceback) = sys.exc_info()
+ ret = str(excType) + ": "+str(excInstance) + "\n" + \
+ uno._uno_extract_printable_stacktrace( excTraceback )
+ return ret
+
+def logLevel2String( level ):
+ ret = " NONE"
+ if level == LogLevel.ERROR:
+ ret = "ERROR"
+ elif level >= LogLevel.DEBUG:
+ ret = "DEBUG"
+ return ret
+
+def getLogTarget():
+ ret = sys.stdout
+ if not LOG_STDOUT:
+ try:
+ pathSubst = uno.getComponentContext().ServiceManager.createInstance(
+ "com.sun.star.util.PathSubstitution" )
+ userInstallation = pathSubst.getSubstituteVariableValue( "user" )
+ if len( userInstallation ) > 0:
+ systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" )
+ ret = open( systemPath , "a" )
+ except:
+ print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delegating log to stdout\n")
+ return ret
+
+class Logger(LogLevel):
+ def __init__(self , target ):
+ self.target = target
+
+ def isDebugLevel( self ):
+ return self.use >= self.DEBUG
+
+ def debug( self, msg ):
+ if self.isDebugLevel():
+ self.log( self.DEBUG, msg )
+
+ def isErrorLevel( self ):
+ return self.use >= self.ERROR
+
+ def error( self, msg ):
+ if self.isErrorLevel():
+ self.log( self.ERROR, msg )
+
+ def log( self, level, msg ):
+ if self.use >= level:
+ try:
+ self.target.write(
+ time.asctime() +
+ " [" +
+ logLevel2String( level ) +
+ "] " +
+ msg +
+ "\n" )
+ self.target.flush()
+ except:
+ print("Error during writing to stdout: " +lastException2String() + "\n")
+
+log = Logger( getLogTarget() )
+
+log.debug( "pythonscript loading" )
+
+#from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
+from com.sun.star.uno import RuntimeException
+from com.sun.star.lang import IllegalArgumentException
+from com.sun.star.container import NoSuchElementException
+from com.sun.star.lang import XServiceInfo
+from com.sun.star.io import IOException
+from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command
+from com.sun.star.task import XInteractionHandler
+from com.sun.star.beans import XPropertySet, Property
+from com.sun.star.container import XNameContainer
+from com.sun.star.xml.sax import XDocumentHandler, InputSource
+from com.sun.star.uno import Exception as UnoException
+from com.sun.star.script import XInvocation
+from com.sun.star.awt import XActionListener
+
+from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException
+from com.sun.star.script.browse import XBrowseNode
+from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT
+from com.sun.star.util import XModifyListener
+
+LANGUAGENAME = "Python"
+GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
+CALLABLE_CONTAINER_NAME = "g_exportedScripts"
+
+# pythonloader looks for a static g_ImplementationHelper variable
+g_ImplementationHelper = unohelper.ImplementationHelper()
+g_implName = "org.libreoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
+
+
+
+BLOCK_SIZE = 65536
+def readTextFromStream( inputStream ):
+ # read the file
+ code = uno.ByteSequence( b"" )
+ while True:
+ read,out = inputStream.readBytes( None , BLOCK_SIZE )
+ code = code + out
+ if read < BLOCK_SIZE:
+ break
+ return code.value
+
+def toIniName( str ):
+ if platform.system() == "Windows":
+ return str + ".ini"
+ else:
+ return str + "rc"
+
+
+""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
+ scriptURI is the system independent uri
+"""
+class MyUriHelper:
+
+ def __init__( self, ctx, location ):
+ self.ctx = ctx
+ self.s_UriMap = \
+ { "share" : "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python" , \
+ "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
+ "user" : "vnd.sun.star.expand:${$BRAND_INI_DIR/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
+ "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
+ self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx)
+ if location.startswith( "vnd.sun.star.tdoc" ):
+ self.m_baseUri = location + "/Scripts/python"
+ self.m_scriptUriLocation = "document"
+ else:
+ self.m_baseUri = expandUri( self.s_UriMap[location] )
+ self.m_scriptUriLocation = location
+ log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation )
+
+ def getRootStorageURI( self ):
+ return self.m_baseUri
+
+ def getStorageURI( self, scriptURI ):
+ return self.scriptURI2StorageUri(scriptURI)
+
+ def getScriptURI( self, storageURI ):
+ return self.storageURI2ScriptUri(storageURI)
+
+ def storageURI2ScriptUri( self, storageURI ):
+ if not storageURI.startswith( self.m_baseUri ):
+ message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ ret = "vnd.sun.star.script:" + \
+ storageURI[len(self.m_baseUri)+1:].replace("/","|") + \
+ "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation
+ log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret )
+ return ret
+
+ def scriptURI2StorageUri( self, scriptURI ):
+ try:
+ # base path to the python script location
+ sBaseUri = self.m_baseUri + "/"
+ xBaseUri = self.m_uriRefFac.parse(sBaseUri)
+
+ # path to the .py file + "$functionname, arguments, etc
+ xStorageUri = self.m_uriRefFac.parse(scriptURI)
+ # getName will apply url-decoding to the name, so encode back
+ sStorageUri = xStorageUri.getName().replace("%", "%25")
+ sStorageUri = sStorageUri.replace( "|", "/" )
+
+ # path to the .py file, relative to the base
+ funcNameStart = sStorageUri.find("$")
+ if funcNameStart != -1:
+ sFileUri = sStorageUri[0:funcNameStart]
+ sFuncName = sStorageUri[funcNameStart+1:]
+ else:
+ sFileUri = sStorageUri
+
+ xFileUri = self.m_uriRefFac.parse(sFileUri)
+ if not xFileUri:
+ message = "pythonscript: invalid relative uri '" + sFileUri+ "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ if not xFileUri.hasRelativePath():
+ message = "pythonscript: an absolute uri is invalid '" + sFileUri+ "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ # absolute path to the .py file
+ xAbsScriptUri = self.m_uriRefFac.makeAbsolute(xBaseUri, xFileUri, True, RETAIN)
+ sAbsScriptUri = xAbsScriptUri.getUriReference()
+
+ # ensure py file is under the base path
+ if not sAbsScriptUri.startswith(sBaseUri):
+ message = "pythonscript: storage uri '" + sAbsScriptUri + "' not in base uri '" + self.m_baseUri + "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ ret = sAbsScriptUri
+ if funcNameStart != -1:
+ ret = ret + "$" + sFuncName
+ log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret )
+ return ret
+ except UnoException as e:
+ log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message)
+ raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + e.Message, self.ctx )
+ except Exception as e:
+ log.error( "error during converting scriptURI="+scriptURI + ": " + str(e))
+ raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), self.ctx )
+
+
+class ModuleEntry:
+ def __init__( self, lastRead, module ):
+ self.lastRead = lastRead
+ self.module = module
+
+def hasChanged( oldDate, newDate ):
+ return newDate.Year > oldDate.Year or \
+ newDate.Month > oldDate.Month or \
+ newDate.Day > oldDate.Day or \
+ newDate.Hours > oldDate.Hours or \
+ newDate.Minutes > oldDate.Minutes or \
+ newDate.Seconds > oldDate.Seconds or \
+ newDate.NanoSeconds > oldDate.NanoSeconds
+
+def ensureSourceState( code ):
+ if code.endswith(b"\n"):
+ code = code + b"\n"
+ code = code.replace(b"\r", b"")
+ return code
+
+
+def checkForPythonPathBesideScript( url ):
+ if url.startswith( "file:" ):
+ path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
+ log.log( LogLevel.DEBUG, "checking for existence of " + path )
+ if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
+ log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
+ sys.path.append( path )
+
+ path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
+ log.log( LogLevel.DEBUG, "checking for existence of " + path )
+ if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
+ log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
+ sys.path.append( path )
+
+
+class ScriptContext(unohelper.Base):
+ def __init__( self, ctx, doc, inv ):
+ self.ctx = ctx
+ self.doc = doc
+ self.inv = inv
+
+ # XScriptContext
+ def getDocument(self):
+ if self.doc:
+ return self.doc
+ return self.getDesktop().getCurrentComponent()
+
+ def getDesktop(self):
+ return self.ctx.ServiceManager.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", self.ctx )
+
+ def getComponentContext(self):
+ return self.ctx
+
+ def getInvocationContext(self):
+ return self.inv
+
+#----------------------------------
+# Global Module Administration
+# does not fit together with script
+# engine lifetime management
+#----------------------------------
+#g_scriptContext = ScriptContext( uno.getComponentContext(), None )
+#g_modules = {}
+#def getModuleByUrl( url, sfa ):
+# entry = g_modules.get(url)
+# load = True
+# lastRead = sfa.getDateTimeModified( url )
+# if entry:
+# if hasChanged( entry.lastRead, lastRead ):
+# log.debug("file " + url + " has changed, reloading")
+# else:
+# load = False
+#
+# if load:
+# log.debug( "opening >" + url + "<" )
+#
+# code = readTextFromStream( sfa.openFileRead( url ) )
+
+ # execute the module
+# entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
+# entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
+# entry.module.__file__ = url
+# exec code in entry.module.__dict__
+# g_modules[ url ] = entry
+# log.debug( "mapped " + url + " to " + str( entry.module ) )
+# return entry.module
+
+class ProviderContext:
+ def __init__( self, storageType, sfa, uriHelper, scriptContext ):
+ self.storageType = storageType
+ self.sfa = sfa
+ self.uriHelper = uriHelper
+ self.scriptContext = scriptContext
+ self.modules = {}
+ self.rootUrl = None
+ self.mapPackageName2Path = None
+
+ def getTransientPartFromUrl( self, url ):
+ rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
+ return rest[0:rest.find("/")]
+
+ def getPackageNameFromUrl( self, url ):
+ rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
+ start = rest.find("/") +1
+ return rest[start:rest.find("/",start)]
+
+
+ def removePackageByUrl( self, url ):
+ items = self.mapPackageName2Path.items()
+ for i in items:
+ if url in i[1].paths:
+ self.mapPackageName2Path.pop(i[0])
+ break
+
+ def addPackageByUrl( self, url ):
+ packageName = self.getPackageNameFromUrl( url )
+ transientPart = self.getTransientPartFromUrl( url )
+ log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl )
+ if packageName in self.mapPackageName2Path:
+ package = self.mapPackageName2Path[ packageName ]
+ package.paths = package.paths + (url, )
+ else:
+ package = Package( (url,), transientPart)
+ self.mapPackageName2Path[ packageName ] = package
+
+ def isUrlInPackage( self, url ):
+ values = self.mapPackageName2Path.values()
+ for i in values:
+# print ("checking " + url + " in " + str(i.paths))
+ if url in i.paths:
+ return True
+# print ("false")
+ return False
+
+ def setPackageAttributes( self, mapPackageName2Path, rootUrl ):
+ self.mapPackageName2Path = mapPackageName2Path
+ self.rootUrl = rootUrl
+
+ def getPersistentUrlFromStorageUrl( self, url ):
+ # package name is the second directory
+ ret = url
+ if self.rootUrl:
+ pos = len( self.rootUrl) +1
+ ret = url[0:pos]+url[url.find("/",pos)+1:len(url)]
+ log.debug( "getPersistentUrlFromStorageUrl " + url + " -> "+ ret)
+ return ret
+
+ def getStorageUrlFromPersistentUrl( self, url):
+ ret = url
+ if self.rootUrl:
+ pos = len(self.rootUrl)+1
+ packageName = url[pos:url.find("/",pos+1)]
+ package = self.mapPackageName2Path[ packageName ]
+ ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)]
+ log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret)
+ return ret
+
+ def getFuncsByUrl( self, url ):
+ src = readTextFromStream( self.sfa.openFileRead( url ) )
+ checkForPythonPathBesideScript( url[0:url.rfind('/')] )
+ src = ensureSourceState( src )
+
+ try:
+ code = ast.parse( src )
+ except:
+ log.isDebugLevel() and log.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String())
+ raise
+
+ allFuncs = []
+
+ if code is None:
+ return allFuncs
+
+ g_exportedScripts = []
+ for node in ast.iter_child_nodes(code):
+ if isinstance(node, ast.FunctionDef):
+ allFuncs.append(node.name)
+ elif isinstance(node, ast.Assign):
+ for target in node.targets:
+ try:
+ identifier = target.id
+ except AttributeError:
+ identifier = ""
+ pass
+ if identifier == "g_exportedScripts":
+ for value in node.value.elts:
+ g_exportedScripts.append(value.id)
+ return g_exportedScripts
+
+# Python 2 only
+# for node in code.node.nodes:
+# if node.__class__.__name__ == 'Function':
+# allFuncs.append(node.name)
+# elif node.__class__.__name__ == 'Assign':
+# for assignee in node.nodes:
+# if assignee.name == 'g_exportedScripts':
+# for item in node.expr.nodes:
+# if item.__class__.__name__ == 'Name':
+# g_exportedScripts.append(item.name)
+# return g_exportedScripts
+
+ return allFuncs
+
+ def getModuleByUrl( self, url ):
+ entry = self.modules.get(url)
+ load = True
+ lastRead = self.sfa.getDateTimeModified( url )
+ if entry:
+ if hasChanged( entry.lastRead, lastRead ):
+ log.debug( "file " + url + " has changed, reloading" )
+ else:
+ load = False
+
+ if load:
+ log.debug( "opening >" + url + "<" )
+
+ src = readTextFromStream( self.sfa.openFileRead( url ) )
+ checkForPythonPathBesideScript( url[0:url.rfind('/')] )
+ src = ensureSourceState( src )
+
+ # execute the module
+ entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
+ entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
+
+ code = None
+ if url.startswith( "file:" ):
+ code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" )
+ else:
+ code = compile( src, url, "exec" )
+ exec(code, entry.module.__dict__)
+ entry.module.__file__ = url
+ self.modules[ url ] = entry
+ log.debug( "mapped " + url + " to " + str( entry.module ) )
+ return entry.module
+
+#--------------------------------------------------
+def isScript( candidate ):
+ ret = False
+ if isinstance( candidate, type(isScript) ):
+ ret = True
+ return ret
+
+#-------------------------------------------------------
+class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ):
+ def __init__( self, provCtx, uri, fileName, funcName ):
+ self.fileName = fileName
+ self.funcName = funcName
+ self.provCtx = provCtx
+ self.uri = uri
+
+ def getName( self ):
+ return self.funcName
+
+ def getChildNodes(self):
+ return ()
+
+ def hasChildNodes(self):
+ return False
+
+ def getType( self):
+ return SCRIPT
+
+ def getPropertyValue( self, name ):
+ ret = None
+ try:
+ if name == "URI":
+ ret = self.provCtx.uriHelper.getScriptURI(
+ self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) )
+ elif name == "Editable" and ENABLE_EDIT_DIALOG:
+ ret = not self.provCtx.sfa.isReadOnly( self.uri )
+
+ log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
+ except:
+ log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
+ raise
+
+ return ret
+ def setPropertyValue( self, name, value ):
+ log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
+ def getPropertySetInfo( self ):
+ log.debug( "ScriptBrowseNode.getPropertySetInfo called " )
+ return None
+
+ def getIntrospection( self ):
+ return None
+
+ def invoke( self, name, params, outparamindex, outparams ):
+ if name == "Editable":
+ servicename = "com.sun.star.awt.DialogProvider"
+ ctx = self.provCtx.scriptContext.getComponentContext()
+ dlgprov = ctx.ServiceManager.createInstanceWithContext(
+ servicename, ctx )
+
+ self.editor = dlgprov.createDialog(
+ "vnd.sun.star.script:" +
+ "ScriptBindingLibrary.MacroEditor?location=application")
+
+ code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri))
+ code = ensureSourceState( code )
+ self.editor.getControl("EditorTextField").setText(code)
+
+ self.editor.getControl("RunButton").setActionCommand("Run")
+ self.editor.getControl("RunButton").addActionListener(self)
+ self.editor.getControl("SaveButton").setActionCommand("Save")
+ self.editor.getControl("SaveButton").addActionListener(self)
+
+ self.editor.execute()
+
+ return None
+
+ def actionPerformed( self, event ):
+ try:
+ if event.ActionCommand == "Run":
+ code = self.editor.getControl("EditorTextField").getText()
+ code = ensureSourceState( code )
+ mod = types.ModuleType("ooo_script_framework")
+ mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
+ exec(code, mod.__dict__)
+ values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
+ if not values:
+ values = mod.__dict__.values()
+
+ for i in values:
+ if isScript( i ):
+ i()
+ break
+
+ elif event.ActionCommand == "Save":
+ toWrite = uno.ByteSequence(
+ self.editor.getControl("EditorTextField").getText().encode(
+ sys.getdefaultencoding()) )
+ copyUrl = self.uri + ".orig"
+ self.provCtx.sfa.move( self.uri, copyUrl )
+ out = self.provCtx.sfa.openFileWrite( self.uri )
+ out.writeBytes( toWrite )
+ out.close()
+ self.provCtx.sfa.kill( copyUrl )
+# log.debug("Save is not implemented yet")
+# text = self.editor.getControl("EditorTextField").getText()
+# log.debug("Would save: " + text)
+ except:
+ # TODO: add an error box here!
+ log.error( lastException2String() )
+
+
+ def setValue( self, name, value ):
+ return None
+
+ def getValue( self, name ):
+ return None
+
+ def hasMethod( self, name ):
+ return False
+
+ def hasProperty( self, name ):
+ return False
+
+
+#-------------------------------------------------------
+class FileBrowseNode( unohelper.Base, XBrowseNode ):
+ def __init__( self, provCtx, uri , name ):
+ self.provCtx = provCtx
+ self.uri = uri
+ self.name = name
+ self.funcnames = None
+
+ def getName( self ):
+ return self.name
+
+ def getChildNodes(self):
+ ret = ()
+ try:
+ self.funcnames = self.provCtx.getFuncsByUrl( self.uri )
+
+ scriptNodeList = []
+ for i in self.funcnames:
+ scriptNodeList.append(
+ ScriptBrowseNode(
+ self.provCtx, self.uri, self.name, i ))
+ ret = tuple( scriptNodeList )
+ log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri )
+ except:
+ text = lastException2String()
+ log.error( "Error while evaluating " + self.uri + ":" + text )
+ raise
+ return ret
+
+ def hasChildNodes(self):
+ try:
+ return len(self.getChildNodes()) > 0
+ except:
+ return False
+
+ def getType( self):
+ return CONTAINER
+
+
+
+class DirBrowseNode( unohelper.Base, XBrowseNode ):
+ def __init__( self, provCtx, name, rootUrl ):
+ self.provCtx = provCtx
+ self.name = name
+ self.rootUrl = rootUrl
+
+ def getName( self ):
+ return self.name
+
+ def getChildNodes( self ):
+ try:
+ log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
+ contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
+ browseNodeList = []
+ for i in contents:
+ if i.endswith( ".py" ):
+ log.debug( "adding filenode " + i )
+ browseNodeList.append(
+ FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) )
+ elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
+ log.debug( "adding DirBrowseNode " + i )
+ browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i))
+ return tuple( browseNodeList )
+ except Exception as e:
+ text = lastException2String()
+ log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
+ log.error( text)
+ return ()
+
+ def hasChildNodes( self ):
+ return True
+
+ def getType( self ):
+ return CONTAINER
+
+ def getScript( self, uri ):
+ log.debug( "DirBrowseNode getScript " + uri + " invoked" )
+ raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
+
+
+class ManifestHandler( XDocumentHandler, unohelper.Base ):
+ def __init__( self, rootUrl ):
+ self.rootUrl = rootUrl
+
+ def startDocument( self ):
+ self.urlList = []
+
+ def endDocument( self ):
+ pass
+
+ def startElement( self , name, attlist):
+ if name == "manifest:file-entry":
+ if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
+ self.urlList.append(
+ self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
+
+ def endElement( self, name ):
+ pass
+
+ def characters ( self, chars ):
+ pass
+
+ def ignoreableWhitespace( self, chars ):
+ pass
+
+ def setDocumentLocator( self, locator ):
+ pass
+
+def isPyFileInPath( sfa, path ):
+ ret = False
+ contents = sfa.getFolderContents( path, True )
+ for i in contents:
+ if sfa.isFolder(i):
+ ret = isPyFileInPath(sfa,i)
+ else:
+ if i.endswith(".py"):
+ ret = True
+ if ret:
+ break
+ return ret
+
+# extracts META-INF directory from
+def getPathsFromPackage( rootUrl, sfa ):
+ ret = ()
+ try:
+ fileUrl = rootUrl + "/META-INF/manifest.xml"
+ inputStream = sfa.openFileRead( fileUrl )
+ parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
+ handler = ManifestHandler( rootUrl )
+ parser.setDocumentHandler( handler )
+ parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
+ for i in tuple(handler.urlList):
+ if not isPyFileInPath( sfa, i ):
+ handler.urlList.remove(i)
+ ret = tuple( handler.urlList )
+ except UnoException:
+ text = lastException2String()
+ log.debug( "getPathsFromPackage " + fileUrl + " Exception: " +text )
+ pass
+ return ret
+
+
+class Package:
+ def __init__( self, paths, transientPathElement ):
+ self.paths = paths
+ self.transientPathElement = transientPathElement
+
+class DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
+ def __init__( self ):
+ pass
+ def handle( self, event):
+ log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
+
+class DummyProgressHandler( unohelper.Base, XProgressHandler ):
+ def __init__( self ):
+ pass
+
+ def push( self,status ):
+ log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
+ def update( self,status ):
+ log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
+ def pop( self, event ):
+ log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
+
+class CommandEnvironment(unohelper.Base, XCommandEnvironment):
+ def __init__( self ):
+ self.progressHandler = DummyProgressHandler()
+ self.interactionHandler = DummyInteractionHandler()
+ def getInteractionHandler( self ):
+ return self.interactionHandler
+ def getProgressHandler( self ):
+ return self.progressHandler
+
+#maybe useful for debugging purposes
+#class ModifyListener( unohelper.Base, XModifyListener ):
+# def __init__( self ):
+# pass
+# def modified( self, event ):
+# log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
+# def disposing( self, event ):
+# log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
+
+def getModelFromDocUrl(ctx, url):
+ """Get document model from document url."""
+ doc = None
+ args = ("Local", "Office")
+ ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
+ "com.sun.star.ucb.UniversalContentBroker", args, ctx)
+ identifier = ucb.createContentIdentifier(url)
+ content = ucb.queryContent(identifier)
+ p = Property()
+ p.Name = "DocumentModel"
+ p.Handle = -1
+
+ c = Command()
+ c.Handle = -1
+ c.Name = "getPropertyValues"
+ c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
+
+ env = CommandEnvironment()
+ try:
+ ret = content.execute(c, 0, env)
+ doc = ret.getObject(1, None)
+ except Exception as e:
+ log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
+ return doc
+
+def mapStorageType2PackageContext( storageType ):
+ ret = storageType
+ if( storageType == "share:uno_packages" ):
+ ret = "shared"
+ if( storageType == "user:uno_packages" ):
+ ret = "user"
+ return ret
+
+def getPackageName2PathMap( sfa, storageType ):
+ ret = {}
+ packageManagerFactory = uno.getComponentContext().getValueByName(
+ "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
+ packageManager = packageManagerFactory.getPackageManager(
+ mapStorageType2PackageContext(storageType))
+# packageManager.addModifyListener( ModifyListener() )
+ log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
+ packages = packageManager.getDeployedPackages(
+ packageManager.createAbortChannel(), CommandEnvironment( ) )
+ log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
+
+ for i in packages:
+ log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
+ transientPathElement = penultimateElement( i.URL )
+ j = expandUri( i.URL )
+ paths = getPathsFromPackage( j, sfa )
+ if len( paths ) > 0:
+ # map package name to url, we need this later
+ log.debug( "adding Package " + transientPathElement + " " + str( paths ) )
+ ret[ lastElement( j ) ] = Package( paths, transientPathElement )
+ return ret
+
+def penultimateElement( aStr ):
+ lastSlash = aStr.rindex("/")
+ penultimateSlash = aStr.rindex("/",0,lastSlash-1)
+ return aStr[ penultimateSlash+1:lastSlash ]
+
+def lastElement( aStr):
+ return aStr[ aStr.rfind( "/" )+1:len(aStr)]
+
+class PackageBrowseNode( unohelper.Base, XBrowseNode ):
+ def __init__( self, provCtx, name, rootUrl ):
+ self.provCtx = provCtx
+ self.name = name
+ self.rootUrl = rootUrl
+
+ def getName( self ):
+ return self.name
+
+ def getChildNodes( self ):
+ items = self.provCtx.mapPackageName2Path.items()
+ browseNodeList = []
+ for i in items:
+ if len( i[1].paths ) == 1:
+ browseNodeList.append(
+ DirBrowseNode( self.provCtx, i[0], i[1].paths[0] ))
+ else:
+ for j in i[1].paths:
+ browseNodeList.append(
+ DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) )
+ return tuple( browseNodeList )
+
+ def hasChildNodes( self ):
+ return len( self.provCtx.mapPackageName2Path ) > 0
+
+ def getType( self ):
+ return CONTAINER
+
+ def getScript( self, uri ):
+ log.debug( "PackageBrowseNode getScript " + uri + " invoked" )
+ raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
+
+
+
+
+class PythonScript( unohelper.Base, XScript ):
+ def __init__( self, func, mod, args ):
+ self.func = func
+ self.mod = mod
+ self.args = args
+
+ def invoke(self, args, out, outindex ):
+ log.debug( "PythonScript.invoke " + str( args ) )
+ try:
+ if (self.args):
+ args += self.args
+ ret = self.func( *args )
+ except UnoException as e:
+ # UNO Exception continue to fly ...
+ text = lastException2String()
+ complete = "Error during invoking function " + \
+ str(self.func.__name__) + " in module " + \
+ self.mod.__file__ + " (" + text + ")"
+ log.debug( complete )
+ # some people may beat me up for modifying the exception text,
+ # but otherwise office just shows
+ # the type name and message text with no more information,
+ # this is really bad for most users.
+ e.Message = e.Message + " (" + complete + ")"
+ raise
+ except Exception as e:
+ # General python exception are converted to uno RuntimeException
+ text = lastException2String()
+ complete = "Error during invoking function " + \
+ str(self.func.__name__) + " in module " + \
+ self.mod.__file__ + " (" + text + ")"
+ log.debug( complete )
+ raise RuntimeException( complete , self )
+ log.debug( "PythonScript.invoke ret = " + str( ret ) )
+ return ret, (), ()
+
+def expandUri( uri ):
+ if uri.startswith( "vnd.sun.star.expand:" ):
+ uri = uri.replace( "vnd.sun.star.expand:", "",1)
+ uri = uno.getComponentContext().getByName(
+ "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( unquote(uri) )
+ if uri.startswith( "file:" ):
+ uri = uno.absolutize("",uri) # necessary to get rid of .. in uri
+ return uri
+
+#--------------------------------------------------------------
+class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer):
+ def __init__( self, ctx, *args ):
+ if log.isDebugLevel():
+ mystr = ""
+ for i in args:
+ if len(mystr) > 0:
+ mystr = mystr +","
+ mystr = mystr + str(i)
+ log.debug( "Entering PythonScriptProvider.ctor" + mystr )
+
+ doc = None
+ inv = None
+ storageType = ""
+
+ if isinstance(args[0], str):
+ storageType = args[0]
+ if storageType.startswith( "vnd.sun.star.tdoc" ):
+ doc = getModelFromDocUrl(ctx, storageType)
+ else:
+ inv = args[0]
+ try:
+ doc = inv.ScriptContainer
+ content = ctx.getServiceManager().createInstanceWithContext(
+ "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
+ ctx).createDocumentContent(doc)
+ storageType = content.getIdentifier().getContentIdentifier()
+ except Exception as e:
+ text = lastException2String()
+ log.error( text )
+
+ isPackage = storageType.endswith( ":uno_packages" )
+
+ try:
+# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
+# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
+ urlHelper = MyUriHelper( ctx, storageType )
+ log.debug( "got urlHelper " + str( urlHelper ) )
+
+ rootUrl = expandUri( urlHelper.getRootStorageURI() )
+ log.debug( storageType + " transformed to " + rootUrl )
+
+ ucbService = "com.sun.star.ucb.SimpleFileAccess"
+ sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
+ if not sfa:
+ log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
+ raise RuntimeException(
+ "PythonScriptProvider couldn't instantiate " +ucbService, self)
+ self.provCtx = ProviderContext(
+ storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) )
+ if isPackage:
+ mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
+ self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
+ self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
+ else:
+ self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
+
+ except Exception as e:
+ text = lastException2String()
+ log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
+ raise e
+
+ def getName( self ):
+ return self.dirBrowseNode.getName()
+
+ def getChildNodes( self ):
+ return self.dirBrowseNode.getChildNodes()
+
+ def hasChildNodes( self ):
+ return self.dirBrowseNode.hasChildNodes()
+
+ def getType( self ):
+ return self.dirBrowseNode.getType()
+
+ # retrieve function args in parenthesis
+ def getFunctionArguments(self, func_signature):
+ nOpenParenthesis = func_signature.find( "(" )
+ if -1 == nOpenParenthesis:
+ function_name = func_signature
+ arguments = None
+ else:
+ function_name = func_signature[0:nOpenParenthesis]
+ arg_part = func_signature[nOpenParenthesis+1:len(func_signature)]
+ nCloseParenthesis = arg_part.find( ")" )
+ if -1 == nCloseParenthesis:
+ raise IllegalArgumentException( "PythonLoader: mismatch parenthesis " + func_signature, self, 0 )
+ arguments = arg_part[0:nCloseParenthesis].strip()
+ if arguments == "":
+ arguments = None
+ else:
+ arguments = tuple([x.strip().strip('"') for x in arguments.split(",")])
+ return function_name, arguments
+
+ def getScript( self, scriptUri ):
+ try:
+ log.debug( "getScript " + scriptUri + " invoked")
+
+ storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
+ self.provCtx.uriHelper.getStorageURI(scriptUri) );
+ log.debug( "getScript: storageUri = " + storageUri)
+ fileUri = storageUri[0:storageUri.find( "$" )]
+ funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]
+
+ # retrieve arguments in parenthesis
+ funcName, funcArgs = self.getFunctionArguments(funcName)
+ log.debug( " getScript : parsed funcname " + str(funcName) )
+ log.debug( " getScript : func args " + str(funcArgs) )
+
+ mod = self.provCtx.getModuleByUrl( fileUri )
+ log.debug( " got mod " + str(mod) )
+
+ func = mod.__dict__[ funcName ]
+
+ log.debug( "got func " + str( func ) )
+ return PythonScript( func, mod, funcArgs )
+ except:
+ text = lastException2String()
+ log.error( text )
+ raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
+
+
+ # XServiceInfo
+ def getSupportedServices( self ):
+ return g_ImplementationHelper.getSupportedServices(g_implName)
+
+ def supportsService( self, ServiceName ):
+ return g_ImplementationHelper.supportsService( g_implName, ServiceName )
+
+ def getImplementationName(self):
+ return g_implName
+
+ def getByName( self, name ):
+ log.debug( "getByName called" + str( name ))
+ return None
+
+
+ def getElementNames( self ):
+ log.debug( "getElementNames called")
+ return ()
+
+ def hasByName( self, name ):
+ try:
+ log.debug( "hasByName called " + str( name ))
+ uri = expandUri(name)
+ ret = self.provCtx.isUrlInPackage( uri )
+ log.debug( "hasByName " + uri + " " +str( ret ) )
+ return ret
+ except:
+ text = lastException2String()
+ log.debug( "Error in hasByName:" + text )
+ return False
+
+ def removeByName( self, name ):
+ log.debug( "removeByName called" + str( name ))
+ uri = expandUri( name )
+ if self.provCtx.isUrlInPackage( uri ):
+ self.provCtx.removePackageByUrl( uri )
+ else:
+ log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
+ raise NoSuchElementException( uri + "is not in package" , self )
+ log.debug( "removeByName called" + str( uri ) + " successful" )
+
+ def insertByName( self, name, value ):
+ log.debug( "insertByName called " + str( name ) + " " + str( value ))
+ uri = expandUri( name )
+ if isPyFileInPath( self.provCtx.sfa, uri ):
+ self.provCtx.addPackageByUrl( uri )
+ else:
+ # package is no python package ...
+ log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
+ raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
+ log.debug( "insertByName called " + str( uri ) + " successful" )
+
+ def replaceByName( self, name, value ):
+ log.debug( "replaceByName called " + str( name ) + " " + str( value ))
+ uri = expandUri( name )
+ self.removeByName( name )
+ self.insertByName( name, value )
+ log.debug( "replaceByName called" + str( uri ) + " successful" )
+
+ def getElementType( self ):
+ log.debug( "getElementType called" )
+ return uno.getTypeByName( "void" )
+
+ def hasElements( self ):
+ log.debug( "hasElements got called")
+ return False
+
+g_ImplementationHelper.addImplementation( \
+ PythonScriptProvider,g_implName, \
+ ("com.sun.star.script.provider.LanguageScriptProvider",
+ "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
+
+
+log.debug( "pythonscript finished initializing" )
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/scripting/source/pyprov/scriptproviderforpython.rdb b/scripting/source/pyprov/scriptproviderforpython.rdb
new file mode 100644
index 0000000000..09a37e4da1
--- /dev/null
+++ b/scripting/source/pyprov/scriptproviderforpython.rdb
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<components xmlns="http://openoffice.org/2010/uno-components">
+ <component loader="com.sun.star.loader.Python"
+ uri="vnd.openoffice.pymodule:pythonscript">
+ <implementation
+ name="org.libreoffice.pyuno.LanguageScriptProviderForPython">
+ <service name="com.sun.star.script.provider.ScriptProviderForPython"/>
+ <service name="com.sun.star.script.provider.LanguageScriptProvider"/>
+ </implementation>
+ </component>
+</components>