Added the ability to store and retrieve macros in the document macro storage.
This commit is contained in:
		
							
								
								
									
										248
									
								
								bin/obasync
									
									
									
									
									
								
							
							
						
						
									
										248
									
								
								bin/obasync
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | ||||
| # Office Basic macro source synchronizer. | ||||
| #   by imacat <imacat@mail.imacat.idv.tw>, 2016-08-31 | ||||
|  | ||||
| #  Copyright (c) 2016 imacat. | ||||
| #  Copyright (c) 2016-2017 imacat. | ||||
| # | ||||
| #  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| #  you may not use this file except in compliance with the License. | ||||
| @@ -70,7 +70,7 @@ LIBRARY         The name of the Basic library.  Default to the same | ||||
|                 OpenOffice/LibreOffice.  The default is 2002.  You can | ||||
|                 change it if port 2002 is already in use. | ||||
|  | ||||
| -x, --ext .EXT  The file name extension of the source files.  The  | ||||
| -x, --ext .EXT  The file name extension of the source files.  The | ||||
|                 default is ".vb".  This may be used for your | ||||
|                 convenience of editor syntax highlighting. | ||||
|  | ||||
| @@ -86,6 +86,13 @@ LIBRARY         The name of the Basic library.  Default to the same | ||||
|                 Run he specific macro after synchronization, for | ||||
|                 convenience. | ||||
|  | ||||
| --user          Store the macros in the user macro storage.  (default) | ||||
|  | ||||
| --doc           Store the macros in the document macro storage. | ||||
|  | ||||
| --target TARGET The target storage document if there are more than one | ||||
|                 opened documents.  A partial path is OK. | ||||
|  | ||||
| -h, --help      Show the help message and exit | ||||
|  | ||||
| -v, --version   Show program’s version number and exit | ||||
| @@ -133,9 +140,8 @@ def main(): | ||||
|     # Downloads the macros from OpenOffice/LibreOffice Basic | ||||
|     if args.get: | ||||
|         oo = Office(args.port) | ||||
|         libraries = oo.service_manager.createInstance( | ||||
|             "com.sun.star.script.ApplicationScriptLibraryContainer") | ||||
|         modules = read_basic_modules(libraries, args.library) | ||||
|         storage = find_storage(oo, args.storage_type, args.target) | ||||
|         modules = read_basic_modules(storage, args.library) | ||||
|         if len(modules) == 0: | ||||
|             print("ERROR: Library %s does not exist" % args.library, | ||||
|                   file=sys.stderr) | ||||
| @@ -153,19 +159,10 @@ def main(): | ||||
|                   file=sys.stderr) | ||||
|             return | ||||
|         oo = Office(args.port) | ||||
|         libraries = oo.service_manager.createInstance( | ||||
|             "com.sun.star.script.ApplicationScriptLibraryContainer") | ||||
|         update_basic_modules(libraries, args.library, modules, oo) | ||||
|         storage = find_storage(oo, args.storage_type, args.target) | ||||
|         update_basic_modules(storage, args.library, modules) | ||||
|         if args.run is not None: | ||||
|             factory = oo.service_manager.DefaultContext.getByName( | ||||
|                 "/singletons/com.sun.star.script.provider." | ||||
|                 "theMasterScriptProviderFactory") | ||||
|             provider = factory.createScriptProvider("") | ||||
|             script = provider.getScript( | ||||
|                 "vnd.sun.star.script:%s.%s" | ||||
|                 "?language=Basic&location=application" % | ||||
|                 (args.library, args.run)) | ||||
|             script.invoke((), (), ()) | ||||
|             run_macro(storage, args.library, args.run) | ||||
|  | ||||
|     print("Done.  %02d:%02d elapsed." % | ||||
|           (int((time.time() - t_start) / 60), | ||||
| @@ -206,6 +203,18 @@ def parse_args(): | ||||
|     parser.add_argument( | ||||
|         "-r", "--run", metavar="MODULE.MACRO", | ||||
|         help="The macro to run after the upload, if any.") | ||||
|     parser.add_argument( | ||||
|         "--user", dest="storage_type", | ||||
|         action="store_const", const="user", | ||||
|         help="Store the macros in the user macro storage.  (default)") | ||||
|     parser.add_argument( | ||||
|         "--doc", dest="storage_type", | ||||
|         action="store_const", const="doc", | ||||
|         help="Store the macros in the document macro storage.") | ||||
|     parser.add_argument( | ||||
|         "--target", metavar="TARGET", | ||||
|         help=("The target storage document if there are more than one" | ||||
|               " opened documents.  A partial path is OK.")) | ||||
|     parser.add_argument( | ||||
|         "-v", "--version", action="version", version="%(prog)s 0.4") | ||||
|     args = parser.parse_args() | ||||
| @@ -219,9 +228,117 @@ def parse_args(): | ||||
|     if args.ext[0] != ".": | ||||
|         args.ext = "." + args.ext | ||||
|  | ||||
|     if args.storage_type is None: | ||||
|         args.storage_type = "user" | ||||
|     # Paths are understood locally, despite of the content encoding. | ||||
|     if args.target is not None: | ||||
|         args.target = args.target.decode(locale.getpreferredencoding()) | ||||
|     return | ||||
|  | ||||
|  | ||||
| def find_storage(oo, type, target): | ||||
|     """Finds the macro storage to store the macros. | ||||
|  | ||||
|     Arguments: | ||||
|         type: The storage type, either "user" or "doc". | ||||
|         target: The file path to locate the storing document if there | ||||
|                 are more than one opened documents.  A partial path | ||||
|                 is OK. | ||||
|  | ||||
|     Returns: | ||||
|         The storage to save the macros, as a Storage object. | ||||
|     """ | ||||
|     if type == "user": | ||||
|         storage = Storage() | ||||
|         storage.type = type | ||||
|         storage.oo = oo | ||||
|         storage.doc = None | ||||
|         storage.libs = oo.service_manager.createInstance( | ||||
|             "com.sun.star.script.ApplicationScriptLibraryContainer") | ||||
|         return storage | ||||
|     elif type == "doc": | ||||
|         storage = Storage() | ||||
|         storage.type = type | ||||
|         storage.oo = oo | ||||
|         storage.doc = find_doc(oo, target) | ||||
|         storage.libs = storage.doc.getPropertyValue("BasicLibraries") | ||||
|         return storage | ||||
|     else: | ||||
|         return None | ||||
|  | ||||
|  | ||||
| def find_doc(oo, target): | ||||
|     """Find the target opened document by a partial path. | ||||
|  | ||||
|     Arguments: | ||||
|         target: A partial path of the document. | ||||
|  | ||||
|     Returns: | ||||
|         If there is only one opened document, it is returned.  If | ||||
|         there are more than one opened document, the document whose | ||||
|         file path matches the "target" is returned.  Otherwise, if | ||||
|         no matching document or more than one matching document are | ||||
|         found, the program exists with an error. | ||||
|     """ | ||||
|     # Checks the opened documents | ||||
|     enum = oo.desktop.getComponents().createEnumeration() | ||||
|     opened = [] | ||||
|     while enum.hasMoreElements(): | ||||
|         component = enum.nextElement() | ||||
|         if component.supportsService( | ||||
|                 "com.sun.star.document.OfficeDocument"): | ||||
|             opened.append(component) | ||||
|     if len(opened) == 0: | ||||
|         print("ERROR: Found no opened document to store the macros", | ||||
|               file=sys.stderr) | ||||
|         sys.exit(1) | ||||
|     # There are opened documents. | ||||
|     if target is None: | ||||
|         if len(opened) == 1: | ||||
|             return opened[0] | ||||
|         print("ERROR: There are more than one opened documens." | ||||
|               "  Please specify the file path.", | ||||
|               file=sys.stderr) | ||||
|         sys.exit(1) | ||||
|     file_content_provider = oo.service_manager.createInstance( | ||||
|         "com.sun.star.ucb.FileContentProvider") | ||||
|     matched = [] | ||||
|     for doc in opened: | ||||
|         if doc.hasLocation(): | ||||
|             path = file_content_provider.getSystemPathFromFileURL( | ||||
|                 doc.getLocation()) | ||||
|         else: | ||||
|             path = doc.getTitle() | ||||
|         if path.find(target) >= 0: | ||||
|             matched.append(doc) | ||||
|     if len(matched) == 1: | ||||
|         return matched[0] | ||||
|     elif len(matched) == 0: | ||||
|         print("ERROR: Found no matching document to store the macros.", | ||||
|               file=sys.stderr) | ||||
|         print("Opened documents:", file=sys.stderr) | ||||
|         for doc in opened: | ||||
|             if doc.hasLocation(): | ||||
|                 path = file_content_provider.getSystemPathFromFileURL( | ||||
|                     doc.getLocation()) | ||||
|             else: | ||||
|                 path = doc.getTitle() | ||||
|             print("* %s" % path, file=sys.stderr) | ||||
|         sys.exit(1) | ||||
|     else: | ||||
|         print("ERROR: There are more than one matching documents.", | ||||
|               file=sys.stderr) | ||||
|         print("Matching documents:", file=sys.stderr) | ||||
|         for doc in matched: | ||||
|             if doc.hasLocation(): | ||||
|                 path = file_content_provider.getSystemPathFromFileURL( | ||||
|                     doc.getLocation()) | ||||
|             else: | ||||
|                 path = doc.getTitle() | ||||
|             print("* %s" % path, file=sys.stderr) | ||||
|         sys.exit(1) | ||||
|  | ||||
|  | ||||
| def read_in_source_dir(projdir, ext, encoding): | ||||
|     """Read-in the source files. | ||||
|  | ||||
| @@ -289,15 +406,13 @@ def update_source_dir(projdir, modules, ext, encoding): | ||||
|     return | ||||
|  | ||||
|  | ||||
| def read_basic_modules(libraries, libname): | ||||
| def read_basic_modules(storage, libname): | ||||
|     """ | ||||
|     Read the OpenOffice/LibreOffice Basic macros from the macro | ||||
|     storage. | ||||
|  | ||||
|     Arguments: | ||||
|         libraries: The Basic library storage, as a | ||||
|             com.sun.star.script.ApplicationScriptLibraryContainer | ||||
|             service object. | ||||
|         storage: The Basic macro storage, as a Storage object. | ||||
|         libname: The name of the Basic library. | ||||
|  | ||||
|     Returns: | ||||
| @@ -306,41 +421,44 @@ def read_basic_modules(libraries, libname): | ||||
|         dictionary are the module contents. | ||||
|     """ | ||||
|     modules = {} | ||||
|     if not libraries.hasByName(libname): | ||||
|     if not storage.libs.hasByName(libname): | ||||
|         return modules | ||||
|     libraries.loadLibrary(libname) | ||||
|     library = libraries.getByName(libname) | ||||
|     storage.libs.loadLibrary(libname) | ||||
|     library = storage.libs.getByName(libname) | ||||
|     for modname in library.getElementNames(): | ||||
|         modules[modname] = library.getByName(modname) | ||||
|     return modules | ||||
|  | ||||
|  | ||||
| def update_basic_modules(libraries, libname, modules, oo): | ||||
| def update_basic_modules(storage, libname, modules): | ||||
|     """Update the OpenOffice/LibreOffice Basic macro storage. | ||||
|  | ||||
|     Arguments: | ||||
|         libraries: The Basic library storage, as a | ||||
|             com.sun.star.script.ApplicationScriptLibraryContainer | ||||
|             service object. | ||||
|         storage: The Basic macro storage, as a Storage object. | ||||
|         libname: The name of the Basic library. | ||||
|         modules: The Basic modules, as a dictionary.  The keys of | ||||
|             the dictionary are the module names, and the values of | ||||
|             the dictionary are the module contents. | ||||
|         oo: The OpenOffice/LibreOffice connection, as an Office | ||||
|             object. | ||||
|     """ | ||||
|     if not libraries.hasByName(libname): | ||||
|         libraries.createLibrary(libname) | ||||
|     if not storage.libs.hasByName(libname): | ||||
|         storage.libs.createLibrary(libname) | ||||
|         print("Script library %s created." % libname, file=sys.stderr) | ||||
|         create_dialog_library(oo, libname) | ||||
|         library = libraries.getByName(libname) | ||||
|         create_dialog_library(storage, libname) | ||||
|         library = storage.libs.getByName(libname) | ||||
|         for modname in sorted(modules.keys()): | ||||
|             library.insertByName(modname, modules[modname]) | ||||
|             print("Module %s added." % modname, file=sys.stderr) | ||||
|     else: | ||||
|         libraries.loadLibrary(libname) | ||||
|         library = libraries.getByName(libname) | ||||
|         curmods = sorted(library.getElementNames()) | ||||
|         storage.libs.loadLibrary(libname) | ||||
|         library = storage.libs.getByName(libname) | ||||
|         # As of OpenOffice 4.1.3, when there is no modules in the | ||||
|         # document storage, it returns a zero-length byte sequence | ||||
|         # instead of an empty string list. | ||||
|         # The byte sequence cannot be sorted directly. | ||||
|         curmods = library.getElementNames() | ||||
|         if len(curmods) == 0: | ||||
|             curmods = [] | ||||
|         curmods = sorted(curmods) | ||||
|         for modname in sorted(modules.keys()): | ||||
|             if modname not in curmods: | ||||
|                 library.insertByName(modname, modules[modname]) | ||||
| @@ -352,28 +470,57 @@ def update_basic_modules(libraries, libname, modules, oo): | ||||
|             if modname not in modules: | ||||
|                 library.removeByName(modname) | ||||
|                 print("Module %s removed." % modname, file=sys.stderr) | ||||
|     if libraries.isModified(): | ||||
|         libraries.storeLibraries() | ||||
|     if storage.libs.isModified(): | ||||
|         storage.libs.storeLibraries() | ||||
|     else: | ||||
|         print("Everything is in sync.", file=sys.stderr) | ||||
|     return | ||||
|  | ||||
|  | ||||
| def create_dialog_library(oo, libname): | ||||
| def create_dialog_library(storage, libname): | ||||
|     """Create the dialog library. | ||||
|  | ||||
|     Arguments: | ||||
|         oo: The OpenOffice/LibreOffice connection, as an Office | ||||
|             object. | ||||
|         storage: The Basic macro storage, as a Storage object. | ||||
|         libname: The name of the dialog library. | ||||
|     """ | ||||
|     libraries = oo.service_manager.createInstance( | ||||
|         "com.sun.star.script.ApplicationDialogLibraryContainer") | ||||
|     if storage.type is "user": | ||||
|         libraries = storage.oo.service_manager.createInstance( | ||||
|             "com.sun.star.script.ApplicationDialogLibraryContainer") | ||||
|     else: | ||||
|         libraries = storage.doc.getPropertyValue("DialogLibraries") | ||||
|     libraries.createLibrary(libname) | ||||
|     print("Dialog library %s created." % libname, file=sys.stderr) | ||||
|     libraries.storeLibraries() | ||||
|  | ||||
|  | ||||
| def run_macro(storage, libname, macro): | ||||
|     """Run a Basic macro. | ||||
|  | ||||
|     Arguments: | ||||
|         storage: The Basic macro storage, as a Storage object. | ||||
|         libname: The name of the dialog library. | ||||
|         macro:   The The macro to run. | ||||
|     """ | ||||
|     if storage.type is "user": | ||||
|         factory = storage.oo.service_manager.DefaultContext.getByName( | ||||
|             "/singletons/com.sun.star.script.provider." | ||||
|             "theMasterScriptProviderFactory") | ||||
|         provider = factory.createScriptProvider("") | ||||
|         script = provider.getScript( | ||||
|             "vnd.sun.star.script:%s.%s" | ||||
|             "?language=Basic&location=application" % | ||||
|             (args.library, args.run)) | ||||
|         script.invoke((), (), ()) | ||||
|     else: | ||||
|         provider = storage.doc.getScriptProvider() | ||||
|         script = provider.getScript( | ||||
|             "vnd.sun.star.script:%s.%s" | ||||
|             "?language=Basic&location=document" % | ||||
|             (args.library, args.run)) | ||||
|         script.invoke((), (), ()) | ||||
|  | ||||
|  | ||||
| def read_file(path, encoding): | ||||
|     """Read a file, and deals with Python 3 / 2 compatibility. | ||||
|  | ||||
| @@ -448,6 +595,17 @@ def update_file(path, content, encoding): | ||||
|     return is_updated | ||||
|  | ||||
|  | ||||
| class Storage: | ||||
|     """A Basic macro storage. | ||||
|  | ||||
|     Attributes: | ||||
|         oo: The office connection, as an Office() object. | ||||
|         doc: The office document component to store the macros. | ||||
|         libs: The Basic macro storage of the document. | ||||
|     """ | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class Office: | ||||
|     """The OpenOffice/LibreOffice connection. | ||||
|  | ||||
| @@ -588,7 +746,7 @@ class Office: | ||||
|  | ||||
|         LibreOffice on POSIX systems accepts "--accept" instead of | ||||
|         "-accept" now. | ||||
|          | ||||
|  | ||||
|         Returns: | ||||
|             True if soffice is LibreOffice, or False otherwise. | ||||
|         """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user