diff --git a/base_external_dbsource/__init__.py b/base_external_dbsource/__init__.py
new file mode 100755
index 00000000..81d83fc8
--- /dev/null
+++ b/base_external_dbsource/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Daniel Reis
+# 2011
+#
+# 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 .
+#
+##############################################################################
+
+import base_external_dbsource
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/base_external_dbsource/__openerp__.py b/base_external_dbsource/__openerp__.py
new file mode 100644
index 00000000..f40b09a9
--- /dev/null
+++ b/base_external_dbsource/__openerp__.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Daniel Reis, 2011
+# Additional contributions by Maxime Chambreuil, Savoir-faire Linux
+#
+# 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 .
+#
+##############################################################################
+
+{
+ 'name': 'External Database Sources',
+ 'version': '61.3',
+ 'category': 'Tools',
+ 'description': """
+This module allows you to define connections to foreign databases using ODBC,
+Oracle Client or SQLAlchemy.
+
+Databases sources can be configured in Settings > Configuration -> Data sources.
+
+Depending on the database, you need:
+ * to install unixodbc and python-pyodbc packages to use ODBC connections.
+ * to install FreeTDS driver (tdsodbc package) and configure it through ODBC to
+ connect to Microsoft SQL Server.
+ * to install and configure Oracle Instant Client and cx_Oracle python library
+ to connect to Oracle.
+ """,
+ 'author': 'Daniel Reis',
+ 'website': 'http://launchpad.net/addons-tko',
+ 'images': [
+ 'images/screenshot01.png',
+ ],
+ 'depends': [
+ 'base',
+ ],
+ 'init': [],
+ 'data': [
+ 'base_external_dbsource_view.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'demo': [
+ 'base_external_dbsource_demo.xml',
+ ],
+ 'test': [
+ 'dbsource_connect.yml',
+ ],
+ 'installable': True,
+ 'active': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/base_external_dbsource/base_external_dbsource.py b/base_external_dbsource/base_external_dbsource.py
new file mode 100644
index 00000000..92d3de19
--- /dev/null
+++ b/base_external_dbsource/base_external_dbsource.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Daniel Reis
+# 2011
+#
+# 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 .
+#
+##############################################################################
+
+import os
+from osv import fields, osv
+from openerp.tools.translate import _
+import openerp.tools as tools
+import logging
+_logger = logging.getLogger(__name__)
+
+CONNECTORS = []
+
+try:
+ import sqlalchemy
+ import pymssql
+ CONNECTORS.append( ('mssql', 'Microsoft SQL Server') )
+except:
+ _logger.info('MS SQL Server not available. Please install "slqalchemy" and "pymssql" python package.')
+
+try:
+ import sqlalchemy
+ import MySQLdb
+ CONNECTORS.append( ('mysql', 'MySQL') )
+except:
+ _logger.info('MySQL not available. Please install "slqalchemy" and "mysqldb" python package.')
+
+try:
+ import pyodbc
+ CONNECTORS.append( ('pyodbc', 'ODBC') )
+except:
+ _logger.info('ODBC libraries not available. Please install "unixodbc" and "python-pyodbc" packages.')
+
+try:
+ import cx_Oracle
+ CONNECTORS.append( ('cx_Oracle', 'Oracle') )
+except:
+ _logger.info('Oracle libraries not available. Please install "cx_Oracle" python package.')
+
+import psycopg2
+CONNECTORS.append( ('postgresql', 'PostgreSQL') )
+
+try:
+ import sqlalchemy
+ CONNECTORS.append( ('sqlite', 'SQLite') )
+except:
+ _logger.info('SQLAlchemy not available. Please install "slqalchemy" python package.')
+
+class base_external_dbsource(osv.osv):
+ _name = "base.external.dbsource"
+ _description = 'External Database Sources'
+ _columns = {
+ 'name': fields.char('Datasource name', required=True, size=64),
+ 'conn_string': fields.text('Connection string', help="""\
+Sample connection strings:
+- Microsoft SQL Server: mssql+pymssql://username:%s@server:port/dbname?charset=utf8
+- MySQL: mysql://user:%s@server:port/dbname
+- ODBC: DRIVER={FreeTDS};SERVER=server.address;Database=mydb;UID=sa
+- ORACLE: username/%s@//server.address:port/instance
+- PostgreSQL: dbname='template1' user='dbuser' host='localhost' port='5432' password=%s
+- SQLite: sqlite:///test.db
+"""),
+ 'password': fields.char('Password' , size=40),
+ 'connector': fields.selection(CONNECTORS, 'Connector', required=True,
+ help = "If a connector is missing from the list, check the " \
+ + "server log to confirm that the required componentes were detected."),
+ }
+
+ def conn_open(self, cr, uid, id1):
+ #Get dbsource record
+ data = self.browse(cr, uid, id1)
+ #Build the full connection string
+ connStr = data.conn_string
+ if data.password:
+ if '%s' not in data.conn_string:
+ connStr += ';PWD=%s'
+ connStr = connStr % data.password
+ #Try to connect
+ if data.connector == 'cx_Oracle':
+ os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.UTF8'
+ conn = cx_Oracle.connect(connStr)
+ elif data.connector == 'pyodbc':
+ conn = pyodbc.connect(connStr)
+ elif data.connector in ('sqlite','mysql','mssql'):
+ conn = sqlalchemy.create_engine(connStr).connect()
+ elif data.connector == 'postgresql':
+ conn = psycopg2.connect(connStr)
+
+ return conn
+
+ def execute_and_inspect(self, cr, uid, ids, sqlquery, sqlparams=None, context=None):
+ """Executes SQL and returns a dict with the rows and the list of columns.
+
+ "sqlparams" can be a dict of values, that can be referenced in the SQL statement
+ using "%(key)s" or, in the case of Oracle, ":key".
+ Example:
+ sqlquery = "select * from mytable where city = %(city)s and date > %(dt)s"
+ params = {'city': 'Lisbon', 'dt': datetime.datetime(2000, 12, 31)}
+
+ Sample result: { 'colnames'; ['col_a', 'col_b', ...]
+ , 'rows': [ (a0, b0, ...), (a1, b1, ...), ...] }
+ """
+ data = self.browse(cr, uid, ids)
+ nams = dict()
+ rows = list()
+ for obj in data:
+ conn = self.conn_open(cr, uid, obj.id)
+ if obj.connector in ["sqlite","mysql","mssql"]:
+ #using sqlalchemy
+ cur = conn.execute(sqlquery, sqlparams)
+ nams = cur.keys()
+ rows = [r for r in cur]
+ else:
+ #using other db connectors
+ cur = conn.cursor()
+ cur.execute(sqlquery, sqlparams)
+ nams = [d[0] for d in cur.description]
+ rows = cur.fetchall()
+ conn.close()
+ return {'colnames': nams, 'rows': rows}
+
+ def execute(self, cr, uid, ids, sqlquery, sqlparams=None, context=None):
+ return self.execute_and_inspect(self, cr, uid, ids, sqlquery, sqlparams)['rows']
+
+ def connection_test(self, cr, uid, ids, context=None):
+ for obj in self.browse(cr, uid, ids, context):
+ conn = False
+ try:
+ conn = self.conn_open(cr, uid, obj.id)
+ except Exception, e:
+ raise osv.except_osv(_("Connection test failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e))
+ finally:
+ try:
+ if conn: conn.close()
+ except Exception:
+ # ignored, just a consequence of the previous exception
+ pass
+ #TODO: if OK a (wizard) message box should be displayed
+ raise osv.except_osv(_("Connection test succeeded!"), _("Everything seems properly set up!"))
+
+base_external_dbsource()
diff --git a/base_external_dbsource/base_external_dbsource_demo.xml b/base_external_dbsource/base_external_dbsource_demo.xml
new file mode 100755
index 00000000..ff2c7e3d
--- /dev/null
+++ b/base_external_dbsource/base_external_dbsource_demo.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ PostgreSQL local
+ dbname='postgres' password=%s
+ postgresql
+ postgresql
+
+
+
+
+
+
diff --git a/base_external_dbsource/base_external_dbsource_view.xml b/base_external_dbsource/base_external_dbsource_view.xml
new file mode 100644
index 00000000..606bc726
--- /dev/null
+++ b/base_external_dbsource/base_external_dbsource_view.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+ base.external.dbsource.tree
+ base.external.dbsource
+ tree
+
+
+
+
+
+
+
+
+
+
+ base.external.dbsource.form
+ base.external.dbsource
+ form
+
+
+
+
+
+
+ External Database Sources
+ base.external.dbsource
+ form
+ tree,form
+
+
+
+
+
+
+
+
+
diff --git a/base_external_dbsource/images/screenshot01.png b/base_external_dbsource/images/screenshot01.png
new file mode 100644
index 00000000..0d6a47a9
Binary files /dev/null and b/base_external_dbsource/images/screenshot01.png differ
diff --git a/base_external_dbsource/security/ir.model.access.csv b/base_external_dbsource/security/ir.model.access.csv
new file mode 100755
index 00000000..66393b79
--- /dev/null
+++ b/base_external_dbsource/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_base_external_dbsource_group_system,bae_external_dbsource_group_system,model_base_external_dbsource,base.group_system,1,1,1,1
diff --git a/base_external_dbsource/test/dbsource_connect.yml b/base_external_dbsource/test/dbsource_connect.yml
new file mode 100755
index 00000000..1eabcf2b
--- /dev/null
+++ b/base_external_dbsource/test/dbsource_connect.yml
@@ -0,0 +1,5 @@
+-
+ Connect to local Postgres.
+-
+ !python {model: base.external.dbsource}: |
+ self.connection_test(cr, uid, [ref("demo_postgresql")]