From 355101e93d86d93a4a5f2a6b508c5ad41d982014 Mon Sep 17 00:00:00 2001 From: "sbh (Open ERP)" Date: Wed, 26 May 2010 10:29:14 +0530 Subject: [PATCH 1/2] Task-ID 588 : OpenOffice Report Designer Integration in Trunk bzr revid: sbh@tinyerp.com-20100526045914-us3lif7njvbscfpl --- addons/base_report_designer/__openerp__.py | 3 +- .../base_report_designer_wizard.xml | 9 +- .../report_desinger/Makefile | 12 + .../report_desinger/Readme.txt | 10 + .../report_desinger/doc/faq.txt | 23 + .../openoffice_report/Makefile | 28 + .../openoffice_report/OOo_run.sh | 2 + .../openoffice_report/package/Addons.xcu | 114 + .../openoffice_report/script/About.py | 73 + .../openoffice_report/script/AddAttachment.py | 230 ++ .../openoffice_report/script/Change.py | 59 + .../openoffice_report/script/LoginTest.py | 10 + .../script/ServerParameter.py | 118 + .../openoffice_report/script/__init__.py | 6 + .../openoffice_report/script/import | 2 + .../openoffice_report/script/lib/__init__.py | 4 + .../openoffice_report/script/lib/actions.py | 64 + .../openoffice_report/script/lib/error.py | 13 + .../openoffice_report/script/lib/functions.py | 296 ++ .../openoffice_report/script/lib/gui.py | 758 ++++ .../openoffice_report/script/lib/tools.py | 19 + .../openoffice_report_designer/bin/Makefile | 42 + .../openoffice_report_designer/bin/OOo_run.sh | 2 + .../bin/package/Addons.xcu | 636 +++ .../bin/package/OpenERPReport.py | 3609 +++++++++++++++++ .../bin/script/About.py | 66 + .../bin/script/AddAttachment.py | 235 ++ .../bin/script/Change.py | 62 + .../bin/script/ConvertBracesToField.py | 240 ++ .../bin/script/ConvertFieldsToBraces.py | 38 + .../bin/script/ExportToRML.py | 86 + .../bin/script/Expression.py | 69 + .../bin/script/Fields.py | 245 ++ .../bin/script/LoginTest.py | 10 + .../bin/script/ModifyExistingReport.py | 168 + .../bin/script/NewReport.py | 74 + .../bin/script/Repeatln.py | 254 ++ .../bin/script/SendToServer.py | 195 + .../bin/script/ServerParameter.py | 162 + .../bin/script/Translation.py | 228 ++ .../bin/script/__init__.py | 15 + .../bin/script/compile_all.py | 3 + .../bin/script/import | 2 + .../bin/script/lib/__init__.py | 7 + .../bin/script/lib/actions.py | 64 + .../bin/script/lib/error.py | 13 + .../bin/script/lib/functions.py | 217 + .../bin/script/lib/gui.py | 775 ++++ .../bin/script/lib/logreport.py | 26 + .../bin/script/lib/rpc.py | 183 + .../bin/script/lib/tiny_socket.py | 93 + .../bin/script/lib/tools.py | 19 + .../bin/script/modify.py | 91 + .../openoffice_report_designer/doc/Readme.txt | 48 + .../doc/test_fields.py | 32 + .../openerp_report_designer.zip | Bin 0 -> 151447 bytes .../base_report_designer/wizard/__init__.py | 1 + .../wizard/base_openoffice.py | 57 + .../wizard/base_openoffice_view.xml | 43 + 59 files changed, 9959 insertions(+), 4 deletions(-) create mode 100644 addons/base_report_designer/report_desinger/Makefile create mode 100644 addons/base_report_designer/report_desinger/Readme.txt create mode 100644 addons/base_report_designer/report_desinger/doc/faq.txt create mode 100755 addons/base_report_designer/report_desinger/openoffice_report/Makefile create mode 100755 addons/base_report_designer/report_desinger/openoffice_report/OOo_run.sh create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/package/Addons.xcu create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/About.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/AddAttachment.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/Change.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/LoginTest.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/ServerParameter.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/__init__.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/import create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/lib/__init__.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/lib/actions.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/lib/error.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/lib/functions.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/lib/gui.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report/script/lib/tools.py create mode 100755 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/Makefile create mode 100755 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/OOo_run.sh create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/Addons.xcu create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/OpenERPReport.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/About.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/AddAttachment.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Change.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertBracesToField.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertFieldsToBraces.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ExportToRML.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Expression.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Fields.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/LoginTest.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ModifyExistingReport.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/NewReport.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Repeatln.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/SendToServer.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ServerParameter.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Translation.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/__init__.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/compile_all.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/import create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/__init__.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/actions.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/error.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/functions.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/gui.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/logreport.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/rpc.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tiny_socket.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tools.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/modify.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/doc/Readme.txt create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/doc/test_fields.py create mode 100644 addons/base_report_designer/report_desinger/openoffice_report_designer/openerp_report_designer.zip create mode 100644 addons/base_report_designer/wizard/base_openoffice.py create mode 100644 addons/base_report_designer/wizard/base_openoffice_view.xml diff --git a/addons/base_report_designer/__openerp__.py b/addons/base_report_designer/__openerp__.py index f19d179a1ed..8934c54fcbc 100644 --- a/addons/base_report_designer/__openerp__.py +++ b/addons/base_report_designer/__openerp__.py @@ -37,7 +37,8 @@ upload the report using the same wizard. 'website': 'http://www.openerp.com', 'depends': ['base'], 'init_xml': ['base_report_data.xml'], - 'update_xml': ['base_report_designer_wizard.xml'], + 'update_xml': ['wizard/base_openoffice_view.xml', + 'base_report_designer_wizard.xml'], 'demo_xml': [], 'installable': True, 'active': False, diff --git a/addons/base_report_designer/base_report_designer_wizard.xml b/addons/base_report_designer/base_report_designer_wizard.xml index 8882d2de9e9..4d6a075d456 100644 --- a/addons/base_report_designer/base_report_designer_wizard.xml +++ b/addons/base_report_designer/base_report_designer_wizard.xml @@ -22,9 +22,12 @@ type="wizard" id="menu_wizard_report_designer_modify" sequence="70"/> - - - + diff --git a/addons/base_report_designer/report_desinger/Makefile b/addons/base_report_designer/report_desinger/Makefile new file mode 100644 index 00000000000..c83843cb5ef --- /dev/null +++ b/addons/base_report_designer/report_desinger/Makefile @@ -0,0 +1,12 @@ +all: + (cd openoffice_report && make pack) + cp openoffice_report/*.zip . + (cd openoffice_report_designer/bin && make pack) + cp openoffice_report_designer/bin/*.zip . + + +clean: + rm -f *.zip + (cd openoffice_report && make clean) + (cd openoffice_report_designer/bin && make clean) + diff --git a/addons/base_report_designer/report_desinger/Readme.txt b/addons/base_report_designer/report_desinger/Readme.txt new file mode 100644 index 00000000000..0ba3be8dae6 --- /dev/null +++ b/addons/base_report_designer/report_desinger/Readme.txt @@ -0,0 +1,10 @@ + +Installation procedure +---------------------- + +Step 1 : + type: make + to generate .zip file + +Step 2 : + follow instruction in file : openoffice_report_designer/doc/Readme.txt diff --git a/addons/base_report_designer/report_desinger/doc/faq.txt b/addons/base_report_designer/report_desinger/doc/faq.txt new file mode 100644 index 00000000000..1ade9a60db5 --- /dev/null +++ b/addons/base_report_designer/report_desinger/doc/faq.txt @@ -0,0 +1,23 @@ +How to install the plugin with OpenOffice 2.4 on Windows +======================================================== + +If you get an error for the installation of the plugin, edit the pythonloader.py file +in $(OPEN_OFFICE_DIR)\program\ and change the value of DEBUG to None. + +If you get an error for the uno.exe in window then just close the Quick starter + Openoffice.2.4.1 Too\Option\Openoffic.org in that select the Memory + in that Openoffice.org.QuickStarter + if checked mark is there then just remove it.. + +If you are using OpenOffice 3.0 and install the Report Desinger with make all + command then change path in makefile + + sh /opt/openoffice.org3/program/unopkg add or remove both + + And for previous version of Openoffice used + + sh /usr/lib/openoffice/program/unopkg add -v $(ZIP_FILE) + + + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/Makefile b/addons/base_report_designer/report_desinger/openoffice_report/Makefile new file mode 100755 index 00000000000..56539500524 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/Makefile @@ -0,0 +1,28 @@ +ZIP_FILE=openerp_report_add_attachment.zip + +all: create pack uninstall install + +create: + cat script/import \ + script/lib/actions.py \ + script/lib/error.py \ + script/lib/functions.py \ + script/lib/gui.py \ + script/lib/tools.py \ + script/Change.py \ + script/LoginTest.py \ + script/AddAttachment.py \ + script/ServerParameter.py \ + script/About.py > package/OpenERPReport.py + +pack: clean + zip $(ZIP_FILE) `find package/ | grep -v svn` + +install: + sh /usr/lib/openoffice/program/unopkg add -v $(ZIP_FILE) + +uninstall: + sh /usr/lib/openoffice/program/unopkg remove -v $(ZIP_FILE) + +clean: + rm -f $(ZIP_FILE) diff --git a/addons/base_report_designer/report_desinger/openoffice_report/OOo_run.sh b/addons/base_report_designer/report_desinger/openoffice_report/OOo_run.sh new file mode 100755 index 00000000000..a22ff96cf53 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/OOo_run.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ooffice "-accept=socket,host=localhost,port=2002;urp;" diff --git a/addons/base_report_designer/report_desinger/openoffice_report/package/Addons.xcu b/addons/base_report_designer/report_desinger/openoffice_report/package/Addons.xcu new file mode 100644 index 00000000000..3ad4210bc98 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/package/Addons.xcu @@ -0,0 +1,114 @@ + + + + + + + + OpenERP ~Report + + + _self + + + + + + + + service:org.openoffice.openerp.report.serverparam?execute + + + + Server parameters + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.addattachment?execute + + + + Add Attachment + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.about?execute + + + + About + + + _self + + + com.sun.star.text.TextDocument + + + + + + + + + + service:org.openoffice.openerp.report.serverparam?execute + + + + %origin%/images/server_s.png + + + %origin%/images/server_b.png + + + + + + service:org.openoffice.openerp.report.about?execute + + + + %origin%/images/aboutus_s.png + + + %origin%/images/aboutus_b.png + + + + + + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/About.py b/addons/base_report_designer/report_desinger/openoffice_report/script/About.py new file mode 100644 index 00000000000..43d0d1b753a --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/About.py @@ -0,0 +1,73 @@ + + +import uno +from com.sun.star.task import XJobExecutor + +if __name__<>'package': + from lib.gui import * + +class About(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + self.win = DBModalDialog(60, 50, 175, 115, ".:: About OpenERP Report ::.") + + fdBigFont = createUnoStruct("com.sun.star.awt.FontDescriptor") + fdBigFont.Width = 20 + fdBigFont.Height = 25 + fdBigFont.Weight = 120 + fdBigFont.Family= 3 + + oLabelTitle1 = self.win.addFixedText("lblTitle1", 1, 1, 35, 30) + oLabelTitle1.Model.TextColor = 16056320 + oLabelTitle1.Model.FontDescriptor = fdBigFont + oLabelTitle1.Model.FontRelief = 1 + oLabelTitle1.Text = "Open" + + oLabelTitle2 = self.win.addFixedText("lblTitle2", 35, 1, 30, 30) + oLabelTitle2.Model.TextColor = 1 + oLabelTitle2.Model.FontDescriptor = fdBigFont + oLabelTitle2.Model.FontRelief = 1 + oLabelTitle2.Text = "ERP" + + oLabelProdDesc = self.win.addFixedText("lblProdDesc", 1, 30, 173, 75) + oLabelProdDesc.Model.TextColor = 1 + fdBigFont.Width = 10 + fdBigFont.Height = 11 + fdBigFont.Weight = 76 + oLabelProdDesc.Model.FontDescriptor = fdBigFont + oLabelProdDesc.Model.Align = 1 + oLabelProdDesc.Model.FontRelief = 1 + oLabelProdDesc.Model.MultiLine = True + oLabelProdDesc.Text = "This package helps you to add attachment in OpenERP.\nOnce connected to the server, you can add an attachment to an entity of OpenERP." + + oLabelFooter = self.win.addFixedText("lblFooter", -1, -1, 173, 25) + oLabelFooter.Model.TextColor = 255 + #oLabelFooter.Model.BackgroundColor = 1 + oLabelFooter.Model.Border = 2 + oLabelFooter.Model.BorderColor = 255 + fdBigFont.Width = 8 + fdBigFont.Height = 9 + fdBigFont.Weight = 100 + oLabelFooter.Model.FontDescriptor = fdBigFont + oLabelFooter.Model.Align = 1 + oLabelFooter.Model.FontRelief = 1 + oLabelFooter.Model.MultiLine = True + sMessage = "OpenERP Report v1.0 \nCopyright 2007-TODAY Tiny sprl \nThis product is free software, under the GPL licence." + oLabelFooter.Text = sMessage + +# +# if __name__=="package": +# +# self.win.addImageControl("imgAbout",0,0,225,169,sImagePath= "file:///home/hjo/Desktop/trunk/bin/package/images/About.jpg")#path.__getslice__(0,path.rfind("/")) + "/package/images/About.jpg") +# else: +# +# self.win.addImageControl("imgAbout",0,0,225,169,sImagePath="file:///home/hjo/Desktop/trunk/bin/package/images/About.jpg") + self.win.doModalDialog("",None) + +if __name__<>"package" and __name__=="__main__": + About(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( About, "org.openoffice.openerp.report.about", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/AddAttachment.py b/addons/base_report_designer/report_desinger/openoffice_report/script/AddAttachment.py new file mode 100644 index 00000000000..1a144b1fbc5 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/AddAttachment.py @@ -0,0 +1,230 @@ +import os +import uno +import unohelper +import xmlrpclib +import base64 +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.tools import * + from LoginTest import * + database="test" + uid = 3 + +class AddAttachment(unohelper.Base, XJobExecutor ): + Kind = { + 'PDF' : 'pdf', + 'OpenOffice': 'sxw', + } + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + self.aSearchResult = [] + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + if docinfo.getUserFieldValue(2) <> "" and docinfo.getUserFieldValue(3) <> "": + self.win = DBModalDialog(60, 50, 180, 70, "Add Attachment to Server") + self.win.addFixedText("lblResourceType", 2 , 5, 100, 10, "Select Appropriate Resource Type:") + self.win.addComboListBox("lstResourceType", -2, 25, 176, 15,True) + self.win.addButton('btnOkWithoutInformation', -2 , -5, 25 , 15,'OK' ,actionListenerProc = self.btnOkWithoutInformation_clicked ) + else: + self.win = DBModalDialog(60, 50, 180, 190, "Add Attachment to Server") + self.win.addFixedText("lblModuleName",2 , 9, 42, 20, "Select Module:") + self.win.addComboListBox("lstmodel", -2, 5, 134, 15,True) + self.lstModel = self.win.getControl( "lstmodel" ) + self.dModel = {} + + global passwd + self.password = passwd + # Open a new connexion to the server + + sock = xmlrpclib.ServerProxy(docinfo.getUserFieldValue(0) +'/xmlrpc/object') + ids = sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_model'),('state', '=', 'installed')]) + if not len(ids): + # If the module 'base_report_model' is not installed, use the default model + self.dModel = { + "Partner":'res.partner', + } + else: + sock = xmlrpclib.ServerProxy( docinfo.getUserFieldValue(0) + '/xmlrpc/object' ) + ids = sock.execute(database, uid, self.password, 'base.report.model' , 'search', []) + res = sock.execute(database, uid, self.password, 'base.report.model' , 'read', ids, ['name','model_id']) + models = sock.execute(database, uid, self.password, 'ir.model' , 'read', map(lambda x:x['model_id'][0], res), ['model']) + models = dict(map(lambda x:(x['id'],x['model']), models)) + self.dModel = dict(map(lambda x: (x['name'],models[x['model_id'][0]]), res)) + + for item in self.dModel.keys(): + self.lstModel.addItem(item, self.lstModel.getItemCount()) + + self.win.addFixedText("lblSearchName",2 , 25, 60, 10, "Enter Search String:") + self.win.addEdit("txtSearchName", 2, 35, 149, 15,) + self.win.addButton('btnSearch', -2 , 35, 25 , 15,'Search' ,actionListenerProc = self.btnSearch_clicked ) + + self.win.addFixedText("lblSearchRecord", 2 , 55, 60, 10, "Search Result:") + self.win.addComboListBox("lstResource", -2, 65, 176, 70, False ) + self.lstResource = self.win.getControl( "lstResource" ) + + self.win.addFixedText("lblResourceType", 2 , 137, 100, 20, "Select Appropriate Resource Type:") + self.win.addComboListBox("lstResourceType", -2, 147, 176, 15,True ) + + self.win.addButton('btnOkWithInformation', -2 , -5, 25 , 15,'OK' ,actionListenerProc = self.btnOkWithInformation_clicked ) + + self.lstResourceType = self.win.getControl( "lstResourceType" ) + for kind in self.Kind.keys(): + self.lstResourceType.addItem( kind, self.lstResourceType.getItemCount() ) + + self.win.addButton('btnCancel', -2 - 27 , -5 , 30 , 15, 'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.doModalDialog("lstResourceType", self.Kind.keys()[0]) + + def btnSearch_clicked( self, oActionEvent ): + modelSelectedItem = self.win.getListBoxSelectedItem("lstmodel") + if modelSelectedItem == "": + return + + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + sock = xmlrpclib.ServerProxy(docinfo.getUserFieldValue(0) +'/xmlrpc/object') + self.aSearchResult = sock.execute( database, uid, self.password, self.dModel[modelSelectedItem], 'name_search', self.win.getEditText("txtSearchName")) + self.win.removeListBoxItems("lstResource", 0, self.win.getListBoxItemCount("lstResource")) + if self.aSearchResult == []: + ErrorDialog("No search result found !!!", "", "Search ERROR" ) + return + + for result in self.aSearchResult: + self.lstResource.addItem(result[1],result[0]) + + def _send_attachment( self, name, data, res_model, res_id ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + sock = xmlrpclib.ServerProxy( docinfo.getUserFieldValue(0) + '/xmlrpc/object' ) + params = { + 'name': name, + 'datas': base64.encodestring( data ), + 'datas_fname': name, + 'res_model' : res_model, + 'res_id' : int(res_id), + } + + return sock.execute( database, uid, self.password, 'ir.attachment', 'create', params ) + + def send_attachment( self, model, resource_id ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + if oDoc2.getURL() == "": + ErrorDialog("Please save your file", "", "Saving ERROR" ) + return None + + url = oDoc2.getURL() + if self.Kind[self.win.getListBoxSelectedItem("lstResourceType")] == "pdf": + url = self.doc2pdf(url[7:]) + + if url == None: + ErrorDialog( "Ploblem in creating PDF", "", "PDF Error" ) + return None + + url = url[7:] + data = read_data_from_file( get_absolute_file_path( url ) ) + return self._send_attachment( os.path.basename( url ), data, model, resource_id ) + + def btnOkWithoutInformation_clicked( self, oActionEvent ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + if self.win.getListBoxSelectedItem("lstResourceType") == "": + ErrorDialog("Please select resource type", "", "Selection ERROR" ) + return + + res = self.send_attachment( docinfo.getUserFieldValue(3), docinfo.getUserFieldValue(2) ) + self.win.endExecute() + + def btnOkWithInformation_clicked(self,oActionEvent): + if self.win.getListBoxSelectedItem("lstResourceType") == "": + ErrorDialog( "Please select resource type", "", "Selection ERROR" ) + return + + if self.win.getListBoxSelectedItem("lstResource") == "" or self.win.getListBoxSelectedItem("lstmodel") == "": + ErrorDialog("Please select Model and Resource","","Selection ERROR") + return + + resourceid = None + for s in self.aSearchResult: + if s[1] == self.win.getListBoxSelectedItem("lstResource"): + resourceid = s[0] + break + + if resourceid == None: + ErrorDialog("No resource selected !!!", "", "Resource ERROR" ) + return + + res = self.send_attachment( self.dModel[self.win.getListBoxSelectedItem('lstmodel')], resourceid ) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def doc2pdf(self, strFile): + oDoc = None + strFilterSubName = '' + + strUrl = convertToURL( strFile ) + desktop = getDesktop() + oDoc = desktop.loadComponentFromURL( strUrl, "_blank", 0, Array(self._MakePropertyValue("Hidden",True))) + if oDoc: + strFilterSubName = "" + # select appropriate filter + if oDoc.supportsService("com.sun.star.presentation.PresentationDocument"): + strFilterSubName = "impress_pdf_Export" + elif oDoc.supportsService("com.sun.star.sheet.SpreadsheetDocument"): + strFilterSubName = "calc_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.WebDocument"): + strFilterSubName = "writer_web_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.GlobalDocument"): + strFilterSubName = "writer_globaldocument_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.TextDocument"): + strFilterSubName = "writer_pdf_Export" + elif oDoc.supportsService("com.sun.star.drawing.DrawingDocument"): + strFilterSubName = "draw_pdf_Export" + elif oDoc.supportsService("com.sun.star.formula.FormulaProperties"): + strFilterSubName = "math_pdf_Export" + elif oDoc.supportsService("com.sun.star.chart.ChartDocument"): + strFilterSubName = "chart_pdf_Export" + else: + pass + + filename = len(strFilterSubName) > 0 and convertToURL( os.path.splitext( strFile )[0] + ".pdf" ) or None + + if len(strFilterSubName) > 0: + oDoc.storeToURL( filename, Array(self._MakePropertyValue("FilterName", strFilterSubName ),self._MakePropertyValue("CompressMode", "1" ))) + + oDoc.close(True) + # Can be None if len(strFilterSubName) <= 0 + return filename + + def _MakePropertyValue(self, cName = "", uValue = u"" ): + oPropertyValue = createUnoStruct( "com.sun.star.beans.PropertyValue" ) + if cName: + oPropertyValue.Name = cName + if uValue: + oPropertyValue.Value = uValue + return oPropertyValue + + +if __name__<>"package" and __name__=="__main__": + AddAttachment(None) +elif __name__=="package": + g_ImplementationHelper = unohelper.ImplementationHelper() + g_ImplementationHelper.addImplementation( AddAttachment, "org.openoffice.openerp.report.addattachment", ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/Change.py b/addons/base_report_designer/report_desinger/openoffice_report/script/Change.py new file mode 100644 index 00000000000..fa662fb4adc --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/Change.py @@ -0,0 +1,59 @@ +if __name__<>"package": + from lib.gui import * + from lib.functions import * + +class Change: + def __init__(self, aVal= None, sURL=""): + self.win=DBModalDialog(60, 50, 120, 90, "Connect to Open ERP Server") + + self.win.addFixedText("lblVariable", 38, 12, 60, 15, "Server") + + self.win.addEdit("txtHost",-2,9,60,15,sURL[sURL.find("/")+2:sURL.rfind(":")]) + + self.win.addFixedText("lblReportName",45 , 31, 60, 15, "Port") + self.win.addEdit("txtPort",-2,28,60,15,sURL[sURL.rfind(":")+1:]) + + self.win.addFixedText("lblLoginName", 2, 51, 60, 15, "Protocol Connection") + + self.win.addComboListBox("lstProtocol", -2, 48, 60, 15, True) + self.lstProtocol = self.win.getControl( "lstProtocol" ) + + self.lstProtocol.addItem( "XML-RPC", 0) + #self.lstProtocol.addItem( "XML-RPC secure", 1) + #self.lstProtocol.addItem( "NET-RPC (faster)", 2) + + self.win.addButton( 'btnOK', -2, -5, 30, 15, 'Ok', actionListenerProc = self.btnOkOrCancel_clicked ) + + self.win.addButton( 'btnCancel', -2 - 30 - 5 ,-5, 30, 15, 'Cancel', actionListenerProc = self.btnOkOrCancel_clicked ) + self.aVal=aVal + self.protocol = { + 'XML-RPC': 'http://', + 'XML-RPC secure': 'https://', + 'NET-RPC (faster)': 'socket://', + } + + sValue=self.protocol.keys()[0] + if sURL<>"": + sValue=self.protocol.keys()[self.protocol.values().index(sURL[:sURL.find("/")+2])] + + self.win.doModalDialog( "lstProtocol", sValue) + + def cmbProtocol_selected(self,oItemEvent): + pass + + def btnOkOrCancel_clicked(self,oActionEvent): + if oActionEvent.Source.getModel().Name == "btnOK": + url = self.protocol[self.win.getListBoxSelectedItem("lstProtocol")]+self.win.getEditText("txtHost")+":"+self.win.getEditText("txtPort") + res = getConnectionStatus(url) + if res == -1: + self.aVal.append("Could not connect to the server!") + elif res == 0: + self.aVal.append("No Database found !!!") + else: + self.aVal.append(url) + self.aVal.append(res) + self.win.endExecute() + elif oActionEvent.Source.getModel().Name =="btnCancel": + self.win.endExecute() + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/LoginTest.py b/addons/base_report_designer/report_desinger/openoffice_report/script/LoginTest.py new file mode 100644 index 00000000000..daf81591c1a --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/LoginTest.py @@ -0,0 +1,10 @@ + +if __name__<>"package": + from ServerParameter import * + from lib.gui import * + +class LoginTest: + def __init__(self): + if not loginstatus: + ServerParameter(None) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/ServerParameter.py b/addons/base_report_designer/report_desinger/openoffice_report/script/ServerParameter.py new file mode 100644 index 00000000000..812dbe061e3 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/ServerParameter.py @@ -0,0 +1,118 @@ +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from Change import * + database="test" + +class ServerParameter( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win=DBModalDialog(60, 50, 160, 108, "Server Connection Parameter") + + self.win.addFixedText("lblVariable", 2, 12, 35, 15, "Server URL") + if docinfo.getUserFieldValue(0)=="": + docinfo.setUserFieldValue(0,"http://localhost:8069") + self.win.addEdit("txtHost",-34,9,91,15,docinfo.getUserFieldValue(0)) + self.win.addButton('btnChange',-2 ,9,30,15,'Change' ,actionListenerProc = self.btnChange_clicked ) + + self.win.addFixedText("lblDatabaseName", 6, 31, 31, 15, "Database") + #self.win.addFixedText("lblMsg", -2,28,123,15) + self.win.addComboListBox("lstDatabase", -2,28,123,15, True) + self.lstDatabase = self.win.getControl( "lstDatabase" ) + #self.win.selectListBoxItem( "lstDatabase", docinfo.getUserFieldValue(2), True ) + #self.win.setEnabled("lblMsg",False) + + self.win.addFixedText("lblLoginName", 17, 51, 20, 15, "Login") + self.win.addEdit("txtLoginName",-2,48,123,15,docinfo.getUserFieldValue(1)) + + self.win.addFixedText("lblPassword", 6, 70, 31, 15, "Password") + self.win.addEdit("txtPassword",-2,67,123,15,) + self.win.setEchoChar("txtPassword",42) + + + self.win.addButton('btnOK',-2 ,-5, 60,15,'Connect', actionListenerProc = self.btnOk_clicked ) + + self.win.addButton('btnCancel',-2 - 60 - 5 ,-5, 35,15,'Cancel', actionListenerProc = self.btnCancel_clicked ) + sValue="" + if docinfo.getUserFieldValue(0)<>"": + res=getConnectionStatus(docinfo.getUserFieldValue(0)) + if res == -1: + sValue="Could not connect to the server!" + self.lstDatabase.addItem("Could not connect to the server!",0) + elif res == 0: + sValue="No Database found !!!" + self.lstDatabase.addItem("No Database found !!!",0) + else: + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + for i in range(len(res)): + self.lstDatabase.addItem(res[i],i) + sValue = database + + self.win.doModalDialog("lstDatabase",sValue) + + #self.win.doModalDialog("lstDatabase",docinfo.getUserFieldValue(2)) + + def btnOk_clicked(self,oActionEvent): + sock = xmlrpclib.ServerProxy(self.win.getEditText("txtHost")+'/xmlrpc/common') + sDatabase=self.win.getListBoxSelectedItem("lstDatabase") + sLogin=self.win.getEditText("txtLoginName") + sPassword=self.win.getEditText("txtPassword") + UID = sock.login(sDatabase,sLogin,sPassword) + if not UID: + ErrorDialog("Connection Refuse...","Please enter valid Login/Password") + else: + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + docinfo.setUserFieldValue(0,self.win.getEditText("txtHost")) + docinfo.setUserFieldValue(1,self.win.getEditText("txtLoginName")) + global passwd + passwd=self.win.getEditText("txtPassword") + global loginstatus + loginstatus=True + global database + database=sDatabase + global uid + uid=UID + #docinfo.setUserFieldValue(2,self.win.getListBoxSelectedItem("lstDatabase")) + #docinfo.setUserFieldValue(3,"") + ErrorDialog(" You can start creating your report in \nthe current document.","Take care to save it as a .SXW file \nbefore sending to the server.","Message") + self.win.endExecute() + + def btnCancel_clicked(self, oActionEvent ): + self.win.endExecute() + + def btnChange_clicked(self,oActionEvent): + try: + aVal=[] + url= self.win.getEditText("txtHost") + Change(aVal,url) + if aVal[1]== -1: + ErrorDialog(aVal[0],"") + elif aVal[1]==0: + ErrorDialog(aVal[0],"") + else: + self.win.setEditText("txtHost",aVal[0]) + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + for i in range(len(aVal[1])): + self.lstDatabase.addItem(aVal[1][i],i) + except Exception, e: + print "Exception: %s\n", repr(e) + + +if __name__<>"package" and __name__=="__main__": + ServerParameter(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ServerParameter, "org.openoffice.openerp.report.serverparam", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/__init__.py b/addons/base_report_designer/report_desinger/openoffice_report/script/__init__.py new file mode 100644 index 00000000000..3ecd05aa6da --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/__init__.py @@ -0,0 +1,6 @@ +import lib +import Change +import ServerParameter +import LoginTest +import About +import AddAttachment diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/import b/addons/base_report_designer/report_desinger/openoffice_report/script/import new file mode 100644 index 00000000000..e14c925b619 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/import @@ -0,0 +1,2 @@ +__name__="package" + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/lib/__init__.py b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/__init__.py new file mode 100644 index 00000000000..0227903f9e6 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/__init__.py @@ -0,0 +1,4 @@ +import actions +import error +import functions +import gui \ No newline at end of file diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/lib/actions.py b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/actions.py new file mode 100644 index 00000000000..4f553346c45 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/actions.py @@ -0,0 +1,64 @@ +import uno +import unohelper +import os +#-------------------------------------------------- +# An ActionListener adapter. +# This object implements com.sun.star.awt.XActionListener. +# When actionPerformed is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oActionEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +if __name__<>"package": + os.system( "ooffice '-accept=socket,host=localhost,port=2002;urp;'" ) +passwd="" +database="" +uid="" +loginstatus=False +from com.sun.star.awt import XActionListener +class ActionListenerProcAdapter( unohelper.Base, XActionListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oActionEvent is a com.sun.star.awt.ActionEvent struct. + def actionPerformed( self, oActionEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oActionEvent,) + self.tParams ) + +#-------------------------------------------------- +# An ItemListener adapter. +# This object implements com.sun.star.awt.XItemListener. +# When itemStateChanged is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oItemEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +from com.sun.star.awt import XItemListener +class ItemListenerProcAdapter( unohelper.Base, XItemListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oItemEvent is a com.sun.star.awt.ItemEvent struct. + def itemStateChanged( self, oItemEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oItemEvent,) + self.tParams ) + +#-------------------------------------------------- +# An TextListener adapter. +# This object implements com.sun.star.awt.XTextistener. +# When textChanged is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oTextEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +from com.sun.star.awt import XTextListener +class TextListenerProcAdapter( unohelper.Base, XTextListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oTextEvent is a com.sun.star.awt.TextEvent struct. + def textChanged( self, oTextEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oTextEvent,) + self.tParams ) + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/lib/error.py b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/error.py new file mode 100644 index 00000000000..2dd692723f9 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/error.py @@ -0,0 +1,13 @@ +if __name__<>"package": + from gui import * +class ErrorDialog: + def __init__(self,sErrorMsg, sErrorHelpMsg="",sTitle="Error Message"): + self.win = DBModalDialog(50, 50, 150, 90, sTitle) + self.win.addFixedText("lblErrMsg", 5, 5, 190, 25, sErrorMsg) + self.win.addFixedText("lblErrHelpMsg", 5, 30, 190, 25, sErrorHelpMsg) + self.win.addButton('btnOK', 55,-5,40,15,'Ok' + ,actionListenerProc = self.btnOkOrCancel_clicked ) + self.win.doModalDialog("",None) + def btnOkOrCancel_clicked( self, oActionEvent ): + self.win.endExecute() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/lib/functions.py b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/functions.py new file mode 100644 index 00000000000..91e2a43e994 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/functions.py @@ -0,0 +1,296 @@ +import uno +import xmlrpclib +import re +import socket +import cPickle +import marshal +import tempfile +if __name__<>"package": + from gui import * + database="test" + uid = 3 + +def genTree(object,aList,insField,host,level=3, ending=[], ending_excl=[], recur=[], root='', actualroot=""): + try: + sock = xmlrpclib.ServerProxy(host+'/xmlrpc/object') + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, docinfo.getUserFieldValue(1), object , 'fields_get') + key = res.keys() + key.sort() + for k in key: + if (not ending or res[k]['type'] in ending) and ((not ending_excl) or not (res[k]['type'] in ending_excl)): + insField.addItem(root+'/'+res[k]["string"],len(aList)) + aList.append(actualroot+'/'+k) + if (res[k]['type'] in recur) and (level>0): + genTree(res[k]['relation'],aList,insField,host ,level-1, ending, ending_excl, recur,root+'/'+res[k]["string"],actualroot+'/'+k) + except: + import traceback;traceback.print_exc() + +def VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList,sTableName=""): + if sTableName.find(".") != -1: + print sTableName,1 + for i in range(len(aItemList)): + if aComponentAdd[i]==sTableName: + sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")] + for j in range(len(aObjectList)): + if aObjectList[j][:aObjectList[j].find("(")] == sLVal: + insVariable.append(aObjectList[j]) + VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList, sTableName[:sTableName.rfind(".")]) + else: + for i in range(len(aItemList)): + if aComponentAdd[i]==sTableName: + sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")] + for j in range(len(aObjectList)): + if aObjectList[j][:aObjectList[j].find("(")] == sLVal and sLVal!="": + print aObjectList[j] + insVariable.append(aObjectList[j]) + +def getList(aObjectList,host,count): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sMain="" + if not count == 0: + if count >= 1: + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sItem[sItem.find("(")+1:sItem.find(",")]=="objects": + sMain = sItem[sItem.find(",'")+2:sItem.find("')")] + oParEnum = doc.getTextFields().createEnumeration() + aObjectList.append("List of " + docinfo.getUserFieldValue(3)) + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn": + if sItem[sItem.find("(")+1:sItem.find(",")]=="objects": + aObjectList.append(sItem[sItem.rfind(",'")+2:sItem.rfind("')")] + "(" + docinfo.getUserFieldValue(3) + ")") + else: + sTemp=sItem[sItem.find("(")+1:sItem.find(",")] + if sMain == sTemp[:sTemp.find(".")]: + getRelation(docinfo.getUserFieldValue(3), sItem[sItem.find(".")+1:sItem.find(",")], sItem[sItem.find(",'")+2:sItem.find("')")],aObjectList,host) + else: + sPath=getPath(sItem[sItem.find("(")+1:sItem.find(",")], sMain) + getRelation(docinfo.getUserFieldValue(3), sPath[sPath.find(".")+1:], sItem[sItem.find(",'")+2:sItem.find("')")],aObjectList,host) + else: + aObjectList.append("List of " + docinfo.getUserFieldValue(3)) + +def getRelation(sRelName, sItem, sObjName, aObjectList, host ): + sock = xmlrpclib.ServerProxy(host+'/xmlrpc/object') + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, docinfo.getUserFieldValue(1), sRelName , 'fields_get') + key = res.keys() + for k in key: + if sItem.find(".") == -1: + if k == sItem: + aObjectList.append(sObjName + "(" + res[k]['relation'] + ")") + return 0 + if k == sItem[:sItem.find(".")]: + getRelation(res[k]['relation'], sItem[sItem.find(".")+1:], sObjName,aObjectList,host) + + +def getPath(sPath,sMain): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sPath[:sPath.find(".")] == sMain: + break; + else: + res = re.findall('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]',sPath) + if len(res) <> 0: + if sItem[sItem.find(",'")+2:sItem.find("')")] == sPath[:sPath.find(".")]: + sPath = sItem[sItem.find("(")+1:sItem.find(",")] + sPath[sPath.find("."):] + getPath(sPath, sMain) + return sPath + +def EnumDocument(aItemList,aComponentAdd): + desktop = getDesktop() + parent="" + bFlag = False + Doc =desktop.getCurrentComponent() + #oVC = Doc.CurrentController.getViewCursor() + oParEnum = Doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.Anchor.TextTable: + #parent = oPar.Anchor.TextTable.Name + getChildTable(oPar.Anchor.TextTable,aItemList,aComponentAdd) + elif oPar.Anchor.TextSection: + parent = oPar.Anchor.TextSection.Name + elif oPar.Anchor.Text: + parent = "Document" + sItem=oPar.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn" and not oPar.Items in aItemList: + aItemList.append( oPar.Items ) + aComponentAdd.append( parent ) + +def getChildTable(oPar,aItemList,aComponentAdd,sTableName=""): + sNames = oPar.getCellNames() + bEmptyTableFlag=True + for val in sNames: + oCell = oPar.getCellByName(val) + oCurEnum = oCell.createEnumeration() + while oCurEnum.hasMoreElements(): + try: + oCur = oCurEnum.nextElement() + + if oCur.supportsService("com.sun.star.text.TextTable"): + if sTableName=="": + print 2 + getChildTable(oCur,aItemList,aComponentAdd,oPar.Name) + else: + print 1 + getChildTable(oCur,aItemList,aComponentAdd,sTableName+"."+oPar.Name) + else: + oSecEnum = oCur.createEnumeration() + while oSecEnum.hasMoreElements(): + oSubSection = oSecEnum.nextElement() + if oSubSection.supportsService("com.sun.star.text.TextField"): + bEmptyTableFlag=False + sItem=oSubSection.TextField.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn": + if aItemList.__contains__(oSubSection.TextField.Items)==False: + aItemList.append(oSubSection.TextField.Items) + if sTableName=="": + if aComponentAdd.__contains__(oPar.Name)==False: + aComponentAdd.append(oPar.Name) + else: + if aComponentAdd.__contains__(sTableName+"."+oPar.Name)==False: + aComponentAdd.append(sTableName+"."+oPar.Name) + + except: + import traceback;traceback.print_exc() + if bEmptyTableFlag==True: + aItemList.append((u'',u'')) + if sTableName=="": + if aComponentAdd.__contains__(oPar.Name)==False: + aComponentAdd.append(oPar.Name) + else: + if aComponentAdd.__contains__(sTableName+"."+oPar.Name)==False: + aComponentAdd.append(sTableName+"."+oPar.Name) + return 0 + +def getRecersiveSection(oCurrentSection,aSectionList): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + oParEnum=doc.getText().createEnumeration() + aSectionList.append(oCurrentSection.Name) + if oCurrentSection.ParentSection: + getRecersiveSection(oCurrentSection.ParentSection,aSectionList) + else: + return +def getConnectionStatus(url): + m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '') + if not m: + return -1 + if m.group(1) == 'http://' or m.group(1) == 'https://': + sock = xmlrpclib.ServerProxy(url + '/xmlrpc/db') + try: + return sock.list() + except: + return -1 + else: + sock = mysocket + #sock = xmlrpclib.ServerProxy(url + '/xmlrpc/common') + try: + sock.connect(m.group(2), int(m.group(3))) + sock.mysend(('db', 'list')) + res = sock.myreceive() + sock.disconnect() + return res + except Exception, e: + return -1 + +class Myexception(Exception): + def __init__(self, faultCode, faultString): + self.faultCode = faultCode + self.faultString = faultString + +class mysocket: + def __init__(self, sock=None): + if sock is None: + self.sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) + else: + self.sock = sock + self.sock.settimeout(60) + def connect(self, host, port=False): + if not port: + protocol, buf = host.split('//') + host, port = buf.split(':') + self.sock.connect((host, int(port))) + def disconnect(self): + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + def mysend(self, msg, exception=False, traceback=None): + msg = cPickle.dumps([msg,traceback]) + size = len(msg) + self.sock.send('%8d' % size) + self.sock.send(exception and "1" or "0") + totalsent = 0 + while totalsent < size: + sent = self.sock.send(msg[totalsent:]) + if sent == 0: + raise RuntimeError, "socket connection broken" + totalsent = totalsent + sent + def myreceive(self): + buf='' + while len(buf) < 8: + chunk = self.sock.recv(8 - len(buf)) + if chunk == '': + raise RuntimeError, "socket connection broken" + buf += chunk + size = int(buf) + buf = self.sock.recv(1) + if buf != "0": + exception = buf + else: + exception = False + msg = '' + while len(msg) < size: + chunk = self.sock.recv(size-len(msg)) + if chunk == '': + raise RuntimeError, "socket connection broken" + msg = msg + chunk + res = cPickle.loads(msg) + if isinstance(res[0],Exception): + if exception: + raise Myexception(str(res[0]), str(res[1])) + raise res[0] + else: + return res[0] + +def GetAFileName(): + oFileDialog=None + iAccept=None + sPath="" + InitPath="" + oUcb=None + oFileDialog = createUnoService("com.sun.star.ui.dialogs.FilePicker") + oUcb = createUnoService("com.sun.star.ucb.SimpleFileAccess") + oFileDialog.appendFilter("OpenERP Report File","*.sxw") + oFileDialog.setCurrentFilter("OpenERP Report File") + if InitPath == "": + InitPath =tempfile.gettempdir() + #End If + if oUcb.exists(InitPath): + oFileDialog.setDisplayDirectory(InitPath) + #End If + iAccept = oFileDialog.execute() + if iAccept == 1: + sPath = oFileDialog.Files[0] + oFileDialog.dispose() + return sPath + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/lib/gui.py b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/gui.py new file mode 100644 index 00000000000..0e41e34f32b --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/gui.py @@ -0,0 +1,758 @@ +import uno +import unohelper +if __name__<>"package": + from actions import * + + +#------------------------------------------------------------ +# Uno ServiceManager access +# A different version of this routine and global variable +# is needed for code running inside a component. +#------------------------------------------------------------ + + +# The ServiceManager of the running OOo. +# It is cached in a global variable. +goServiceManager = False +def getServiceManager( cHost="localhost", cPort="2002" ): + """Get the ServiceManager from the running OpenOffice.org. + Then retain it in the global variable goServiceManager for future use. + This is similar to the GetProcessServiceManager() in OOo Basic. + """ + global goServiceManager + if not goServiceManager: + # Get the uno component context from the PyUNO runtime + oLocalContext = uno.getComponentContext() + # Create the UnoUrlResolver on the Python side. + + # Connect to the running OpenOffice.org and get its context. + if __name__<>"package": + oLocalResolver = oLocalContext.ServiceManager.createInstanceWithContext( + "com.sun.star.bridge.UnoUrlResolver", oLocalContext ) + oContext = oLocalResolver.resolve( "uno:socket,host=" + cHost + ",port=" + cPort + ";urp;StarOffice.ComponentContext" ) + # Get the ServiceManager object + goServiceManager = oContext.ServiceManager + else: + goServiceManager=oLocalContext.ServiceManager + + return goServiceManager + + +#------------------------------------------------------------ +# Uno convenience functions +# The stuff in this section is just to make +# python progrmaming of OOo more like using OOo Basic. +#------------------------------------------------------------ + + +# This is the same as ServiceManager.createInstance( ... ) +def createUnoService( cClass ): + """A handy way to create a global objects within the running OOo. + Similar to the function of the same name in OOo Basic. + """ + oServiceManager = getServiceManager() + oObj = oServiceManager.createInstance( cClass ) + return oObj + + +# The StarDesktop object. (global like in OOo Basic) +# It is cached in a global variable. +StarDesktop = None +def getDesktop(): + """An easy way to obtain the Desktop object from a running OOo. + """ + global StarDesktop + if StarDesktop == None: + StarDesktop = createUnoService( "com.sun.star.frame.Desktop" ) + return StarDesktop +# preload the StarDesktop variable. +#getDesktop() + + +# The CoreReflection object. +# It is cached in a global variable. +goCoreReflection = False +def getCoreReflection(): + global goCoreReflection + if not goCoreReflection: + goCoreReflection = createUnoService( "com.sun.star.reflection.CoreReflection" ) + return goCoreReflection + + +def createUnoStruct( cTypeName ): + """Create a UNO struct and return it. + Similar to the function of the same name in OOo Basic. + """ + oCoreReflection = getCoreReflection() + # Get the IDL class for the type name + oXIdlClass = oCoreReflection.forName( cTypeName ) + # Create the struct. + oReturnValue, oStruct = oXIdlClass.createObject( None ) + return oStruct + +#------------------------------------------------------------ +# API helpers +#------------------------------------------------------------ + +def hasUnoInterface( oObject, cInterfaceName ): + """Similar to Basic's HasUnoInterfaces() function, but singular not plural.""" + + # Get the Introspection service. + oIntrospection = createUnoService( "com.sun.star.beans.Introspection" ) + + # Now inspect the object to learn about it. + oObjInfo = oIntrospection.inspect( oObject ) + + # Obtain an array describing all methods of the object. + oMethods = oObjInfo.getMethods( uno.getConstantByName( "com.sun.star.beans.MethodConcept.ALL" ) ) + # Now look at every method. + for oMethod in oMethods: + # Check the method's interface to see if + # these aren't the droids you're looking for. + cMethodInterfaceName = oMethod.getDeclaringClass().getName() + if cMethodInterfaceName == cInterfaceName: + return True + return False + +def hasUnoInterfaces( oObject, *cInterfaces ): + """Similar to the function of the same name in OOo Basic.""" + for cInterface in cInterfaces: + if not hasUnoInterface( oObject, cInterface ): + return False + return True + +#------------------------------------------------------------ +# High level general purpose functions +#------------------------------------------------------------ + + +def makePropertyValue( cName=None, uValue=None, nHandle=None, nState=None ): + """Create a com.sun.star.beans.PropertyValue struct and return it. + """ + oPropertyValue = createUnoStruct( "com.sun.star.beans.PropertyValue" ) + + if cName != None: + oPropertyValue.Name = cName + if uValue != None: + oPropertyValue.Value = uValue + if nHandle != None: + oPropertyValue.Handle = nHandle + if nState != None: + oPropertyValue.State = nState + + return oPropertyValue + + +def makePoint( nX, nY ): + """Create a com.sun.star.awt.Point struct.""" + oPoint = createUnoStruct( "com.sun.star.awt.Point" ) + oPoint.X = nX + oPoint.Y = nY + return oPoint + + +def makeSize( nWidth, nHeight ): + """Create a com.sun.star.awt.Size struct.""" + oSize = createUnoStruct( "com.sun.star.awt.Size" ) + oSize.Width = nWidth + oSize.Height = nHeight + return oSize + + +def makeRectangle( nX, nY, nWidth, nHeight ): + """Create a com.sun.star.awt.Rectangle struct.""" + oRect = createUnoStruct( "com.sun.star.awt.Rectangle" ) + oRect.X = nX + oRect.Y = nY + oRect.Width = nWidth + oRect.Height = nHeight + return oRect + + +def Array( *args ): + """This is just sugar coating so that code from OOoBasic which + contains the Array() function can work perfectly in python.""" + tArray = () + for arg in args: + tArray += (arg,) + return tArray + + +def loadComponentFromURL( cUrl, tProperties=() ): + """Open or Create a document from it's URL. + New documents are created from URL's such as: + private:factory/sdraw + private:factory/swriter + private:factory/scalc + private:factory/simpress + """ + StarDesktop = getDesktop() + oDocument = StarDesktop.loadComponentFromURL( cUrl, "_blank", 0, tProperties ) + return oDocument + + +#------------------------------------------------------------ +# Styles +#------------------------------------------------------------ + + +def defineStyle( oDrawDoc, cStyleFamily, cStyleName, cParentStyleName=None ): + """Add a new style to the style catalog if it is not already present. + This returns the style object so that you can alter its properties. + """ + + oStyleFamily = oDrawDoc.getStyleFamilies().getByName( cStyleFamily ) + + # Does the style already exist? + if oStyleFamily.hasByName( cStyleName ): + # then get it so we can return it. + oStyle = oStyleFamily.getByName( cStyleName ) + else: + # Create new style object. + oStyle = oDrawDoc.createInstance( "com.sun.star.style.Style" ) + + # Set its parent style + if cParentStyleName != None: + oStyle.setParentStyle( cParentStyleName ) + + # Add the new style to the style family. + oStyleFamily.insertByName( cStyleName, oStyle ) + + return oStyle + + +def getStyle( oDrawDoc, cStyleFamily, cStyleName ): + """Lookup and return a style from the document. + """ + return oDrawDoc.getStyleFamilies().getByName( cStyleFamily ).getByName( cStyleName ) + +#------------------------------------------------------------ +# General Utility functions +#------------------------------------------------------------ + + +def convertToURL( cPathname ): + """Convert a Windows or Linux pathname into an OOo URL.""" + if len( cPathname ) > 1: + if cPathname[1:2] == ":": + cPathname = "/" + cPathname[0] + "|" + cPathname[2:] + cPathname = cPathname.replace( "\\", "/" ) + cPathname = "file://" + cPathname + return cPathname + +# The global Awt Toolkit. +# This is initialized the first time it is needed. +#goAwtToolkit = createUnoService( "com.sun.star.awt.Toolkit" ) +goAwtToolkit = None + +def getAwtToolkit(): + global goAwtToolkit + if goAwtToolkit == None: + goAwtToolkit = createUnoService( "com.sun.star.awt.Toolkit" ) + return goAwtToolkit + +# This class builds dialog boxes. +# This can be used in two different ways... +# 1. by subclassing it (elegant) +# 2. without subclassing it (less elegant) +class DBModalDialog: + """Class to build a dialog box from the com.sun.star.awt.* services. + This doesn't do anything you couldn't already do using OOo's UNO API, + this just makes it much easier. + You can change the dialog box size, position, title, etc. + You can add controls, and listeners for those controls to the dialog box. + This class can be used by subclassing it, or without subclassing it. + """ + def __init__( self, nPositionX=None, nPositionY=None, nWidth=None, nHeight=None, cTitle=None ): + self.oDialogModel = createUnoService( "com.sun.star.awt.UnoControlDialogModel" ) + if nPositionX != None: self.oDialogModel.PositionX = nPositionX + if nPositionY != None: self.oDialogModel.PositionY = nPositionY + if nWidth != None: self.oDialogModel.Width = nWidth + if nHeight != None: self.oDialogModel.Height = nHeight + if cTitle != None: self.oDialogModel.Title = cTitle + self.oDialogControl = createUnoService( "com.sun.star.awt.UnoControlDialog" ) + self.oDialogControl.setModel( self.oDialogModel ) + + def release( self ): + """Release resources. + After calling this, you can no longer use this object. + """ + self.oDialogControl.dispose() + + #-------------------------------------------------- + # Dialog box adjustments + #-------------------------------------------------- + + def setDialogPosition( self, nX, nY ): + self.oDialogModel.PositionX = nX + self.oDialogModel.PositionY = nY + + def setDialogSize( self, nWidth, nHeight ): + self.oDialogModel.Width = nWidth + self.oDialogModel.Height = nHeight + + def setDialogTitle( self, cCaption ): + self.oDialogModel.Title = cCaption + + def setVisible( self, bVisible ): + self.oDialogControl.setVisible( bVisible ) + + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlButton + #-------------------------------------------------- + + # After you add a Button control, you can call self.setControlModelProperty() + # passing any of the properties for a... + # com.sun.star.awt.UnoControlButtonModel + # com.sun.star.awt.UnoControlDialogElement + # com.sun.star.awt.UnoControlModel + def addButton( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None, + actionListenerProc=None, + nTabIndex=None ): + self.addControl( "com.sun.star.awt.UnoControlButtonModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None, + cLabel=cLabel, + nTabIndex=nTabIndex ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + def setButtonLabel( self, cCtrlName, cLabel ): + """Set the label of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setLabel( cLabel ) + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlEditModel + #-------------------------------------------------- + def addEdit( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cText=None, + textListenerProc=None ): + """Add a Edit control to the window.""" + self.addControl( "com.sun.star.awt.UnoControlEditModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None) + + if cText != None: + self.setEditText( cCtrlName, cText ) + if textListenerProc != None: + self.addTextListenerProc( cCtrlName, textListenerProc ) + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlCheckBox + #-------------------------------------------------- + + # After you add a CheckBox control, you can call self.setControlModelProperty() + # passing any of the properties for a... + # com.sun.star.awt.UnoControlCheckBoxModel + # com.sun.star.awt.UnoControlDialogElement + # com.sun.star.awt.UnoControlModel + def addCheckBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None, + itemListenerProc=None, + nTabIndex=None ): + self.addControl( "com.sun.star.awt.UnoControlCheckBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None, + cLabel=cLabel, + nTabIndex=nTabIndex ) + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + + def setEditText( self, cCtrlName, cText ): + """Set the text of the edit box.""" + oControl = self.getControl( cCtrlName ) + oControl.setText( cText ) + + def getEditText( self, cCtrlName): + """Set the text of the edit box.""" + oControl = self.getControl( cCtrlName ) + return oControl.getText() + + def setCheckBoxLabel( self, cCtrlName, cLabel ): + """Set the label of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setLabel( cLabel ) + + def getCheckBoxState( self, cCtrlName ): + """Get the state of the control.""" + oControl = self.getControl( cCtrlName ) + return oControl.getState(); + + def setCheckBoxState( self, cCtrlName, nState ): + """Set the state of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setState( nState ) + + def enableCheckBoxTriState( self, cCtrlName, bTriStateEnable ): + """Enable or disable the tri state mode of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.enableTriState( bTriStateEnable ) + + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlFixedText + #-------------------------------------------------- + + def addFixedText( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None ): + self.addControl( "com.sun.star.awt.UnoControlFixedTextModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=None, + cLabel=cLabel ) + + return self.getControl( cCtrlName ) + + #-------------------------------------------------- + # Add Controls to dialog + #-------------------------------------------------- + + def addControl( self, cCtrlServiceName, + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=None, + cLabel=None, + nTabIndex=None, + sImagePath=None, + ): + oControlModel = self.oDialogModel.createInstance( cCtrlServiceName ) + self.oDialogModel.insertByName( cCtrlName, oControlModel ) + + # if negative coordinates are given for X or Y position, + # then make that coordinate be relative to the right/bottom + # edge of the dialog box instead of to the left/top. + if nPositionX < 0: nPositionX = self.oDialogModel.Width + nPositionX - nWidth + if nPositionY < 0: nPositionY = self.oDialogModel.Height + nPositionY - nHeight + oControlModel.PositionX = nPositionX + oControlModel.PositionY = nPositionY + oControlModel.Width = nWidth + oControlModel.Height = nHeight + oControlModel.Name = cCtrlName + + if bDropdown != None: + oControlModel.Dropdown = bDropdown + + if cLabel != None: + oControlModel.Label = cLabel + + if nTabIndex != None: + oControlModel.TabIndex = nTabIndex + + if sImagePath != None: + oControlModel.ImageURL = sImagePath + #-------------------------------------------------- + # Access controls and control models + #-------------------------------------------------- + + #-------------------------------------------------- + # com.sun.star.awt.UnoContorlListBoxModel + #-------------------------------------------------- + + + def addComboListBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=True, + itemListenerProc=None, + actionListenerProc=None ): + mod = self.addControl( "com.sun.star.awt.UnoControlListBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight,bDropdown) + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + + def addListBoxItems( self, cCtrlName, tcItemTexts, nPosition=0 ): + """Add a tupple of items to the ListBox at specified position.""" + oControl = self.getControl( cCtrlName ) + oControl.addItems( tcItemTexts, nPosition ) + + def selectListBoxItem( self, cCtrlName, cItemText, bSelect=True ): + """Selects/Deselects the ispecified item.""" + oControl = self.getControl( cCtrlName ) + return oControl.selectItem( cItemText, bSelect ) + + def selectListBoxItemPos( self, cCtrlName, nItemPos, bSelect=True ): + """Select/Deselect the item at the specified position.""" + oControl = self.getControl( cCtrlName ) + return oControl.selectItemPos( nItemPos, bSelect ) + + def removeListBoxItems( self, cCtrlName, nPosition, nCount=1 ): + """Remove items from a ListBox.""" + oControl = self.getControl( cCtrlName ) + oControl.removeItems( nPosition, nCount ) + + def getListBoxItemCount( self, cCtrlName ): + """Get the number of items in a ListBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getItemCount() + + def getListBoxSelectedItem( self, cCtrlName ): + """Returns the currently selected item.""" + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItem() + + def getListBoxItem( self, cCtrlName, nPosition ): + """Return the item at specified position within the ListBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getItem( nPosition ) + + def getListBoxSelectedItemPos(self,cCtrlName): + + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItemPos() + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlComboBoxModel + #-------------------------------------------------- + def addComboBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=True, + itemListenerProc=None, + actionListenerProc=None ): + + mod = self.addControl( "com.sun.star.awt.UnoControlComboBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight,bDropdown) + + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + + def setComboBoxText( self, cCtrlName, cText ): + """Set the text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + oControl.setText( cText ) + + def getComboBoxText( self, cCtrlName): + """Set the text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getText() + + def getComboBoxSelectedText( self, cCtrlName ): + """Get the selected text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedText(); + + def getControl( self, cCtrlName ): + """Get the control (not its model) for a particular control name. + The control returned includes the service com.sun.star.awt.UnoControl, + and another control-specific service which inherits from it. + """ + oControl = self.oDialogControl.getControl( cCtrlName ) + return oControl + + def getControlModel( self, cCtrlName ): + """Get the control model (not the control) for a particular control name. + The model returned includes the service UnoControlModel, + and another control-specific service which inherits from it. + """ + oControl = self.getControl( cCtrlName ) + oControlModel = oControl.getModel() + return oControlModel + #--------------------------------------------------- + # com.sun.star.awt.UnoControlImageControlModel + #--------------------------------------------------- + def addImageControl( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + sImagePath="", + itemListenerProc=None, + actionListenerProc=None ): + + mod = self.addControl( "com.sun.star.awt.UnoControlImageControlModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, sImagePath=sImagePath) + + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + + #-------------------------------------------------- + # Adjust properties of control models + #-------------------------------------------------- + + def setControlModelProperty( self, cCtrlName, cPropertyName, uValue ): + """Set the value of a property of a control's model. + This affects the control model, not the control. + """ + oControlModel = self.getControlModel( cCtrlName ) + oControlModel.setPropertyValue( cPropertyName, uValue ) + + def getControlModelProperty( self, cCtrlName, cPropertyName ): + """Get the value of a property of a control's model. + This affects the control model, not the control. + """ + oControlModel = self.getControlModel( cCtrlName ) + return oControlModel.getPropertyValue( cPropertyName ) + + #-------------------------------------------------- + # Sugar coated property adjustments to control models. + #-------------------------------------------------- + + def setEnabled( self, cCtrlName, bEnabled=True ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "Enabled", bEnabled ) + + def getEnabled( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + + return self.getControlModelProperty( cCtrlName, "Enabled" ) + + def setState( self, cCtrlName, nState ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "State", nState ) + + def getState( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "State" ) + + def setLabel( self, cCtrlName, cLabel ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "Label", cLabel ) + + def getLabel( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "Label" ) + + def setHelpText( self, cCtrlName, cHelpText ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "HelpText", cHelpText ) + + def getHelpText( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "HelpText" ) + + + #-------------------------------------------------- + # Adjust controls (not models) + #-------------------------------------------------- + + # The following apply to all controls which are a + # com.sun.star.awt.UnoControl + + def setDesignMode( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + oControl.setDesignMode( bDesignMode ) + + def isDesignMode( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + return oControl.isDesignMode() + + def isTransparent( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + return oControl.isTransparent() + + + # The following apply to all controls which are a + # com.sun.star.awt.UnoControlDialogElement + + def setPosition( self, cCtrlName, nPositionX, nPositionY ): + self.setControlModelProperty( cCtrlName, "PositionX", nPositionX ) + self.setControlModelProperty( cCtrlName, "PositionY", nPositionY ) + def setPositionX( self, cCtrlName, nPositionX ): + self.setControlModelProperty( cCtrlName, "PositionX", nPositionX ) + def setPositionY( self, cCtrlName, nPositionY ): + self.setControlModelProperty( cCtrlName, "PositionY", nPositionY ) + def getPositionX( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "PositionX" ) + def getPositionY( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "PositionY" ) + + def setSize( self, cCtrlName, nWidth, nHeight ): + self.setControlModelProperty( cCtrlName, "Width", nWidth ) + self.setControlModelProperty( cCtrlName, "Height", nHeight ) + def setWidth( self, cCtrlName, nWidth ): + self.setControlModelProperty( cCtrlName, "Width", nWidth ) + def setHeight( self, cCtrlName, nHeight ): + self.setControlModelProperty( cCtrlName, "Height", nHeight ) + def getWidth( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Width" ) + def getHeight( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Height" ) + + def setTabIndex( self, cCtrlName, nWidth, nTabIndex ): + self.setControlModelProperty( cCtrlName, "TabIndex", nTabIndex ) + def getTabIndex( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "TabIndex" ) + + def setStep( self, cCtrlName, nWidth, nStep ): + self.setControlModelProperty( cCtrlName, "Step", nStep ) + def getStep( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Step" ) + + def setTag( self, cCtrlName, nWidth, cTag ): + self.setControlModelProperty( cCtrlName, "Tag", cTag ) + def getTag( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Tag" ) + + def setEchoChar(self, cCtrlName , cVal): + self.setControlModelProperty(cCtrlName, "EchoChar", cVal) + def getEchoChar(self, cCtrlName): + return self.setControlModelProperty(cCtrlName, "EchoChar") + + #-------------------------------------------------- + # Add listeners to controls. + #-------------------------------------------------- + + # This applies to... + # UnoControlButton + def addActionListenerProc( self, cCtrlName, actionListenerProc ): + """Create an com.sun.star.awt.XActionListener object and add it to a control. + A listener object is created which will call the python procedure actionListenerProc. + The actionListenerProc can be either a method or a global procedure. + The following controls support XActionListener: + UnoControlButton + """ + oControl = self.getControl( cCtrlName ) + oActionListener = ActionListenerProcAdapter( actionListenerProc ) + oControl.addActionListener( oActionListener ) + + # This applies to... + # UnoControlCheckBox + def addItemListenerProc( self, cCtrlName, itemListenerProc ): + """Create an com.sun.star.awt.XItemListener object and add it to a control. + A listener object is created which will call the python procedure itemListenerProc. + The itemListenerProc can be either a method or a global procedure. + The following controls support XActionListener: + UnoControlCheckBox + """ + oControl = self.getControl( cCtrlName ) + oActionListener = ItemListenerProcAdapter( itemListenerProc ) + oControl.addItemListener( oActionListener ) + + #-------------------------------------------------- + # Display the modal dialog. + #-------------------------------------------------- + + def doModalDialog( self, sObjName,sValue): + """Display the dialog as a modal dialog.""" + self.oDialogControl.setVisible( True ) + if not sValue==None: + self.selectListBoxItem( sObjName, sValue, True ) + self.oDialogControl.execute() + + def endExecute( self ): + """Call this from within one of the listeners to end the modal dialog. + For instance, the listener on your OK or Cancel button would call this to end the dialog. + """ + self.oDialogControl.endExecute() + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report/script/lib/tools.py b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/tools.py new file mode 100644 index 00000000000..09e7f96c46e --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report/script/lib/tools.py @@ -0,0 +1,19 @@ +import urllib + +def get_absolute_file_path( url ): + url_unquoted = urllib.unquote(url) + return os.name == 'nt' and url_unquoted[1:] or url_unquoted + +# This function reads the content of a file and return it to the caller +def read_data_from_file( filename ): + fp = file( filename, "rb" ) + data = fp.read() + fp.close() + return data + +# This function writes the content to a file +def write_data_to_file( filename, data ): + fp = file( filename, 'wb' ) + fp.write( data ) + fp.close() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/Makefile b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/Makefile new file mode 100755 index 00000000000..9f6f50fe577 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/Makefile @@ -0,0 +1,42 @@ +ZIP_FILE=openerp_report_designer.zip + +all: pack uninstall install + +create: + cat script/import \ + script/lib/actions.py \ + script/lib/error.py \ + script/lib/functions.py \ + script/lib/gui.py \ + script/lib/tools.py \ + script/lib/logreport.py\ + script/lib/rpc.py\ + script/lib/tiny_socket.py\ + script/Repeatln.py \ + script/Fields.py \ + script/Expression.py \ + script/modify.py \ + script/ServerParameter.py \ + script/Change.py \ + script/NewReport.py \ + script/LoginTest.py \ + script/ModifyExistingReport.py \ + script/SendToServer.py \ + script/About.py \ + script/ConvertBracesToField.py \ + script/ConvertFieldsToBraces.py \ + script/ExportToRML.py \ + script/Translation.py \ + script/AddAttachment.py > package/OpenERPReport.py + +pack: clean create + zip $(ZIP_FILE) `find package/ | grep -v svn` + +install: + sh /opt/openoffice.org3/program/unopkg add -v $(ZIP_FILE) + +uninstall: + sh /opt/openoffice.org3/program/unopkg remove -v $(ZIP_FILE) + +clean: + rm -f $(ZIP_FILE) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/OOo_run.sh b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/OOo_run.sh new file mode 100755 index 00000000000..a22ff96cf53 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/OOo_run.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ooffice "-accept=socket,host=localhost,port=2002;urp;" diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/Addons.xcu b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/Addons.xcu new file mode 100644 index 00000000000..8c840fd6620 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/Addons.xcu @@ -0,0 +1,636 @@ + + + + + + + + Open ERP ~Report + Open ERP Rapport + + + _self + + + + + + + + service:org.openoffice.openerp.report.serverparam?execute + + + + Server parameters + ParamĆØtres serveur + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.repeatln?execute + + + + Add a loop + Ajouter une boucle + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.fields?execute + + + + Add a field + Ajouter un champ + + + _self + + + com.sun.star.text.TextDocument + + + + + + service:org.openoffice.openerp.report.expression?execute + + + + Add an expression + Ajouter une expression + + + _self + + + com.sun.star.text.TextDocument + + + + + + service:org.openoffice.openerp.report.langtag?execute + + + + Add a lang tag + Ajouter un tag lang + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.modify?execute + + + + Modify + Modifier + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.convertFB?execute + + + + Conversion Fields -> Brackets + Conversion champs -> crochets + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.convertBF?execute + + + + Conversion Brackets -> Fields + Conversion crochets -> champs + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.exporttorml?execute + + + + Export to RML + Exporter en RML + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.addattachment?execute + + + + Add Attachment + Envoyer comme attachement + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.sendtoserver?execute + + + + Send to the server + Envoyer au serveur + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.opennewreport?execute + + + + Open a new report + Nouveau rapport + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.modifyreport?execute + + + + Modify Existing Report + Modifier un rapport + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.about?execute + + + + About + ƀ propos de + + + _self + + + com.sun.star.text.TextDocument + + + + + + + + + + + service:org.openoffice.openerp.report.serverparam?execute + + + + + + + Server parameters + ParamĆØtres serveur + + + _self + + + com.sun.star.text.TextDocument + + + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.repeatln?execute + + + + + + + Add a loop + Ajouter une boucle + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.fields?execute + + + + + + + Add a field + + + _self + + + com.sun.star.text.TextDocument + + + + + + service:org.openoffice.openerp.report.expression?execute + + + + + + + Add an expression + Ajouter une expression> + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.langtag?execute + + + + + + + Add a lang tag + Ajouter un tag lang + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.modify?execute + + + + + + + Modify + Modifier + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.convertFB?execute + + + + + + + Conversion Fields -> Brackets + Domaines de conversion -> Supports + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.convertBF?execute + + + + + + + Conversion Brackets -> Fields + Supports de conversion -> Domaines + + + _self + + + com.sun.star.text.TextDocument + + + + + service:org.openoffice.openerp.report.exporttorml?execute + + + + + + + Export to RML + RML Ć  l'exportation + + + _self + + + com.sun.star.text.TextDocument + + + + + com.sun.star.text.TextDocument + + + private:separator + + + + + service:org.openoffice.openerp.report.about?execute + + + + + + + About + ƀ propos de + + + _self + + + com.sun.star.text.TextDocument + + + + + + + + service:org.openoffice.openerp.report.expression?execute + + + + %origin%/images/expression_s.png + + + %origin%/images/expression_b.png + + + + + + service:org.openoffice.openerp.report.repeatln?execute + + + + %origin%/images/loop_s.png + + + %origin%/images/loop_b.png + + + + + + service:org.openoffice.openerp.report.fields?execute + + + + %origin%/images/field_s.png + + + %origin%/images/field_b.png + + + + + + service:org.openoffice.openerp.report.exporttorml?execute + + + + %origin%/images/export_rml_s.png + + + %origin%/images/export_rml_b.png + + + + + + service:org.openoffice.openerp.report.convertFB?execute + + + + %origin%/images/convetFtoB_s.png + + + %origin%/images/convetFtoB_b.png + + + + + + service:org.openoffice.openerp.report.convertBF?execute + + + + %origin%/images/convetBtoF_s.png + + + %origin%/images/convetBtoF_b.png + + + + + + service:org.openoffice.openerp.report.modify?execute + + + + %origin%/images/modify_s.png + + + %origin%/images/modify_b.png + + + + + + service:org.openoffice.openerp.report.serverparam?execute + + + + %origin%/images/logo_1.png + + + %origin%/images/logo_s.png + + + + + + service:org.openoffice.openerp.report.about?execute + + + + %origin%/images/aboutus_s.png + + + %origin%/images/aboutus_b.png + + + + + + service:org.openoffice.openerp.report.langtag?execute + + + + %origin%/images/lang_s.png + + + %origin%/images/lang_b.png + + + + + + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/OpenERPReport.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/OpenERPReport.py new file mode 100644 index 00000000000..d6e32b51df7 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/package/OpenERPReport.py @@ -0,0 +1,3609 @@ +__name__="package" + +import uno +import unohelper +import os +#-------------------------------------------------- +# An ActionListener adapter. +# This object implements com.sun.star.awt.XActionListener. +# When actionPerformed is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oActionEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +if __name__<>"package": + os.system( "ooffice '-accept=socket,host=localhost,port=2002;urp;'" ) +passwd="" +database="" +uid="" +loginstatus=False +from com.sun.star.awt import XActionListener +class ActionListenerProcAdapter( unohelper.Base, XActionListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oActionEvent is a com.sun.star.awt.ActionEvent struct. + def actionPerformed( self, oActionEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oActionEvent,) + self.tParams ) + +#-------------------------------------------------- +# An ItemListener adapter. +# This object implements com.sun.star.awt.XItemListener. +# When itemStateChanged is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oItemEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +from com.sun.star.awt import XItemListener +class ItemListenerProcAdapter( unohelper.Base, XItemListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oItemEvent is a com.sun.star.awt.ItemEvent struct. + def itemStateChanged( self, oItemEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oItemEvent,) + self.tParams ) + +#-------------------------------------------------- +# An TextListener adapter. +# This object implements com.sun.star.awt.XTextistener. +# When textChanged is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oTextEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +from com.sun.star.awt import XTextListener +class TextListenerProcAdapter( unohelper.Base, XTextListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oTextEvent is a com.sun.star.awt.TextEvent struct. + def textChanged( self, oTextEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oTextEvent,) + self.tParams ) + + +if __name__<>"package": + from gui import * +class ErrorDialog: + def __init__(self,sErrorMsg, sErrorHelpMsg="",sTitle="Error Message"): + self.win = DBModalDialog(50, 50, 150, 90, sTitle) + self.win.addFixedText("lblErrMsg", 5, 5, 190, 25, sErrorMsg) + self.win.addFixedText("lblErrHelpMsg", 5, 30, 190, 25, sErrorHelpMsg) + self.win.addButton('btnOK', 55,-5,40,15,'Ok' + ,actionListenerProc = self.btnOkOrCancel_clicked ) + self.win.doModalDialog("",None) + def btnOkOrCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +import uno +import xmlrpclib +import re +import socket +import cPickle +import marshal +import tempfile +if __name__<>"package": + from gui import * + from logreport import * + from rpc import * + database="test" + uid = 1 + +def genTree(object,aList,insField,host,level=3, ending=[], ending_excl=[], recur=[], root='', actualroot=""): + try: + global url + sock=RPCSession(url) + global passwd + res = sock.execute(database, uid, passwd, object , 'fields_get') + key = res.keys() + key.sort() + for k in key: + if (not ending or res[k]['type'] in ending) and ((not ending_excl) or not (res[k]['type'] in ending_excl)): + insField.addItem(root+'/'+res[k]["string"],len(aList)) + aList.append(actualroot+'/'+k) + if (res[k]['type'] in recur) and (level>0): + genTree(res[k]['relation'],aList,insField,host ,level-1, ending, ending_excl, recur,root+'/'+res[k]["string"],actualroot+'/'+k) + except: + obj=Logger() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + obj.log_write('Function', LOG_ERROR, info) + +def VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList,sTableName=""): + if sTableName.find(".") != -1: + for i in range(len(aItemList)): + if aComponentAdd[i]==sTableName: + sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")] + for j in range(len(aObjectList)): + if aObjectList[j][:aObjectList[j].find("(")] == sLVal: + insVariable.append(aObjectList[j]) + VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList, sTableName[:sTableName.rfind(".")]) + else: + for i in range(len(aItemList)): + if aComponentAdd[i]==sTableName: + sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")] + for j in range(len(aObjectList)): + if aObjectList[j][:aObjectList[j].find("(")] == sLVal and sLVal!="": + insVariable.append(aObjectList[j]) + +def getList(aObjectList,host,count): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sMain="" + if not count == 0: + if count >= 1: + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sItem[sItem.find("(")+1:sItem.find(",")]=="objects": + sMain = sItem[sItem.find(",'")+2:sItem.find("')")] + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn": + if sItem[sItem.find("(")+1:sItem.find(",")]=="objects": + aObjectList.append(sItem[sItem.rfind(",'")+2:sItem.rfind("')")] + "(" + docinfo.getUserFieldValue(3) + ")") + else: + sTemp=sItem[sItem.find("(")+1:sItem.find(",")] + if sMain == sTemp[:sTemp.find(".")]: + getRelation(docinfo.getUserFieldValue(3), sItem[sItem.find(".")+1:sItem.find(",")], sItem[sItem.find(",'")+2:sItem.find("')")],aObjectList,host) + else: + sPath=getPath(sItem[sItem.find("(")+1:sItem.find(",")], sMain) + getRelation(docinfo.getUserFieldValue(3), sPath, sItem[sItem.find(",'")+2:sItem.find("')")],aObjectList,host) + else: + aObjectList.append("List of " + docinfo.getUserFieldValue(3)) + +def getRelation(sRelName, sItem, sObjName, aObjectList, host ): + global url + sock=RPCSession(url) + global passwd + res = sock.execute(database, uid, passwd, sRelName , 'fields_get') + key = res.keys() + for k in key: + if sItem.find(".") == -1: + if k == sItem: + aObjectList.append(sObjName + "(" + res[k]['relation'] + ")") + return 0 + if k == sItem[:sItem.find(".")]: + getRelation(res[k]['relation'], sItem[sItem.find(".")+1:], sObjName,aObjectList,host) + + +def getPath(sPath,sMain): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sPath[:sPath.find(".")] == sMain: + break; + else: + res = re.findall('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]',sPath) + if len(res) <> 0: + if sItem[sItem.find(",'")+2:sItem.find("')")] == sPath[:sPath.find(".")]: + sPath = sItem[sItem.find("(")+1:sItem.find(",")] + sPath[sPath.find("."):] + getPath(sPath, sMain) + return sPath + +def EnumDocument(aItemList,aComponentAdd): + desktop = getDesktop() + parent="" + bFlag = False + Doc =desktop.getCurrentComponent() + #oVC = Doc.CurrentController.getViewCursor() + oParEnum = Doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.Anchor.TextTable: + #parent = oPar.Anchor.TextTable.Name + getChildTable(oPar.Anchor.TextTable,aItemList,aComponentAdd) + elif oPar.Anchor.TextSection: + parent = oPar.Anchor.TextSection.Name + elif oPar.Anchor.Text: + parent = "Document" + sItem=oPar.Items[1].replace(' ',"") + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn" and not oPar.Items in aItemList: + templist=oPar.Items[0],sItem + aItemList.append( templist ) + aComponentAdd.append( parent ) + +def getChildTable(oPar,aItemList,aComponentAdd,sTableName=""): + sNames = oPar.getCellNames() + bEmptyTableFlag=True + for val in sNames: + oCell = oPar.getCellByName(val) + oCurEnum = oCell.createEnumeration() + while oCurEnum.hasMoreElements(): + try: + oCur = oCurEnum.nextElement() + + if oCur.supportsService("com.sun.star.text.TextTable"): + if sTableName=="": + getChildTable(oCur,aItemList,aComponentAdd,oPar.Name) + else: + getChildTable(oCur,aItemList,aComponentAdd,sTableName+"."+oPar.Name) + else: + oSecEnum = oCur.createEnumeration() + while oSecEnum.hasMoreElements(): + oSubSection = oSecEnum.nextElement() + if oSubSection.supportsService("com.sun.star.text.TextField"): + bEmptyTableFlag=False + sItem=oSubSection.TextField.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn": + if aItemList.__contains__(oSubSection.TextField.Items)==False: + aItemList.append(oSubSection.TextField.Items) + if sTableName=="": + if aComponentAdd.__contains__(oPar.Name)==False: + aComponentAdd.append(oPar.Name) + else: + if aComponentAdd.__contains__(sTableName+"."+oPar.Name)==False: + aComponentAdd.append(sTableName+"."+oPar.Name) + except: + obj=Logger() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + obj.log_write('Function', LOG_ERROR, info) + if bEmptyTableFlag==True: + aItemList.append((u'',u'')) + if sTableName=="": + if aComponentAdd.__contains__(oPar.Name)==False: + aComponentAdd.append(oPar.Name) + else: + if aComponentAdd.__contains__(sTableName+"."+oPar.Name)==False: + aComponentAdd.append(sTableName+"."+oPar.Name) + return 0 + +def getRecersiveSection(oCurrentSection,aSectionList): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + oParEnum=doc.getText().createEnumeration() + aSectionList.append(oCurrentSection.Name) + if oCurrentSection.ParentSection: + getRecersiveSection(oCurrentSection.ParentSection,aSectionList) + else: + return + +def GetAFileName(): + oFileDialog=None + iAccept=None + sPath="" + InitPath="" + oUcb=None + oFileDialog = createUnoService("com.sun.star.ui.dialogs.FilePicker") + oUcb = createUnoService("com.sun.star.ucb.SimpleFileAccess") + oFileDialog.appendFilter("OpenERP Report File","*.sxw") + oFileDialog.setCurrentFilter("OpenERP Report File") + if InitPath == "": + InitPath =tempfile.gettempdir() + #End If + if oUcb.exists(InitPath): + oFileDialog.setDisplayDirectory(InitPath) + #End If + iAccept = oFileDialog.execute() + if iAccept == 1: + sPath = oFileDialog.Files[0] + oFileDialog.dispose() + return sPath + + +import uno +import unohelper +import pythonloader +if __name__<>"package": + from actions import * + + +#------------------------------------------------------------ +# Uno ServiceManager access +# A different version of this routine and global variable +# is needed for code running inside a component. +#------------------------------------------------------------ + + +# The ServiceManager of the running OOo. +# It is cached in a global variable. +goServiceManager = False +pythonloader.DEBUG = 0 +def getServiceManager( cHost="localhost", cPort="2002" ): + """Get the ServiceManager from the running OpenOffice.org. + Then retain it in the global variable goServiceManager for future use. + This is similar to the GetProcessServiceManager() in OOo Basic. + """ + global goServiceManager + global pythonloader + if not goServiceManager: + # Get the uno component context from the PyUNO runtime + oLocalContext = uno.getComponentContext() + # Create the UnoUrlResolver on the Python side. + + # Connect to the running OpenOffice.org and get its context. + if __name__<>"package": + oLocalResolver = oLocalContext.ServiceManager.createInstanceWithContext( + "com.sun.star.bridge.UnoUrlResolver", oLocalContext ) + oContext = oLocalResolver.resolve( "uno:socket,host=" + cHost + ",port=" + cPort + ";urp;StarOffice.ComponentContext" ) + # Get the ServiceManager object + goServiceManager = oContext.ServiceManager + else: + goServiceManager=oLocalContext.ServiceManager + + return goServiceManager + + +#------------------------------------------------------------ +# Uno convenience functions +# The stuff in this section is just to make +# python progrmaming of OOo more like using OOo Basic. +#------------------------------------------------------------ + + +# This is the same as ServiceManager.createInstance( ... ) +def createUnoService( cClass ): + """A handy way to create a global objects within the running OOo. + Similar to the function of the same name in OOo Basic. + """ + oServiceManager = getServiceManager() + oObj = oServiceManager.createInstance( cClass ) + return oObj + + +# The StarDesktop object. (global like in OOo Basic) +# It is cached in a global variable. +StarDesktop = None +def getDesktop(): + """An easy way to obtain the Desktop object from a running OOo. + """ + global StarDesktop + if StarDesktop == None: + StarDesktop = createUnoService( "com.sun.star.frame.Desktop" ) + return StarDesktop +# preload the StarDesktop variable. +#getDesktop() + + +# The CoreReflection object. +# It is cached in a global variable. +goCoreReflection = False +def getCoreReflection(): + global goCoreReflection + if not goCoreReflection: + goCoreReflection = createUnoService( "com.sun.star.reflection.CoreReflection" ) + return goCoreReflection + + +def createUnoStruct( cTypeName ): + """Create a UNO struct and return it. + Similar to the function of the same name in OOo Basic. + """ + oCoreReflection = getCoreReflection() + # Get the IDL class for the type name + oXIdlClass = oCoreReflection.forName( cTypeName ) + # Create the struct. + oReturnValue, oStruct = oXIdlClass.createObject( None ) + return oStruct + +#------------------------------------------------------------ +# API helpers +#------------------------------------------------------------ + +def hasUnoInterface( oObject, cInterfaceName ): + """Similar to Basic's HasUnoInterfaces() function, but singular not plural.""" + + # Get the Introspection service. + oIntrospection = createUnoService( "com.sun.star.beans.Introspection" ) + + # Now inspect the object to learn about it. + oObjInfo = oIntrospection.inspect( oObject ) + + # Obtain an array describing all methods of the object. + oMethods = oObjInfo.getMethods( uno.getConstantByName( "com.sun.star.beans.MethodConcept.ALL" ) ) + # Now look at every method. + for oMethod in oMethods: + # Check the method's interface to see if + # these aren't the droids you're looking for. + cMethodInterfaceName = oMethod.getDeclaringClass().getName() + if cMethodInterfaceName == cInterfaceName: + return True + return False + +def hasUnoInterfaces( oObject, *cInterfaces ): + """Similar to the function of the same name in OOo Basic.""" + for cInterface in cInterfaces: + if not hasUnoInterface( oObject, cInterface ): + return False + return True + +#------------------------------------------------------------ +# High level general purpose functions +#------------------------------------------------------------ + + +def makePropertyValue( cName=None, uValue=None, nHandle=None, nState=None ): + """Create a com.sun.star.beans.PropertyValue struct and return it. + """ + oPropertyValue = createUnoStruct( "com.sun.star.beans.PropertyValue" ) + + if cName != None: + oPropertyValue.Name = cName + if uValue != None: + oPropertyValue.Value = uValue + if nHandle != None: + oPropertyValue.Handle = nHandle + if nState != None: + oPropertyValue.State = nState + + return oPropertyValue + + +def makePoint( nX, nY ): + """Create a com.sun.star.awt.Point struct.""" + oPoint = createUnoStruct( "com.sun.star.awt.Point" ) + oPoint.X = nX + oPoint.Y = nY + return oPoint + + +def makeSize( nWidth, nHeight ): + """Create a com.sun.star.awt.Size struct.""" + oSize = createUnoStruct( "com.sun.star.awt.Size" ) + oSize.Width = nWidth + oSize.Height = nHeight + return oSize + + +def makeRectangle( nX, nY, nWidth, nHeight ): + """Create a com.sun.star.awt.Rectangle struct.""" + oRect = createUnoStruct( "com.sun.star.awt.Rectangle" ) + oRect.X = nX + oRect.Y = nY + oRect.Width = nWidth + oRect.Height = nHeight + return oRect + + +def Array( *args ): + """This is just sugar coating so that code from OOoBasic which + contains the Array() function can work perfectly in python.""" + tArray = () + for arg in args: + tArray += (arg,) + return tArray + + +def loadComponentFromURL( cUrl, tProperties=() ): + """Open or Create a document from it's URL. + New documents are created from URL's such as: + private:factory/sdraw + private:factory/swriter + private:factory/scalc + private:factory/simpress + """ + StarDesktop = getDesktop() + oDocument = StarDesktop.loadComponentFromURL( cUrl, "_blank", 0, tProperties ) + return oDocument + + +#------------------------------------------------------------ +# Styles +#------------------------------------------------------------ + + +def defineStyle( oDrawDoc, cStyleFamily, cStyleName, cParentStyleName=None ): + """Add a new style to the style catalog if it is not already present. + This returns the style object so that you can alter its properties. + """ + + oStyleFamily = oDrawDoc.getStyleFamilies().getByName( cStyleFamily ) + + # Does the style already exist? + if oStyleFamily.hasByName( cStyleName ): + # then get it so we can return it. + oStyle = oStyleFamily.getByName( cStyleName ) + else: + # Create new style object. + oStyle = oDrawDoc.createInstance( "com.sun.star.style.Style" ) + + # Set its parent style + if cParentStyleName != None: + oStyle.setParentStyle( cParentStyleName ) + + # Add the new style to the style family. + oStyleFamily.insertByName( cStyleName, oStyle ) + + return oStyle + + +def getStyle( oDrawDoc, cStyleFamily, cStyleName ): + """Lookup and return a style from the document. + """ + return oDrawDoc.getStyleFamilies().getByName( cStyleFamily ).getByName( cStyleName ) + +#------------------------------------------------------------ +# General Utility functions +#------------------------------------------------------------ + + +def convertToURL( cPathname ): + """Convert a Windows or Linux pathname into an OOo URL.""" + if len( cPathname ) > 1: + if cPathname[1:2] == ":": + cPathname = "/" + cPathname[0] + "|" + cPathname[2:] + cPathname = cPathname.replace( "\\", "/" ) + cPathname = "file://" + cPathname + return cPathname + +# The global Awt Toolkit. +# This is initialized the first time it is needed. +#goAwtToolkit = createUnoService( "com.sun.star.awt.Toolkit" ) +goAwtToolkit = None + +def getAwtToolkit(): + global goAwtToolkit + if goAwtToolkit == None: + goAwtToolkit = createUnoService( "com.sun.star.awt.Toolkit" ) + return goAwtToolkit + +# This class builds dialog boxes. +# This can be used in two different ways... +# 1. by subclassing it (elegant) +# 2. without subclassing it (less elegant) +class DBModalDialog: + """Class to build a dialog box from the com.sun.star.awt.* services. + This doesn't do anything you couldn't already do using OOo's UNO API, + this just makes it much easier. + You can change the dialog box size, position, title, etc. + You can add controls, and listeners for those controls to the dialog box. + This class can be used by subclassing it, or without subclassing it. + """ + def __init__( self, nPositionX=None, nPositionY=None, nWidth=None, nHeight=None, cTitle=None ): + self.oDialogModel = createUnoService( "com.sun.star.awt.UnoControlDialogModel" ) + if nPositionX != None: self.oDialogModel.PositionX = nPositionX + if nPositionY != None: self.oDialogModel.PositionY = nPositionY + if nWidth != None: self.oDialogModel.Width = nWidth + if nHeight != None: self.oDialogModel.Height = nHeight + if cTitle != None: self.oDialogModel.Title = cTitle + self.oDialogControl = createUnoService( "com.sun.star.awt.UnoControlDialog" ) + self.oDialogControl.setModel( self.oDialogModel ) + + def release( self ): + """Release resources. + After calling this, you can no longer use this object. + """ + self.oDialogControl.dispose() + + #-------------------------------------------------- + # Dialog box adjustments + #-------------------------------------------------- + + def setDialogPosition( self, nX, nY ): + self.oDialogModel.PositionX = nX + self.oDialogModel.PositionY = nY + + def setDialogSize( self, nWidth, nHeight ): + self.oDialogModel.Width = nWidth + self.oDialogModel.Height = nHeight + + def setDialogTitle( self, cCaption ): + self.oDialogModel.Title = cCaption + + def setVisible( self, bVisible ): + self.oDialogControl.setVisible( bVisible ) + + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlButton + #-------------------------------------------------- + + # After you add a Button control, you can call self.setControlModelProperty() + # passing any of the properties for a... + # com.sun.star.awt.UnoControlButtonModel + # com.sun.star.awt.UnoControlDialogElement + # com.sun.star.awt.UnoControlModel + def addButton( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None, + actionListenerProc=None, + nTabIndex=None ): + self.addControl( "com.sun.star.awt.UnoControlButtonModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None, bMultiSelection=None, + cLabel=cLabel, + nTabIndex=nTabIndex ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + def setButtonLabel( self, cCtrlName, cLabel ): + """Set the label of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setLabel( cLabel ) + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlEditModel + #-------------------------------------------------- + def addEdit( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cText=None, + textListenerProc=None ): + """Add a Edit control to the window.""" + self.addControl( "com.sun.star.awt.UnoControlEditModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None) + + if cText != None: + self.setEditText( cCtrlName, cText ) + if textListenerProc != None: + self.addTextListenerProc( cCtrlName, textListenerProc ) + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlCheckBox + #-------------------------------------------------- + + # After you add a CheckBox control, you can call self.setControlModelProperty() + # passing any of the properties for a... + # com.sun.star.awt.UnoControlCheckBoxModel + # com.sun.star.awt.UnoControlDialogElement + # com.sun.star.awt.UnoControlModel + def addCheckBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None, + itemListenerProc=None, + nTabIndex=None ): + self.addControl( "com.sun.star.awt.UnoControlCheckBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None, bMultiSelection=None, + cLabel=cLabel, + nTabIndex=nTabIndex ) + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + + def setEditText( self, cCtrlName, cText ): + """Set the text of the edit box.""" + oControl = self.getControl( cCtrlName ) + oControl.setText( cText ) + + def getEditText( self, cCtrlName): + """Set the text of the edit box.""" + oControl = self.getControl( cCtrlName ) + return oControl.getText() + + def setCheckBoxLabel( self, cCtrlName, cLabel ): + """Set the label of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setLabel( cLabel ) + + def getCheckBoxState( self, cCtrlName ): + """Get the state of the control.""" + oControl = self.getControl( cCtrlName ) + return oControl.getState(); + + def setCheckBoxState( self, cCtrlName, nState ): + """Set the state of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setState( nState ) + + def enableCheckBoxTriState( self, cCtrlName, bTriStateEnable ): + """Enable or disable the tri state mode of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.enableTriState( bTriStateEnable ) + + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlFixedText + #-------------------------------------------------- + + def addFixedText( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None ): + self.addControl( "com.sun.star.awt.UnoControlFixedTextModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=None, bMultiSelection=None, + cLabel=cLabel ) + + return self.getControl( cCtrlName ) + + #-------------------------------------------------- + # Add Controls to dialog + #-------------------------------------------------- + + def addControl( self, cCtrlServiceName, + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=None, + bMultiSelection=None, + cLabel=None, + nTabIndex=None, + sImagePath=None, + ): + oControlModel = self.oDialogModel.createInstance( cCtrlServiceName ) + self.oDialogModel.insertByName( cCtrlName, oControlModel ) + # if negative coordinates are given for X or Y position, + # then make that coordinate be relative to the right/bottom + # edge of the dialog box instead of to the left/top. + if nPositionX < 0: nPositionX = self.oDialogModel.Width + nPositionX - nWidth + if nPositionY < 0: nPositionY = self.oDialogModel.Height + nPositionY - nHeight + oControlModel.PositionX = nPositionX + oControlModel.PositionY = nPositionY + oControlModel.Width = nWidth + oControlModel.Height = nHeight + oControlModel.Name = cCtrlName + + if bDropdown != None: + oControlModel.Dropdown = bDropdown + + if bMultiSelection!=None: + oControlModel.MultiSelection=bMultiSelection + + if cLabel != None: + oControlModel.Label = cLabel + + if nTabIndex != None: + oControlModel.TabIndex = nTabIndex + + if sImagePath != None: + oControlModel.ImageURL = sImagePath + #-------------------------------------------------- + # Access controls and control models + #-------------------------------------------------- + + #-------------------------------------------------- + # com.sun.star.awt.UnoContorlListBoxModel + #-------------------------------------------------- + + + def addComboListBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=True, + bMultiSelection=False, + itemListenerProc=None, + actionListenerProc=None, + ): + + mod = self.addControl( "com.sun.star.awt.UnoControlListBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight,bDropdown,bMultiSelection ) + + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + + def addListBoxItems( self, cCtrlName, tcItemTexts, nPosition=0 ): + """Add a tupple of items to the ListBox at specified position.""" + oControl = self.getControl( cCtrlName ) + oControl.addItems( tcItemTexts, nPosition ) + + def selectListBoxItem( self, cCtrlName, cItemText, bSelect=True ): + """Selects/Deselects the ispecified item.""" + oControl = self.getControl( cCtrlName ) + return oControl.selectItem( cItemText, bSelect ) + + def selectListBoxItemPos( self, cCtrlName, nItemPos, bSelect=True ): + """Select/Deselect the item at the specified position.""" + oControl = self.getControl( cCtrlName ) + return oControl.selectItemPos( nItemPos, bSelect ) + + def removeListBoxItems( self, cCtrlName, nPosition, nCount=1 ): + """Remove items from a ListBox.""" + oControl = self.getControl( cCtrlName ) + oControl.removeItems( nPosition, nCount ) + + def getListBoxItemCount( self, cCtrlName ): + """Get the number of items in a ListBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getItemCount() + + def getListBoxSelectedItem( self, cCtrlName ): + """Returns the currently selected item.""" + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItem() + + def getListBoxItem( self, cCtrlName, nPosition ): + """Return the item at specified position within the ListBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getItem( nPosition ) + + def getListBoxSelectedItemPos(self,cCtrlName): + + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItemPos() + + def getListBoxSelectedItems(self,cCtrlName): + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItems() + + def getListBoxSelectedItemsPos(self,cCtrlName): + + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItemsPos() + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlComboBoxModel + #-------------------------------------------------- + def addComboBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=True, + itemListenerProc=None, + actionListenerProc=None ): + + mod = self.addControl( "com.sun.star.awt.UnoControlComboBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight,bDropdown) + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + + def setComboBoxText( self, cCtrlName, cText ): + """Set the text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + oControl.setText( cText ) + + def getComboBoxText( self, cCtrlName): + """Set the text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getText() + + def getComboBoxSelectedText( self, cCtrlName ): + """Get the selected text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedText(); + + def getControl( self, cCtrlName ): + """Get the control (not its model) for a particular control name. + The control returned includes the service com.sun.star.awt.UnoControl, + and another control-specific service which inherits from it. + """ + oControl = self.oDialogControl.getControl( cCtrlName ) + return oControl + + def getControlModel( self, cCtrlName ): + """Get the control model (not the control) for a particular control name. + The model returned includes the service UnoControlModel, + and another control-specific service which inherits from it. + """ + oControl = self.getControl( cCtrlName ) + oControlModel = oControl.getModel() + return oControlModel + #--------------------------------------------------- + # com.sun.star.awt.UnoControlImageControlModel + #--------------------------------------------------- + def addImageControl( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + sImagePath="", + itemListenerProc=None, + actionListenerProc=None ): + + mod = self.addControl( "com.sun.star.awt.UnoControlImageControlModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, sImagePath=sImagePath) + + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + + #-------------------------------------------------- + # Adjust properties of control models + #-------------------------------------------------- + + def setControlModelProperty( self, cCtrlName, cPropertyName, uValue ): + """Set the value of a property of a control's model. + This affects the control model, not the control. + """ + oControlModel = self.getControlModel( cCtrlName ) + oControlModel.setPropertyValue( cPropertyName, uValue ) + + def getControlModelProperty( self, cCtrlName, cPropertyName ): + """Get the value of a property of a control's model. + This affects the control model, not the control. + """ + oControlModel = self.getControlModel( cCtrlName ) + return oControlModel.getPropertyValue( cPropertyName ) + + #-------------------------------------------------- + # Sugar coated property adjustments to control models. + #-------------------------------------------------- + + def setEnabled( self, cCtrlName, bEnabled=True ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "Enabled", bEnabled ) + + def getEnabled( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + + return self.getControlModelProperty( cCtrlName, "Enabled" ) + + def setState( self, cCtrlName, nState ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "State", nState ) + + def getState( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "State" ) + + def setLabel( self, cCtrlName, cLabel ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "Label", cLabel ) + + def getLabel( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "Label" ) + + def setHelpText( self, cCtrlName, cHelpText ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "HelpText", cHelpText ) + + def getHelpText( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "HelpText" ) + + + #-------------------------------------------------- + # Adjust controls (not models) + #-------------------------------------------------- + + # The following apply to all controls which are a + # com.sun.star.awt.UnoControl + + def setDesignMode( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + oControl.setDesignMode( bDesignMode ) + + def isDesignMode( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + return oControl.isDesignMode() + + def isTransparent( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + return oControl.isTransparent() + + + # The following apply to all controls which are a + # com.sun.star.awt.UnoControlDialogElement + + def setPosition( self, cCtrlName, nPositionX, nPositionY ): + self.setControlModelProperty( cCtrlName, "PositionX", nPositionX ) + self.setControlModelProperty( cCtrlName, "PositionY", nPositionY ) + def setPositionX( self, cCtrlName, nPositionX ): + self.setControlModelProperty( cCtrlName, "PositionX", nPositionX ) + def setPositionY( self, cCtrlName, nPositionY ): + self.setControlModelProperty( cCtrlName, "PositionY", nPositionY ) + def getPositionX( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "PositionX" ) + def getPositionY( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "PositionY" ) + + def setSize( self, cCtrlName, nWidth, nHeight ): + self.setControlModelProperty( cCtrlName, "Width", nWidth ) + self.setControlModelProperty( cCtrlName, "Height", nHeight ) + def setWidth( self, cCtrlName, nWidth ): + self.setControlModelProperty( cCtrlName, "Width", nWidth ) + def setHeight( self, cCtrlName, nHeight ): + self.setControlModelProperty( cCtrlName, "Height", nHeight ) + def getWidth( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Width" ) + def getHeight( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Height" ) + + def setTabIndex( self, cCtrlName, nWidth, nTabIndex ): + self.setControlModelProperty( cCtrlName, "TabIndex", nTabIndex ) + def getTabIndex( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "TabIndex" ) + + def setStep( self, cCtrlName, nWidth, nStep ): + self.setControlModelProperty( cCtrlName, "Step", nStep ) + def getStep( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Step" ) + + def setTag( self, cCtrlName, nWidth, cTag ): + self.setControlModelProperty( cCtrlName, "Tag", cTag ) + def getTag( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Tag" ) + + def setEchoChar(self, cCtrlName , cVal): + self.setControlModelProperty(cCtrlName, "EchoChar", cVal) + def getEchoChar(self, cCtrlName): + return self.setControlModelProperty(cCtrlName, "EchoChar") + + #-------------------------------------------------- + # Add listeners to controls. + #-------------------------------------------------- + + # This applies to... + # UnoControlButton + def addActionListenerProc( self, cCtrlName, actionListenerProc ): + """Create an com.sun.star.awt.XActionListener object and add it to a control. + A listener object is created which will call the python procedure actionListenerProc. + The actionListenerProc can be either a method or a global procedure. + The following controls support XActionListener: + UnoControlButton + """ + oControl = self.getControl( cCtrlName ) + oActionListener = ActionListenerProcAdapter( actionListenerProc ) + oControl.addActionListener( oActionListener ) + + # This applies to... + # UnoControlCheckBox + def addItemListenerProc( self, cCtrlName, itemListenerProc ): + """Create an com.sun.star.awt.XItemListener object and add it to a control. + A listener object is created which will call the python procedure itemListenerProc. + The itemListenerProc can be either a method or a global procedure. + The following controls support XActionListener: + UnoControlCheckBox + """ + oControl = self.getControl( cCtrlName ) + oActionListener = ItemListenerProcAdapter( itemListenerProc ) + oControl.addItemListener( oActionListener ) + + #-------------------------------------------------- + # Display the modal dialog. + #-------------------------------------------------- + + def doModalDialog( self, sObjName,sValue): + """Display the dialog as a modal dialog.""" + self.oDialogControl.setVisible( True ) + if not sValue==None: + self.selectListBoxItem( sObjName, sValue, True ) + self.oDialogControl.execute() + + def endExecute( self ): + """Call this from within one of the listeners to end the modal dialog. + For instance, the listener on your OK or Cancel button would call this to end the dialog. + """ + self.oDialogControl.endExecute() + +import urllib + +def get_absolute_file_path( url ): + url_unquoted = urllib.unquote(url) + return os.name == 'nt' and url_unquoted[1:] or url_unquoted + +# This function reads the content of a file and return it to the caller +def read_data_from_file( filename ): + fp = file( filename, "rb" ) + data = fp.read() + fp.close() + return data + +# This function writes the content to a file +def write_data_to_file( filename, data ): + fp = file( filename, 'wb' ) + fp.write( data ) + fp.close() + +import logging +import tempfile +LOG_DEBUG='debug' +LOG_INFO='info' +LOG_WARNING='warn' +LOG_ERROR='error' +LOG_CRITICAL='critical' + +def log_detail(self): + import os + logger = logging.getLogger() + logfile_name = os.path.join(tempfile.gettempdir(), "report_designer.log") + hdlr = logging.FileHandler(logfile_name) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + logger.setLevel(logging.INFO) + +class Logger(object): + def log_write(self,name,level,msg): + log = logging.getLogger(name) + getattr(log,level)(msg) + + def shutdown(self): + logging.shutdown() + +import time +import socket +import xmlrpclib +#import tiny_socket +import re + +class RPCGateway(object): + def __init__(self, host, port, protocol): + + self.protocol = protocol + self.host = host + self.port = port + + def get_url(self): + + """Get the url + """ + return "%s://%s:%s/"%(self.protocol, self.host, self.port) + + def listdb(self): + """Get the list of databases. + """ + pass + + def login(self, db, user, password): + pass + + def execute(self, obj, method, *args): + pass + + + +class RPCSession(object): + def __init__(self,url): + + m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '') + + host = m.group(2) + port = m.group(3) + protocol = m.group(1) + if not m: + return -1 + if protocol == 'http://' or protocol == 'http://': + self.gateway = XMLRPCGateway(host, port, 'http') + elif protocol == 'socket://': + + self.gateway = NETRPCGateway(host, port) + + def listdb(self): + return self.gateway.listdb() + + def login(self, db, user, password): + + if password is None: + return -1 + + uid = self.gateway.login(db, user or '', password or '') + + if uid <= 0: + return -1 + + self.uid = uid + self.db = db + self.password = password + self.open = True + return uid + + + def execute(self, obj, method, *args): + try: + result = self.gateway.execute(obj, method, *args) + return self.__convert(result) + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + + def __convert(self, result): + + if isinstance(result, basestring): + # try to convert into unicode string + try: + return ustr(result) + except Exception, e: + return result + + elif isinstance(result, list): + return [self.__convert(val) for val in result] + + elif isinstance(result, tuple): + return tuple([self.__convert(val) for val in result]) + + elif isinstance(result, dict): + newres = {} + for key, val in result.items(): + newres[key] = self.__convert(val) + + return newres + + else: + return result + +class XMLRPCGateway(RPCGateway): + """XML-RPC implementation. + """ + def __init__(self, host, port, protocol='http'): + + super(XMLRPCGateway, self).__init__(host, port, protocol) + global rpc_url + rpc_url = self.get_url() + 'xmlrpc/' + + def listdb(self): + global rpc_url + sock = xmlrpclib.ServerProxy(rpc_url + 'db') + try: + return sock.list() + except Exception, e: + return -1 + + def login(self, db, user, password): + + global rpc_url + + sock = xmlrpclib.ServerProxy(rpc_url + 'common') + + try: + res = sock.login(db, user, password) + except Exception, e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + return -1 + + return res + + def execute(self, sDatabase,UID,sPassword,obj, method, *args): + global rpc_url + + sock = xmlrpclib.ServerProxy(rpc_url + 'object') + + return sock.execute(sDatabase,UID,sPassword, obj ,method,*args) + + + +class NETRPCGateway(RPCGateway): + def __init__(self, host, port): + + super(NETRPCGateway, self).__init__(host, port, 'socket') + + def listdb(self): + sock = mysocket() + try: + sock.connect(self.host, self.port) + sock.mysend(('db', 'list')) + res = sock.myreceive() + sock.disconnect() + return res + except Exception, e: + return -1 + + def login(self, db, user, password): + sock = mysocket() + try: + sock.connect(self.host, self.port) + sock.mysend(('common', 'login', db, user, password)) + res = sock.myreceive() + sock.disconnect() + except Exception, e: + return -1 + return res + def execute(self,obj, method, *args): + sock = mysocket() + try: + sock.connect(self.host, self.port) + data=(('object', 'execute',obj,method,)+args) + sock.mysend(data) + res=sock.myreceive() + sock.disconnect() + return res + + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + + +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved +# $Id$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## +import socket +import cPickle +import cStringIO +import marshal + +class Myexception(Exception): + def __init__(self, faultCode, faultString): + self.faultCode = faultCode + self.faultString = faultString + self.args = (faultCode, faultString) + +class mysocket: + def __init__(self, sock=None): + if sock is None: + self.sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) + else: + self.sock = sock + self.sock.settimeout(120) + def connect(self, host, port=False): + if not port: + protocol, buf = host.split('//') + host, port = buf.split(':') + self.sock.connect((host, int(port))) + def disconnect(self): + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + def mysend(self, msg, exception=False, traceback=None): + + msg = cPickle.dumps([msg,traceback]) + size = len(msg) + self.sock.send('%8d' % size) + self.sock.send(exception and "1" or "0") + totalsent = 0 + while totalsent < size: + sent = self.sock.send(msg[totalsent:]) + if sent == 0: + raise RuntimeError, "socket connection broken" + totalsent = totalsent + sent + def myreceive(self): + buf='' + while len(buf) < 8: + chunk = self.sock.recv(8 - len(buf)) + if chunk == '': + raise RuntimeError, "socket connection broken" + buf += chunk + size = int(buf) + buf = self.sock.recv(1) + if buf != "0": + exception = buf + else: + exception = False + msg = '' + while len(msg) < size: + chunk = self.sock.recv(size-len(msg)) + if chunk == '': + raise RuntimeError, "socket connection broken" + msg = msg + chunk + msgio = cStringIO.StringIO(msg) + unpickler = cPickle.Unpickler(msgio) + unpickler.find_global = None + res = unpickler.load() + + if isinstance(res[0],Exception): + if exception: + raise Myexception(str(res[0]), str(res[1])) + raise res[0] + else: + return res[0] + + + +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from ServerParameter import * + from lib.logreport import * + from lib.rpc import * + from LoginTest import * + + database="test_db1" + uid = 3 + + +#class RepeatIn: +class RepeatIn( unohelper.Base, XJobExecutor ): + def __init__(self,sObject="",sVariable="",sFields="",sDisplayName="",bFromModify=False): + # Interface Design + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + + self.win = DBModalDialog(60, 50, 180, 250, "RepeatIn Builder") + self.win.addFixedText("lblVariable", 2, 12, 60, 15, "Objects to loop on :") + self.win.addComboBox("cmbVariable", 180-120-2, 10, 120, 15,True, itemListenerProc=self.cmbVariable_selected) + self.insVariable = self.win.getControl( "cmbVariable" ) + + self.win.addFixedText("lblFields", 10, 32, 60, 15, "Field to loop on :") + self.win.addComboListBox("lstFields", 180-120-2, 30, 120, 150, False,itemListenerProc=self.lstbox_selected) + self.insField = self.win.getControl( "lstFields" ) + + self.win.addFixedText("lblName", 12, 187, 60, 15, "Variable name :") + self.win.addEdit("txtName", 180-120-2, 185, 120, 15,) + + self.win.addFixedText("lblUName", 8, 207, 60, 15, "Displayed name :") + self.win.addEdit("txtUName", 180-120-2, 205, 120, 15,) + + self.win.addButton('btnOK',-2 ,-10,45,15,'Ok', actionListenerProc = self.btnOk_clicked ) + + self.win.addButton('btnCancel',-2 - 45 - 5 ,-10,45,15,'Cancel', actionListenerProc = self.btnCancel_clicked ) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + # Variable Declaration + self.sValue=None + self.sObj=None + self.aSectionList=[] + self.sGVariable=sVariable + self.sGDisplayName=sDisplayName + self.aItemList=[] + self.aComponentAdd=[] + self.aObjectList=[] + self.aListRepeatIn=[] + self.aVariableList=[] + # Call method to perform Enumration on Report Document + EnumDocument(self.aItemList,self.aComponentAdd) + # Perform checking that Field-1 and Field - 4 is available or not alos get Combobox + # filled if condition is true + desktop = getDesktop() + doc = desktop.getCurrentComponent() + docinfo = doc.getDocumentInfo() + # Check weather Field-1 is available if not then exit from application + self.sMyHost= "" + + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + self.sMyHost= docinfo.getUserFieldValue(0) + self.count=0 + oParEnum = doc.getTextFields().createEnumeration() + + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + self.count += 1 + + getList(self.aObjectList, self.sMyHost,self.count) + cursor = doc.getCurrentController().getViewCursor() + text = cursor.getText() + tcur = text.createTextCursorByRange(cursor) + + self.aVariableList.extend( filter( lambda obj: obj[:obj.find(" ")] == "List", self.aObjectList ) ) + + for i in range(len(self.aItemList)): + try: + anItem = self.aItemList[i][1] + component = self.aComponentAdd[i] + + if component == "Document": + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextSection: + getRecersiveSection(tcur.TextSection,self.aSectionList) + if component in self.aSectionList: + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextTable: + if not component == "Document" and component[component.rfind(".") + 1:] == tcur.TextTable.Name: + VariableScope( tcur, self.aVariableList, self.aObjectList, self.aComponentAdd, self.aItemList, component ) + except : + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('RepeatIn', LOG_ERROR, info) + self.bModify=bFromModify + + if self.bModify==True: + + if sObject=="": + self.insVariable.setText("List of "+docinfo.getUserFieldValue(3)) + self.insField.addItem("objects",self.win.getListBoxItemCount("lstFields")) + self.win.setEditText("txtName", sVariable) + self.win.setEditText("txtUName",sDisplayName) + self.sValue= "objects" + else: + sItem="" + for anObject in self.aObjectList: + if anObject[:anObject.find("(")] == sObject: + sItem = anObject + self.insVariable.setText( sItem ) + + genTree( + sItem[sItem.find("(")+1:sItem.find(")")], + self.aListRepeatIn, + self.insField, + self.sMyHost, + 2, + ending=['one2many','many2many'], + recur=['one2many','many2many'] + ) + + self.sValue= self.win.getListBoxItem("lstFields",self.aListRepeatIn.index(sFields)) + + for var in self.aVariableList: + + if var[:8] <> 'List of ': + self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])]) + else: + self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[8:])]) + fields=['name','model'] + self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields) + if self.model_res <> []: + if var[:8]<>'List of ': + self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem('List of ' + self.model_res[0]['name'] ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem(var ,self.insVariable.getItemCount()) + + self.win.doModalDialog("lstFields",self.sValue) + else: + ErrorDialog("Please Select Appropriate module" ,"Create new report from: \nOpenERP -> Open a New Report") + self.win.endExecute() + + def lstbox_selected(self,oItemEvent): + sItem=self.win.getListBoxSelectedItem("lstFields") + sMain=self.aListRepeatIn[self.win.getListBoxSelectedItemPos("lstFields")] + + if self.bModify==True: + self.win.setEditText("txtName", self.sGVariable) + self.win.setEditText("txtUName",self.sGDisplayName) + else: + self.win.setEditText("txtName",sMain[sMain.rfind("/")+1:]) + self.win.setEditText("txtUName","|-."+sItem[sItem.rfind("/")+1:]+".-|") + + def cmbVariable_selected(self,oItemEvent): + + if self.count > 0 : + + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields")) + sItem=self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:8]=='List of ': + if var[:8]==sItem[:8]: + sItem = var + elif var[:var.find("(")+1] == sItem[:sItem.find("(")+1]: + sItem = var + self.aListRepeatIn=[] + + data = ( sItem[sItem.rfind(" ") + 1:] == docinfo.getUserFieldValue(3) ) and docinfo.getUserFieldValue(3) or sItem[sItem.find("(")+1:sItem.find(")")] + genTree( data, self.aListRepeatIn, self.insField, self.sMyHost, 2, ending=['one2many','many2many'], recur=['one2many','many2many'] ) + + self.win.selectListBoxItemPos("lstFields", 0, True ) + + else: + sItem=self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:8]=='List of ' and var[:8] == sItem[:8]: + sItem = var + if sItem.find(".")==-1: + temp=sItem[sItem.rfind("x_"):] + else: + temp=sItem[sItem.rfind(".")+1:] + self.win.setEditText("txtName",temp) + self.win.setEditText("txtUName","|-."+temp+".-|") + self.insField.addItem("objects",self.win.getListBoxItemCount("lstFields")) + self.win.selectListBoxItemPos("lstFields", 0, True ) + + def btnOk_clicked(self, oActionEvent): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + cursor = doc.getCurrentController().getViewCursor() + selectedItem = self.win.getListBoxSelectedItem( "lstFields" ) + selectedItemPos = self.win.getListBoxSelectedItemPos( "lstFields" ) + txtName = self.win.getEditText( "txtName" ) + txtUName = self.win.getEditText( "txtUName" ) + if selectedItem != "" and txtName != "" and txtUName != "": + sKey=u""+ txtUName + if selectedItem == "objects": + sValue=u"[[ repeatIn(" + selectedItem + ",'" + txtName + "') ]]" + else: + sObjName=self.win.getComboBoxText("cmbVariable") + sObjName=sObjName[:sObjName.find("(")] + sValue=u"[[ repeatIn(" + sObjName + self.aListRepeatIn[selectedItemPos].replace("/",".") + ",'" + txtName +"') ]]" + + if self.bModify == True: + oCurObj = cursor.TextField + oCurObj.Items = (sKey,sValue) + oCurObj.update() + else: + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if self.win.getListBoxSelectedItem("lstFields") == "objects": + oInputList.Items = (sKey,sValue) + doc.Text.insertTextContent(cursor,oInputList,False) + else: + sValue=u"[[ repeatIn(" + sObjName + self.aListRepeatIn[selectedItemPos].replace("/",".") + ",'" + txtName +"') ]]" + if cursor.TextTable==None: + oInputList.Items = (sKey,sValue) + doc.Text.insertTextContent(cursor,oInputList,False) + else: + oInputList.Items = (sKey,sValue) + widget = ( cursor.TextTable or selectedItem <> 'objects' ) and cursor.TextTable.getCellByName( cursor.Cell.CellName ) or doc.Text + widget.insertTextContent(cursor,oInputList,False) + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Object Field or Name field \nor select perticular value from the list of fields") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + RepeatIn() +elif __name__=="package": + g_ImplementationHelper = unohelper.ImplementationHelper() + g_ImplementationHelper.addImplementation( RepeatIn, "org.openoffice.openerp.report.repeatln", ("com.sun.star.task.Job",),) + + +import uno +import string +import unohelper +import xmlrpclib + +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.functions import * + from lib.error import ErrorDialog + from LoginTest import * + from lib.logreport import * + from lib.rpc import * + database="report" + uid = 3 + + +class Fields(unohelper.Base, XJobExecutor ): + def __init__(self,sVariable="",sFields="",sDisplayName="",bFromModify=False): + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + self.logobj=Logger() + self.win = DBModalDialog(60, 50, 200, 225, "Field Builder") + + self.win.addFixedText("lblVariable", 27, 12, 60, 15, "Variable :") + self.win.addComboBox("cmbVariable", 180-120-2, 10, 130, 15,True, itemListenerProc=self.cmbVariable_selected) + self.insVariable = self.win.getControl( "cmbVariable" ) + + self.win.addFixedText("lblFields", 10, 32, 60, 15, "Variable Fields :") + self.win.addComboListBox("lstFields", 180-120-2, 30, 130, 150, False,True,itemListenerProc=self.lstbox_selected) + self.insField = self.win.getControl( "lstFields" ) + + self.win.addFixedText("lblUName", 8, 187, 60, 15, "Displayed name :") + self.win.addEdit("txtUName", 180-120-2, 185, 130, 15,) + + self.win.addButton('btnOK',-5 ,-5,45,15,'Ok' ,actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-5 - 45 - 5 ,-5,45,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + + self.sValue=None + self.sObj=None + self.aSectionList=[] + self.sGDisplayName=sDisplayName + self.aItemList=[] + self.aComponentAdd=[] + self.aObjectList=[] + self.aListFields=[] + self.aVariableList=[] + EnumDocument(self.aItemList,self.aComponentAdd) + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.sMyHost= "" + + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + self.sMyHost = docinfo.getUserFieldValue(0) + self.count = 0 + oParEnum = doc.getTextFields().createEnumeration() + + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + self.count += 1 + + getList(self.aObjectList, self.sMyHost,self.count) + cursor = doc.getCurrentController().getViewCursor() + text = cursor.getText() + tcur = text.createTextCursorByRange(cursor) + + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == "Objects", self.aObjectList ) ) + + for i in range(len(self.aItemList)): + try: + anItem = self.aItemList[i][1] + component = self.aComponentAdd[i] + + if component == "Document": + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextSection: + getRecersiveSection(tcur.TextSection,self.aSectionList) + if component in self.aSectionList: + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextTable: + if not component == "Document" and component[component.rfind(".")+1:] == tcur.TextTable.Name: + VariableScope(tcur, self.aVariableList, self.aObjectList, self.aComponentAdd, self.aItemList, component) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('Fields', LOG_ERROR, info) + + self.bModify=bFromModify + if self.bModify==True: + sItem="" + for anObject in self.aObjectList: + if anObject[:anObject.find("(")] == sVariable: + sItem = anObject + self.insVariable.setText(sItem) + genTree( + sItem[sItem.find("(")+1:sItem.find(")")], + self.aListFields, + self.insField, + self.sMyHost, + 2, + ending_excl=['one2many','many2one','many2many','reference'], + recur=['many2one'] + ) + self.sValue= self.win.getListBoxItem("lstFields",self.aListFields.index(sFields)) + for var in self.aVariableList: + + self.model_ids =self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])]) + fields=['name','model'] + self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields) + if self.model_res <> []: + self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem(var ,self.insVariable.getItemCount()) + + self.win.doModalDialog("lstFields",self.sValue) + else: + ErrorDialog("Please insert user define field Field-1 or Field-4","Just go to File->Properties->User Define \nField-1 Eg. http://localhost:8069 \nOR \nField-4 Eg. account.invoice") + self.win.endExecute() + + def lstbox_selected(self,oItemEvent): + try: + + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sItem= self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")+1]==sItem[:sItem.find("(")+1]: + sItem = var + sMain=self.aListFields[self.win.getListBoxSelectedItemPos("lstFields")] + sObject=self.getRes(self.sock,sItem[sItem.find("(")+1:-1],sMain[1:]) + ids = self.sock.execute(database, uid, self.password, sObject , 'search', []) + res = self.sock.execute(database, uid, self.password, sObject , 'read',[ids[0]]) + self.win.setEditText("txtUName",res[0][sMain[sMain.rfind("/")+1:]]) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('Fields', LOG_ERROR, info) + self.win.setEditText("txtUName","TTT") + if self.bModify: + self.win.setEditText("txtUName",self.sGDisplayName) + + def getRes(self,sock ,sObject,sVar): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, self.password, sObject , 'fields_get') + key = res.keys() + key.sort() + myval=None + if not sVar.find("/")==-1: + myval=sVar[:sVar.find("/")] + else: + myval=sVar + if myval in key: + if (res[myval]['type'] in ['many2one']): + sObject = res[myval]['relation'] + return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:]) + else: + return sObject + + def cmbVariable_selected(self,oItemEvent): + if self.count > 0 : + try: + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields")) + self.aListFields=[] + tempItem = self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")] == tempItem[:tempItem.find("(")]: + sItem=var + + genTree( + sItem[sItem.find("(")+1:sItem.find(")")], + self.aListFields, + self.insField, + self.sMyHost, + 2, + ending_excl=['one2many','many2one','many2many','reference'], + recur=['many2one'] + ) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('Fields', LOG_ERROR, info) + + def btnOk_clicked( self, oActionEvent ): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + cursor = doc.getCurrentController().getViewCursor() + for i in self.win.getListBoxSelectedItemsPos("lstFields"): + itemSelected = self.aListFields[i] + itemSelectedPos = i + txtUName=self.win.getEditText("txtUName") + sKey=u""+txtUName + if itemSelected != "" and txtUName != "" and self.bModify==True : + txtUName=self.sGDisplayName + sKey=u""+txtUName + txtUName=self.sGDisplayName + oCurObj=cursor.TextField + sObjName=self.insVariable.getText() + sObjName=sObjName[:sObjName.find("(")] + sValue=u"[[ " + sObjName + self.aListFields[itemSelectedPos].replace("/",".") + " ]]" + oCurObj.Items = (sKey,sValue) + oCurObj.update() + self.win.endExecute() + elif itemSelected != "" and txtUName != "" : + + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + sObjName=self.win.getComboBoxText("cmbVariable") + sObjName=sObjName[:sObjName.find("(")] + + widget = ( cursor.TextTable and cursor.TextTable.getCellByName( cursor.Cell.CellName ) or doc.Text ) + + sValue = u"[[ " + sObjName + self.aListFields[itemSelectedPos].replace("/",".") + " ]]" + oInputList.Items = (sKey,sValue) + widget.insertTextContent(cursor,oInputList,False) + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Name field \nor select perticular value from the list of fields") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + Fields() +elif __name__=="package": + g_ImplementationHelper.addImplementation( Fields, "org.openoffice.openerp.report.fields", ("com.sun.star.task.Job",),) + +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + database="test" + uid = 3 + +class Expression(unohelper.Base, XJobExecutor ): + def __init__(self,sExpression="",sName="", bFromModify=False): + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + self.win = DBModalDialog(60, 50, 180, 65, "Expression Builder") + self.win.addFixedText("lblExpression",17 , 10, 35, 15, "Expression :") + self.win.addEdit("txtExpression", -5, 5, 123, 15) + self.win.addFixedText("lblName", 2, 30, 50, 15, "Displayed Name :") + self.win.addEdit("txtName", -5, 25, 123, 15) + self.win.addButton( "btnOK", -5, -5, 40, 15, "OK", actionListenerProc = self.btnOk_clicked ) + self.win.addButton( "btnCancel", -5 - 40 -5, -5, 40, 15, "Cancel", actionListenerProc = self.btnCancel_clicked ) + self.bModify=bFromModify + if self.bModify==True: + self.win.setEditText("txtExpression",sExpression) + self.win.setEditText("txtName",sName) + self.win.doModalDialog("",None) + + + def btnOk_clicked( self, oActionEvent ): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + text = doc.Text + cursor = doc.getCurrentController().getViewCursor() + if self.bModify==True: + oCurObj=cursor.TextField + sKey=u""+self.win.getEditText("txtName") + sValue=u"[[ " + self.win.getEditText("txtExpression") + " ]]" + oCurObj.Items = (sKey,sValue) + oCurObj.update() + self.win.endExecute() + else: + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if self.win.getEditText("txtName")!="" and self.win.getEditText("txtExpression")!="": + sKey=u""+self.win.getEditText("txtName") + sValue=u"[[ " + self.win.getEditText("txtExpression") + " ]]" + if cursor.TextTable==None: + oInputList.Items = (sKey,sValue) + text.insertTextContent(cursor,oInputList,False) + else: + oTable = cursor.TextTable + oCurCell = cursor.Cell + tableText = oTable.getCellByName( oCurCell.CellName ) + oInputList.Items = (sKey,sValue) + tableText.insertTextContent(cursor,oInputList,False) + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Name field or \nExpression field") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + Expression() +elif __name__=="package": + g_ImplementationHelper.addImplementation( Expression, "org.openoffice.openerp.report.expression", ("com.sun.star.task.Job",),) + +import re +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from Expression import Expression + from Fields import Fields + from Repeatln import RepeatIn + from lib.error import * + database="test" + uid = 3 + +class modify(unohelper.Base, XJobExecutor ): + def __init__( self, ctx ): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + + # Variable Declaration + desktop = getDesktop() + doc = desktop.getCurrentComponent() + docinfo = doc.getDocumentInfo() + self.oVC = doc.CurrentController.getViewCursor() + if not docinfo.getUserFieldValue(0)=="": + self.sMyHost= docinfo.getUserFieldValue(0) + else: + ErrorDialog( + "Please insert user define field Field-1", + "Just go to File->Properties->User Define \n" + "Field-1 Eg. http://localhost:8069" + ) + exit(1) + + # Check weather Field-4 is available or not otherwise exit from application + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + if self.oVC.TextField: + self.oCurObj=self.oVC.TextField + item = self.oCurObj.Items[0] + + kind, group1, group2 = self.getOperation(self.oCurObj.Items[1] ) + + start_group1 = group1[:group1.find(".")] + stop_group1 = group1[group1.find("."):].replace(".", "/") + + if kind == "field": + Fields( start_group1, stop_group1, item, True ) + elif kind == "expression": + Expression( group1, item, True ) + elif kind == "repeatIn": + RepeatIn( start_group1, group2, stop_group1, item, True ) + else: + ErrorDialog( + "Please place your cursor at begaining of field \n" + "which you want to modify","" + ) + + else: + ErrorDialog( + "Please insert user define field Field-1 or Field-4", + "Just go to File->Properties->User Define \n" + "Field-1 Eg. http://localhost:8069 \n" + "OR \n" + "Field-4 Eg. account.invoice" + ) + exit(1) + + def getOperation(self, str): + #str = "[[ RepeatIn(objects, 'variable') ]]" #repeatIn + #str = "[[ saleorder.partner_id.name ]]" # field + #str = "[[ some thing complex ]]" # expression + method1 = lambda x: (u'repeatIn', x.group(1), x.group(2)) + method2 = lambda x: (u'field', x.group(1), None) + method3 = lambda x: (u'expression', x.group(1), None) + regexes = [ + ('\\[\\[ *repeatIn\\( *(.+)*, *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', method1), + ('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', method2), + ('\\[\\[ *(.+) *\\]\\]', method3) + ] + for (rule,method) in regexes: + res = re.match(rule, str) + if res: + return method(res) + +if __name__<>"package": + modify(None) +else: + g_ImplementationHelper.addImplementation( modify, "org.openoffice.openerp.report.modify", ("com.sun.star.task.Job",),) + +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from lib.logreport import * + from lib.rpc import * + from Change import * + database="test" + +class ServerParameter( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + desktop=getDesktop() + log_detail(self) + self.logobj=Logger() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win=DBModalDialog(60, 50, 160, 108, "Server Connection Parameter") + + self.win.addFixedText("lblVariable", 2, 12, 35, 15, "Server URL") + if docinfo.getUserFieldValue(0)=="": + docinfo.setUserFieldValue(0,"http://localhost:8069") + self.win.addEdit("txtHost",-34,9,91,15,docinfo.getUserFieldValue(0)) + self.win.addButton('btnChange',-2 ,9,30,15,'Change', actionListenerProc = self.btnChange_clicked ) + + self.win.addFixedText("lblDatabaseName", 6, 31, 31, 15, "Database") + #self.win.addFixedText("lblMsg", -2,28,123,15) +# self.win.addComboListBox("lstDatabase", -2,28,123,15, True) +# self.lstDatabase = self.win.getControl( "lstDatabase" ) + #self.win.selectListBoxItem( "lstDatabase", docinfo.getUserFieldValue(2), True ) + #self.win.setEnabled("lblMsg",False) + + self.win.addFixedText("lblLoginName", 17, 51, 20, 15, "Login") + self.win.addEdit("txtLoginName",-2,48,123,15,docinfo.getUserFieldValue(1)) + + self.win.addFixedText("lblPassword", 6, 70, 31, 15, "Password") + self.win.addEdit("txtPassword",-2,67,123,15,) + self.win.setEchoChar("txtPassword",42) + + + self.win.addButton('btnOK',-2 ,-5, 60,15,'Connect' ,actionListenerProc = self.btnOk_clicked ) + + self.win.addButton('btnCancel',-2 - 60 - 5 ,-5, 35,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + sValue="" + if docinfo.getUserFieldValue(0)<>"": + global url + global result + url=docinfo.getUserFieldValue(0) + self.sock=RPCSession(url) + res=self.sock.listdb() + result=res + if res == -1: + self.win.addEdit("lstDatabase",-2,28,123,15) +# sValue="Could not connect to the server!" +# self.lstDatabase.addItem("Could not connect to the server!",0) + elif res == 0: + sValue="No Database found !!!" + self.lstDatabase.addItem("No Database found !!!",0) + else: + self.win.addComboListBox("lstDatabase", -2,28,123,15, True) + self.lstDatabase = self.win.getControl( "lstDatabase" ) + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + for i in range(len(res)): + self.lstDatabase.addItem(res[i],i) + sValue = database + if sValue: + self.win.doModalDialog("lstDatabase",sValue) + else: + self.win.doModalDialog("lstDatabase",None) + + #self.win.doModalDialog("lstDatabase",docinfo.getUserFieldValue(2)) + + def btnOk_clicked(self,oActionEvent): + + + sLogin=self.win.getEditText("txtLoginName") + sPassword=self.win.getEditText("txtPassword") + global url + global result + if result==-1: + sDatabase=self.win.getEditText("lstDatabase") + else: + sDatabase=self.win.getListBoxSelectedItem("lstDatabase") + self.sock=RPCSession(url) + UID = self.sock.login(sDatabase,sLogin,sPassword) + if not UID : + ErrorDialog("Connection Refuse...","Please enter valid Login/Password") + self.win.endExecute() + try: + + ids = self.sock.execute(sDatabase,UID,sPassword, 'res.groups' , 'search', [('name','=','OpenOfficeReportDesigner')]) + ids_module =self.sock.execute(sDatabase, UID, sPassword, 'ir.module.module', 'search', [('name','=','base_report_designer'),('state', '=', 'installed')]) + dict_groups =self.sock.execute(sDatabase, UID,sPassword, 'res.groups' , 'read',ids,['users']) + except : + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ServerParameter', LOG_ERROR, info) + + if not len(ids) : + ErrorDialog("Group Not Found!!! Create a group named \n\n"'"OpenOfficeReportDesigner"'" \n\n ","","Group Name Error") + self.logobj.log_write('Group Error',LOG_WARNING, ':Create a group OpenOfficeReportDesigner using database %s' % (sDatabase)) + self.win.endExecute() + if not len(ids_module): + ErrorDialog("Please Install base_report_designer module", "", "Module Uninstalled Error") + self.logobj.log_write('Module Not Found',LOG_WARNING, ':base_report_designer not install in database %s' % (sDatabase)) + self.win.endExecute() + + if UID not in dict_groups[0]['users']: + ErrorDialog("Connection Refuse...","You have not access these Report Designer") + self.logobj.log_write('Connection Refuse',LOG_WARNING, " Not Access Report Designer ") + self.win.endExecute() + else: + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + docinfo.setUserFieldValue(0,self.win.getEditText("txtHost")) + docinfo.setUserFieldValue(1,self.win.getEditText("txtLoginName")) + global passwd + passwd=self.win.getEditText("txtPassword") + global loginstatus + loginstatus=True + global database + database=sDatabase + global uid + uid=UID + #docinfo.setUserFieldValue(2,self.win.getListBoxSelectedItem("lstDatabase")) + #docinfo.setUserFieldValue(3,"") + + ErrorDialog(" You can start creating your report in \n \t the current document.","After Creating sending to the server.","Message") + self.logobj.log_write('successful login',LOG_INFO, ':successful login from %s using database %s' % (sLogin, sDatabase)) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def btnChange_clicked(self,oActionEvent): + aVal=[] + url= self.win.getEditText("txtHost") + Change(aVal,url) + if aVal[1]== -1: + self.win.getEditText("lstDatabase") + elif aVal[1]==0: + ErrorDialog(aVal[0],"") + else: + self.win.setEditText("txtHost",aVal[0]) + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + for i in range(len(aVal[1])): + self.lstDatabase.addItem(aVal[1][i],i) + + +if __name__<>"package" and __name__=="__main__": + ServerParameter(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ServerParameter, "org.openoffice.openerp.report.serverparam", ("com.sun.star.task.Job",),) + +if __name__<>"package": + from lib.gui import * + from lib.functions import * + from lib.rpc import * + +class Change: + def __init__(self, aVal= None, sURL=""): + self.win=DBModalDialog(60, 50, 120, 90, "Connect to Open ERP Server") + + self.win.addFixedText("lblVariable", 38, 12, 60, 15, "Server") + + self.win.addEdit("txtHost",-2,9,60,15,sURL[sURL.find("/")+2:sURL.rfind(":")]) + + self.win.addFixedText("lblReportName",45 , 31, 60, 15, "Port") + self.win.addEdit("txtPort",-2,28,60,15,sURL[sURL.rfind(":")+1:]) + + self.win.addFixedText("lblLoginName", 2, 51, 60, 15, "Protocol Connection") + + self.win.addComboListBox("lstProtocol", -2, 48, 60, 15, True) + self.lstProtocol = self.win.getControl( "lstProtocol" ) + +# self.lstProtocol.addItem( "XML-RPC", 0) + #self.lstProtocol.addItem( "XML-RPC secure", 1) + #self.lstProtocol.addItem( "NET-RPC (faster)", 2) + + self.win.addButton( 'btnOK', -2, -5, 30, 15, 'Ok', actionListenerProc = self.btnOk_clicked ) + + self.win.addButton( 'btnCancel', -2 - 30 - 5 ,-5, 30, 15, 'Cancel', actionListenerProc = self.btnCancel_clicked ) + self.aVal=aVal + self.protocol = { + 'XML-RPC': 'http://', + 'XML-RPC secure': 'https://', + 'NET-RPC': 'socket://', + } + for i in self.protocol.keys(): + self.lstProtocol.addItem(i,self.lstProtocol.getItemCount() ) + + sValue=self.protocol.keys()[0] + if sURL<>"": + sValue=self.protocol.keys()[self.protocol.values().index(sURL[:sURL.find("/")+2])] + + self.win.doModalDialog( "lstProtocol", sValue) + + def btnOk_clicked(self,oActionEvent): + global url + url = self.protocol[self.win.getListBoxSelectedItem("lstProtocol")]+self.win.getEditText("txtHost")+":"+self.win.getEditText("txtPort") + self.sock=RPCSession(url) + res=self.sock.listdb() + if res == -1: + self.aVal.append(url) + elif res == 0: + self.aVal.append("No Database found !!!") + else: + self.aVal.append(url) + self.aVal.append(res) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + +import uno +import string +import unohelper +import xmlrpclib + +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from lib.logreport import * + from LoginTest import * + from lib.rpc import * + database="test" + uid = 3 +# +# +# +# Start OpenOffice.org, listen for connections and open testing document +# +# +class NewReport(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + self.win=DBModalDialog(60, 50, 180, 115, "Open New Report") + self.win.addFixedText("lblModuleSelection", 2, 2, 60, 15, "Module Selection") + self.win.addComboListBox("lstModule", -2,13,176,80 , False) + self.lstModule = self.win.getControl( "lstModule" ) + self.aModuleName=[] + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search',[]) + fields = [ 'model','name'] + res = self.sock.execute(database, uid, self.password, 'ir.model' , 'read', ids, fields) + res.sort(lambda x, y: cmp(x['name'],y['name'])) + + for i in range(len(res)): + self.lstModule.addItem(res[i]['name'],self.lstModule.getItemCount()) + self.aModuleName.append(res[i]['model']) + self.win.addButton('btnOK',-2 ,-5, 70,15,'Use Module in Report' ,actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-2 - 70 - 5 ,-5, 35,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.doModalDialog("",None) + + def btnOk_clicked(self,oActionEvent): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + docinfo.setUserFieldValue(3,self.aModuleName[self.lstModule.getSelectedItemPos()]) + self.logobj.log_write('Module Name',LOG_INFO, ':Module use in creating a report %s using database %s' % (self.aModuleName[self.lstModule.getSelectedItemPos()], database)) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + NewReport(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( \ + NewReport, + "org.openoffice.openerp.report.opennewreport", + ("com.sun.star.task.Job",),) + +if __name__<>"package": + from ServerParameter import * + from lib.gui import * + +class LoginTest: + def __init__(self): + if not loginstatus: + ServerParameter(None) + + + +import uno +import string +import unohelper +import xmlrpclib +import base64, tempfile + + +from com.sun.star.task import XJobExecutor +import os +import sys +if __name__<>'package': + from lib.gui import * + from lib.error import * + from LoginTest import * + from lib.logreport import * + from lib.rpc import * + database="test" + uid = 3 + +# +class ModifyExistingReport(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + self.win = DBModalDialog(60, 50, 180, 120, "Modify Existing Report") + self.win.addFixedText("lblReport", 2, 3, 60, 15, "Report Selection") + self.win.addComboListBox("lstReport", -1,15,178,80 , False ) + self.lstReport = self.win.getControl( "lstReport" ) + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.logobj=Logger() + self.hostname = docinfo.getUserFieldValue(0) + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + # Open a new connexion to the server + + + ids = self.sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_designer'),('state', '=', 'installed')]) + if not len(ids): + ErrorDialog("Please Install base_report_designer module", "", "Module Uninstalled Error") + exit(1) + + ids = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'search', [('report_xsl', '=', False),('report_xml', '=', False)]) + + fields=['id', 'name','report_name','model'] + + self.reports = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'read', ids, fields) + self.report_with_id = [] + + for report in self.reports: + if report['name']<>"": + model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=', report['model'])]) + model_res_other =self.sock.execute(database, uid, self.password, 'ir.model', 'read', model_ids, [ 'name', 'model' ] ) + if model_res_other <> []: + name = model_res_other[0]['name'] + " - " + report['name'] + else: + name = report['name'] + " - " + report['model'] + self.report_with_id.append( (report['id'], name, report['model'] ) ) + + self.report_with_id.sort( lambda x, y: cmp( x[1], y[1] ) ) + + for id, report_name, model_name in self.report_with_id: + self.lstReport.addItem( report_name, self.lstReport.getItemCount() ) + + self.win.addButton('btnSave',10,-5,50,15,'Open Report' ,actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-10 ,-5,50,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.addButton('btnDelete',15 -80 ,-5,50,15,'Delete Report',actionListenerProc = self.btnDelete_clicked) + self.win.doModalDialog("lstReport",self.report_with_id[0][1] ) + + def btnOk_clicked(self, oActionEvent): + try: + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + + selectedItemPos = self.win.getListBoxSelectedItemPos( "lstReport" ) + id = self.report_with_id[ selectedItemPos ][0] + + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'report_get', id) + + if res['file_type'] in ['sxw','odt'] : + file_type = res['file_type'] + else : + file_type = 'sxw' + + fp_name = tempfile.mktemp('.'+file_type) + fp_name1="r"+fp_name + fp_path=os.path.join(fp_name1).replace("\\","/") + fp_win=fp_path[1:] + + filename = ( os.name == 'nt' and fp_win or fp_name ) + if res['report_sxw_content']: + write_data_to_file( filename, base64.decodestring(res['report_sxw_content'])) + url = "file:///%s" % filename + + arr=Array(makePropertyValue("MediaType","application/vnd.sun.xml.writer"),) + oDoc2 = desktop.loadComponentFromURL(url, "openerp", 55, arr) + docinfo2=oDoc2.getDocumentInfo() + docinfo2.setUserFieldValue(0, self.hostname) + docinfo2.setUserFieldValue(1,self.password) + docinfo2.setUserFieldValue(2,id) + docinfo2.setUserFieldValue(3,self.report_with_id[selectedItemPos][2]) + + oParEnum = oDoc2.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + oPar.SelectedItem = oPar.Items[0] + oPar.update() + if oDoc2.isModified(): + if oDoc2.hasLocation() and not oDoc2.isReadonly(): + oDoc2.store() + + ErrorDialog("Download is Completed","Your file has been placed here :\n"+ fp_name,"Download Message") + obj=Logger() + obj.log_write('Modify Existing Report',LOG_INFO, ':successful download report %s using database %s' % (self.report_with_id[selectedItemPos][2], database)) + except Exception, e: + ErrorDialog("Report has not been downloaded", "Report: %s\nDetails: %s" % ( fp_name, str(e) ),"Download Message") + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ModifyExistingReport', LOG_ERROR, info) + + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def btnDelete_clicked( self, oActionEvent ): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + + selectedItemPos = self.win.getListBoxSelectedItemPos( "lstReport" ) + name=self.win.getListBoxSelectedItem ("lstReport") + id = self.report_with_id[ selectedItemPos ][0] + temp = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'unlink', id,) + str_value='ir.actions.report.xml,'+str(id) + ids = self.sock.execute(database, uid, self.password, 'ir.values' , 'search',[('value','=',str_value)]) + if ids: + rec = self.sock.execute(database, uid, self.password, 'ir.values', 'unlink', ids,) + else : + pass + if temp: + ErrorDialog("Report","Report has been Delete:\n "+name,"Message") + self.logobj.log_write('Delete Report',LOG_INFO, ':successful delete report %s using database %s' % (name, database)) + + else: + ErrorDialog("Report","Report has not Delete:\n"+name," Message") + self.win.endExecute() + + + +if __name__<>"package" and __name__=="__main__": + ModifyExistingReport(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ModifyExistingReport, "org.openoffice.openerp.report.modifyreport", ("com.sun.star.task.Job",),) + + +import uno +import string +import unohelper +import random +import xmlrpclib +import base64, tempfile +from com.sun.star.task import XJobExecutor +import os +import sys +if __name__<>'package': + from lib.gui import * + from lib.error import * + from lib.functions import * + from lib.logreport import * + from lib.tools import * + from LoginTest import * + from lib.rpc import * + database="report" + uid = 3 + +# +# +class SendtoServer(unohelper.Base, XJobExecutor): + Kind = { + 'PDF' : 'pdf', + 'OpenOffice': 'sxw', + 'HTML' : 'html' + } + + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + self.ids = self.sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_designer'),('state', '=', 'installed')]) + if not len(self.ids): + ErrorDialog("Please Install base_report_designer module", "", "Module Uninstalled Error") + exit(1) + + report_name = "" + name="" + if docinfo.getUserFieldValue(2)<>"" : + try: + fields=['name','report_name'] + self.res_other = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'read', [int(docinfo.getUserFieldValue(2))],fields) + name = self.res_other[0]['name'] + report_name = self.res_other[0]['report_name'] + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logob.log_write('ServerParameter', LOG_ERROR, info) + elif docinfo.getUserFieldValue(3) <> "": + name = "" + result = "rnd" + for i in range(5): + result =result + random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890') + + report_name = docinfo.getUserFieldValue(3) + "." + result + else: + ErrorDialog("Please select appropriate module...","Note: use OpenERP Report -> Open a new Report", "Module selection ERROR"); + exit(1) + + self.win = DBModalDialog(60, 50, 180, 100, "Send To Server") + self.win.addFixedText("lblName",10 , 9, 40, 15, "Report Name :") + self.win.addEdit("txtName", -5, 5, 123, 15,name) + self.win.addFixedText("lblReportName", 2, 30, 50, 15, "Technical Name :") + self.win.addEdit("txtReportName", -5, 25, 123, 15,report_name) + self.win.addCheckBox("chkHeader", 51, 45, 70 ,15, "Corporate Header") + self.win.addFixedText("lblResourceType", 2 , 60, 50, 15, "Select Rpt. Type :") + self.win.addComboListBox("lstResourceType", -5, 58, 123, 15,True,itemListenerProc=self.lstbox_selected) + self.lstResourceType = self.win.getControl( "lstResourceType" ) + self.txtReportName=self.win.getControl( "txtReportName" ) + self.txtReportName.Enable=False + for kind in self.Kind.keys(): + self.lstResourceType.addItem( kind, self.lstResourceType.getItemCount() ) + + self.win.addButton( "btnSend", -5, -5, 80, 15, "Send Report to Server", actionListenerProc = self.btnOk_clicked) + self.win.addButton( "btnCancel", -5 - 80 -5, -5, 40, 15, "Cancel", actionListenerProc = self.btnCancel_clicked) + + self.win.doModalDialog("lstResourceType", self.Kind.keys()[0]) + + def lstbox_selected(self,oItemEvent): + pass + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def btnOk_clicked(self, oActionEvent): + if self.win.getEditText("txtName") <> "" and self.win.getEditText("txtReportName") <> "": + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + self.getInverseFieldsRecord(1) + fp_name = tempfile.mktemp('.'+"sxw") + if not oDoc2.hasLocation(): + oDoc2.storeAsURL("file://"+fp_name,Array(makePropertyValue("MediaType","application/vnd.sun.xml.writer"),)) + + if docinfo.getUserFieldValue(2)=="": + name=self.win.getEditText("txtName"), + name_id={} + try: + name_id = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml' , 'search',[('name','=',name)]) + if not name_id: + id=self.getID() + docinfo.setUserFieldValue(2,id) + rec = { + 'name': self.win.getEditText("txtReportName"), + 'key': 'action', + 'model': docinfo.getUserFieldValue(3), + 'value': 'ir.actions.report.xml,'+str(id), + 'key2': 'client_print_multi', + 'object': True + } + res = self.sock.execute(database, uid, self.password, 'ir.values' , 'create',rec ) + else : + ErrorDialog(" Report Name is all ready given !!!\n\n\n Please specify other Name","","Report Name") + self.logobj.log_write('SendToServer',LOG_WARNING, ':Report name all ready given DB %s' % (database)) + self.win.endExecute() + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ServerParameter', LOG_ERROR, info) + else: + + id = docinfo.getUserFieldValue(2) + vId = self.sock.execute(database, uid, self.password, 'ir.values' , 'search', [('value','=','ir.actions.report.xml,'+str(id))]) + rec = { 'name': self.win.getEditText("txtReportName") } + res = self.sock.execute(database, uid, self.password, 'ir.values' , 'write',vId,rec) + oDoc2.store() + data = read_data_from_file( get_absolute_file_path( oDoc2.getURL()[7:] ) ) + self.getInverseFieldsRecord(0) + #sock = xmlrpclib.ServerProxy(docinfo.getUserFieldValue(0) +'/xmlrpc/object') + + file_type = oDoc2.getURL()[7:].split(".")[-1] + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'upload_report', int(docinfo.getUserFieldValue(2)),base64.encodestring(data),file_type,{}) + params = { + 'name': self.win.getEditText("txtName"), + 'model': docinfo.getUserFieldValue(3), + 'report_name': self.win.getEditText("txtReportName"), + 'header': (self.win.getCheckBoxState("chkHeader") <> 0), + 'report_type': self.Kind[self.win.getListBoxSelectedItem("lstResourceType")], + } + if self.win.getListBoxSelectedItem("lstResourceType")=='OpenOffice': + params['report_type']=file_type + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'write', int(docinfo.getUserFieldValue(2)), params) + self.logobj.log_write('SendToServer',LOG_INFO, ':Report %s successfully send using %s'%(params['name'],database)) + self.win.endExecute() + else: + ErrorDialog("Either Report Name or Technical Name is blank !!!\nPlease specify appropriate Name","","Blank Field ERROR") + self.logobj.log_write('SendToServer',LOG_WARNING, ':Either Report Name or Technical Name is blank') + self.win.endExecute() + + def getID(self): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + params = { + 'name': self.win.getEditText("txtName"), + 'model': docinfo.getUserFieldValue(3), + 'report_name': self.win.getEditText('txtReportName') + } + + + id=self.sock.execute(database, uid, self.password, 'ir.actions.report.xml' ,'create', params) + return id + + def getInverseFieldsRecord(self,nVal): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + count=0 + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + oPar.SelectedItem = oPar.Items[nVal] + if nVal==0: + oPar.update() + +if __name__<>"package" and __name__=="__main__": + SendtoServer(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( SendtoServer, "org.openoffice.openerp.report.sendtoserver", ("com.sun.star.task.Job",),) + + +import uno +from com.sun.star.task import XJobExecutor + +if __name__<>'package': + from lib.gui import * + +class About(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + self.win = DBModalDialog(60, 50, 175, 115, ".:: About OpenERP Report Designer ::.") + + fdBigFont = createUnoStruct("com.sun.star.awt.FontDescriptor") + fdBigFont.Width = 20 + fdBigFont.Height = 25 + fdBigFont.Weight = 120 + fdBigFont.Family= 3 + + oLabelTitle1 = self.win.addFixedText("lblTitle1", 1, 1, 35, 30) + oLabelTitle1.Model.TextColor = 16056320 + oLabelTitle1.Model.FontDescriptor = fdBigFont + oLabelTitle1.Model.FontRelief = 1 + oLabelTitle1.Text = "Open" + + oLabelTitle2 = self.win.addFixedText("lblTitle2", 35, 1, 30, 30) + oLabelTitle2.Model.TextColor = 1 + oLabelTitle2.Model.FontDescriptor = fdBigFont + oLabelTitle2.Model.FontRelief = 1 + oLabelTitle2.Text = "ERP" + + oLabelProdDesc = self.win.addFixedText("lblProdDesc", 1, 30, 173, 75) + oLabelProdDesc.Model.TextColor = 1 + fdBigFont.Width = 10 + fdBigFont.Height = 11 + fdBigFont.Weight = 76 + oLabelProdDesc.Model.FontDescriptor = fdBigFont + oLabelProdDesc.Model.Align = 1 + oLabelProdDesc.Model.FontRelief = 1 + oLabelProdDesc.Model.MultiLine = True + oLabelProdDesc.Text = "This package helps you to create or modify\nreports in OpenERP. Once connected to the\nserver, you can design your template of reports\nusing fields and expressions and browsing the\ncomplete structure of OpenERP object database." + + oLabelFooter = self.win.addFixedText("lblFooter", -1, -1, 173, 25) + oLabelFooter.Model.TextColor = 255 + #oLabelFooter.Model.BackgroundColor = 1 + oLabelFooter.Model.Border = 2 + oLabelFooter.Model.BorderColor = 255 + fdBigFont.Width = 8 + fdBigFont.Height = 9 + fdBigFont.Weight = 100 + oLabelFooter.Model.FontDescriptor = fdBigFont + oLabelFooter.Model.Align = 1 + oLabelFooter.Model.FontRelief = 1 + oLabelFooter.Model.MultiLine = True + sMessage = "OpenERP Report Designer v1.0 \nCopyright 2007-TODAY Tiny sprl \nThis product is free software, under the GPL licence." + oLabelFooter.Text = sMessage + + self.win.doModalDialog("",None) + +if __name__<>"package" and __name__=="__main__": + About(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( About, "org.openoffice.openerp.report.about", ("com.sun.star.task.Job",),) + + +import uno +import unohelper +import string +import re +import base64 + +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from LoginTest import * + from lib.logreport import * + from lib.rpc import * + database="test" + uid = 1 + + + +class ConvertBracesToField( unohelper.Base, XJobExecutor ): + + def __init__(self,ctx): + + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + self.aReportSyntex=[] + self.getBraces(self.aReportSyntex) + + self.setValue() + + + def setValue(self): + + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo= doc.getDocumentInfo() + count = 0 + regexes = [ + ['[a-zA-Z0-9_]+\.[a-zA-Z0-9_.]+',"Field"], + ['\\[\\[ *repeatIn\\( *([a-zA-Z0-9_\.]+), *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', "RepeatIn"], + ['\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', "Field"] + # ['\\[\\[ ([a-zA-Z0-9_]+\.[a-zA-Z1-9]) \\]\\]',"Field"], + # ['\\[\\[ [a-zA-Z0-9_\.]+ and ([a-zA-Z0-9_\.]+) or .+? \\]\\]',"Field"], + # ['\\[\\[ ([a-zA-Z0-9_\.]+) or .+? \\]\\]',"Field"], + # ['\\[\\[ ([a-zA-Z0-9_\.]+) and .+? \\]\\]',"Field"], + # ['\\[\\[ .+? or ([a-zA-Z0-9_\.]+) \\]\\]',"Field"], + # ['\\[\\[ (.+?) and ([a-zA-Z0-9_\.]+) \\]\\]',"Field"], + # ['\\[\\[ .+? % ([a-zA-Z0-9_\.]+) \\]\\]',"Field"] + ] + oFieldObject = [] + oRepeatInObjects = [] + saRepeatInList = [] + sHost = docinfo.getUserFieldValue(0) + nCount = 0 + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + nCount += 1 + getList(oRepeatInObjects,sHost,nCount) + for ro in oRepeatInObjects: + if ro.find("(")<>-1: + saRepeatInList.append( [ ro[:ro.find("(")], ro[ro.find("(")+1:ro.find(")")] ]) + try: + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + for reg in regexes: + res=re.findall(reg[0],oPar.Items[1]) + if len(res) <> 0: + if res[0][0] == "objects": + sTemp = docinfo.getUserFieldValue(3) + sTemp = "|-." + sTemp[sTemp.rfind(".")+1:] + ".-|" + oPar.Items=(sTemp.encode("utf-8"),oPar.Items[1].replace(' ',"")) + oPar.update() + elif type(res[0]) <> type(u''): + + sObject = self.getRes(self.sock, docinfo.getUserFieldValue(3), res[0][0][res[0][0].find(".")+1:].replace(".","/")) + r = self.sock.execute(database, uid, self.password, docinfo.getUserFieldValue(3) , 'fields_get') + sExpr="|-." + r[res[0][0][res[0][0].rfind(".")+1:]]["string"] + ".-|" + oPar.Items=(sExpr.encode("utf-8"),oPar.Items[1].replace(' ',"")) + oPar.update() + else: + + obj = None + for rl in saRepeatInList: + if rl[0] == res[0][:res[0].find(".")]: + obj=rl[1] + try: + sObject = self.getRes(self.sock, obj, res[0][res[0].find(".")+1:].replace(".","/")) + r = self.sock.execute(database, uid, self.password, sObject , 'read',[1]) + except Exception,e: + r = "TTT" + self.logobj.log_write('ConvertBracesToField', LOG_ERROR, str(e)) + if len(r) <> 0: + if r <> "TTT": + if len(res)>1: + sExpr="" + print res + if reg[1] == 'Field': + for ires in res: + try: + sExpr=r[0][ires[ires.rfind(".")+1:]] + break + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBracesToField', LOG_ERROR,info) + try: + oPar.Items=(sExpr.encode("utf-8") ,oPar.Items[1]) + oPar.update() + except: + oPar.Items=(str(sExpr) ,oPar.Items[1]) + oPar.update() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBracesToField', LOG_ERROR, info) + + else: + sExpr=r[0][res[0][res[0].rfind(".")+1:]] + try: + + if sExpr: + oPar.Items=(sExpr.encode("utf-8") ,oPar.Items[1]) + oPar.update() + else: + oPar.Items=(u"/",oPar.Items[1]) + oPar.update() + except: + oPar.Items=(str(sExpr) ,oPar.Items[1]) + oPar.update() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBracesToField', LOG_ERROR,info) + + else: + oPar.Items=(u""+r,oPar.Items[1]) + oPar.update() + else: + oPar.Items=(u"TTT",oPar.Items[1]) + oPar.update() + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBraceToField', LOG_ERROR, info) + + def getRes(self,sock,sObject,sVar): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, self.password, sObject , 'fields_get') + key = res.keys() + key.sort() + myval=None + if not sVar.find("/")==-1: + myval=sVar[:sVar.find("/")] + else: + myval=sVar + for k in key: + if (res[k]['type'] in ['many2one']) and k==myval: + sObject = self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:]) + return sObject + + def getBraces(self,aReportSyntex=[]): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + aSearchString=[] + aReplaceString=[] + aRes=[] + + try: + regexes = [ + ['\\[\\[ *repeatIn\\( *([a-zA-Z0-9_\.]+), *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', "RepeatIn"], + ['\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', "Field"], + ['\\[\\[ *.+? *\\]\\]', "Expression"] + ] + + search = doc.createSearchDescriptor() + search.SearchRegularExpression = True + + for reg in regexes: + search.SearchString = reg[0] + found = doc.findFirst( search ) + while found: + res=re.findall(reg[0],found.String) + print len(res) + + if found.String not in [r[0] for r in aReportSyntex] and len(res) == 1 : + text=found.getText() + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if reg[1]<>"Expression": + oInputList.Items=(u""+found.String,u""+found.String) + else: + oInputList.Items=(u"?",u""+found.String) + aReportSyntex.append([oInputList,reg[1]]) + text.insertTextContent(found,oInputList,False) + found.String ="" + + else: + aRes.append([res,reg[1]]) + found = doc.findNext(found.End, search) + search = doc.createSearchDescriptor() + search.SearchRegularExpression = False + + for res in aRes: + for r in res[0]: + search.SearchString=r + found=doc.findFirst(search) + while found: + + text=found.getText() + + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if res[1]<>"Expression": + oInputList.Items=(u""+found.String,u""+found.String) + else: + oInputList.Items=(u"?",u""+found.String) + aReportSyntex.append([oInputList,res[1]]) + text.insertTextContent(found,oInputList,False) + found.String ="" + found = doc.findNext(found.End, search) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBraceToField', LOG_ERROR, info) + +if __name__<>"package": + ConvertBracesToField(None) +else: + g_ImplementationHelper.addImplementation( ConvertBracesToField, "org.openoffice.openerp.report.convertBF", ("com.sun.star.task.Job",),) + +import uno +import unohelper +import string +import re +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from LoginTest import * + database="test" + uid = 3 + +class ConvertFieldsToBraces( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + self.aReportSyntex=[] + self.getFields() + + def getFields(self): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + oPar.getAnchor().Text.insertString(oPar.getAnchor(),oPar.Items[1],False) + oPar.dispose() + +if __name__<>"package": + ConvertFieldsToBraces(None) +else: + g_ImplementationHelper.addImplementation( ConvertFieldsToBraces, "org.openoffice.openerp.report.convertFB", ("com.sun.star.task.Job",),) + +import os +import uno +import unohelper +import string +import tempfile +import base64 +import sys + +reload(sys) +sys.setdefaultencoding("utf8") +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from LoginTest import * + from lib.error import * + from lib.tools import * + from lib.logreport import * + from lib.rpc import * + database="test" + uid = 3 + + +class ExportToRML( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + global url + self.sock=RPCSession(url) + + # Read Data from sxw file + tmpsxw = tempfile.mktemp('.'+"sxw") + + if not doc.hasLocation(): + mytype = Array(makePropertyValue("MediaType","application/vnd.sun.xml.writer"),) + doc.storeAsURL("file://"+tmpsxw,mytype) + data = read_data_from_file( get_absolute_file_path( doc.getURL()[7:] ) ) + file_type = doc.getURL()[7:].split(".")[-1] + if docinfo.getUserFieldValue(2) == "": + ErrorDialog("Please Save this file on server","Use Send To Server Option in OpenERP Report Menu","Error") + exit(1) + filename = self.GetAFileName() + if not filename: + exit(1) + global passwd + self.password = passwd + try: + + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'sxwtorml',base64.encodestring(data),file_type) + if res['report_rml_content']: + write_data_to_file( get_absolute_file_path( filename[7:] ), res['report_rml_content'] ) + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ExportToRML',LOG_ERROR, info) + ErrorDialog("Can't save the file to the hard drive.", "Exception: %s" % e, "Error" ) + + def GetAFileName(self): + sFilePickerArgs = Array(10) + oFileDialog = createUnoService("com.sun.star.ui.dialogs.FilePicker") + oFileDialog.initialize(sFilePickerArgs) + oFileDialog.appendFilter("OpenERP Report File Save To ....","*.rml") + + f_path = "OpenERP-"+ os.path.basename( tempfile.mktemp("","") ) + ".rml" + initPath = tempfile.gettempdir() + oUcb = createUnoService("com.sun.star.ucb.SimpleFileAccess") + if oUcb.exists(initPath): + oFileDialog.setDisplayDirectory('file://' + ( os.name == 'nt' and '/' or '' ) + initPath ) + + oFileDialog.setDefaultName(f_path ) + + sPath = oFileDialog.execute() == 1 and oFileDialog.Files[0] or None + oFileDialog.dispose() + return sPath + +if __name__<>"package" and __name__=="__main__": + ExportToRML(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ExportToRML, "org.openoffice.openerp.report.exporttorml", ("com.sun.star.task.Job",),) + +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.functions import * + from lib.error import ErrorDialog + from LoginTest import * + from lib.rpc import * + database="test_001" + uid = 3 + + +class AddLang(unohelper.Base, XJobExecutor ): + def __init__(self,sVariable="",sFields="",sDisplayName="",bFromModify=False): + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + global passwd + self.password = passwd + self.win = DBModalDialog(60, 50, 180, 225, "Set Lang Builder") + + self.win.addFixedText("lblVariable", 27, 12, 60, 15, "Variable :") + self.win.addComboBox("cmbVariable", 180-120-2, 10, 120, 15,True, + itemListenerProc=self.cmbVariable_selected) + self.insVariable = self.win.getControl( "cmbVariable" ) + + self.win.addFixedText("lblFields", 10, 32, 60, 15, "Variable Fields :") + self.win.addComboListBox("lstFields", 180-120-2, 30, 120, 150, False,itemListenerProc=self.lstbox_selected) + self.insField = self.win.getControl( "lstFields" ) + self.win.addFixedText("lblUName", 8, 187, 60, 15, "Displayed name :") + self.win.addEdit("txtUName", 180-120-2, 185, 120, 15,) + + self.win.addButton('btnOK',-5 ,-5, 45, 15, 'Ok', actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-5 - 45 - 5 ,-5,45,15,'Cancel', actionListenerProc = self.btnCancel_clicked ) + self.sValue=None + self.sObj=None + self.aSectionList=[] + self.sGDisplayName=sDisplayName + self.aItemList=[] + self.aComponentAdd=[] + self.aObjectList=[] + self.aListFields=[] + self.aVariableList=[] + EnumDocument(self.aItemList,self.aComponentAdd) + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.sMyHost= "" + global url + self.sock=RPCSession(url) + + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + self.sMyHost= docinfo.getUserFieldValue(0) + self.count=0 + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + self.count += 1 + getList(self.aObjectList, self.sMyHost,self.count) + cursor = doc.getCurrentController().getViewCursor() + text=cursor.getText() + tcur=text.createTextCursorByRange(cursor) + + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == "Objects", self.aObjectList ) ) + + for i in range(len(self.aItemList)): + anItem = self.aItemList[i][1] + component = self.aComponentAdd[i] + + if component == "Document": + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextSection: + getRecersiveSection(tcur.TextSection,self.aSectionList) + if component in self.aSectionList: + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextTable: + if not component == "Document" and component[component.rfind(".") + 1:] == tcur.TextTable.Name: + VariableScope(tcur,self.insVariable,self.aObjectList,self.aComponentAdd,self.aItemList,component) + + self.bModify=bFromModify + if self.bModify==True: + sItem="" + for anObject in self.aObjectList: + if anObject[:anObject.find("(")] == sVariable: + sItem = anObject + self.insVariable.setText( sItem ) + genTree(sItem[sItem.find("(")+1:sItem.find(")")],self.aListFields, self.insField,self.sMyHost,2,ending_excl=['one2many','many2one','many2many','reference'], recur=['many2one']) + self.sValue= self.win.getListBoxItem("lstFields",self.aListFields.index(sFields)) + + for var in self.aVariableList: + + self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])]) + fields=['name','model'] + self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields) + if self.model_res <> []: + self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem(var ,self.insVariable.getItemCount()) + + self.win.doModalDialog("lstFields",self.sValue) + else: + ErrorDialog("Please insert user define field Field-1 or Field-4","Just go to File->Properties->User Define \nField-1 Eg. http://localhost:8069 \nOR \nField-4 Eg. account.invoice") + self.win.endExecute() + + def lstbox_selected(self,oItemEvent): + try: + + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sItem= self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")+1]==sItem[:sItem.find("(")+1]: + sItem = var + sMain=self.aListFields[self.win.getListBoxSelectedItemPos("lstFields")] + t=sMain.rfind('/lang') + if t!=-1: + sObject=self.getRes(self.sock,sItem[sItem.find("(")+1:-1],sMain[1:]) + ids = self.sock.execute(database, uid, self.password, sObject , 'search', []) + res = self.sock.execute(database, uid, self.password, sObject , 'read',[ids[0]]) + self.win.setEditText("txtUName",res[0][sMain[sMain.rfind("/")+1:]]) + else: + ErrorDialog("Please select the Language Field") + + except: + import traceback;traceback.print_exc() + self.win.setEditText("txtUName","TTT") + if self.bModify: + self.win.setEditText("txtUName",self.sGDisplayName) + + def getRes(self,sock ,sObject,sVar): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, self.password, sObject , 'fields_get') + key = res.keys() + key.sort() + myval=None + if not sVar.find("/")==-1: + myval=sVar[:sVar.find("/")] + else: + myval=sVar + if myval in key: + if (res[myval]['type'] in ['many2one']): + sObject = res[myval]['relation'] + return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:]) + else: + return sObject + + + def cmbVariable_selected(self,oItemEvent): + if self.count > 0 : + try: + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields")) + self.aListFields=[] + tempItem = self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")] == tempItem[:tempItem.find("(")]: + sItem=var + + genTree( + sItem[ sItem.find("(") + 1:sItem.find(")")], + self.aListFields, + self.insField, + self.sMyHost, + 2, + ending_excl=['one2many','many2one','many2many','reference'], + recur=['many2one'] + ) + + except: + import traceback;traceback.print_exc() + + def btnOk_clicked( self, oActionEvent ): + self.bOkay = True + desktop=getDesktop() + doc = desktop.getCurrentComponent() + cursor = doc.getCurrentController().getViewCursor() + + itemSelected = self.win.getListBoxSelectedItem( "lstFields" ) + itemSelectedPos = self.win.getListBoxSelectedItemPos( "lstFields" ) + txtUName = self.win.getEditText("txtUName") + sKey=u""+ txtUName + + if itemSelected != "" and txtUName != "" and self.bModify==True : + oCurObj=cursor.TextField + sObjName=self.insVariable.getText() + sObjName=sObjName[:sObjName.find("(")] + sValue=u"[[ setLang" + sObjName + self.aListFields[itemSelectedPos].replace("/",".") + ")" " ]]" + oCurObj.Items = (sKey,sValue) + oCurObj.update() + self.win.endExecute() + elif itemSelected != "" and txtUName != "" : + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + sObjName=self.win.getComboBoxText("cmbVariable") + sObjName=sObjName[:sObjName.find("(")] + + widget = ( cursor.TextTable and cursor.TextTable.getCellByName( cursor.Cell.CellName ) or doc.Text ) + + sValue = u"[[setLang" + "(" + sObjName + self.aListFields[itemSelectedPos].replace("/",".") +")" " ]]" + oInputList.Items = (sKey,sValue) + widget.insertTextContent(cursor,oInputList,False) + + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Name field \nor select perticular value from the list of fields") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + +if __name__<>"package" and __name__=="__main__": + AddLang() +elif __name__=="package": + g_ImplementationHelper.addImplementation( AddLang, "org.openoffice.openerp.report.langtag", ("com.sun.star.task.Job",),) + +import os +import uno +import unohelper +import xmlrpclib +import base64 +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.tools import * + from LoginTest import * + from lib.rpc import * + database="test" + uid = 3 + +class AddAttachment(unohelper.Base, XJobExecutor ): + Kind = { + 'PDF' : 'pdf', + 'OpenOffice': 'sxw', + } + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + self.aSearchResult = [] + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + if docinfo.getUserFieldValue(2) <> "" and docinfo.getUserFieldValue(3) <> "": + self.win = DBModalDialog(60, 50, 180, 70, "Add Attachment to Server") + self.win.addFixedText("lblResourceType", 2 , 5, 100, 10, "Select Appropriate Resource Type:") + self.win.addComboListBox("lstResourceType", -2, 25, 176, 15,True) + self.win.addButton('btnOkWithoutInformation', -2 , -5, 25 , 15,'OK' ,actionListenerProc = self.btnOkWithoutInformation_clicked ) + else: + self.win = DBModalDialog(60, 50, 180, 190, "Add Attachment to Server") + self.win.addFixedText("lblModuleName",2 , 9, 42, 20, "Select Module:") + self.win.addComboListBox("lstmodel", -2, 5, 134, 15,True) + self.lstModel = self.win.getControl( "lstmodel" ) + self.dModel = {} + + # Open a new connexion to the server + + ids = self.sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_model'),('state', '=', 'installed')]) + if not len(ids): + # If the module 'base_report_model' is not installed, use the default model + self.dModel = { + "Partner":'res.partner', + } + else: + + ids =self.sock.execute(database, uid, self.password, 'base.report.model' , 'search', []) + res = self.sock.execute(database, uid, self.password, 'base.report.model' , 'read', ids, ['name','model_id']) + models = self.sock.execute(database, uid, self.password, 'ir.model' , 'read', map(lambda x:x['model_id'][0], res), ['model']) + models = dict(map(lambda x:(x['id'],x['model']), models)) + self.dModel = dict(map(lambda x: (x['name'],models[x['model_id'][0]]), res)) + + for item in self.dModel.keys(): + self.lstModel.addItem(item, self.lstModel.getItemCount()) + + self.win.addFixedText("lblSearchName",2 , 25, 60, 10, "Enter Search String:") + self.win.addEdit("txtSearchName", 2, 35, 149, 15,) + self.win.addButton('btnSearch', -2 , 35, 25 , 15,'Search' ,actionListenerProc = self.btnSearch_clicked ) + + self.win.addFixedText("lblSearchRecord", 2 , 55, 60, 10, "Search Result:") + self.win.addComboListBox("lstResource", -2, 65, 176, 70, False ) + self.lstResource = self.win.getControl( "lstResource" ) + + self.win.addFixedText("lblResourceType", 2 , 137, 100, 20, "Select Appropriate Resource Type:") + self.win.addComboListBox("lstResourceType", -2, 147, 176, 15,True ) + + self.win.addButton('btnOkWithInformation', -2 , -5, 25 , 15,'OK' ,actionListenerProc = self.btnOkWithInformation_clicked ) + + self.lstResourceType = self.win.getControl( "lstResourceType" ) + for kind in self.Kind.keys(): + self.lstResourceType.addItem( kind, self.lstResourceType.getItemCount() ) + + self.win.addButton('btnCancel', -2 - 27 , -5 , 30 , 15, 'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.doModalDialog("lstResourceType", self.Kind.keys()[0]) + + def btnSearch_clicked( self, oActionEvent ): + modelSelectedItem = self.win.getListBoxSelectedItem("lstmodel") + if modelSelectedItem == "": + return + + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + + self.aSearchResult =self.sock.execute( database, uid, self.password, self.dModel[modelSelectedItem], 'name_search', self.win.getEditText("txtSearchName")) + self.win.removeListBoxItems("lstResource", 0, self.win.getListBoxItemCount("lstResource")) + if self.aSearchResult == []: + ErrorDialog("No search result found !!!", "", "Search ERROR" ) + return + + for result in self.aSearchResult: + self.lstResource.addItem(result[1],result[0]) + + def _send_attachment( self, name, data, res_model, res_id ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + params = { + 'name': name, + 'datas': base64.encodestring( data ), + 'datas_fname': name, + 'res_model' : res_model, + 'res_id' : int(res_id), + } + + return self.sock.execute( database, uid, self.password, 'ir.attachment', 'create', params ) + + def send_attachment( self, model, resource_id ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + if oDoc2.getURL() == "": + ErrorDialog("Please save your file", "", "Saving ERROR" ) + return None + + url = oDoc2.getURL() + if self.Kind[self.win.getListBoxSelectedItem("lstResourceType")] == "pdf": + url = self.doc2pdf(url[7:]) + + if url == None: + ErrorDialog( "Ploblem in creating PDF", "", "PDF Error" ) + return None + + url = url[7:] + data = read_data_from_file( get_absolute_file_path( url ) ) + return self._send_attachment( os.path.basename( url ), data, model, resource_id ) + + def btnOkWithoutInformation_clicked( self, oActionEvent ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + if self.win.getListBoxSelectedItem("lstResourceType") == "": + ErrorDialog("Please select resource type", "", "Selection ERROR" ) + return + + res = self.send_attachment( docinfo.getUserFieldValue(3), docinfo.getUserFieldValue(2) ) + self.win.endExecute() + + def btnOkWithInformation_clicked(self,oActionEvent): + if self.win.getListBoxSelectedItem("lstResourceType") == "": + ErrorDialog( "Please select resource type", "", "Selection ERROR" ) + return + + if self.win.getListBoxSelectedItem("lstResource") == "" or self.win.getListBoxSelectedItem("lstmodel") == "": + ErrorDialog("Please select Model and Resource","","Selection ERROR") + return + + resourceid = None + for s in self.aSearchResult: + if s[1] == self.win.getListBoxSelectedItem("lstResource"): + resourceid = s[0] + break + + if resourceid == None: + ErrorDialog("No resource selected !!!", "", "Resource ERROR" ) + return + + res = self.send_attachment( self.dModel[self.win.getListBoxSelectedItem('lstmodel')], resourceid ) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def doc2pdf(self, strFile): + oDoc = None + strFilterSubName = '' + + strUrl = convertToURL( strFile ) + desktop = getDesktop() + oDoc = desktop.loadComponentFromURL( strUrl, "_blank", 0, Array(self._MakePropertyValue("Hidden",True))) + if oDoc: + strFilterSubName = "" + # select appropriate filter + if oDoc.supportsService("com.sun.star.presentation.PresentationDocument"): + strFilterSubName = "impress_pdf_Export" + elif oDoc.supportsService("com.sun.star.sheet.SpreadsheetDocument"): + strFilterSubName = "calc_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.WebDocument"): + strFilterSubName = "writer_web_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.GlobalDocument"): + strFilterSubName = "writer_globaldocument_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.TextDocument"): + strFilterSubName = "writer_pdf_Export" + elif oDoc.supportsService("com.sun.star.drawing.DrawingDocument"): + strFilterSubName = "draw_pdf_Export" + elif oDoc.supportsService("com.sun.star.formula.FormulaProperties"): + strFilterSubName = "math_pdf_Export" + elif oDoc.supportsService("com.sun.star.chart.ChartDocument"): + strFilterSubName = "chart_pdf_Export" + else: + pass + + filename = len(strFilterSubName) > 0 and convertToURL( os.path.splitext( strFile )[0] + ".pdf" ) or None + + if len(strFilterSubName) > 0: + oDoc.storeToURL( filename, Array(self._MakePropertyValue("FilterName", strFilterSubName ),self._MakePropertyValue("CompressMode", "1" ))) + + oDoc.close(True) + # Can be None if len(strFilterSubName) <= 0 + return filename + + def _MakePropertyValue(self, cName = "", uValue = u"" ): + oPropertyValue = createUnoStruct( "com.sun.star.beans.PropertyValue" ) + if cName: + oPropertyValue.Name = cName + if uValue: + oPropertyValue.Value = uValue + return oPropertyValue + + +if __name__<>"package" and __name__=="__main__": + AddAttachment(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( AddAttachment, "org.openoffice.openerp.report.addattachment", ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/About.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/About.py new file mode 100644 index 00000000000..504bfb06943 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/About.py @@ -0,0 +1,66 @@ + + +import uno +from com.sun.star.task import XJobExecutor + +if __name__<>'package': + from lib.gui import * + +class About(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + self.win = DBModalDialog(60, 50, 175, 115, ".:: About OpenERP Report Designer ::.") + + fdBigFont = createUnoStruct("com.sun.star.awt.FontDescriptor") + fdBigFont.Width = 20 + fdBigFont.Height = 25 + fdBigFont.Weight = 120 + fdBigFont.Family= 3 + + oLabelTitle1 = self.win.addFixedText("lblTitle1", 1, 1, 35, 30) + oLabelTitle1.Model.TextColor = 16056320 + oLabelTitle1.Model.FontDescriptor = fdBigFont + oLabelTitle1.Model.FontRelief = 1 + oLabelTitle1.Text = "Open" + + oLabelTitle2 = self.win.addFixedText("lblTitle2", 35, 1, 30, 30) + oLabelTitle2.Model.TextColor = 1 + oLabelTitle2.Model.FontDescriptor = fdBigFont + oLabelTitle2.Model.FontRelief = 1 + oLabelTitle2.Text = "ERP" + + oLabelProdDesc = self.win.addFixedText("lblProdDesc", 1, 30, 173, 75) + oLabelProdDesc.Model.TextColor = 1 + fdBigFont.Width = 10 + fdBigFont.Height = 11 + fdBigFont.Weight = 76 + oLabelProdDesc.Model.FontDescriptor = fdBigFont + oLabelProdDesc.Model.Align = 1 + oLabelProdDesc.Model.FontRelief = 1 + oLabelProdDesc.Model.MultiLine = True + oLabelProdDesc.Text = "This package helps you to create or modify\nreports in OpenERP. Once connected to the\nserver, you can design your template of reports\nusing fields and expressions and browsing the\ncomplete structure of OpenERP object database." + + oLabelFooter = self.win.addFixedText("lblFooter", -1, -1, 173, 25) + oLabelFooter.Model.TextColor = 255 + #oLabelFooter.Model.BackgroundColor = 1 + oLabelFooter.Model.Border = 2 + oLabelFooter.Model.BorderColor = 255 + fdBigFont.Width = 8 + fdBigFont.Height = 9 + fdBigFont.Weight = 100 + oLabelFooter.Model.FontDescriptor = fdBigFont + oLabelFooter.Model.Align = 1 + oLabelFooter.Model.FontRelief = 1 + oLabelFooter.Model.MultiLine = True + sMessage = "OpenERP Report Designer v1.0 \nCopyright 2007-TODAY Tiny sprl \nThis product is free software, under the GPL licence." + oLabelFooter.Text = sMessage + + self.win.doModalDialog("",None) + +if __name__<>"package" and __name__=="__main__": + About(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( About, "org.openoffice.openerp.report.about", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/AddAttachment.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/AddAttachment.py new file mode 100644 index 00000000000..e884f23570a --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/AddAttachment.py @@ -0,0 +1,235 @@ +import os +import uno +import unohelper +import xmlrpclib +import base64 +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.tools import * + from LoginTest import * + from lib.rpc import * + database="test" + uid = 3 + +class AddAttachment(unohelper.Base, XJobExecutor ): + Kind = { + 'PDF' : 'pdf', + 'OpenOffice': 'sxw', + } + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + self.aSearchResult = [] + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + global passwd + self.password = passwd + global url + + + docinfo=oDoc2.getDocumentInfo() + print ":docinfodocinfo::::",docinfo.getUserFieldValue(0) + self.sock=RPCSession(url) + if docinfo.getUserFieldValue(2) <> "" and docinfo.getUserFieldValue(3) <> "": + self.win = DBModalDialog(60, 50, 180, 70, "Add Attachment to Server") + self.win.addFixedText("lblResourceType", 2 , 5, 100, 10, "Select Appropriate Resource Type:") + self.win.addComboListBox("lstResourceType", -2, 25, 176, 15,True) + self.win.addButton('btnOkWithoutInformation', -2 , -5, 25 , 15,'OK' ,actionListenerProc = self.btnOkWithoutInformation_clicked ) + else: + self.win = DBModalDialog(60, 50, 180, 190, "Add Attachment to Server") + self.win.addFixedText("lblModuleName",2 , 9, 42, 20, "Select Module:") + self.win.addComboListBox("lstmodel", -2, 5, 134, 15,True) + self.lstModel = self.win.getControl( "lstmodel" ) + self.dModel = {} + + # Open a new connexion to the server + + ids = self.sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_model'),('state', '=', 'installed')]) + if not len(ids): + # If the module 'base_report_model' is not installed, use the default model + self.dModel = { + "Partner":'res.partner', + } + else: + + ids =self.sock.execute(database, uid, self.password, 'base.report.model' , 'search', []) + res = self.sock.execute(database, uid, self.password, 'base.report.model' , 'read', ids, ['name','model_id']) + models = self.sock.execute(database, uid, self.password, 'ir.model' , 'read', map(lambda x:x['model_id'][0], res), ['model']) + models = dict(map(lambda x:(x['id'],x['model']), models)) + self.dModel = dict(map(lambda x: (x['name'],models[x['model_id'][0]]), res)) + + for item in self.dModel.keys(): + self.lstModel.addItem(item, self.lstModel.getItemCount()) + + self.win.addFixedText("lblSearchName",2 , 25, 60, 10, "Enter Search String:") + self.win.addEdit("txtSearchName", 2, 35, 149, 15,) + self.win.addButton('btnSearch', -2 , 35, 25 , 15,'Search' ,actionListenerProc = self.btnSearch_clicked ) + + self.win.addFixedText("lblSearchRecord", 2 , 55, 60, 10, "Search Result:") + self.win.addComboListBox("lstResource", -2, 65, 176, 70, False ) + self.lstResource = self.win.getControl( "lstResource" ) + + self.win.addFixedText("lblResourceType", 2 , 137, 100, 20, "Select Appropriate Resource Type:") + self.win.addComboListBox("lstResourceType", -2, 147, 176, 15,True ) + + self.win.addButton('btnOkWithInformation', -2 , -5, 25 , 15,'OK' ,actionListenerProc = self.btnOkWithInformation_clicked ) + + self.lstResourceType = self.win.getControl( "lstResourceType" ) + for kind in self.Kind.keys(): + self.lstResourceType.addItem( kind, self.lstResourceType.getItemCount() ) + + self.win.addButton('btnCancel', -2 - 27 , -5 , 30 , 15, 'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.doModalDialog("lstResourceType", self.Kind.keys()[0]) + + def btnSearch_clicked( self, oActionEvent ): + modelSelectedItem = self.win.getListBoxSelectedItem("lstmodel") + if modelSelectedItem == "": + return + + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + + self.aSearchResult =self.sock.execute( database, uid, self.password, self.dModel[modelSelectedItem], 'name_search', self.win.getEditText("txtSearchName")) + self.win.removeListBoxItems("lstResource", 0, self.win.getListBoxItemCount("lstResource")) + if self.aSearchResult == []: + ErrorDialog("No search result found !!!", "", "Search ERROR" ) + return + + for result in self.aSearchResult: + self.lstResource.addItem(result[1],result[0]) + + def _send_attachment( self, name, data, res_model, res_id ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + params = { + 'name': name, + 'datas': base64.encodestring( data ), + 'datas_fname': name, + 'res_model' : res_model, + 'res_id' : int(res_id), + } + + return self.sock.execute( database, uid, self.password, 'ir.attachment', 'create', params ) + + def send_attachment( self, model, resource_id ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + if oDoc2.getURL() == "": + ErrorDialog("Please save your file", "", "Saving ERROR" ) + return None + + url = oDoc2.getURL() + if self.Kind[self.win.getListBoxSelectedItem("lstResourceType")] == "pdf": + url = self.doc2pdf(url[7:]) + + if url == None: + ErrorDialog( "Ploblem in creating PDF", "", "PDF Error" ) + return None + + url = url[7:] + data = read_data_from_file( get_absolute_file_path( url ) ) + return self._send_attachment( os.path.basename( url ), data, model, resource_id ) + + def btnOkWithoutInformation_clicked( self, oActionEvent ): + desktop = getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo = oDoc2.getDocumentInfo() + + if self.win.getListBoxSelectedItem("lstResourceType") == "": + ErrorDialog("Please select resource type", "", "Selection ERROR" ) + return + + res = self.send_attachment( docinfo.getUserFieldValue(3), docinfo.getUserFieldValue(2) ) + self.win.endExecute() + + def btnOkWithInformation_clicked(self,oActionEvent): + if self.win.getListBoxSelectedItem("lstResourceType") == "": + ErrorDialog( "Please select resource type", "", "Selection ERROR" ) + return + + if self.win.getListBoxSelectedItem("lstResource") == "" or self.win.getListBoxSelectedItem("lstmodel") == "": + ErrorDialog("Please select Model and Resource","","Selection ERROR") + return + + resourceid = None + for s in self.aSearchResult: + if s[1] == self.win.getListBoxSelectedItem("lstResource"): + resourceid = s[0] + break + + if resourceid == None: + ErrorDialog("No resource selected !!!", "", "Resource ERROR" ) + return + + res = self.send_attachment( self.dModel[self.win.getListBoxSelectedItem('lstmodel')], resourceid ) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def doc2pdf(self, strFile): + oDoc = None + strFilterSubName = '' + + strUrl = convertToURL( strFile ) + desktop = getDesktop() + oDoc = desktop.loadComponentFromURL( strUrl, "_blank", 0, Array(self._MakePropertyValue("Hidden",True))) + if oDoc: + strFilterSubName = "" + # select appropriate filter + if oDoc.supportsService("com.sun.star.presentation.PresentationDocument"): + strFilterSubName = "impress_pdf_Export" + elif oDoc.supportsService("com.sun.star.sheet.SpreadsheetDocument"): + strFilterSubName = "calc_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.WebDocument"): + strFilterSubName = "writer_web_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.GlobalDocument"): + strFilterSubName = "writer_globaldocument_pdf_Export" + elif oDoc.supportsService("com.sun.star.text.TextDocument"): + strFilterSubName = "writer_pdf_Export" + elif oDoc.supportsService("com.sun.star.drawing.DrawingDocument"): + strFilterSubName = "draw_pdf_Export" + elif oDoc.supportsService("com.sun.star.formula.FormulaProperties"): + strFilterSubName = "math_pdf_Export" + elif oDoc.supportsService("com.sun.star.chart.ChartDocument"): + strFilterSubName = "chart_pdf_Export" + else: + pass + + filename = len(strFilterSubName) > 0 and convertToURL( os.path.splitext( strFile )[0] + ".pdf" ) or None + + if len(strFilterSubName) > 0: + oDoc.storeToURL( filename, Array(self._MakePropertyValue("FilterName", strFilterSubName ),self._MakePropertyValue("CompressMode", "1" ))) + + oDoc.close(True) + # Can be None if len(strFilterSubName) <= 0 + return filename + + def _MakePropertyValue(self, cName = "", uValue = u"" ): + oPropertyValue = createUnoStruct( "com.sun.star.beans.PropertyValue" ) + if cName: + oPropertyValue.Name = cName + if uValue: + oPropertyValue.Value = uValue + return oPropertyValue + + +if __name__<>"package" and __name__=="__main__": + AddAttachment(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( AddAttachment, "org.openoffice.openerp.report.addattachment", ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Change.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Change.py new file mode 100644 index 00000000000..aeecd4e522c --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Change.py @@ -0,0 +1,62 @@ +if __name__<>"package": + from lib.gui import * + from lib.functions import * + from lib.rpc import * + +class Change: + def __init__(self, aVal= None, sURL=""): + self.win=DBModalDialog(60, 50, 120, 90, "Connect to Open ERP Server") + + self.win.addFixedText("lblVariable", 38, 12, 60, 15, "Server") + + self.win.addEdit("txtHost",-2,9,60,15,sURL[sURL.find("/")+2:sURL.rfind(":")]) + + self.win.addFixedText("lblReportName",45 , 31, 60, 15, "Port") + self.win.addEdit("txtPort",-2,28,60,15,sURL[sURL.rfind(":")+1:]) + + self.win.addFixedText("lblLoginName", 2, 51, 60, 15, "Protocol Connection") + + self.win.addComboListBox("lstProtocol", -2, 48, 60, 15, True) + self.lstProtocol = self.win.getControl( "lstProtocol" ) + +# self.lstProtocol.addItem( "XML-RPC", 0) + #self.lstProtocol.addItem( "XML-RPC secure", 1) + #self.lstProtocol.addItem( "NET-RPC (faster)", 2) + + self.win.addButton( 'btnOK', -2, -5, 30, 15, 'Ok', actionListenerProc = self.btnOk_clicked ) + + self.win.addButton( 'btnCancel', -2 - 30 - 5 ,-5, 30, 15, 'Cancel', actionListenerProc = self.btnCancel_clicked ) + self.aVal=aVal + self.protocol = { + 'XML-RPC': 'http://', + 'XML-RPC secure': 'https://', + 'NET-RPC': 'socket://', + } + for i in self.protocol.keys(): + self.lstProtocol.addItem(i,self.lstProtocol.getItemCount() ) + + sValue=self.protocol.keys()[0] + if sURL<>"": + sValue=self.protocol.keys()[self.protocol.values().index(sURL[:sURL.find("/")+2])] + + self.win.doModalDialog( "lstProtocol", sValue) + + def btnOk_clicked(self,oActionEvent): + global url + url = self.protocol[self.win.getListBoxSelectedItem("lstProtocol")]+self.win.getEditText("txtHost")+":"+self.win.getEditText("txtPort") + self.sock=RPCSession(url) + docinfo=doc.getDocumentInfo() + docinfo.setUserFieldValue(0,url) + res=self.sock.listdb() + if res == -1: + self.aVal.append(url) + elif res == 0: + self.aVal.append("No Database found !!!") + else: + self.aVal.append(url) + self.aVal.append(res) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertBracesToField.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertBracesToField.py new file mode 100644 index 00000000000..fb40ec19a20 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertBracesToField.py @@ -0,0 +1,240 @@ + +import uno +import unohelper +import string +import re +import base64 + +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from LoginTest import * + from lib.logreport import * + from lib.rpc import * + database="test" + uid = 1 + + + +class ConvertBracesToField( unohelper.Base, XJobExecutor ): + + def __init__(self,ctx): + + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + self.aReportSyntex=[] + self.getBraces(self.aReportSyntex) + + self.setValue() + + + def setValue(self): + + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo= doc.getDocumentInfo() + count = 0 + regexes = [ + ['[a-zA-Z0-9_]+\.[a-zA-Z0-9_.]+',"Field"], + ['\\[\\[ *repeatIn\\( *([a-zA-Z0-9_\.]+), *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', "RepeatIn"], + ['\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', "Field"] + # ['\\[\\[ ([a-zA-Z0-9_]+\.[a-zA-Z1-9]) \\]\\]',"Field"], + # ['\\[\\[ [a-zA-Z0-9_\.]+ and ([a-zA-Z0-9_\.]+) or .+? \\]\\]',"Field"], + # ['\\[\\[ ([a-zA-Z0-9_\.]+) or .+? \\]\\]',"Field"], + # ['\\[\\[ ([a-zA-Z0-9_\.]+) and .+? \\]\\]',"Field"], + # ['\\[\\[ .+? or ([a-zA-Z0-9_\.]+) \\]\\]',"Field"], + # ['\\[\\[ (.+?) and ([a-zA-Z0-9_\.]+) \\]\\]',"Field"], + # ['\\[\\[ .+? % ([a-zA-Z0-9_\.]+) \\]\\]',"Field"] + ] + oFieldObject = [] + oRepeatInObjects = [] + saRepeatInList = [] + sHost = docinfo.getUserFieldValue(0) + nCount = 0 + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + nCount += 1 + getList(oRepeatInObjects,sHost,nCount) + for ro in oRepeatInObjects: + if ro.find("(")<>-1: + saRepeatInList.append( [ ro[:ro.find("(")], ro[ro.find("(")+1:ro.find(")")] ]) + try: + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + for reg in regexes: + res=re.findall(reg[0],oPar.Items[1]) + if len(res) <> 0: + if res[0][0] == "objects": + sTemp = docinfo.getUserFieldValue(3) + sTemp = "|-." + sTemp[sTemp.rfind(".")+1:] + ".-|" + oPar.Items=(sTemp.encode("utf-8"),oPar.Items[1].replace(' ',"")) + oPar.update() + elif type(res[0]) <> type(u''): + + sObject = self.getRes(self.sock, docinfo.getUserFieldValue(3), res[0][0][res[0][0].find(".")+1:].replace(".","/")) + r = self.sock.execute(database, uid, self.password, docinfo.getUserFieldValue(3) , 'fields_get') + sExpr="|-." + r[res[0][0][res[0][0].rfind(".")+1:]]["string"] + ".-|" + oPar.Items=(sExpr.encode("utf-8"),oPar.Items[1].replace(' ',"")) + oPar.update() + else: + + obj = None + for rl in saRepeatInList: + if rl[0] == res[0][:res[0].find(".")]: + obj=rl[1] + try: + sObject = self.getRes(self.sock, obj, res[0][res[0].find(".")+1:].replace(".","/")) + r = self.sock.execute(database, uid, self.password, sObject , 'read',[1]) + except Exception,e: + r = "TTT" + self.logobj.log_write('ConvertBracesToField', LOG_ERROR, str(e)) + if len(r) <> 0: + if r <> "TTT": + if len(res)>1: + sExpr="" + print res + if reg[1] == 'Field': + for ires in res: + try: + sExpr=r[0][ires[ires.rfind(".")+1:]] + break + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBracesToField', LOG_ERROR,info) + try: + oPar.Items=(sExpr.encode("utf-8") ,oPar.Items[1]) + oPar.update() + except: + oPar.Items=(str(sExpr) ,oPar.Items[1]) + oPar.update() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBracesToField', LOG_ERROR, info) + + else: + sExpr=r[0][res[0][res[0].rfind(".")+1:]] + try: + + if sExpr: + oPar.Items=(sExpr.encode("utf-8") ,oPar.Items[1]) + oPar.update() + else: + oPar.Items=(u"/",oPar.Items[1]) + oPar.update() + except: + oPar.Items=(str(sExpr) ,oPar.Items[1]) + oPar.update() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBracesToField', LOG_ERROR,info) + + else: + oPar.Items=(u""+r,oPar.Items[1]) + oPar.update() + else: + oPar.Items=(u"TTT",oPar.Items[1]) + oPar.update() + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBraceToField', LOG_ERROR, info) + + def getRes(self,sock,sObject,sVar): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, self.password, sObject , 'fields_get') + key = res.keys() + key.sort() + myval=None + if not sVar.find("/")==-1: + myval=sVar[:sVar.find("/")] + else: + myval=sVar + for k in key: + if (res[k]['type'] in ['many2one']) and k==myval: + sObject = self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:]) + return sObject + + def getBraces(self,aReportSyntex=[]): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + aSearchString=[] + aReplaceString=[] + aRes=[] + + try: + regexes = [ + ['\\[\\[ *repeatIn\\( *([a-zA-Z0-9_\.]+), *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', "RepeatIn"], + ['\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', "Field"], + ['\\[\\[ *.+? *\\]\\]', "Expression"] + ] + + search = doc.createSearchDescriptor() + search.SearchRegularExpression = True + + for reg in regexes: + search.SearchString = reg[0] + found = doc.findFirst( search ) + while found: + res=re.findall(reg[0],found.String) + print len(res) + + if found.String not in [r[0] for r in aReportSyntex] and len(res) == 1 : + text=found.getText() + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if reg[1]<>"Expression": + oInputList.Items=(u""+found.String,u""+found.String) + else: + oInputList.Items=(u"?",u""+found.String) + aReportSyntex.append([oInputList,reg[1]]) + text.insertTextContent(found,oInputList,False) + found.String ="" + + else: + aRes.append([res,reg[1]]) + found = doc.findNext(found.End, search) + search = doc.createSearchDescriptor() + search.SearchRegularExpression = False + + for res in aRes: + for r in res[0]: + search.SearchString=r + found=doc.findFirst(search) + while found: + + text=found.getText() + + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if res[1]<>"Expression": + oInputList.Items=(u""+found.String,u""+found.String) + else: + oInputList.Items=(u"?",u""+found.String) + aReportSyntex.append([oInputList,res[1]]) + text.insertTextContent(found,oInputList,False) + found.String ="" + found = doc.findNext(found.End, search) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ConvertBraceToField', LOG_ERROR, info) + +if __name__<>"package": + ConvertBracesToField(None) +else: + g_ImplementationHelper.addImplementation( ConvertBracesToField, "org.openoffice.openerp.report.convertBF", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertFieldsToBraces.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertFieldsToBraces.py new file mode 100644 index 00000000000..a556c277049 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ConvertFieldsToBraces.py @@ -0,0 +1,38 @@ +import uno +import unohelper +import string +import re +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from LoginTest import * + database="test" + uid = 3 + +class ConvertFieldsToBraces( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + self.aReportSyntex=[] + self.getFields() + + def getFields(self): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + oPar.getAnchor().Text.insertString(oPar.getAnchor(),oPar.Items[1],False) + oPar.dispose() + +if __name__<>"package": + ConvertFieldsToBraces(None) +else: + g_ImplementationHelper.addImplementation( ConvertFieldsToBraces, "org.openoffice.openerp.report.convertFB", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ExportToRML.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ExportToRML.py new file mode 100644 index 00000000000..9178dbbf6de --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ExportToRML.py @@ -0,0 +1,86 @@ +import os +import uno +import unohelper +import string +import tempfile +import base64 +import sys + +reload(sys) +sys.setdefaultencoding("utf8") +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from LoginTest import * + from lib.error import * + from lib.tools import * + from lib.logreport import * + from lib.rpc import * + database="test" + uid = 3 + + +class ExportToRML( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + global url + self.sock=RPCSession(url) + + # Read Data from sxw file + tmpsxw = tempfile.mktemp('.'+"sxw") + + if not doc.hasLocation(): + mytype = Array(makePropertyValue("MediaType","application/vnd.sun.xml.writer"),) + doc.storeAsURL("file://"+tmpsxw,mytype) + data = read_data_from_file( get_absolute_file_path( doc.getURL()[7:] ) ) + file_type = doc.getURL()[7:].split(".")[-1] + if docinfo.getUserFieldValue(2) == "": + ErrorDialog("Please Save this file on server","Use Send To Server Option in OpenERP Report Menu","Error") + exit(1) + filename = self.GetAFileName() + if not filename: + exit(1) + global passwd + self.password = passwd + try: + + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'sxwtorml',base64.encodestring(data),file_type) + if res['report_rml_content']: + write_data_to_file( get_absolute_file_path( filename[7:] ), res['report_rml_content'] ) + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ExportToRML',LOG_ERROR, info) + ErrorDialog("Can't save the file to the hard drive.", "Exception: %s" % e, "Error" ) + + def GetAFileName(self): + sFilePickerArgs = Array(10) + oFileDialog = createUnoService("com.sun.star.ui.dialogs.FilePicker") + oFileDialog.initialize(sFilePickerArgs) + oFileDialog.appendFilter("OpenERP Report File Save To ....","*.rml") + + f_path = "OpenERP-"+ os.path.basename( tempfile.mktemp("","") ) + ".rml" + initPath = tempfile.gettempdir() + oUcb = createUnoService("com.sun.star.ucb.SimpleFileAccess") + if oUcb.exists(initPath): + oFileDialog.setDisplayDirectory('file://' + ( os.name == 'nt' and '/' or '' ) + initPath ) + + oFileDialog.setDefaultName(f_path ) + + sPath = oFileDialog.execute() == 1 and oFileDialog.Files[0] or None + oFileDialog.dispose() + return sPath + +if __name__<>"package" and __name__=="__main__": + ExportToRML(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ExportToRML, "org.openoffice.openerp.report.exporttorml", ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Expression.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Expression.py new file mode 100644 index 00000000000..8da231cafb6 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Expression.py @@ -0,0 +1,69 @@ +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + database="test" + uid = 3 + +class Expression(unohelper.Base, XJobExecutor ): + def __init__(self,sExpression="",sName="", bFromModify=False): + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + self.win = DBModalDialog(60, 50, 180, 65, "Expression Builder") + self.win.addFixedText("lblExpression",17 , 10, 35, 15, "Expression :") + self.win.addEdit("txtExpression", -5, 5, 123, 15) + self.win.addFixedText("lblName", 2, 30, 50, 15, "Displayed Name :") + self.win.addEdit("txtName", -5, 25, 123, 15) + self.win.addButton( "btnOK", -5, -5, 40, 15, "OK", actionListenerProc = self.btnOk_clicked ) + self.win.addButton( "btnCancel", -5 - 40 -5, -5, 40, 15, "Cancel", actionListenerProc = self.btnCancel_clicked ) + self.bModify=bFromModify + if self.bModify==True: + self.win.setEditText("txtExpression",sExpression) + self.win.setEditText("txtName",sName) + self.win.doModalDialog("",None) + + + def btnOk_clicked( self, oActionEvent ): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + text = doc.Text + cursor = doc.getCurrentController().getViewCursor() + if self.bModify==True: + oCurObj=cursor.TextField + sKey=u""+self.win.getEditText("txtName") + sValue=u"[[ " + self.win.getEditText("txtExpression") + " ]]" + oCurObj.Items = (sKey,sValue) + oCurObj.update() + self.win.endExecute() + else: + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if self.win.getEditText("txtName")!="" and self.win.getEditText("txtExpression")!="": + sKey=u""+self.win.getEditText("txtName") + sValue=u"[[ " + self.win.getEditText("txtExpression") + " ]]" + if cursor.TextTable==None: + oInputList.Items = (sKey,sValue) + text.insertTextContent(cursor,oInputList,False) + else: + oTable = cursor.TextTable + oCurCell = cursor.Cell + tableText = oTable.getCellByName( oCurCell.CellName ) + oInputList.Items = (sKey,sValue) + tableText.insertTextContent(cursor,oInputList,False) + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Name field or \nExpression field") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + Expression() +elif __name__=="package": + g_ImplementationHelper.addImplementation( Expression, "org.openoffice.openerp.report.expression", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Fields.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Fields.py new file mode 100644 index 00000000000..83295a9928c --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Fields.py @@ -0,0 +1,245 @@ + + +import uno +import string +import unohelper +import xmlrpclib + +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.functions import * + from lib.error import ErrorDialog + from LoginTest import * + from lib.logreport import * + from lib.rpc import * + database="report" + uid = 3 + + +class Fields(unohelper.Base, XJobExecutor ): + def __init__(self,sVariable="",sFields="",sDisplayName="",bFromModify=False): + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + self.logobj=Logger() + self.win = DBModalDialog(60, 50, 200, 225, "Field Builder") + + self.win.addFixedText("lblVariable", 27, 12, 60, 15, "Variable :") + self.win.addComboBox("cmbVariable", 180-120-2, 10, 130, 15,True, itemListenerProc=self.cmbVariable_selected) + self.insVariable = self.win.getControl( "cmbVariable" ) + + self.win.addFixedText("lblFields", 10, 32, 60, 15, "Variable Fields :") + self.win.addComboListBox("lstFields", 180-120-2, 30, 130, 150, False,True,itemListenerProc=self.lstbox_selected) + self.insField = self.win.getControl( "lstFields" ) + + self.win.addFixedText("lblUName", 8, 187, 60, 15, "Displayed name :") + self.win.addEdit("txtUName", 180-120-2, 185, 130, 15,) + + self.win.addButton('btnOK',-5 ,-5,45,15,'Ok' ,actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-5 - 45 - 5 ,-5,45,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + + self.sValue=None + self.sObj=None + self.aSectionList=[] + self.sGDisplayName=sDisplayName + self.aItemList=[] + self.aComponentAdd=[] + self.aObjectList=[] + self.aListFields=[] + self.aVariableList=[] + EnumDocument(self.aItemList,self.aComponentAdd) + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.sMyHost= "" + + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + self.sMyHost = docinfo.getUserFieldValue(0) + self.count = 0 + oParEnum = doc.getTextFields().createEnumeration() + + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + self.count += 1 + + getList(self.aObjectList, self.sMyHost,self.count) + cursor = doc.getCurrentController().getViewCursor() + text = cursor.getText() + tcur = text.createTextCursorByRange(cursor) + + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == "Objects", self.aObjectList ) ) + + for i in range(len(self.aItemList)): + try: + anItem = self.aItemList[i][1] + component = self.aComponentAdd[i] + + if component == "Document": + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextSection: + getRecersiveSection(tcur.TextSection,self.aSectionList) + if component in self.aSectionList: + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextTable: + if not component == "Document" and component[component.rfind(".")+1:] == tcur.TextTable.Name: + VariableScope(tcur, self.aVariableList, self.aObjectList, self.aComponentAdd, self.aItemList, component) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('Fields', LOG_ERROR, info) + + self.bModify=bFromModify + if self.bModify==True: + sItem="" + for anObject in self.aObjectList: + if anObject[:anObject.find("(")] == sVariable: + sItem = anObject + self.insVariable.setText(sItem) + genTree( + sItem[sItem.find("(")+1:sItem.find(")")], + self.aListFields, + self.insField, + self.sMyHost, + 2, + ending_excl=['one2many','many2one','many2many','reference'], + recur=['many2one'] + ) + self.sValue= self.win.getListBoxItem("lstFields",self.aListFields.index(sFields)) + for var in self.aVariableList: + + self.model_ids =self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])]) + fields=['name','model'] + self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields) + if self.model_res <> []: + self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem(var ,self.insVariable.getItemCount()) + + self.win.doModalDialog("lstFields",self.sValue) + else: + ErrorDialog("Please insert user define field Field-1 or Field-4","Just go to File->Properties->User Define \nField-1 Eg. http://localhost:8069 \nOR \nField-4 Eg. account.invoice") + self.win.endExecute() + + def lstbox_selected(self,oItemEvent): + try: + + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sItem= self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")+1]==sItem[:sItem.find("(")+1]: + sItem = var + sMain=self.aListFields[self.win.getListBoxSelectedItemPos("lstFields")] + sObject=self.getRes(self.sock,sItem[sItem.find("(")+1:-1],sMain[1:]) + ids = self.sock.execute(database, uid, self.password, sObject , 'search', []) + res = self.sock.execute(database, uid, self.password, sObject , 'read',[ids[0]]) + self.win.setEditText("txtUName",res[0][sMain[sMain.rfind("/")+1:]]) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('Fields', LOG_ERROR, info) + self.win.setEditText("txtUName","TTT") + if self.bModify: + self.win.setEditText("txtUName",self.sGDisplayName) + + def getRes(self,sock ,sObject,sVar): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, self.password, sObject , 'fields_get') + key = res.keys() + key.sort() + myval=None + if not sVar.find("/")==-1: + myval=sVar[:sVar.find("/")] + else: + myval=sVar + if myval in key: + if (res[myval]['type'] in ['many2one']): + sObject = res[myval]['relation'] + return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:]) + else: + return sObject + + def cmbVariable_selected(self,oItemEvent): + if self.count > 0 : + try: + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields")) + self.aListFields=[] + tempItem = self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")] == tempItem[:tempItem.find("(")]: + sItem=var + + genTree( + sItem[sItem.find("(")+1:sItem.find(")")], + self.aListFields, + self.insField, + self.sMyHost, + 2, + ending_excl=['one2many','many2one','many2many','reference'], + recur=['many2one'] + ) + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('Fields', LOG_ERROR, info) + + def btnOk_clicked( self, oActionEvent ): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + cursor = doc.getCurrentController().getViewCursor() + for i in self.win.getListBoxSelectedItemsPos("lstFields"): + itemSelected = self.aListFields[i] + itemSelectedPos = i + txtUName=self.win.getEditText("txtUName") + sKey=u""+txtUName + if itemSelected != "" and txtUName != "" and self.bModify==True : + txtUName=self.sGDisplayName + sKey=u""+txtUName + txtUName=self.sGDisplayName + oCurObj=cursor.TextField + sObjName=self.insVariable.getText() + sObjName=sObjName[:sObjName.find("(")] + sValue=u"[[ " + sObjName + self.aListFields[itemSelectedPos].replace("/",".") + " ]]" + oCurObj.Items = (sKey,sValue) + oCurObj.update() + self.win.endExecute() + elif itemSelected != "" and txtUName != "" : + + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + sObjName=self.win.getComboBoxText("cmbVariable") + sObjName=sObjName[:sObjName.find("(")] + + widget = ( cursor.TextTable and cursor.TextTable.getCellByName( cursor.Cell.CellName ) or doc.Text ) + + sValue = u"[[ " + sObjName + self.aListFields[itemSelectedPos].replace("/",".") + " ]]" + oInputList.Items = (sKey,sValue) + widget.insertTextContent(cursor,oInputList,False) + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Name field \nor select perticular value from the list of fields") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + Fields() +elif __name__=="package": + g_ImplementationHelper.addImplementation( Fields, "org.openoffice.openerp.report.fields", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/LoginTest.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/LoginTest.py new file mode 100644 index 00000000000..daf81591c1a --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/LoginTest.py @@ -0,0 +1,10 @@ + +if __name__<>"package": + from ServerParameter import * + from lib.gui import * + +class LoginTest: + def __init__(self): + if not loginstatus: + ServerParameter(None) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ModifyExistingReport.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ModifyExistingReport.py new file mode 100644 index 00000000000..9781cbe5b6b --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ModifyExistingReport.py @@ -0,0 +1,168 @@ + + +import uno +import string +import unohelper +import xmlrpclib +import base64, tempfile + + +from com.sun.star.task import XJobExecutor +import os +import sys +if __name__<>'package': + from lib.gui import * + from lib.error import * + from LoginTest import * + from lib.logreport import * + from lib.rpc import * + database="test" + uid = 3 + +# +class ModifyExistingReport(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + + self.win = DBModalDialog(60, 50, 180, 120, "Modify Existing Report") + self.win.addFixedText("lblReport", 2, 3, 60, 15, "Report Selection") + self.win.addComboListBox("lstReport", -1,15,178,80 , False ) + self.lstReport = self.win.getControl( "lstReport" ) + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.logobj=Logger() + self.hostname = docinfo.getUserFieldValue(0) + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + # Open a new connexion to the server + + + ids = self.sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_designer'),('state', '=', 'installed')]) + if not len(ids): + ErrorDialog("Please Install base_report_designer module", "", "Module Uninstalled Error") + exit(1) + + ids = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'search', [('report_xsl', '=', False),('report_xml', '=', False)]) + + fields=['id', 'name','report_name','model'] + + self.reports = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'read', ids, fields) + self.report_with_id = [] + + for report in self.reports: + if report['name']<>"": + model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=', report['model'])]) + model_res_other =self.sock.execute(database, uid, self.password, 'ir.model', 'read', model_ids, [ 'name', 'model' ] ) + if model_res_other <> []: + name = model_res_other[0]['name'] + " - " + report['name'] + else: + name = report['name'] + " - " + report['model'] + self.report_with_id.append( (report['id'], name, report['model'] ) ) + + self.report_with_id.sort( lambda x, y: cmp( x[1], y[1] ) ) + + for id, report_name, model_name in self.report_with_id: + self.lstReport.addItem( report_name, self.lstReport.getItemCount() ) + + self.win.addButton('btnSave',10,-5,50,15,'Open Report' ,actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-10 ,-5,50,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.addButton('btnDelete',15 -80 ,-5,50,15,'Delete Report',actionListenerProc = self.btnDelete_clicked) + self.win.doModalDialog("lstReport",self.report_with_id[0][1] ) + + def btnOk_clicked(self, oActionEvent): + try: + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + + selectedItemPos = self.win.getListBoxSelectedItemPos( "lstReport" ) + id = self.report_with_id[ selectedItemPos ][0] + + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'report_get', id) + + if res['file_type'] in ['sxw','odt'] : + file_type = res['file_type'] + else : + file_type = 'sxw' + + fp_name = tempfile.mktemp('.'+file_type) + fp_name1="r"+fp_name + fp_path=os.path.join(fp_name1).replace("\\","/") + fp_win=fp_path[1:] + + filename = ( os.name == 'nt' and fp_win or fp_name ) + if res['report_sxw_content']: + write_data_to_file( filename, base64.decodestring(res['report_sxw_content'])) + url = "file:///%s" % filename + + arr=Array(makePropertyValue("MediaType","application/vnd.sun.xml.writer"),) + oDoc2 = desktop.loadComponentFromURL(url, "openerp", 55, arr) + docinfo2=oDoc2.getDocumentInfo() + docinfo2.setUserFieldValue(0, self.hostname) + docinfo2.setUserFieldValue(1,self.password) + docinfo2.setUserFieldValue(2,id) + docinfo2.setUserFieldValue(3,self.report_with_id[selectedItemPos][2]) + + oParEnum = oDoc2.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + oPar.SelectedItem = oPar.Items[0] + oPar.update() + if oDoc2.isModified(): + if oDoc2.hasLocation() and not oDoc2.isReadonly(): + oDoc2.store() + + ErrorDialog("Download is Completed","Your file has been placed here :\n"+ fp_name,"Download Message") + obj=Logger() + obj.log_write('Modify Existing Report',LOG_INFO, ':successful download report %s using database %s' % (self.report_with_id[selectedItemPos][2], database)) + except Exception, e: + ErrorDialog("Report has not been downloaded", "Report: %s\nDetails: %s" % ( fp_name, str(e) ),"Download Message") + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ModifyExistingReport', LOG_ERROR, info) + + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def btnDelete_clicked( self, oActionEvent ): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + + selectedItemPos = self.win.getListBoxSelectedItemPos( "lstReport" ) + name=self.win.getListBoxSelectedItem ("lstReport") + id = self.report_with_id[ selectedItemPos ][0] + temp = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'unlink', id,) + str_value='ir.actions.report.xml,'+str(id) + ids = self.sock.execute(database, uid, self.password, 'ir.values' , 'search',[('value','=',str_value)]) + if ids: + rec = self.sock.execute(database, uid, self.password, 'ir.values', 'unlink', ids,) + else : + pass + if temp: + ErrorDialog("Report","Report has been Delete:\n "+name,"Message") + self.logobj.log_write('Delete Report',LOG_INFO, ':successful delete report %s using database %s' % (name, database)) + + else: + ErrorDialog("Report","Report has not Delete:\n"+name," Message") + self.win.endExecute() + + + +if __name__<>"package" and __name__=="__main__": + ModifyExistingReport(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ModifyExistingReport, "org.openoffice.openerp.report.modifyreport", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/NewReport.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/NewReport.py new file mode 100644 index 00000000000..5bd571d45a4 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/NewReport.py @@ -0,0 +1,74 @@ + +import uno +import string +import unohelper +import xmlrpclib + +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from lib.logreport import * + from LoginTest import * + from lib.rpc import * + database="test" + uid = 3 +# +# +# +# Start OpenOffice.org, listen for connections and open testing document +# +# +class NewReport(unohelper.Base, XJobExecutor): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + self.win=DBModalDialog(60, 50, 180, 115, "Open New Report") + self.win.addFixedText("lblModuleSelection", 2, 2, 60, 15, "Module Selection") + self.win.addComboListBox("lstModule", -2,13,176,80 , False) + self.lstModule = self.win.getControl( "lstModule" ) + self.aModuleName=[] + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search',[]) + fields = [ 'model','name'] + res = self.sock.execute(database, uid, self.password, 'ir.model' , 'read', ids, fields) + res.sort(lambda x, y: cmp(x['name'],y['name'])) + + for i in range(len(res)): + self.lstModule.addItem(res[i]['name'],self.lstModule.getItemCount()) + self.aModuleName.append(res[i]['model']) + self.win.addButton('btnOK',-2 ,-5, 70,15,'Use Module in Report' ,actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-2 - 70 - 5 ,-5, 35,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + self.win.doModalDialog("",None) + + def btnOk_clicked(self,oActionEvent): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + docinfo.setUserFieldValue(3,self.aModuleName[self.lstModule.getSelectedItemPos()]) + self.logobj.log_write('Module Name',LOG_INFO, ':Module use in creating a report %s using database %s' % (self.aModuleName[self.lstModule.getSelectedItemPos()], database)) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + NewReport(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( \ + NewReport, + "org.openoffice.openerp.report.opennewreport", + ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Repeatln.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Repeatln.py new file mode 100644 index 00000000000..4c161fc17c5 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Repeatln.py @@ -0,0 +1,254 @@ +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from ServerParameter import * + from lib.logreport import * + from lib.rpc import * + from LoginTest import * + + database="test_db1" + uid = 3 + + +#class RepeatIn: +class RepeatIn( unohelper.Base, XJobExecutor ): + def __init__(self,sObject="",sVariable="",sFields="",sDisplayName="",bFromModify=False): + # Interface Design + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + + self.win = DBModalDialog(60, 50, 180, 250, "RepeatIn Builder") + self.win.addFixedText("lblVariable", 2, 12, 60, 15, "Objects to loop on :") + self.win.addComboBox("cmbVariable", 180-120-2, 10, 120, 15,True, itemListenerProc=self.cmbVariable_selected) + self.insVariable = self.win.getControl( "cmbVariable" ) + + self.win.addFixedText("lblFields", 10, 32, 60, 15, "Field to loop on :") + self.win.addComboListBox("lstFields", 180-120-2, 30, 120, 150, False,itemListenerProc=self.lstbox_selected) + self.insField = self.win.getControl( "lstFields" ) + + self.win.addFixedText("lblName", 12, 187, 60, 15, "Variable name :") + self.win.addEdit("txtName", 180-120-2, 185, 120, 15,) + + self.win.addFixedText("lblUName", 8, 207, 60, 15, "Displayed name :") + self.win.addEdit("txtUName", 180-120-2, 205, 120, 15,) + + self.win.addButton('btnOK',-2 ,-10,45,15,'Ok', actionListenerProc = self.btnOk_clicked ) + + self.win.addButton('btnCancel',-2 - 45 - 5 ,-10,45,15,'Cancel', actionListenerProc = self.btnCancel_clicked ) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + # Variable Declaration + self.sValue=None + self.sObj=None + self.aSectionList=[] + self.sGVariable=sVariable + self.sGDisplayName=sDisplayName + self.aItemList=[] + self.aComponentAdd=[] + self.aObjectList=[] + self.aListRepeatIn=[] + self.aVariableList=[] + # Call method to perform Enumration on Report Document + EnumDocument(self.aItemList,self.aComponentAdd) + # Perform checking that Field-1 and Field - 4 is available or not alos get Combobox + # filled if condition is true + desktop = getDesktop() + doc = desktop.getCurrentComponent() + docinfo = doc.getDocumentInfo() + # Check weather Field-1 is available if not then exit from application + self.sMyHost= "" + + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + self.sMyHost= docinfo.getUserFieldValue(0) + self.count=0 + oParEnum = doc.getTextFields().createEnumeration() + + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + self.count += 1 + + getList(self.aObjectList, self.sMyHost,self.count) + cursor = doc.getCurrentController().getViewCursor() + text = cursor.getText() + tcur = text.createTextCursorByRange(cursor) + + self.aVariableList.extend( filter( lambda obj: obj[:obj.find(" ")] == "List", self.aObjectList ) ) + + for i in range(len(self.aItemList)): + try: + anItem = self.aItemList[i][1] + component = self.aComponentAdd[i] + + if component == "Document": + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextSection: + getRecersiveSection(tcur.TextSection,self.aSectionList) + if component in self.aSectionList: + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextTable: + if not component == "Document" and component[component.rfind(".") + 1:] == tcur.TextTable.Name: + VariableScope( tcur, self.aVariableList, self.aObjectList, self.aComponentAdd, self.aItemList, component ) + except : + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('RepeatIn', LOG_ERROR, info) + self.bModify=bFromModify + + if self.bModify==True: + + if sObject=="": + self.insVariable.setText("List of "+docinfo.getUserFieldValue(3)) + self.insField.addItem("objects",self.win.getListBoxItemCount("lstFields")) + self.win.setEditText("txtName", sVariable) + self.win.setEditText("txtUName",sDisplayName) + self.sValue= "objects" + else: + sItem="" + for anObject in self.aObjectList: + if anObject[:anObject.find("(")] == sObject: + sItem = anObject + self.insVariable.setText( sItem ) + + genTree( + sItem[sItem.find("(")+1:sItem.find(")")], + self.aListRepeatIn, + self.insField, + self.sMyHost, + 2, + ending=['one2many','many2many'], + recur=['one2many','many2many'] + ) + + self.sValue= self.win.getListBoxItem("lstFields",self.aListRepeatIn.index(sFields)) + + for var in self.aVariableList: + + if var[:8] <> 'List of ': + self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])]) + else: + self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[8:])]) + fields=['name','model'] + self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields) + if self.model_res <> []: + if var[:8]<>'List of ': + self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem('List of ' + self.model_res[0]['name'] ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem(var ,self.insVariable.getItemCount()) + + self.win.doModalDialog("lstFields",self.sValue) + else: + ErrorDialog("Please Select Appropriate module" ,"Create new report from: \nOpenERP -> Open a New Report") + self.win.endExecute() + + def lstbox_selected(self,oItemEvent): + sItem=self.win.getListBoxSelectedItem("lstFields") + sMain=self.aListRepeatIn[self.win.getListBoxSelectedItemPos("lstFields")] + + if self.bModify==True: + self.win.setEditText("txtName", self.sGVariable) + self.win.setEditText("txtUName",self.sGDisplayName) + else: + self.win.setEditText("txtName",sMain[sMain.rfind("/")+1:]) + self.win.setEditText("txtUName","|-."+sItem[sItem.rfind("/")+1:]+".-|") + + def cmbVariable_selected(self,oItemEvent): + + if self.count > 0 : + + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields")) + sItem=self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:8]=='List of ': + if var[:8]==sItem[:8]: + sItem = var + elif var[:var.find("(")+1] == sItem[:sItem.find("(")+1]: + sItem = var + self.aListRepeatIn=[] + + data = ( sItem[sItem.rfind(" ") + 1:] == docinfo.getUserFieldValue(3) ) and docinfo.getUserFieldValue(3) or sItem[sItem.find("(")+1:sItem.find(")")] + genTree( data, self.aListRepeatIn, self.insField, self.sMyHost, 2, ending=['one2many','many2many'], recur=['one2many','many2many'] ) + + self.win.selectListBoxItemPos("lstFields", 0, True ) + + else: + sItem=self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:8]=='List of ' and var[:8] == sItem[:8]: + sItem = var + if sItem.find(".")==-1: + temp=sItem[sItem.rfind("x_"):] + else: + temp=sItem[sItem.rfind(".")+1:] + self.win.setEditText("txtName",temp) + self.win.setEditText("txtUName","|-."+temp+".-|") + self.insField.addItem("objects",self.win.getListBoxItemCount("lstFields")) + self.win.selectListBoxItemPos("lstFields", 0, True ) + + def btnOk_clicked(self, oActionEvent): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + cursor = doc.getCurrentController().getViewCursor() + selectedItem = self.win.getListBoxSelectedItem( "lstFields" ) + selectedItemPos = self.win.getListBoxSelectedItemPos( "lstFields" ) + txtName = self.win.getEditText( "txtName" ) + txtUName = self.win.getEditText( "txtUName" ) + if selectedItem != "" and txtName != "" and txtUName != "": + sKey=u""+ txtUName + if selectedItem == "objects": + sValue=u"[[ repeatIn(" + selectedItem + ",'" + txtName + "') ]]" + else: + sObjName=self.win.getComboBoxText("cmbVariable") + sObjName=sObjName[:sObjName.find("(")] + sValue=u"[[ repeatIn(" + sObjName + self.aListRepeatIn[selectedItemPos].replace("/",".") + ",'" + txtName +"') ]]" + + if self.bModify == True: + oCurObj = cursor.TextField + oCurObj.Items = (sKey,sValue) + oCurObj.update() + else: + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + if self.win.getListBoxSelectedItem("lstFields") == "objects": + oInputList.Items = (sKey,sValue) + doc.Text.insertTextContent(cursor,oInputList,False) + else: + sValue=u"[[ repeatIn(" + sObjName + self.aListRepeatIn[selectedItemPos].replace("/",".") + ",'" + txtName +"') ]]" + if cursor.TextTable==None: + oInputList.Items = (sKey,sValue) + doc.Text.insertTextContent(cursor,oInputList,False) + else: + oInputList.Items = (sKey,sValue) + widget = ( cursor.TextTable or selectedItem <> 'objects' ) and cursor.TextTable.getCellByName( cursor.Cell.CellName ) or doc.Text + widget.insertTextContent(cursor,oInputList,False) + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Object Field or Name field \nor select perticular value from the list of fields") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + +if __name__<>"package" and __name__=="__main__": + RepeatIn() +elif __name__=="package": + g_ImplementationHelper = unohelper.ImplementationHelper() + g_ImplementationHelper.addImplementation( RepeatIn, "org.openoffice.openerp.report.repeatln", ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/SendToServer.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/SendToServer.py new file mode 100644 index 00000000000..61a926de218 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/SendToServer.py @@ -0,0 +1,195 @@ + +import uno +import string +import unohelper +import random +import xmlrpclib +import base64, tempfile +from com.sun.star.task import XJobExecutor +import os +import sys +if __name__<>'package': + from lib.gui import * + from lib.error import * + from lib.functions import * + from lib.logreport import * + from lib.tools import * + from LoginTest import * + from lib.rpc import * + database="report" + uid = 3 + +# +# +class SendtoServer(unohelper.Base, XJobExecutor): + Kind = { + 'PDF' : 'pdf', + 'OpenOffice': 'sxw', + 'HTML' : 'html' + } + + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + LoginTest() + self.logobj=Logger() + if not loginstatus and __name__=="package": + exit(1) + + global passwd + self.password = passwd + global url + self.sock=RPCSession(url) + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + + self.ids = self.sock.execute(database, uid, self.password, 'ir.module.module', 'search', [('name','=','base_report_designer'),('state', '=', 'installed')]) + if not len(self.ids): + ErrorDialog("Please Install base_report_designer module", "", "Module Uninstalled Error") + exit(1) + + report_name = "" + name="" + if docinfo.getUserFieldValue(2)<>"" : + try: + fields=['name','report_name'] + self.res_other = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'read', [int(docinfo.getUserFieldValue(2))],fields) + name = self.res_other[0]['name'] + report_name = self.res_other[0]['report_name'] + except: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logob.log_write('ServerParameter', LOG_ERROR, info) + elif docinfo.getUserFieldValue(3) <> "": + name = "" + result = "rnd" + for i in range(5): + result =result + random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890') + + report_name = docinfo.getUserFieldValue(3) + "." + result + else: + ErrorDialog("Please select appropriate module...","Note: use OpenERP Report -> Open a new Report", "Module selection ERROR"); + exit(1) + + self.win = DBModalDialog(60, 50, 180, 100, "Send To Server") + self.win.addFixedText("lblName",10 , 9, 40, 15, "Report Name :") + self.win.addEdit("txtName", -5, 5, 123, 15,name) + self.win.addFixedText("lblReportName", 2, 30, 50, 15, "Technical Name :") + self.win.addEdit("txtReportName", -5, 25, 123, 15,report_name) + self.win.addCheckBox("chkHeader", 51, 45, 70 ,15, "Corporate Header") + self.win.addFixedText("lblResourceType", 2 , 60, 50, 15, "Select Rpt. Type :") + self.win.addComboListBox("lstResourceType", -5, 58, 123, 15,True,itemListenerProc=self.lstbox_selected) + self.lstResourceType = self.win.getControl( "lstResourceType" ) + self.txtReportName=self.win.getControl( "txtReportName" ) + self.txtReportName.Enable=False + for kind in self.Kind.keys(): + self.lstResourceType.addItem( kind, self.lstResourceType.getItemCount() ) + + self.win.addButton( "btnSend", -5, -5, 80, 15, "Send Report to Server", actionListenerProc = self.btnOk_clicked) + self.win.addButton( "btnCancel", -5 - 80 -5, -5, 40, 15, "Cancel", actionListenerProc = self.btnCancel_clicked) + + self.win.doModalDialog("lstResourceType", self.Kind.keys()[0]) + + def lstbox_selected(self,oItemEvent): + pass + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def btnOk_clicked(self, oActionEvent): + if self.win.getEditText("txtName") <> "" and self.win.getEditText("txtReportName") <> "": + desktop=getDesktop() + oDoc2 = desktop.getCurrentComponent() + docinfo=oDoc2.getDocumentInfo() + self.getInverseFieldsRecord(1) + fp_name = tempfile.mktemp('.'+"sxw") + if not oDoc2.hasLocation(): + oDoc2.storeAsURL("file://"+fp_name,Array(makePropertyValue("MediaType","application/vnd.sun.xml.writer"),)) + + if docinfo.getUserFieldValue(2)=="": + name=self.win.getEditText("txtName"), + name_id={} + try: + name_id = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml' , 'search',[('name','=',name)]) + if not name_id: + id=self.getID() + docinfo.setUserFieldValue(2,id) + rec = { + 'name': self.win.getEditText("txtReportName"), + 'key': 'action', + 'model': docinfo.getUserFieldValue(3), + 'value': 'ir.actions.report.xml,'+str(id), + 'key2': 'client_print_multi', + 'object': True + } + res = self.sock.execute(database, uid, self.password, 'ir.values' , 'create',rec ) + else : + ErrorDialog(" Report Name is all ready given !!!\n\n\n Please specify other Name","","Report Name") + self.logobj.log_write('SendToServer',LOG_WARNING, ':Report name all ready given DB %s' % (database)) + self.win.endExecute() + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ServerParameter', LOG_ERROR, info) + else: + + id = docinfo.getUserFieldValue(2) + vId = self.sock.execute(database, uid, self.password, 'ir.values' , 'search', [('value','=','ir.actions.report.xml,'+str(id))]) + rec = { 'name': self.win.getEditText("txtReportName") } + res = self.sock.execute(database, uid, self.password, 'ir.values' , 'write',vId,rec) + oDoc2.store() + data = read_data_from_file( get_absolute_file_path( oDoc2.getURL()[7:] ) ) + self.getInverseFieldsRecord(0) + #sock = xmlrpclib.ServerProxy(docinfo.getUserFieldValue(0) +'/xmlrpc/object') + + file_type = oDoc2.getURL()[7:].split(".")[-1] + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'upload_report', int(docinfo.getUserFieldValue(2)),base64.encodestring(data),file_type,{}) + params = { + 'name': self.win.getEditText("txtName"), + 'model': docinfo.getUserFieldValue(3), + 'report_name': self.win.getEditText("txtReportName"), + 'header': (self.win.getCheckBoxState("chkHeader") <> 0), + 'report_type': self.Kind[self.win.getListBoxSelectedItem("lstResourceType")], + } + if self.win.getListBoxSelectedItem("lstResourceType")=='OpenOffice': + params['report_type']=file_type + res = self.sock.execute(database, uid, self.password, 'ir.actions.report.xml', 'write', int(docinfo.getUserFieldValue(2)), params) + self.logobj.log_write('SendToServer',LOG_INFO, ':Report %s successfully send using %s'%(params['name'],database)) + self.win.endExecute() + else: + ErrorDialog("Either Report Name or Technical Name is blank !!!\nPlease specify appropriate Name","","Blank Field ERROR") + self.logobj.log_write('SendToServer',LOG_WARNING, ':Either Report Name or Technical Name is blank') + self.win.endExecute() + + def getID(self): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + params = { + 'name': self.win.getEditText("txtName"), + 'model': docinfo.getUserFieldValue(3), + 'report_name': self.win.getEditText('txtReportName') + } + + + id=self.sock.execute(database, uid, self.password, 'ir.actions.report.xml' ,'create', params) + return id + + def getInverseFieldsRecord(self,nVal): + desktop=getDesktop() + doc = desktop.getCurrentComponent() + count=0 + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + oPar.SelectedItem = oPar.Items[nVal] + if nVal==0: + oPar.update() + +if __name__<>"package" and __name__=="__main__": + SendtoServer(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( SendtoServer, "org.openoffice.openerp.report.sendtoserver", ("com.sun.star.task.Job",),) diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ServerParameter.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ServerParameter.py new file mode 100644 index 00000000000..ec871231794 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/ServerParameter.py @@ -0,0 +1,162 @@ +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.error import ErrorDialog + from lib.functions import * + from lib.logreport import * + from lib.rpc import * + from Change import * + database="test" + +class ServerParameter( unohelper.Base, XJobExecutor ): + def __init__(self,ctx): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + desktop=getDesktop() + log_detail(self) + self.logobj=Logger() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win=DBModalDialog(60, 50, 160, 108, "Server Connection Parameter") + + self.win.addFixedText("lblVariable", 2, 12, 35, 15, "Server URL") + if docinfo.getUserFieldValue(0)=="": + docinfo.setUserFieldValue(0,"http://localhost:8069") + self.win.addEdit("txtHost",-34,9,91,15,docinfo.getUserFieldValue(0)) + self.win.addButton('btnChange',-2 ,9,30,15,'Change', actionListenerProc = self.btnChange_clicked ) + + self.win.addFixedText("lblDatabaseName", 6, 31, 31, 15, "Database") + #self.win.addFixedText("lblMsg", -2,28,123,15) +# self.win.addComboListBox("lstDatabase", -2,28,123,15, True) +# self.lstDatabase = self.win.getControl( "lstDatabase" ) + #self.win.selectListBoxItem( "lstDatabase", docinfo.getUserFieldValue(2), True ) + #self.win.setEnabled("lblMsg",False) + + self.win.addFixedText("lblLoginName", 17, 51, 20, 15, "Login") + self.win.addEdit("txtLoginName",-2,48,123,15,docinfo.getUserFieldValue(1)) + + self.win.addFixedText("lblPassword", 6, 70, 31, 15, "Password") + self.win.addEdit("txtPassword",-2,67,123,15,) + self.win.setEchoChar("txtPassword",42) + + + self.win.addButton('btnOK',-2 ,-5, 60,15,'Connect' ,actionListenerProc = self.btnOk_clicked ) + + self.win.addButton('btnCancel',-2 - 60 - 5 ,-5, 35,15,'Cancel' ,actionListenerProc = self.btnCancel_clicked ) + sValue="" + if docinfo.getUserFieldValue(0)<>"": + global url + global result + url=docinfo.getUserFieldValue(0) + self.sock=RPCSession(url) + res=self.sock.listdb() + result=res + if res == -1: + self.win.addEdit("lstDatabase",-2,28,123,15) +# sValue="Could not connect to the server!" +# self.lstDatabase.addItem("Could not connect to the server!",0) + elif res == 0: + sValue="No Database found !!!" + self.lstDatabase.addItem("No Database found !!!",0) + else: + self.win.addComboListBox("lstDatabase", -2,28,123,15, True) + self.lstDatabase = self.win.getControl( "lstDatabase" ) + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + for i in range(len(res)): + self.lstDatabase.addItem(res[i],i) + sValue = database + if sValue: + self.win.doModalDialog("lstDatabase",sValue) + else: + self.win.doModalDialog("lstDatabase",None) + + #self.win.doModalDialog("lstDatabase",docinfo.getUserFieldValue(2)) + + def btnOk_clicked(self,oActionEvent): + + + sLogin=self.win.getEditText("txtLoginName") + sPassword=self.win.getEditText("txtPassword") + global url + global result + if result==-1: + sDatabase=self.win.getEditText("lstDatabase") + else: + sDatabase=self.win.getListBoxSelectedItem("lstDatabase") + self.sock=RPCSession(url) + UID = self.sock.login(sDatabase,sLogin,sPassword) + if not UID : + ErrorDialog("Connection Refuse...","Please enter valid Login/Password") + self.win.endExecute() + try: + + ids = self.sock.execute(sDatabase,UID,sPassword, 'res.groups' , 'search', [('name','=','OpenOfficeReportDesigner')]) + ids_module =self.sock.execute(sDatabase, UID, sPassword, 'ir.module.module', 'search', [('name','=','base_report_designer'),('state', '=', 'installed')]) + dict_groups =self.sock.execute(sDatabase, UID,sPassword, 'res.groups' , 'read',ids,['users']) + except : + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + self.logobj.log_write('ServerParameter', LOG_ERROR, info) + + if not len(ids) : + ErrorDialog("Group Not Found!!! Create a group named \n\n"'"OpenOfficeReportDesigner"'" \n\n ","","Group Name Error") + self.logobj.log_write('Group Error',LOG_WARNING, ':Create a group OpenOfficeReportDesigner using database %s' % (sDatabase)) + self.win.endExecute() + if not len(ids_module): + ErrorDialog("Please Install base_report_designer module", "", "Module Uninstalled Error") + self.logobj.log_write('Module Not Found',LOG_WARNING, ':base_report_designer not install in database %s' % (sDatabase)) + self.win.endExecute() + + if UID not in dict_groups[0]['users']: + ErrorDialog("Connection Refuse...","You have not access these Report Designer") + self.logobj.log_write('Connection Refuse',LOG_WARNING, " Not Access Report Designer ") + self.win.endExecute() + else: + desktop=getDesktop() + doc = desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + docinfo.setUserFieldValue(0,self.win.getEditText("txtHost")) + docinfo.setUserFieldValue(1,self.win.getEditText("txtLoginName")) + global passwd + passwd=self.win.getEditText("txtPassword") + global loginstatus + loginstatus=True + global database + database=sDatabase + global uid + uid=UID + #docinfo.setUserFieldValue(2,self.win.getListBoxSelectedItem("lstDatabase")) + #docinfo.setUserFieldValue(3,"") + + ErrorDialog(" You can start creating your report in \n \t the current document.","After Creating sending to the server.","Message") + self.logobj.log_write('successful login',LOG_INFO, ':successful login from %s using database %s' % (sLogin, sDatabase)) + self.win.endExecute() + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + def btnChange_clicked(self,oActionEvent): + aVal=[] + url= self.win.getEditText("txtHost") + Change(aVal,url) + if aVal[1]== -1: + self.win.getEditText("lstDatabase") + elif aVal[1]==0: + ErrorDialog(aVal[0],"") + else: + self.win.setEditText("txtHost",aVal[0]) + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + for i in range(len(aVal[1])): + self.lstDatabase.addItem(aVal[1][i],i) + + +if __name__<>"package" and __name__=="__main__": + ServerParameter(None) +elif __name__=="package": + g_ImplementationHelper.addImplementation( ServerParameter, "org.openoffice.openerp.report.serverparam", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Translation.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Translation.py new file mode 100644 index 00000000000..8de7560a04c --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/Translation.py @@ -0,0 +1,228 @@ + +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from lib.functions import * + from lib.error import ErrorDialog + from LoginTest import * + from lib.rpc import * + database="test_001" + uid = 3 + + +class AddLang(unohelper.Base, XJobExecutor ): + def __init__(self,sVariable="",sFields="",sDisplayName="",bFromModify=False): + LoginTest() + if not loginstatus and __name__=="package": + exit(1) + global passwd + self.password = passwd + self.win = DBModalDialog(60, 50, 180, 225, "Set Lang Builder") + + self.win.addFixedText("lblVariable", 27, 12, 60, 15, "Variable :") + self.win.addComboBox("cmbVariable", 180-120-2, 10, 120, 15,True, + itemListenerProc=self.cmbVariable_selected) + self.insVariable = self.win.getControl( "cmbVariable" ) + + self.win.addFixedText("lblFields", 10, 32, 60, 15, "Variable Fields :") + self.win.addComboListBox("lstFields", 180-120-2, 30, 120, 150, False,itemListenerProc=self.lstbox_selected) + self.insField = self.win.getControl( "lstFields" ) + self.win.addFixedText("lblUName", 8, 187, 60, 15, "Displayed name :") + self.win.addEdit("txtUName", 180-120-2, 185, 120, 15,) + + self.win.addButton('btnOK',-5 ,-5, 45, 15, 'Ok', actionListenerProc = self.btnOk_clicked ) + self.win.addButton('btnCancel',-5 - 45 - 5 ,-5,45,15,'Cancel', actionListenerProc = self.btnCancel_clicked ) + self.sValue=None + self.sObj=None + self.aSectionList=[] + self.sGDisplayName=sDisplayName + self.aItemList=[] + self.aComponentAdd=[] + self.aObjectList=[] + self.aListFields=[] + self.aVariableList=[] + EnumDocument(self.aItemList,self.aComponentAdd) + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.sMyHost= "" + global url + self.sock=RPCSession(url) + + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + self.sMyHost= docinfo.getUserFieldValue(0) + self.count=0 + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + self.count += 1 + getList(self.aObjectList, self.sMyHost,self.count) + cursor = doc.getCurrentController().getViewCursor() + text=cursor.getText() + tcur=text.createTextCursorByRange(cursor) + + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == "Objects", self.aObjectList ) ) + + for i in range(len(self.aItemList)): + anItem = self.aItemList[i][1] + component = self.aComponentAdd[i] + + if component == "Document": + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextSection: + getRecersiveSection(tcur.TextSection,self.aSectionList) + if component in self.aSectionList: + sLVal = anItem[anItem.find(",'") + 2:anItem.find("')")] + self.aVariableList.extend( filter( lambda obj: obj[:obj.find("(")] == sLVal, self.aObjectList ) ) + + if tcur.TextTable: + if not component == "Document" and component[component.rfind(".") + 1:] == tcur.TextTable.Name: + VariableScope(tcur,self.insVariable,self.aObjectList,self.aComponentAdd,self.aItemList,component) + + self.bModify=bFromModify + if self.bModify==True: + sItem="" + for anObject in self.aObjectList: + if anObject[:anObject.find("(")] == sVariable: + sItem = anObject + self.insVariable.setText( sItem ) + genTree(sItem[sItem.find("(")+1:sItem.find(")")],self.aListFields, self.insField,self.sMyHost,2,ending_excl=['one2many','many2one','many2many','reference'], recur=['many2one']) + self.sValue= self.win.getListBoxItem("lstFields",self.aListFields.index(sFields)) + + for var in self.aVariableList: + + self.model_ids = self.sock.execute(database, uid, self.password, 'ir.model' , 'search', [('model','=',var[var.find("(")+1:var.find(")")])]) + fields=['name','model'] + self.model_res = self.sock.execute(database, uid, self.password, 'ir.model', 'read', self.model_ids,fields) + if self.model_res <> []: + self.insVariable.addItem(var[:var.find("(")+1] + self.model_res[0]['name'] + ")" ,self.insVariable.getItemCount()) + else: + self.insVariable.addItem(var ,self.insVariable.getItemCount()) + + self.win.doModalDialog("lstFields",self.sValue) + else: + ErrorDialog("Please insert user define field Field-1 or Field-4","Just go to File->Properties->User Define \nField-1 Eg. http://localhost:8069 \nOR \nField-4 Eg. account.invoice") + self.win.endExecute() + + def lstbox_selected(self,oItemEvent): + try: + + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sItem= self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")+1]==sItem[:sItem.find("(")+1]: + sItem = var + sMain=self.aListFields[self.win.getListBoxSelectedItemPos("lstFields")] + t=sMain.rfind('/lang') + if t!=-1: + sObject=self.getRes(self.sock,sItem[sItem.find("(")+1:-1],sMain[1:]) + ids = self.sock.execute(database, uid, self.password, sObject , 'search', []) + res = self.sock.execute(database, uid, self.password, sObject , 'read',[ids[0]]) + self.win.setEditText("txtUName",res[0][sMain[sMain.rfind("/")+1:]]) + else: + ErrorDialog("Please select the Language Field") + + except: + import traceback;traceback.print_exc() + self.win.setEditText("txtUName","TTT") + if self.bModify: + self.win.setEditText("txtUName",self.sGDisplayName) + + def getRes(self,sock ,sObject,sVar): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + res = sock.execute(database, uid, self.password, sObject , 'fields_get') + key = res.keys() + key.sort() + myval=None + if not sVar.find("/")==-1: + myval=sVar[:sVar.find("/")] + else: + myval=sVar + if myval in key: + if (res[myval]['type'] in ['many2one']): + sObject = res[myval]['relation'] + return self.getRes(sock,res[myval]['relation'], sVar[sVar.find("/")+1:]) + else: + return sObject + + + def cmbVariable_selected(self,oItemEvent): + if self.count > 0 : + try: + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + self.win.removeListBoxItems("lstFields", 0, self.win.getListBoxItemCount("lstFields")) + self.aListFields=[] + tempItem = self.win.getComboBoxText("cmbVariable") + for var in self.aVariableList: + if var[:var.find("(")] == tempItem[:tempItem.find("(")]: + sItem=var + + genTree( + sItem[ sItem.find("(") + 1:sItem.find(")")], + self.aListFields, + self.insField, + self.sMyHost, + 2, + ending_excl=['one2many','many2one','many2many','reference'], + recur=['many2one'] + ) + + except: + import traceback;traceback.print_exc() + + def btnOk_clicked( self, oActionEvent ): + self.bOkay = True + desktop=getDesktop() + doc = desktop.getCurrentComponent() + cursor = doc.getCurrentController().getViewCursor() + + itemSelected = self.win.getListBoxSelectedItem( "lstFields" ) + itemSelectedPos = self.win.getListBoxSelectedItemPos( "lstFields" ) + txtUName = self.win.getEditText("txtUName") + sKey=u""+ txtUName + + if itemSelected != "" and txtUName != "" and self.bModify==True : + oCurObj=cursor.TextField + sObjName=self.insVariable.getText() + sObjName=sObjName[:sObjName.find("(")] + sValue=u"[[ setLang" + sObjName + self.aListFields[itemSelectedPos].replace("/",".") + ")" " ]]" + oCurObj.Items = (sKey,sValue) + oCurObj.update() + self.win.endExecute() + elif itemSelected != "" and txtUName != "" : + oInputList = doc.createInstance("com.sun.star.text.TextField.DropDown") + sObjName=self.win.getComboBoxText("cmbVariable") + sObjName=sObjName[:sObjName.find("(")] + + widget = ( cursor.TextTable and cursor.TextTable.getCellByName( cursor.Cell.CellName ) or doc.Text ) + + sValue = u"[[setLang" + "(" + sObjName + self.aListFields[itemSelectedPos].replace("/",".") +")" " ]]" + oInputList.Items = (sKey,sValue) + widget.insertTextContent(cursor,oInputList,False) + + self.win.endExecute() + else: + ErrorDialog("Please Fill appropriate data in Name field \nor select perticular value from the list of fields") + + def btnCancel_clicked( self, oActionEvent ): + self.win.endExecute() + + +if __name__<>"package" and __name__=="__main__": + AddLang() +elif __name__=="package": + g_ImplementationHelper.addImplementation( AddLang, "org.openoffice.openerp.report.langtag", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/__init__.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/__init__.py new file mode 100644 index 00000000000..d519fb1d00b --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/__init__.py @@ -0,0 +1,15 @@ +import Expression +import lib +import Fields +import modify +import Repeatln +import ServerParameter +import NewReport +import LoginTest +import Change +import About +import AddAttachment +import ConvertBracesToField +import ConvertFieldsToBraces +import ExportToRML +import SendtoServer \ No newline at end of file diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/compile_all.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/compile_all.py new file mode 100644 index 00000000000..42c53e72940 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/compile_all.py @@ -0,0 +1,3 @@ +import compileall + +compileall.compile_dir('package') \ No newline at end of file diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/import b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/import new file mode 100644 index 00000000000..e14c925b619 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/import @@ -0,0 +1,2 @@ +__name__="package" + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/__init__.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/__init__.py new file mode 100644 index 00000000000..c135ea31616 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/__init__.py @@ -0,0 +1,7 @@ +import actions +import error +import functions +import gui +import logreport +import rpc +import tiny_socket \ No newline at end of file diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/actions.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/actions.py new file mode 100644 index 00000000000..4f553346c45 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/actions.py @@ -0,0 +1,64 @@ +import uno +import unohelper +import os +#-------------------------------------------------- +# An ActionListener adapter. +# This object implements com.sun.star.awt.XActionListener. +# When actionPerformed is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oActionEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +if __name__<>"package": + os.system( "ooffice '-accept=socket,host=localhost,port=2002;urp;'" ) +passwd="" +database="" +uid="" +loginstatus=False +from com.sun.star.awt import XActionListener +class ActionListenerProcAdapter( unohelper.Base, XActionListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oActionEvent is a com.sun.star.awt.ActionEvent struct. + def actionPerformed( self, oActionEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oActionEvent,) + self.tParams ) + +#-------------------------------------------------- +# An ItemListener adapter. +# This object implements com.sun.star.awt.XItemListener. +# When itemStateChanged is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oItemEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +from com.sun.star.awt import XItemListener +class ItemListenerProcAdapter( unohelper.Base, XItemListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oItemEvent is a com.sun.star.awt.ItemEvent struct. + def itemStateChanged( self, oItemEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oItemEvent,) + self.tParams ) + +#-------------------------------------------------- +# An TextListener adapter. +# This object implements com.sun.star.awt.XTextistener. +# When textChanged is called, this will call an arbitrary +# python procedure, passing it... +# 1. the oTextEvent +# 2. any other parameters you specified to this object's constructor (as a tuple). +from com.sun.star.awt import XTextListener +class TextListenerProcAdapter( unohelper.Base, XTextListener ): + def __init__( self, oProcToCall, tParams=() ): + self.oProcToCall = oProcToCall # a python procedure + self.tParams = tParams # a tuple + + # oTextEvent is a com.sun.star.awt.TextEvent struct. + def textChanged( self, oTextEvent ): + if callable( self.oProcToCall ): + apply( self.oProcToCall, (oTextEvent,) + self.tParams ) + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/error.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/error.py new file mode 100644 index 00000000000..2dd692723f9 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/error.py @@ -0,0 +1,13 @@ +if __name__<>"package": + from gui import * +class ErrorDialog: + def __init__(self,sErrorMsg, sErrorHelpMsg="",sTitle="Error Message"): + self.win = DBModalDialog(50, 50, 150, 90, sTitle) + self.win.addFixedText("lblErrMsg", 5, 5, 190, 25, sErrorMsg) + self.win.addFixedText("lblErrHelpMsg", 5, 30, 190, 25, sErrorHelpMsg) + self.win.addButton('btnOK', 55,-5,40,15,'Ok' + ,actionListenerProc = self.btnOkOrCancel_clicked ) + self.win.doModalDialog("",None) + def btnOkOrCancel_clicked( self, oActionEvent ): + self.win.endExecute() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/functions.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/functions.py new file mode 100644 index 00000000000..cd1db284867 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/functions.py @@ -0,0 +1,217 @@ +import uno +import xmlrpclib +import re +import socket +import cPickle +import marshal +import tempfile +if __name__<>"package": + from gui import * + from logreport import * + from rpc import * + database="test" + uid = 1 + +def genTree(object,aList,insField,host,level=3, ending=[], ending_excl=[], recur=[], root='', actualroot=""): + try: + global url + sock=RPCSession(url) + global passwd + res = sock.execute(database, uid, passwd, object , 'fields_get') + key = res.keys() + key.sort() + for k in key: + if (not ending or res[k]['type'] in ending) and ((not ending_excl) or not (res[k]['type'] in ending_excl)): + insField.addItem(root+'/'+res[k]["string"],len(aList)) + aList.append(actualroot+'/'+k) + if (res[k]['type'] in recur) and (level>0): + genTree(res[k]['relation'],aList,insField,host ,level-1, ending, ending_excl, recur,root+'/'+res[k]["string"],actualroot+'/'+k) + except: + obj=Logger() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + obj.log_write('Function', LOG_ERROR, info) + +def VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList,sTableName=""): + if sTableName.find(".") != -1: + for i in range(len(aItemList)): + if aComponentAdd[i]==sTableName: + sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")] + for j in range(len(aObjectList)): + if aObjectList[j][:aObjectList[j].find("(")] == sLVal: + insVariable.append(aObjectList[j]) + VariableScope(oTcur,insVariable,aObjectList,aComponentAdd,aItemList, sTableName[:sTableName.rfind(".")]) + else: + for i in range(len(aItemList)): + if aComponentAdd[i]==sTableName: + sLVal=aItemList[i][1][aItemList[i][1].find(",'")+2:aItemList[i][1].find("')")] + for j in range(len(aObjectList)): + if aObjectList[j][:aObjectList[j].find("(")] == sLVal and sLVal!="": + insVariable.append(aObjectList[j]) + +def getList(aObjectList,host,count): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + docinfo=doc.getDocumentInfo() + sMain="" + if not count == 0: + if count >= 1: + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sItem[sItem.find("(")+1:sItem.find(",")]=="objects": + sMain = sItem[sItem.find(",'")+2:sItem.find("')")] + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn": + if sItem[sItem.find("(")+1:sItem.find(",")]=="objects": + aObjectList.append(sItem[sItem.rfind(",'")+2:sItem.rfind("')")] + "(" + docinfo.getUserFieldValue(3) + ")") + else: + sTemp=sItem[sItem.find("(")+1:sItem.find(",")] + if sMain == sTemp[:sTemp.find(".")]: + getRelation(docinfo.getUserFieldValue(3), sItem[sItem.find(".")+1:sItem.find(",")], sItem[sItem.find(",'")+2:sItem.find("')")],aObjectList,host) + else: + sPath=getPath(sItem[sItem.find("(")+1:sItem.find(",")], sMain) + getRelation(docinfo.getUserFieldValue(3), sPath, sItem[sItem.find(",'")+2:sItem.find("')")],aObjectList,host) + else: + aObjectList.append("List of " + docinfo.getUserFieldValue(3)) + +def getRelation(sRelName, sItem, sObjName, aObjectList, host ): + global url + sock=RPCSession(url) + global passwd + res = sock.execute(database, uid, passwd, sRelName , 'fields_get') + key = res.keys() + for k in key: + if sItem.find(".") == -1: + if k == sItem: + aObjectList.append(sObjName + "(" + res[k]['relation'] + ")") + return 0 + if k == sItem[:sItem.find(".")]: + getRelation(res[k]['relation'], sItem[sItem.find(".")+1:], sObjName,aObjectList,host) + + +def getPath(sPath,sMain): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + oParEnum = doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.supportsService("com.sun.star.text.TextField.DropDown"): + sItem=oPar.Items[1] + if sPath[:sPath.find(".")] == sMain: + break; + else: + res = re.findall('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]',sPath) + if len(res) <> 0: + if sItem[sItem.find(",'")+2:sItem.find("')")] == sPath[:sPath.find(".")]: + sPath = sItem[sItem.find("(")+1:sItem.find(",")] + sPath[sPath.find("."):] + getPath(sPath, sMain) + return sPath + +def EnumDocument(aItemList,aComponentAdd): + desktop = getDesktop() + parent="" + bFlag = False + Doc =desktop.getCurrentComponent() + #oVC = Doc.CurrentController.getViewCursor() + oParEnum = Doc.getTextFields().createEnumeration() + while oParEnum.hasMoreElements(): + oPar = oParEnum.nextElement() + if oPar.Anchor.TextTable: + #parent = oPar.Anchor.TextTable.Name + getChildTable(oPar.Anchor.TextTable,aItemList,aComponentAdd) + elif oPar.Anchor.TextSection: + parent = oPar.Anchor.TextSection.Name + elif oPar.Anchor.Text: + parent = "Document" + sItem=oPar.Items[1].replace(' ',"") + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn" and not oPar.Items in aItemList: + templist=oPar.Items[0],sItem + aItemList.append( templist ) + aComponentAdd.append( parent ) + +def getChildTable(oPar,aItemList,aComponentAdd,sTableName=""): + sNames = oPar.getCellNames() + bEmptyTableFlag=True + for val in sNames: + oCell = oPar.getCellByName(val) + oCurEnum = oCell.createEnumeration() + while oCurEnum.hasMoreElements(): + try: + oCur = oCurEnum.nextElement() + + if oCur.supportsService("com.sun.star.text.TextTable"): + if sTableName=="": + getChildTable(oCur,aItemList,aComponentAdd,oPar.Name) + else: + getChildTable(oCur,aItemList,aComponentAdd,sTableName+"."+oPar.Name) + else: + oSecEnum = oCur.createEnumeration() + while oSecEnum.hasMoreElements(): + oSubSection = oSecEnum.nextElement() + if oSubSection.supportsService("com.sun.star.text.TextField"): + bEmptyTableFlag=False + sItem=oSubSection.TextField.Items[1] + if sItem[sItem.find("[[ ")+3:sItem.find("(")]=="repeatIn": + if aItemList.__contains__(oSubSection.TextField.Items)==False: + aItemList.append(oSubSection.TextField.Items) + if sTableName=="": + if aComponentAdd.__contains__(oPar.Name)==False: + aComponentAdd.append(oPar.Name) + else: + if aComponentAdd.__contains__(sTableName+"."+oPar.Name)==False: + aComponentAdd.append(sTableName+"."+oPar.Name) + except: + obj=Logger() + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + obj.log_write('Function', LOG_ERROR, info) + if bEmptyTableFlag==True: + aItemList.append((u'',u'')) + if sTableName=="": + if aComponentAdd.__contains__(oPar.Name)==False: + aComponentAdd.append(oPar.Name) + else: + if aComponentAdd.__contains__(sTableName+"."+oPar.Name)==False: + aComponentAdd.append(sTableName+"."+oPar.Name) + return 0 + +def getRecersiveSection(oCurrentSection,aSectionList): + desktop=getDesktop() + doc =desktop.getCurrentComponent() + oParEnum=doc.getText().createEnumeration() + aSectionList.append(oCurrentSection.Name) + if oCurrentSection.ParentSection: + getRecersiveSection(oCurrentSection.ParentSection,aSectionList) + else: + return + +def GetAFileName(): + oFileDialog=None + iAccept=None + sPath="" + InitPath="" + oUcb=None + oFileDialog = createUnoService("com.sun.star.ui.dialogs.FilePicker") + oUcb = createUnoService("com.sun.star.ucb.SimpleFileAccess") + oFileDialog.appendFilter("OpenERP Report File","*.sxw") + oFileDialog.setCurrentFilter("OpenERP Report File") + if InitPath == "": + InitPath =tempfile.gettempdir() + #End If + if oUcb.exists(InitPath): + oFileDialog.setDisplayDirectory(InitPath) + #End If + iAccept = oFileDialog.execute() + if iAccept == 1: + sPath = oFileDialog.Files[0] + oFileDialog.dispose() + return sPath + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/gui.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/gui.py new file mode 100644 index 00000000000..1583d45437b --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/gui.py @@ -0,0 +1,775 @@ +import uno +import unohelper +import pythonloader +if __name__<>"package": + from actions import * + + +#------------------------------------------------------------ +# Uno ServiceManager access +# A different version of this routine and global variable +# is needed for code running inside a component. +#------------------------------------------------------------ + + +# The ServiceManager of the running OOo. +# It is cached in a global variable. +goServiceManager = False +pythonloader.DEBUG = 0 +def getServiceManager( cHost="localhost", cPort="2002" ): + """Get the ServiceManager from the running OpenOffice.org. + Then retain it in the global variable goServiceManager for future use. + This is similar to the GetProcessServiceManager() in OOo Basic. + """ + global goServiceManager + global pythonloader + if not goServiceManager: + # Get the uno component context from the PyUNO runtime + oLocalContext = uno.getComponentContext() + # Create the UnoUrlResolver on the Python side. + + # Connect to the running OpenOffice.org and get its context. + if __name__<>"package": + oLocalResolver = oLocalContext.ServiceManager.createInstanceWithContext( + "com.sun.star.bridge.UnoUrlResolver", oLocalContext ) + oContext = oLocalResolver.resolve( "uno:socket,host=" + cHost + ",port=" + cPort + ";urp;StarOffice.ComponentContext" ) + # Get the ServiceManager object + goServiceManager = oContext.ServiceManager + else: + goServiceManager=oLocalContext.ServiceManager + + return goServiceManager + + +#------------------------------------------------------------ +# Uno convenience functions +# The stuff in this section is just to make +# python progrmaming of OOo more like using OOo Basic. +#------------------------------------------------------------ + + +# This is the same as ServiceManager.createInstance( ... ) +def createUnoService( cClass ): + """A handy way to create a global objects within the running OOo. + Similar to the function of the same name in OOo Basic. + """ + oServiceManager = getServiceManager() + oObj = oServiceManager.createInstance( cClass ) + return oObj + + +# The StarDesktop object. (global like in OOo Basic) +# It is cached in a global variable. +StarDesktop = None +def getDesktop(): + """An easy way to obtain the Desktop object from a running OOo. + """ + global StarDesktop + if StarDesktop == None: + StarDesktop = createUnoService( "com.sun.star.frame.Desktop" ) + return StarDesktop +# preload the StarDesktop variable. +#getDesktop() + + +# The CoreReflection object. +# It is cached in a global variable. +goCoreReflection = False +def getCoreReflection(): + global goCoreReflection + if not goCoreReflection: + goCoreReflection = createUnoService( "com.sun.star.reflection.CoreReflection" ) + return goCoreReflection + + +def createUnoStruct( cTypeName ): + """Create a UNO struct and return it. + Similar to the function of the same name in OOo Basic. + """ + oCoreReflection = getCoreReflection() + # Get the IDL class for the type name + oXIdlClass = oCoreReflection.forName( cTypeName ) + # Create the struct. + oReturnValue, oStruct = oXIdlClass.createObject( None ) + return oStruct + +#------------------------------------------------------------ +# API helpers +#------------------------------------------------------------ + +def hasUnoInterface( oObject, cInterfaceName ): + """Similar to Basic's HasUnoInterfaces() function, but singular not plural.""" + + # Get the Introspection service. + oIntrospection = createUnoService( "com.sun.star.beans.Introspection" ) + + # Now inspect the object to learn about it. + oObjInfo = oIntrospection.inspect( oObject ) + + # Obtain an array describing all methods of the object. + oMethods = oObjInfo.getMethods( uno.getConstantByName( "com.sun.star.beans.MethodConcept.ALL" ) ) + # Now look at every method. + for oMethod in oMethods: + # Check the method's interface to see if + # these aren't the droids you're looking for. + cMethodInterfaceName = oMethod.getDeclaringClass().getName() + if cMethodInterfaceName == cInterfaceName: + return True + return False + +def hasUnoInterfaces( oObject, *cInterfaces ): + """Similar to the function of the same name in OOo Basic.""" + for cInterface in cInterfaces: + if not hasUnoInterface( oObject, cInterface ): + return False + return True + +#------------------------------------------------------------ +# High level general purpose functions +#------------------------------------------------------------ + + +def makePropertyValue( cName=None, uValue=None, nHandle=None, nState=None ): + """Create a com.sun.star.beans.PropertyValue struct and return it. + """ + oPropertyValue = createUnoStruct( "com.sun.star.beans.PropertyValue" ) + + if cName != None: + oPropertyValue.Name = cName + if uValue != None: + oPropertyValue.Value = uValue + if nHandle != None: + oPropertyValue.Handle = nHandle + if nState != None: + oPropertyValue.State = nState + + return oPropertyValue + + +def makePoint( nX, nY ): + """Create a com.sun.star.awt.Point struct.""" + oPoint = createUnoStruct( "com.sun.star.awt.Point" ) + oPoint.X = nX + oPoint.Y = nY + return oPoint + + +def makeSize( nWidth, nHeight ): + """Create a com.sun.star.awt.Size struct.""" + oSize = createUnoStruct( "com.sun.star.awt.Size" ) + oSize.Width = nWidth + oSize.Height = nHeight + return oSize + + +def makeRectangle( nX, nY, nWidth, nHeight ): + """Create a com.sun.star.awt.Rectangle struct.""" + oRect = createUnoStruct( "com.sun.star.awt.Rectangle" ) + oRect.X = nX + oRect.Y = nY + oRect.Width = nWidth + oRect.Height = nHeight + return oRect + + +def Array( *args ): + """This is just sugar coating so that code from OOoBasic which + contains the Array() function can work perfectly in python.""" + tArray = () + for arg in args: + tArray += (arg,) + return tArray + + +def loadComponentFromURL( cUrl, tProperties=() ): + """Open or Create a document from it's URL. + New documents are created from URL's such as: + private:factory/sdraw + private:factory/swriter + private:factory/scalc + private:factory/simpress + """ + StarDesktop = getDesktop() + oDocument = StarDesktop.loadComponentFromURL( cUrl, "_blank", 0, tProperties ) + return oDocument + + +#------------------------------------------------------------ +# Styles +#------------------------------------------------------------ + + +def defineStyle( oDrawDoc, cStyleFamily, cStyleName, cParentStyleName=None ): + """Add a new style to the style catalog if it is not already present. + This returns the style object so that you can alter its properties. + """ + + oStyleFamily = oDrawDoc.getStyleFamilies().getByName( cStyleFamily ) + + # Does the style already exist? + if oStyleFamily.hasByName( cStyleName ): + # then get it so we can return it. + oStyle = oStyleFamily.getByName( cStyleName ) + else: + # Create new style object. + oStyle = oDrawDoc.createInstance( "com.sun.star.style.Style" ) + + # Set its parent style + if cParentStyleName != None: + oStyle.setParentStyle( cParentStyleName ) + + # Add the new style to the style family. + oStyleFamily.insertByName( cStyleName, oStyle ) + + return oStyle + + +def getStyle( oDrawDoc, cStyleFamily, cStyleName ): + """Lookup and return a style from the document. + """ + return oDrawDoc.getStyleFamilies().getByName( cStyleFamily ).getByName( cStyleName ) + +#------------------------------------------------------------ +# General Utility functions +#------------------------------------------------------------ + + +def convertToURL( cPathname ): + """Convert a Windows or Linux pathname into an OOo URL.""" + if len( cPathname ) > 1: + if cPathname[1:2] == ":": + cPathname = "/" + cPathname[0] + "|" + cPathname[2:] + cPathname = cPathname.replace( "\\", "/" ) + cPathname = "file://" + cPathname + return cPathname + +# The global Awt Toolkit. +# This is initialized the first time it is needed. +#goAwtToolkit = createUnoService( "com.sun.star.awt.Toolkit" ) +goAwtToolkit = None + +def getAwtToolkit(): + global goAwtToolkit + if goAwtToolkit == None: + goAwtToolkit = createUnoService( "com.sun.star.awt.Toolkit" ) + return goAwtToolkit + +# This class builds dialog boxes. +# This can be used in two different ways... +# 1. by subclassing it (elegant) +# 2. without subclassing it (less elegant) +class DBModalDialog: + """Class to build a dialog box from the com.sun.star.awt.* services. + This doesn't do anything you couldn't already do using OOo's UNO API, + this just makes it much easier. + You can change the dialog box size, position, title, etc. + You can add controls, and listeners for those controls to the dialog box. + This class can be used by subclassing it, or without subclassing it. + """ + def __init__( self, nPositionX=None, nPositionY=None, nWidth=None, nHeight=None, cTitle=None ): + self.oDialogModel = createUnoService( "com.sun.star.awt.UnoControlDialogModel" ) + if nPositionX != None: self.oDialogModel.PositionX = nPositionX + if nPositionY != None: self.oDialogModel.PositionY = nPositionY + if nWidth != None: self.oDialogModel.Width = nWidth + if nHeight != None: self.oDialogModel.Height = nHeight + if cTitle != None: self.oDialogModel.Title = cTitle + self.oDialogControl = createUnoService( "com.sun.star.awt.UnoControlDialog" ) + self.oDialogControl.setModel( self.oDialogModel ) + + def release( self ): + """Release resources. + After calling this, you can no longer use this object. + """ + self.oDialogControl.dispose() + + #-------------------------------------------------- + # Dialog box adjustments + #-------------------------------------------------- + + def setDialogPosition( self, nX, nY ): + self.oDialogModel.PositionX = nX + self.oDialogModel.PositionY = nY + + def setDialogSize( self, nWidth, nHeight ): + self.oDialogModel.Width = nWidth + self.oDialogModel.Height = nHeight + + def setDialogTitle( self, cCaption ): + self.oDialogModel.Title = cCaption + + def setVisible( self, bVisible ): + self.oDialogControl.setVisible( bVisible ) + + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlButton + #-------------------------------------------------- + + # After you add a Button control, you can call self.setControlModelProperty() + # passing any of the properties for a... + # com.sun.star.awt.UnoControlButtonModel + # com.sun.star.awt.UnoControlDialogElement + # com.sun.star.awt.UnoControlModel + def addButton( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None, + actionListenerProc=None, + nTabIndex=None ): + self.addControl( "com.sun.star.awt.UnoControlButtonModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None, bMultiSelection=None, + cLabel=cLabel, + nTabIndex=nTabIndex ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + def setButtonLabel( self, cCtrlName, cLabel ): + """Set the label of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setLabel( cLabel ) + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlEditModel + #-------------------------------------------------- + def addEdit( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cText=None, + textListenerProc=None ): + """Add a Edit control to the window.""" + self.addControl( "com.sun.star.awt.UnoControlEditModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None) + + if cText != None: + self.setEditText( cCtrlName, cText ) + if textListenerProc != None: + self.addTextListenerProc( cCtrlName, textListenerProc ) + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlCheckBox + #-------------------------------------------------- + + # After you add a CheckBox control, you can call self.setControlModelProperty() + # passing any of the properties for a... + # com.sun.star.awt.UnoControlCheckBoxModel + # com.sun.star.awt.UnoControlDialogElement + # com.sun.star.awt.UnoControlModel + def addCheckBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None, + itemListenerProc=None, + nTabIndex=None ): + self.addControl( "com.sun.star.awt.UnoControlCheckBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, bDropdown=None, bMultiSelection=None, + cLabel=cLabel, + nTabIndex=nTabIndex ) + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + + def setEditText( self, cCtrlName, cText ): + """Set the text of the edit box.""" + oControl = self.getControl( cCtrlName ) + oControl.setText( cText ) + + def getEditText( self, cCtrlName): + """Set the text of the edit box.""" + oControl = self.getControl( cCtrlName ) + return oControl.getText() + + def setCheckBoxLabel( self, cCtrlName, cLabel ): + """Set the label of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setLabel( cLabel ) + + def getCheckBoxState( self, cCtrlName ): + """Get the state of the control.""" + oControl = self.getControl( cCtrlName ) + return oControl.getState(); + + def setCheckBoxState( self, cCtrlName, nState ): + """Set the state of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.setState( nState ) + + def enableCheckBoxTriState( self, cCtrlName, bTriStateEnable ): + """Enable or disable the tri state mode of the control.""" + oControl = self.getControl( cCtrlName ) + oControl.enableTriState( bTriStateEnable ) + + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlFixedText + #-------------------------------------------------- + + def addFixedText( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + cLabel=None ): + self.addControl( "com.sun.star.awt.UnoControlFixedTextModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=None, bMultiSelection=None, + cLabel=cLabel ) + + return self.getControl( cCtrlName ) + + #-------------------------------------------------- + # Add Controls to dialog + #-------------------------------------------------- + + def addControl( self, cCtrlServiceName, + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=None, + bMultiSelection=None, + cLabel=None, + nTabIndex=None, + sImagePath=None, + ): + oControlModel = self.oDialogModel.createInstance( cCtrlServiceName ) + self.oDialogModel.insertByName( cCtrlName, oControlModel ) + # if negative coordinates are given for X or Y position, + # then make that coordinate be relative to the right/bottom + # edge of the dialog box instead of to the left/top. + if nPositionX < 0: nPositionX = self.oDialogModel.Width + nPositionX - nWidth + if nPositionY < 0: nPositionY = self.oDialogModel.Height + nPositionY - nHeight + oControlModel.PositionX = nPositionX + oControlModel.PositionY = nPositionY + oControlModel.Width = nWidth + oControlModel.Height = nHeight + oControlModel.Name = cCtrlName + + if bDropdown != None: + oControlModel.Dropdown = bDropdown + + if bMultiSelection!=None: + oControlModel.MultiSelection=bMultiSelection + + if cLabel != None: + oControlModel.Label = cLabel + + if nTabIndex != None: + oControlModel.TabIndex = nTabIndex + + if sImagePath != None: + oControlModel.ImageURL = sImagePath + #-------------------------------------------------- + # Access controls and control models + #-------------------------------------------------- + + #-------------------------------------------------- + # com.sun.star.awt.UnoContorlListBoxModel + #-------------------------------------------------- + + + def addComboListBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=True, + bMultiSelection=False, + itemListenerProc=None, + actionListenerProc=None, + ): + + mod = self.addControl( "com.sun.star.awt.UnoControlListBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight,bDropdown,bMultiSelection ) + + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + + def addListBoxItems( self, cCtrlName, tcItemTexts, nPosition=0 ): + """Add a tupple of items to the ListBox at specified position.""" + oControl = self.getControl( cCtrlName ) + oControl.addItems( tcItemTexts, nPosition ) + + def selectListBoxItem( self, cCtrlName, cItemText, bSelect=True ): + """Selects/Deselects the ispecified item.""" + oControl = self.getControl( cCtrlName ) + return oControl.selectItem( cItemText, bSelect ) + + def selectListBoxItemPos( self, cCtrlName, nItemPos, bSelect=True ): + """Select/Deselect the item at the specified position.""" + oControl = self.getControl( cCtrlName ) + return oControl.selectItemPos( nItemPos, bSelect ) + + def removeListBoxItems( self, cCtrlName, nPosition, nCount=1 ): + """Remove items from a ListBox.""" + oControl = self.getControl( cCtrlName ) + oControl.removeItems( nPosition, nCount ) + + def getListBoxItemCount( self, cCtrlName ): + """Get the number of items in a ListBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getItemCount() + + def getListBoxSelectedItem( self, cCtrlName ): + """Returns the currently selected item.""" + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItem() + + def getListBoxItem( self, cCtrlName, nPosition ): + """Return the item at specified position within the ListBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getItem( nPosition ) + + def getListBoxSelectedItemPos(self,cCtrlName): + + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItemPos() + + def getListBoxSelectedItems(self,cCtrlName): + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItems() + + def getListBoxSelectedItemsPos(self,cCtrlName): + + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedItemsPos() + + #-------------------------------------------------- + # com.sun.star.awt.UnoControlComboBoxModel + #-------------------------------------------------- + def addComboBox( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + bDropdown=True, + itemListenerProc=None, + actionListenerProc=None ): + + mod = self.addControl( "com.sun.star.awt.UnoControlComboBoxModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight,bDropdown) + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + + def setComboBoxText( self, cCtrlName, cText ): + """Set the text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + oControl.setText( cText ) + + def getComboBoxText( self, cCtrlName): + """Set the text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getText() + + def getComboBoxSelectedText( self, cCtrlName ): + """Get the selected text of the ComboBox.""" + oControl = self.getControl( cCtrlName ) + return oControl.getSelectedText(); + + def getControl( self, cCtrlName ): + """Get the control (not its model) for a particular control name. + The control returned includes the service com.sun.star.awt.UnoControl, + and another control-specific service which inherits from it. + """ + oControl = self.oDialogControl.getControl( cCtrlName ) + return oControl + + def getControlModel( self, cCtrlName ): + """Get the control model (not the control) for a particular control name. + The model returned includes the service UnoControlModel, + and another control-specific service which inherits from it. + """ + oControl = self.getControl( cCtrlName ) + oControlModel = oControl.getModel() + return oControlModel + #--------------------------------------------------- + # com.sun.star.awt.UnoControlImageControlModel + #--------------------------------------------------- + def addImageControl( self, cCtrlName, nPositionX, nPositionY, nWidth, nHeight, + sImagePath="", + itemListenerProc=None, + actionListenerProc=None ): + + mod = self.addControl( "com.sun.star.awt.UnoControlImageControlModel", + cCtrlName, nPositionX, nPositionY, nWidth, nHeight, sImagePath=sImagePath) + + if itemListenerProc != None: + self.addItemListenerProc( cCtrlName, itemListenerProc ) + if actionListenerProc != None: + self.addActionListenerProc( cCtrlName, actionListenerProc ) + + + #-------------------------------------------------- + # Adjust properties of control models + #-------------------------------------------------- + + def setControlModelProperty( self, cCtrlName, cPropertyName, uValue ): + """Set the value of a property of a control's model. + This affects the control model, not the control. + """ + oControlModel = self.getControlModel( cCtrlName ) + oControlModel.setPropertyValue( cPropertyName, uValue ) + + def getControlModelProperty( self, cCtrlName, cPropertyName ): + """Get the value of a property of a control's model. + This affects the control model, not the control. + """ + oControlModel = self.getControlModel( cCtrlName ) + return oControlModel.getPropertyValue( cPropertyName ) + + #-------------------------------------------------- + # Sugar coated property adjustments to control models. + #-------------------------------------------------- + + def setEnabled( self, cCtrlName, bEnabled=True ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "Enabled", bEnabled ) + + def getEnabled( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + + return self.getControlModelProperty( cCtrlName, "Enabled" ) + + def setState( self, cCtrlName, nState ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "State", nState ) + + def getState( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "State" ) + + def setLabel( self, cCtrlName, cLabel ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "Label", cLabel ) + + def getLabel( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "Label" ) + + def setHelpText( self, cCtrlName, cHelpText ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + self.setControlModelProperty( cCtrlName, "HelpText", cHelpText ) + + def getHelpText( self, cCtrlName ): + """Supported controls... + UnoControlButtonModel + UnoControlCheckBoxModel + """ + return self.getControlModelProperty( cCtrlName, "HelpText" ) + + + #-------------------------------------------------- + # Adjust controls (not models) + #-------------------------------------------------- + + # The following apply to all controls which are a + # com.sun.star.awt.UnoControl + + def setDesignMode( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + oControl.setDesignMode( bDesignMode ) + + def isDesignMode( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + return oControl.isDesignMode() + + def isTransparent( self, cCtrlName, bDesignMode=True ): + oControl = self.getControl( cCtrlName ) + return oControl.isTransparent() + + + # The following apply to all controls which are a + # com.sun.star.awt.UnoControlDialogElement + + def setPosition( self, cCtrlName, nPositionX, nPositionY ): + self.setControlModelProperty( cCtrlName, "PositionX", nPositionX ) + self.setControlModelProperty( cCtrlName, "PositionY", nPositionY ) + def setPositionX( self, cCtrlName, nPositionX ): + self.setControlModelProperty( cCtrlName, "PositionX", nPositionX ) + def setPositionY( self, cCtrlName, nPositionY ): + self.setControlModelProperty( cCtrlName, "PositionY", nPositionY ) + def getPositionX( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "PositionX" ) + def getPositionY( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "PositionY" ) + + def setSize( self, cCtrlName, nWidth, nHeight ): + self.setControlModelProperty( cCtrlName, "Width", nWidth ) + self.setControlModelProperty( cCtrlName, "Height", nHeight ) + def setWidth( self, cCtrlName, nWidth ): + self.setControlModelProperty( cCtrlName, "Width", nWidth ) + def setHeight( self, cCtrlName, nHeight ): + self.setControlModelProperty( cCtrlName, "Height", nHeight ) + def getWidth( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Width" ) + def getHeight( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Height" ) + + def setTabIndex( self, cCtrlName, nWidth, nTabIndex ): + self.setControlModelProperty( cCtrlName, "TabIndex", nTabIndex ) + def getTabIndex( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "TabIndex" ) + + def setStep( self, cCtrlName, nWidth, nStep ): + self.setControlModelProperty( cCtrlName, "Step", nStep ) + def getStep( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Step" ) + + def setTag( self, cCtrlName, nWidth, cTag ): + self.setControlModelProperty( cCtrlName, "Tag", cTag ) + def getTag( self, cCtrlName ): + return self.getControlModelProperty( cCtrlName, "Tag" ) + + def setEchoChar(self, cCtrlName , cVal): + self.setControlModelProperty(cCtrlName, "EchoChar", cVal) + def getEchoChar(self, cCtrlName): + return self.setControlModelProperty(cCtrlName, "EchoChar") + + #-------------------------------------------------- + # Add listeners to controls. + #-------------------------------------------------- + + # This applies to... + # UnoControlButton + def addActionListenerProc( self, cCtrlName, actionListenerProc ): + """Create an com.sun.star.awt.XActionListener object and add it to a control. + A listener object is created which will call the python procedure actionListenerProc. + The actionListenerProc can be either a method or a global procedure. + The following controls support XActionListener: + UnoControlButton + """ + oControl = self.getControl( cCtrlName ) + oActionListener = ActionListenerProcAdapter( actionListenerProc ) + oControl.addActionListener( oActionListener ) + + # This applies to... + # UnoControlCheckBox + def addItemListenerProc( self, cCtrlName, itemListenerProc ): + """Create an com.sun.star.awt.XItemListener object and add it to a control. + A listener object is created which will call the python procedure itemListenerProc. + The itemListenerProc can be either a method or a global procedure. + The following controls support XActionListener: + UnoControlCheckBox + """ + oControl = self.getControl( cCtrlName ) + oActionListener = ItemListenerProcAdapter( itemListenerProc ) + oControl.addItemListener( oActionListener ) + + #-------------------------------------------------- + # Display the modal dialog. + #-------------------------------------------------- + + def doModalDialog( self, sObjName,sValue): + """Display the dialog as a modal dialog.""" + self.oDialogControl.setVisible( True ) + if not sValue==None: + self.selectListBoxItem( sObjName, sValue, True ) + self.oDialogControl.execute() + + def endExecute( self ): + """Call this from within one of the listeners to end the modal dialog. + For instance, the listener on your OK or Cancel button would call this to end the dialog. + """ + self.oDialogControl.endExecute() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/logreport.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/logreport.py new file mode 100644 index 00000000000..1cd6a16bd32 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/logreport.py @@ -0,0 +1,26 @@ +import logging +import tempfile +LOG_DEBUG='debug' +LOG_INFO='info' +LOG_WARNING='warn' +LOG_ERROR='error' +LOG_CRITICAL='critical' + +def log_detail(self): + import os + logger = logging.getLogger() + logfile_name = os.path.join(tempfile.gettempdir(), "report_designer.log") + hdlr = logging.FileHandler(logfile_name) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + logger.setLevel(logging.INFO) + +class Logger(object): + def log_write(self,name,level,msg): + log = logging.getLogger(name) + getattr(log,level)(msg) + + def shutdown(self): + logging.shutdown() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/rpc.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/rpc.py new file mode 100644 index 00000000000..ca741858e09 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/rpc.py @@ -0,0 +1,183 @@ +import time +import socket +import xmlrpclib +#import tiny_socket +import re + +class RPCGateway(object): + def __init__(self, host, port, protocol): + + self.protocol = protocol + self.host = host + self.port = port + + def get_url(self): + + """Get the url + """ + return "%s://%s:%s/"%(self.protocol, self.host, self.port) + + def listdb(self): + """Get the list of databases. + """ + pass + + def login(self, db, user, password): + pass + + def execute(self, obj, method, *args): + pass + + + +class RPCSession(object): + def __init__(self,url): + + m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '') + + host = m.group(2) + port = m.group(3) + protocol = m.group(1) + if not m: + return -1 + if protocol == 'http://' or protocol == 'http://': + self.gateway = XMLRPCGateway(host, port, 'http') + elif protocol == 'socket://': + + self.gateway = NETRPCGateway(host, port) + + def listdb(self): + return self.gateway.listdb() + + def login(self, db, user, password): + + if password is None: + return -1 + + uid = self.gateway.login(db, user or '', password or '') + + if uid <= 0: + return -1 + + self.uid = uid + self.db = db + self.password = password + self.open = True + return uid + + + def execute(self, obj, method, *args): + try: + result = self.gateway.execute(obj, method, *args) + return self.__convert(result) + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + + def __convert(self, result): + + if isinstance(result, basestring): + # try to convert into unicode string + try: + return ustr(result) + except Exception, e: + return result + + elif isinstance(result, list): + return [self.__convert(val) for val in result] + + elif isinstance(result, tuple): + return tuple([self.__convert(val) for val in result]) + + elif isinstance(result, dict): + newres = {} + for key, val in result.items(): + newres[key] = self.__convert(val) + + return newres + + else: + return result + +class XMLRPCGateway(RPCGateway): + """XML-RPC implementation. + """ + def __init__(self, host, port, protocol='http'): + + super(XMLRPCGateway, self).__init__(host, port, protocol) + global rpc_url + rpc_url = self.get_url() + 'xmlrpc/' + + def listdb(self): + global rpc_url + sock = xmlrpclib.ServerProxy(rpc_url + 'db') + try: + return sock.list() + except Exception, e: + return -1 + + def login(self, db, user, password): + + global rpc_url + + sock = xmlrpclib.ServerProxy(rpc_url + 'common') + + try: + res = sock.login(db, user, password) + except Exception, e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + return -1 + + return res + + def execute(self, sDatabase,UID,sPassword,obj, method, *args): + global rpc_url + + sock = xmlrpclib.ServerProxy(rpc_url + 'object') + + return sock.execute(sDatabase,UID,sPassword, obj ,method,*args) + + + +class NETRPCGateway(RPCGateway): + def __init__(self, host, port): + + super(NETRPCGateway, self).__init__(host, port, 'socket') + + def listdb(self): + sock = mysocket() + try: + sock.connect(self.host, self.port) + sock.mysend(('db', 'list')) + res = sock.myreceive() + sock.disconnect() + return res + except Exception, e: + return -1 + + def login(self, db, user, password): + sock = mysocket() + try: + sock.connect(self.host, self.port) + sock.mysend(('common', 'login', db, user, password)) + res = sock.myreceive() + sock.disconnect() + except Exception, e: + return -1 + return res + def execute(self,obj, method, *args): + sock = mysocket() + try: + sock.connect(self.host, self.port) + data=(('object', 'execute',obj,method,)+args) + sock.mysend(data) + res=sock.myreceive() + sock.disconnect() + return res + + except Exception,e: + import traceback,sys + info = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tiny_socket.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tiny_socket.py new file mode 100644 index 00000000000..35754a04ded --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tiny_socket.py @@ -0,0 +1,93 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved +# $Id$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## +import socket +import cPickle +import cStringIO +import marshal + +class Myexception(Exception): + def __init__(self, faultCode, faultString): + self.faultCode = faultCode + self.faultString = faultString + self.args = (faultCode, faultString) + +class mysocket: + def __init__(self, sock=None): + if sock is None: + self.sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) + else: + self.sock = sock + self.sock.settimeout(120) + def connect(self, host, port=False): + if not port: + protocol, buf = host.split('//') + host, port = buf.split(':') + self.sock.connect((host, int(port))) + def disconnect(self): + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + def mysend(self, msg, exception=False, traceback=None): + + msg = cPickle.dumps([msg,traceback]) + size = len(msg) + self.sock.send('%8d' % size) + self.sock.send(exception and "1" or "0") + totalsent = 0 + while totalsent < size: + sent = self.sock.send(msg[totalsent:]) + if sent == 0: + raise RuntimeError, "socket connection broken" + totalsent = totalsent + sent + def myreceive(self): + buf='' + while len(buf) < 8: + chunk = self.sock.recv(8 - len(buf)) + if chunk == '': + raise RuntimeError, "socket connection broken" + buf += chunk + size = int(buf) + buf = self.sock.recv(1) + if buf != "0": + exception = buf + else: + exception = False + msg = '' + while len(msg) < size: + chunk = self.sock.recv(size-len(msg)) + if chunk == '': + raise RuntimeError, "socket connection broken" + msg = msg + chunk + msgio = cStringIO.StringIO(msg) + unpickler = cPickle.Unpickler(msgio) + unpickler.find_global = None + res = unpickler.load() + + if isinstance(res[0],Exception): + if exception: + raise Myexception(str(res[0]), str(res[1])) + raise res[0] + else: + return res[0] + + + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tools.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tools.py new file mode 100644 index 00000000000..09e7f96c46e --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/lib/tools.py @@ -0,0 +1,19 @@ +import urllib + +def get_absolute_file_path( url ): + url_unquoted = urllib.unquote(url) + return os.name == 'nt' and url_unquoted[1:] or url_unquoted + +# This function reads the content of a file and return it to the caller +def read_data_from_file( filename ): + fp = file( filename, "rb" ) + data = fp.read() + fp.close() + return data + +# This function writes the content to a file +def write_data_to_file( filename, data ): + fp = file( filename, 'wb' ) + fp.write( data ) + fp.close() + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/modify.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/modify.py new file mode 100644 index 00000000000..8a710a6e959 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/bin/script/modify.py @@ -0,0 +1,91 @@ +import re +import uno +import string +import unohelper +import xmlrpclib +from com.sun.star.task import XJobExecutor +if __name__<>"package": + from lib.gui import * + from Expression import Expression + from Fields import Fields + from Repeatln import RepeatIn + from lib.error import * + database="test" + uid = 3 + +class modify(unohelper.Base, XJobExecutor ): + def __init__( self, ctx ): + self.ctx = ctx + self.module = "openerp_report" + self.version = "0.1" + + # Variable Declaration + desktop = getDesktop() + doc = desktop.getCurrentComponent() + docinfo = doc.getDocumentInfo() + self.oVC = doc.CurrentController.getViewCursor() + if not docinfo.getUserFieldValue(0)=="": + self.sMyHost= docinfo.getUserFieldValue(0) + else: + ErrorDialog( + "Please insert user define field Field-1", + "Just go to File->Properties->User Define \n" + "Field-1 Eg. http://localhost:8069" + ) + exit(1) + + # Check weather Field-4 is available or not otherwise exit from application + if not docinfo.getUserFieldValue(3) == "" and not docinfo.getUserFieldValue(0)=="": + if self.oVC.TextField: + self.oCurObj=self.oVC.TextField + item = self.oCurObj.Items[0] + + kind, group1, group2 = self.getOperation(self.oCurObj.Items[1] ) + + start_group1 = group1[:group1.find(".")] + stop_group1 = group1[group1.find("."):].replace(".", "/") + + if kind == "field": + Fields( start_group1, stop_group1, item, True ) + elif kind == "expression": + Expression( group1, item, True ) + elif kind == "repeatIn": + RepeatIn( start_group1, group2, stop_group1, item, True ) + else: + ErrorDialog( + "Please place your cursor at begaining of field \n" + "which you want to modify","" + ) + + else: + ErrorDialog( + "Please insert user define field Field-1 or Field-4", + "Just go to File->Properties->User Define \n" + "Field-1 Eg. http://localhost:8069 \n" + "OR \n" + "Field-4 Eg. account.invoice" + ) + exit(1) + + def getOperation(self, str): + #str = "[[ RepeatIn(objects, 'variable') ]]" #repeatIn + #str = "[[ saleorder.partner_id.name ]]" # field + #str = "[[ some thing complex ]]" # expression + method1 = lambda x: (u'repeatIn', x.group(1), x.group(2)) + method2 = lambda x: (u'field', x.group(1), None) + method3 = lambda x: (u'expression', x.group(1), None) + regexes = [ + ('\\[\\[ *repeatIn\\( *(.+)*, *\'([a-zA-Z0-9_]+)\' *\\) *\\]\\]', method1), + ('\\[\\[ *([a-zA-Z0-9_\.]+) *\\]\\]', method2), + ('\\[\\[ *(.+) *\\]\\]', method3) + ] + for (rule,method) in regexes: + res = re.match(rule, str) + if res: + return method(res) + +if __name__<>"package": + modify(None) +else: + g_ImplementationHelper.addImplementation( modify, "org.openoffice.openerp.report.modify", ("com.sun.star.task.Job",),) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/doc/Readme.txt b/addons/base_report_designer/report_desinger/openoffice_report_designer/doc/Readme.txt new file mode 100644 index 00000000000..dc67455d1f7 --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/doc/Readme.txt @@ -0,0 +1,48 @@ +Steps to Install package in Openoffice + + - In Openoffice writer select Tools Menu + + - Inside of Tools menu you will get option like "Package Manager" + + - Select Package Manager you will get one dialog box + + - In Dialog box you will get one listbox below "Browse Packages". + You will find different options like 'My Packages, + Openoffice Packages'. + + - You have select 'My Packages' and then click on button named 'Add...' + then select your path where the package is located that is + ZIP archive file. + + - On the completion of adding package you will get your package + under 'My Packages' and the status of your package become 'Enabled' + then close openoffice writer + + - Now you have start tiny server + + - then in unother terminal u have to write command + + ooffice "-accept=socket,host=localhost,port=2002;urp;" + + - Above command will open openoffice writer with socket connection + if you use different host then localhost then chang it accordingly + + - Now in openoffice writer u will find one new menu named "Tiny Report" + +Steps to execute scripts + + - You can directly us scripts for perticular operation. + + - If you want to execute script then u have to use terminal. + + - Now you have start tiny server + + - then in unother terminal u have to write command + + ooffice "-accept=socket,host=localhost,port=2002;urp;" + + - Go to the perticular directory of scripts and then use below command + + python scriptname.py + + - you will get execute yor script diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/doc/test_fields.py b/addons/base_report_designer/report_desinger/openoffice_report_designer/doc/test_fields.py new file mode 100644 index 00000000000..8afd773b3fb --- /dev/null +++ b/addons/base_report_designer/report_desinger/openoffice_report_designer/doc/test_fields.py @@ -0,0 +1,32 @@ +# +# Use this module to retrive the fields you need according to the type +# of the OpenOffice operation: +# * Insert a Field +# * Insert a RepeatIn +# + +import xmlrpclib +import time + +sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object') + +def get(object, level=3, ending=[], ending_excl=[], recur=[], root=''): + res = sock.execute('terp', 3, 'admin', 'account.invoice', 'fields_get') + key = res.keys() + key.sort() + for k in key: + if (not ending or res[k]['type'] in ending) and ((not ending_excl) or not (res[k]['type'] in ending_excl)): + print root+'/'+k + + if res[k]['type'] in recur: + print root+'/'+k + if (res[k]['type'] in recur) and (level>0): + get(res[k]['relation'], level-1, ending, ending_excl, recur, root+'/'+k) + +print 'Field selection for a rields', '='*40 +get('account.invoice', level=0, ending_excl=['one2many','many2one','many2many','reference'], recur=['many2one']) + +print +print 'Field selection for a repeatIn', '='*40 +get('account.invoice', level=0, ending=['one2many','many2many'], recur=['many2one']) + diff --git a/addons/base_report_designer/report_desinger/openoffice_report_designer/openerp_report_designer.zip b/addons/base_report_designer/report_desinger/openoffice_report_designer/openerp_report_designer.zip new file mode 100644 index 0000000000000000000000000000000000000000..27f7fd48236ed97315e1c6da2e8fd7d15d9c71f6 GIT binary patch literal 151447 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 PZ~Qm!jF2G;zrN64r0{k9 literal 0 HcmV?d00001 diff --git a/addons/base_report_designer/wizard/__init__.py b/addons/base_report_designer/wizard/__init__.py index f80f87da1d5..1df66bdd200 100644 --- a/addons/base_report_designer/wizard/__init__.py +++ b/addons/base_report_designer/wizard/__init__.py @@ -20,5 +20,6 @@ ############################################################################## import base_report_designer_modify +import base_openoffice # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_report_designer/wizard/base_openoffice.py b/addons/base_report_designer/wizard/base_openoffice.py new file mode 100644 index 00000000000..d8e585b7ddb --- /dev/null +++ b/addons/base_report_designer/wizard/base_openoffice.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from osv import fields +from osv import osv +from tools import config + +import base64 +class base_openoffice(osv.osv_memory): + _name = "base.openoffice" + _description = "Open Office Report Desinger" + _columns = { + 'base_desinger_url': fields.char('Documentation URL', size=300, readonly=True), + 'plugin_file':fields.binary('OpenOffice Report Desinger',readonly=True), + 'pdf_file':fields.binary('Documenation', size=64 ,readonly=True), + } + def add_plugin(self, cr, uid, ids, context): + data = {} + file = open(config['addons_path'] + "/base_report_designer/report_desinger/openoffice_report_designer/openerp_report_designer.zip", 'r') + data['plugin_file'] = base64.encodestring(file.read()) + self.write(cr, uid, ids, data) + return False + + def process_pdf_file(self, cr, uid, ids, context): + """ + Default Attach Thunderbird Plug-in Installation File. + """ + data = {} + pdf_file = open(config['addons_path'] + "/base_report_designer/report_desinger/doc/OpenOffice.pdf", 'r') + data['pdf_file'] = base64.encodestring(pdf_file.read()) + self.write(cr, uid, ids, data) + return False + _defaults = { + 'name' : 'tiny_plugin-2.0.xpi', + 'pdf_name' : 'Installation Guide to OpenOffice Report Desinger.pdf', + 'base_desinger_url':'http://doc.openerp.com/book/8/8_20_Config/8_20_Config_reports.html' + } + +base_openoffice() \ No newline at end of file diff --git a/addons/base_report_designer/wizard/base_openoffice_view.xml b/addons/base_report_designer/wizard/base_openoffice_view.xml new file mode 100644 index 00000000000..d3ed246a0f8 --- /dev/null +++ b/addons/base_report_designer/wizard/base_openoffice_view.xml @@ -0,0 +1,43 @@ + + + + + + + OpenOffice Report Designer + base.openoffice + form + +
+ + + + + +