summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorDenis Ledoux <dle@openerp.com>2013-06-26 15:35:52 +0200
committerDenis Ledoux <dle@openerp.com>2013-06-26 15:35:52 +0200
commitf694c37b83f23daab9f7f6c86fd01a0184630386 (patch)
treebae21d2a9c747f52eb892fcd433821659a627790 /addons
parentc180afab26171617c1c7c22db5d9836b159e5e7d (diff)
parent66520d9e8e3d62eb4bec66ccd797211d3841c13f (diff)
tmp
bzr revid: dle@openerp.com-20130626133552-gtfhzvxsi1oj3pdm
Diffstat (limited to 'addons')
-rw-r--r--addons/base_setup/res_config.py2
-rw-r--r--addons/base_setup/res_config_view.xml9
-rw-r--r--addons/crm/crm_lead_demo.xml1
-rw-r--r--addons/google_base_account/__init__.py1
-rw-r--r--addons/google_base_account/__openerp__.py5
-rw-r--r--addons/google_base_account/google_base_account.py49
-rw-r--r--addons/google_base_account/google_base_account_data.xml9
-rw-r--r--addons/google_base_account/google_base_account_view.xml20
-rw-r--r--addons/google_base_account/wizard/__init__.py25
-rw-r--r--addons/google_base_account/wizard/google_login.py85
-rw-r--r--addons/google_base_account/wizard/google_login_view.xml35
-rw-r--r--addons/google_docs/__init__.py1
-rw-r--r--addons/google_docs/google_docs.py193
-rw-r--r--addons/google_docs/res_config_user_view.xml54
-rw-r--r--addons/google_docs/security/ir.model.access.csv3
-rw-r--r--addons/google_docs/static/src/js/gdocs.js40
-rw-r--r--addons/google_drive/__init__.py1
-rw-r--r--addons/google_drive/__openerp__.py (renamed from addons/google_docs/__openerp__.py)23
-rw-r--r--addons/google_drive/google_drive.py211
-rw-r--r--addons/google_drive/google_drive_data.xml25
-rw-r--r--addons/google_drive/google_drive_demo.xml24
-rw-r--r--addons/google_drive/i18n/ar.po (renamed from addons/google_docs/i18n/ar.po)0
-rw-r--r--addons/google_drive/i18n/cs.po (renamed from addons/google_docs/i18n/cs.po)0
-rw-r--r--addons/google_drive/i18n/de.po (renamed from addons/google_docs/i18n/de.po)0
-rw-r--r--addons/google_drive/i18n/es.po (renamed from addons/google_docs/i18n/es.po)0
-rw-r--r--addons/google_drive/i18n/fr.po (renamed from addons/google_docs/i18n/fr.po)0
-rw-r--r--addons/google_drive/i18n/google_docs.pot (renamed from addons/google_docs/i18n/google_docs.pot)0
-rw-r--r--addons/google_drive/i18n/hr.po (renamed from addons/google_docs/i18n/hr.po)0
-rw-r--r--addons/google_drive/i18n/hu.po (renamed from addons/google_docs/i18n/hu.po)0
-rw-r--r--addons/google_drive/i18n/it.po (renamed from addons/google_docs/i18n/it.po)0
-rw-r--r--addons/google_drive/i18n/ln.po (renamed from addons/google_docs/i18n/ln.po)0
-rw-r--r--addons/google_drive/i18n/mk.po (renamed from addons/google_docs/i18n/mk.po)0
-rw-r--r--addons/google_drive/i18n/mn.po (renamed from addons/google_docs/i18n/mn.po)0
-rw-r--r--addons/google_drive/i18n/nl.po (renamed from addons/google_docs/i18n/nl.po)0
-rw-r--r--addons/google_drive/i18n/pl.po (renamed from addons/google_docs/i18n/pl.po)0
-rw-r--r--addons/google_drive/i18n/pt.po (renamed from addons/google_docs/i18n/pt.po)0
-rw-r--r--addons/google_drive/i18n/pt_BR.po (renamed from addons/google_docs/i18n/pt_BR.po)0
-rw-r--r--addons/google_drive/i18n/ro.po (renamed from addons/google_docs/i18n/ro.po)0
-rw-r--r--addons/google_drive/i18n/ru.po (renamed from addons/google_docs/i18n/ru.po)0
-rw-r--r--addons/google_drive/i18n/sl.po (renamed from addons/google_docs/i18n/sl.po)0
-rw-r--r--addons/google_drive/i18n/sv.po (renamed from addons/google_docs/i18n/sv.po)0
-rw-r--r--addons/google_drive/i18n/tr.po (renamed from addons/google_docs/i18n/tr.po)0
-rw-r--r--addons/google_drive/i18n/zh_CN.po (renamed from addons/google_docs/i18n/zh_CN.po)0
-rw-r--r--addons/google_drive/res_config_user_view.xml93
-rw-r--r--addons/google_drive/security/ir.model.access.csv3
-rw-r--r--addons/google_drive/static/lib/gapi/client.js7
-rw-r--r--addons/google_drive/static/src/img/drive_icon.pngbin0 -> 2148 bytes
-rw-r--r--addons/google_drive/static/src/img/drive_icon_mono.pngbin0 -> 479 bytes
-rw-r--r--addons/google_drive/static/src/img/icon.pngbin0 -> 19252 bytes
-rw-r--r--addons/google_drive/static/src/js/gdrive.js76
-rw-r--r--addons/google_drive/static/src/xml/gdocs.xml (renamed from addons/google_docs/static/src/xml/gdocs.xml)0
51 files changed, 519 insertions, 476 deletions
diff --git a/addons/base_setup/res_config.py b/addons/base_setup/res_config.py
index 33c5aa863bf..1e5169fc2b3 100644
--- a/addons/base_setup/res_config.py
+++ b/addons/base_setup/res_config.py
@@ -36,6 +36,8 @@ class base_config_settings(osv.osv_memory):
help="""Enable the public part of openerp, openerp becomes a public website."""),
'module_auth_oauth': fields.boolean('Use external authentication providers, sign in with google, facebook, ...'),
'module_base_import': fields.boolean("Allow users to import data from CSV files"),
+ 'module_google_drive': fields.boolean('Attach a google document to any record',
+ help="""This installs the module google_docs."""),
}
def open_company(self, cr, uid, ids, context=None):
diff --git a/addons/base_setup/res_config_view.xml b/addons/base_setup/res_config_view.xml
index 8c48f5d7a6b..73685be95df 100644
--- a/addons/base_setup/res_config_view.xml
+++ b/addons/base_setup/res_config_view.xml
@@ -81,6 +81,15 @@
</div>
</div>
</group>
+ <group>
+ <label for="id" string="Google Drive"/>
+ <div name="google_drive">
+ <div name="module_google_drive">
+ <field name="module_google_drive" class="oe_inline"/>
+ <label for="module_google_drive"/>
+ </div>
+ </div>
+ </group>
</form>
</field>
</record>
diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml
index e7e6beae33e..4c6cf47c4f7 100644
--- a/addons/crm/crm_lead_demo.xml
+++ b/addons/crm/crm_lead_demo.xml
@@ -709,5 +709,6 @@ Andrew</field>
eval="[ ref('msg_case18_1'), ref('msg_case18_2')], True, {}"
/>
+
</data>
</openerp>
diff --git a/addons/google_base_account/__init__.py b/addons/google_base_account/__init__.py
index e1a476431af..44a5aabe277 100644
--- a/addons/google_base_account/__init__.py
+++ b/addons/google_base_account/__init__.py
@@ -20,7 +20,6 @@
##############################################################################
import google_base_account
-import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/google_base_account/__openerp__.py b/addons/google_base_account/__openerp__.py
index b4d996f2807..99678d2785d 100644
--- a/addons/google_base_account/__openerp__.py
+++ b/addons/google_base_account/__openerp__.py
@@ -30,10 +30,9 @@ The module adds google user in res user.
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
- 'depends': ['base'],
+ 'depends': ['base_setup'],
'data': [
- 'google_base_account_view.xml',
- 'wizard/google_login_view.xml',
+ 'google_base_account_data.xml',
],
'demo': [],
'installable': True,
diff --git a/addons/google_base_account/google_base_account.py b/addons/google_base_account/google_base_account.py
index 06e5643344c..39ccc569767 100644
--- a/addons/google_base_account/google_base_account.py
+++ b/addons/google_base_account/google_base_account.py
@@ -19,14 +19,45 @@
#
##############################################################################
-from openerp.osv import fields,osv
-
-class res_users(osv.osv):
- _inherit = "res.users"
- _columns = {
- 'gmail_user': fields.char('Username', size=64,),
- 'gmail_password': fields.char('Password', size=64),
- }
+from openerp.osv import osv
+from openerp import SUPERUSER_ID
-# vim:expandtab:smartindent:toabstop=4:softtabstop=4:shiftwidth=4:
+from httplib2 import Http
+import urllib
+import simplejson
+
+
+class base_config_settings(osv.osv):
+ _inherit = "base.config.settings"
+
+ def onchange_google_authorization_code(self, cr, uid, ids, service, authorization_code, context=None):
+ res = {}
+ if authorization_code:
+ ir_config = self.pool['ir.config_parameter']
+ client_id = ir_config.get_param(cr, SUPERUSER_ID, 'google_%s_client_id' % service)
+ client_secret = ir_config.get_param(cr, SUPERUSER_ID, 'google_%s_client_secret' % service)
+ redirect_uri = ir_config.get_param(cr, SUPERUSER_ID, 'google_redirect_uri')
+ #Get the Refresh Token From Google And store it in ir.config_parameter
+ headers = {"Content-type": "application/x-www-form-urlencoded"}
+ data = dict(code=authorization_code, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, grant_type="authorization_code")
+ data = urllib.urlencode(data)
+ resp, content = Http().request("https://accounts.google.com/o/oauth2/token", "POST", data, headers)
+ content = simplejson.loads(content)
+ if 'refresh_token' in content.keys():
+ ir_config.set_param(cr, uid, 'google_%s_refresh_token' % service, content['refresh_token'])
+ return res
+
+ def _get_google_token_uri(self, cr, uid, service, context=None):
+ ir_config = self.pool['ir.config_parameter']
+ params = {
+ 'scope': 'https://www.googleapis.com/auth/drive',
+ 'redirect_uri': ir_config.get_param(cr, SUPERUSER_ID, 'google_redirect_uri'),
+ 'client_id': ir_config.get_param(cr, SUPERUSER_ID, 'google_%s_client_id' % service),
+ 'response_type': 'code',
+ 'client_id': ir_config.get_param(cr, SUPERUSER_ID, 'google_%s_client_id' % service),
+ }
+ uri = 'https://accounts.google.com/o/oauth2/auth?%s' % urllib.urlencode(params)
+ return uri
+
+# vim:expandtab:smartindent:toabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/google_base_account/google_base_account_data.xml b/addons/google_base_account/google_base_account_data.xml
new file mode 100644
index 00000000000..c6748df7dc9
--- /dev/null
+++ b/addons/google_base_account/google_base_account_data.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<openerp>
+ <data noupdate="1">
+ <record id="config_google_redirect_uri" model="ir.config_parameter">
+ <field name="key">google_redirect_uri</field>
+ <field name="value">urn:ietf:wg:oauth:2.0:oob</field>
+ </record>
+ </data>
+</openerp> \ No newline at end of file
diff --git a/addons/google_base_account/google_base_account_view.xml b/addons/google_base_account/google_base_account_view.xml
deleted file mode 100644
index 8613582692f..00000000000
--- a/addons/google_base_account/google_base_account_view.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0"?>
-<openerp>
- <data>
- <record id="view_users_gogole_form" model="ir.ui.view">
- <field name="name">res.users.google.form1</field>
- <field name="model">res.users</field>
- <field name="inherit_id" ref="base.view_users_form"/>
- <field name="arch" type="xml">
- <xpath expr="//notebook" position="inside">
- <page string="Synchronization">
- <group string="Google Account" colspan="4">
- <field name="gmail_user"/>
- <field name="gmail_password" password="True"/>
- </group>
- </page>
- </xpath>
- </field>
- </record>
- </data>
-</openerp>
diff --git a/addons/google_base_account/wizard/__init__.py b/addons/google_base_account/wizard/__init__.py
deleted file mode 100644
index a6b9c644a1e..00000000000
--- a/addons/google_base_account/wizard/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-import google_login
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/google_base_account/wizard/google_login.py b/addons/google_base_account/wizard/google_login.py
deleted file mode 100644
index 1771d99b51e..00000000000
--- a/addons/google_base_account/wizard/google_login.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-from openerp.osv import fields,osv
-from openerp.tools.translate import _
-try:
- import gdata.contacts.service
- import gdata.contacts.client
- import gdata.calendar.service
-except ImportError:
- raise osv.except_osv(_('Google Contacts Import Error!'), _('Please install gdata-python-client from http://code.google.com/p/gdata-python-client/downloads/list'))
-
-class google_login(osv.osv_memory):
- _description ='Google Contact'
- _name = 'google.login'
- _columns = {
- 'user': fields.char('Google Username', size=64, required=True),
- 'password': fields.char('Google Password', size=64),
- }
-
- def google_login(self, user, password, type='', context=None):
- if type == 'group':
- gd_client = gdata.contacts.service.ContactsService()
- elif type == 'contact':
- gd_client = gdata.contacts.service.ContactsService()
- elif type == 'calendar':
- gd_client = gdata.calendar.service.CalendarService()
- elif type =='docs_client':
- gd_client = gdata.docs.client.DocsClient()
- else:
- gd_client = gdata.contacts.service.ContactsService()
- try:
- gd_client.ClientLogin(user, password, gd_client.source)
- except Exception:
- return False
- return gd_client
-
-
- def default_get(self, cr, uid, fields, context=None):
- res = super(google_login, self).default_get(cr, uid, fields, context=context)
- user_obj = self.pool.get('res.users').browse(cr, uid, uid)
- if 'user' in fields:
- res.update({'user': user_obj.gmail_user})
- if 'password' in fields:
- res.update({'password': user_obj.gmail_password})
- return res
-
- def login(self, cr, uid, ids, context=None):
- data = self.read(cr, uid, ids)[0]
- user = data['user']
- password = data['password']
- if self.google_login(user, password):
- res = {
- 'gmail_user': user,
- 'gmail_password': password
- }
- self.pool.get('res.users').write(cr, uid, uid, res, context=context)
- else:
- raise osv.except_osv(_('Error!'), _("Authentication failed. Check the user and password."))
-
- return self._get_next_action(cr, uid, context=context)
-
- def _get_next_action(self, cr, uid, context=None):
- return {'type': 'ir.actions.act_window_close'}
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/google_base_account/wizard/google_login_view.xml b/addons/google_base_account/wizard/google_login_view.xml
deleted file mode 100644
index ed345ab5a0f..00000000000
--- a/addons/google_base_account/wizard/google_login_view.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0"?>
-<openerp>
- <data>
- <record model="ir.ui.view" id="view_google_login_form">
- <field name="name">google.login.form</field>
- <field name="model">google.login</field>
- <field name="arch" type="xml">
- <form string="Google login" version="7.0">
- <group>
- <field name="user" placeholder="e.g. user@gmail.com"/>
- <field name="password" password="True"/>
- </group>
- <footer>
- <button name="login" string="_Login" type="object" class="oe_highlight"/>
- or
- <button string="Cancel" class="oe_link" special="cancel" />
- </footer>
- </form>
- </field>
- </record>
-
- <!--
- Login Action
- -->
- <record model="ir.actions.act_window" id="act_google_login_form">
- <field name="name">Google Login</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">google.login</field>
- <field name="view_type">form</field>
- <field name="view_mode">form</field>
- <field name="target">new</field>
- <field name="view_id" ref="view_google_login_form" />
- </record>
- </data>
-</openerp>
diff --git a/addons/google_docs/__init__.py b/addons/google_docs/__init__.py
deleted file mode 100644
index dd6285502c0..00000000000
--- a/addons/google_docs/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-import google_docs
diff --git a/addons/google_docs/google_docs.py b/addons/google_docs/google_docs.py
deleted file mode 100644
index 2fd1976ae3b..00000000000
--- a/addons/google_docs/google_docs.py
+++ /dev/null
@@ -1,193 +0,0 @@
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2012 OpenERP SA (<http://www.openerp.com>).
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-import logging
-from datetime import datetime
-
-from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-
-_logger = logging.getLogger(__name__)
-
-try:
- import gdata.docs.data
- import gdata.docs.client
-
- # API breakage madness in the gdata API - those guys are insane.
- try:
- # gdata 2.0.15+
- gdata.docs.client.DocsClient.copy_resource
- except AttributeError:
- # gdata 2.0.14- : copy_resource() was copy()
- gdata.docs.client.DocsClient.copy_resource = gdata.docs.client.DocsClient.copy
-
- try:
- # gdata 2.0.16+
- gdata.docs.client.DocsClient.get_resource_by_id
- except AttributeError:
- try:
- # gdata 2.0.15+
- gdata.docs.client.DocsClient.get_resource_by_self_link
- def get_resource_by_id_2_0_16(self, resource_id, **kwargs):
- return self.GetResourceBySelfLink(
- gdata.docs.client.RESOURCE_FEED_URI + ('/%s' % resource_id), **kwargs)
- gdata.docs.client.DocsClient.get_resource_by_id = get_resource_by_id_2_0_16
- except AttributeError:
- # gdata 2.0.14- : alias get_resource_by_id()
- gdata.docs.client.DocsClient.get_resource_by_id = gdata.docs.client.DocsClient.get_doc
-
- try:
- import atom.http_interface
- _logger.info('GData lib version `%s` detected' % atom.http_interface.USER_AGENT)
- except (ImportError, AttributeError):
- _logger.debug('GData lib version could not be detected', exc_info=True)
-
-except ImportError:
- _logger.warning("Please install latest gdata-python-client from http://code.google.com/p/gdata-python-client/downloads/list")
-
-
-class google_docs_ir_attachment(osv.osv):
- _inherit = 'ir.attachment'
-
- def _auth(self, cr, uid, context=None):
- '''
- Connexion with google base account
- @return client object for connexion
- '''
- #pool the google.login in google_base_account
- google_pool = self.pool.get('google.login')
- #get gmail password and login. We use default_get() instead of a create() followed by a read() on the
- # google.login object, because it is easier. The keys 'user' and 'password' ahve to be passed in the dict
- # but the values will be replaced by the user gmail password and login.
- user_config = google_pool.default_get(cr, uid, {'user' : '' , 'password' : ''}, context=context)
- #login gmail account
- client = google_pool.google_login(user_config['user'], user_config['password'], type='docs_client', context=context)
- if not client:
- raise osv.except_osv(_('Google Docs Error!'), _("Check your google configuration in Users/Users/Synchronization tab."))
- _logger.info('Logged into google docs as %s', user_config['user'])
- return client
-
- def create_empty_google_doc(self, cr, uid, res_model, res_id, context=None):
- '''Create a new google document, empty and with a default type (txt)
- :param res_model: the object for which the google doc is created
- :param res_id: the Id of the object for which the google doc is created
- :return: the ID of the google document object created
- '''
- #login with the base account google module
- client = self._auth(cr, uid, context=context)
- # create the document in google docs
- title = "%s %s" % (context.get("name","Untitled Document."), datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT))
- local_resource = gdata.docs.data.Resource(gdata.docs.data.DOCUMENT_LABEL,title=title)
- #create a new doc in Google Docs
- gdocs_resource = client.post(entry=local_resource, uri='https://docs.google.com/feeds/default/private/full/')
- # create an ir.attachment into the db
- self.create(cr, uid, {
- 'res_model': res_model,
- 'res_id': res_id,
- 'type': 'url',
- 'name': title,
- 'url': gdocs_resource.get_alternate_link().href,
- }, context=context)
- return {'resource_id': gdocs_resource.resource_id.text,
- 'title': title,
- 'url': gdocs_resource.get_alternate_link().href}
-
- def copy_gdoc(self, cr, uid, res_model, res_id, name_gdocs, gdoc_template_id, context=None):
- '''
- copy an existing document in google docs
- :param res_model: the object for which the google doc is created
- :param res_id: the Id of the object for which the google doc is created
- :param name_gdocs: the name of the future ir.attachment that will be created. Based on the google doc template foun.
- :param gdoc_template_id: the id of the google doc document to copy
- :return: the ID of the google document object created
- '''
- #login with the base account google module
- client = self._auth(cr, uid)
- # fetch and copy the original document
- try:
- doc = client.get_resource_by_id(gdoc_template_id)
- #copy the document you choose in the configuration
- copy_resource = client.copy_resource(doc, name_gdocs)
- except:
- raise osv.except_osv(_('Google Docs Error!'), _("Your resource id is not correct. You can find the id in the google docs URL."))
- # create an ir.attachment
- self.create(cr, uid, {
- 'res_model': res_model,
- 'res_id': res_id,
- 'type': 'url',
- 'name': name_gdocs,
- 'url': copy_resource.get_alternate_link().href
- }, context=context)
- return copy_resource.resource_id.text
-
- def google_doc_get(self, cr, uid, res_model, ids, context=None):
- '''
- Function called by the js, when no google doc are yet associated with a record, with the aim to create one. It
- will first seek for a google.docs.config associated with the model `res_model` to find out what's the template
- of google doc to copy (this is usefull if you want to start with a non-empty document, a type or a name
- different than the default values). If no config is associated with the `res_model`, then a blank text document
- with a default name is created.
- :param res_model: the object for which the google doc is created
- :param ids: the list of ids of the objects for which the google doc is created. This list is supposed to have
- a length of 1 element only (batch processing is not supported in the code, though nothing really prevent it)
- :return: the google document object created
- '''
- if len(ids) != 1:
- raise osv.except_osv(_('Google Docs Error!'), _("Creating google docs may only be done by one at a time."))
- res_id = ids[0]
- pool_ir_attachment = self.pool.get('ir.attachment')
- pool_gdoc_config = self.pool.get('google.docs.config')
- name_gdocs = ''
- model_fields_dic = self.pool[res_model].read(cr, uid, res_id, [], context=context)
-
- # check if a model is configured with a template
- google_docs_config = pool_gdoc_config.search(cr, uid, [('model_id', '=', res_model)], context=context)
- if google_docs_config:
- name_gdocs = pool_gdoc_config.browse(cr, uid, google_docs_config, context=context)[0].name_template
- try:
- name_gdocs = name_gdocs % model_fields_dic
- except:
- raise osv.except_osv(_('Key Error!'), _("Your Google Doc Name Pattern's key does not found in object."))
- google_template_id = pool_gdoc_config.browse(cr, uid, google_docs_config[0], context=context).gdocs_resource_id
- google_document = pool_ir_attachment.copy_gdoc(cr, uid, res_model, res_id, name_gdocs, google_template_id, context=context)
- else:
- google_document = pool_ir_attachment.create_empty_google_doc(cr, uid, res_model, res_id, context=context)
- return google_document
-
-class config(osv.osv):
- _name = 'google.docs.config'
- _description = "Google Docs templates config"
-
- _columns = {
- 'model_id': fields.many2one('ir.model', 'Model', required=True),
- 'gdocs_resource_id': fields.char('Google Resource ID to Use as Template', size=64, help='''
-This is the id of the template document, on google side. You can find it thanks to its URL:
-*for a text document with url like `https://docs.google.com/a/openerp.com/document/d/123456789/edit`, the ID is `document:123456789`
-*for a spreadsheet document with url like `https://docs.google.com/a/openerp.com/spreadsheet/ccc?key=123456789#gid=0`, the ID is `spreadsheet:123456789`
-*for a presentation (slide show) document with url like `https://docs.google.com/a/openerp.com/presentation/d/123456789/edit#slide=id.p`, the ID is `presentation:123456789`
-*for a drawing document with url like `https://docs.google.com/a/openerp.com/drawings/d/123456789/edit`, the ID is `drawings:123456789`
-...
-''', required=True),
- 'name_template': fields.char('Google Doc Name Pattern', size=64, help='Choose how the new google docs will be named, on google side. Eg. gdoc_%(field_name)s', required=True),
- }
-
- _defaults = {
- 'name_template': 'gdoc_%(name)s',
- }
diff --git a/addons/google_docs/res_config_user_view.xml b/addons/google_docs/res_config_user_view.xml
deleted file mode 100644
index e6c4223735f..00000000000
--- a/addons/google_docs/res_config_user_view.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
- <data>
-
- <!-- add google docs config field in user form -->
-
- <record model="ir.ui.view" id="view_google_docs_config_tree">
- <field name="name">google_docs.config.tree</field>
- <field name="model">google.docs.config</field>
- <field name="arch" type="xml">
- <tree string="Google Docs Configuration">
- <field name="model_id"/>
- <field name="name_template"/>
- </tree>
- </field>
- </record>
-
- <record model="ir.ui.view" id="view_google_docs_config_form">
- <field name="name">google_docs.config.form</field>
- <field name="model">google.docs.config</field>
- <field name="arch" type="xml">
- <form string="Google Docs Configuration" version="7.0">
- <group>
- <field name="model_id"/>
- <label for='gdocs_resource_id'/>
- <div>
- <field name='gdocs_resource_id'/>
- <p class="oe_grey">
-This is the id of the template document, on google side. You can find it thanks to its URL:
-<ul>
-<li>for a text document with url like `https://docs.google.com/a/openerp.com/document/d/123456789/edit`, the ID is `document:123456789`</li>
-<li>for a spreadsheet document with url like `https://docs.google.com/a/openerp.com/spreadsheet/ccc?key=123456789#gid=0`, the ID is `spreadsheet:123456789`</li>
-<li>for a presentation (slide show) document with url like `https://docs.google.com/a/openerp.com/presentation/d/123456789/edit#slide=id.p`, the ID is `presentation:123456789`</li>
-<li>for a drawing document with url like `https://docs.google.com/a/openerp.com/drawings/d/123456789/edit`, the ID is `drawings:123456789`</li>
-</ul>
- </p>
- </div>
- <field name='name_template'/>
- </group>
- </form>
- </field>
- </record>
-
- <record model='ir.actions.act_window' id='action_google_docs_users_config'>
- <field name='name'>Models configuration</field>
- <field name='res_model'>google.docs.config</field>
- <field name='type'>ir.actions.act_window</field>
- <field name='view_type'>form</field>
- <field name='view_id' ref='view_google_docs_config_tree'/>
- </record>
- <menuitem name='Google Docs configuration' id='menu_gdocs_config' parent='base.menu_administration'/>
- <menuitem name='Models configuration' id='menu_gdocs_model_config' parent='menu_gdocs_config' action='action_google_docs_users_config'/>
- </data>
-</openerp>
diff --git a/addons/google_docs/security/ir.model.access.csv b/addons/google_docs/security/ir.model.access.csv
deleted file mode 100644
index 9393d586fb8..00000000000
--- a/addons/google_docs/security/ir.model.access.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_google_docs,google.docs.config,model_google_docs_config,,1,0,0,0
-access_google_docs,google.docs.config,model_google_docs_config,base.group_system,1,1,1,1 \ No newline at end of file
diff --git a/addons/google_docs/static/src/js/gdocs.js b/addons/google_docs/static/src/js/gdocs.js
deleted file mode 100644
index 1cc511ef4d9..00000000000
--- a/addons/google_docs/static/src/js/gdocs.js
+++ /dev/null
@@ -1,40 +0,0 @@
-openerp.google_docs = function(instance, m) {
-var _t = instance.web._t,
- QWeb = instance.web.qweb;
-
- instance.web.Sidebar.include({
- redraw: function() {
- var self = this;
- this._super.apply(this, arguments);
- self.$el.find('.oe_sidebar_add_attachment').after(QWeb.render('AddGoogleDocumentItem', {widget: self}))
- self.$el.find('.oe_sidebar_add_google_doc').on('click', function (e) {
- self.on_google_doc();
- });
- },
- on_google_doc: function() {
- var self = this;
- var view = self.getParent();
- var ids = ( view.fields_view.type != "form" )? view.groups.get_selection().ids : [ view.datarecord.id ];
- if( !_.isEmpty(ids) ){
- view.sidebar_eval_context().done(function (context) {
- var ds = new instance.web.DataSet(this, 'ir.attachment', context);
- ds.call('google_doc_get', [view.dataset.model, ids, context]).done(function(r) {
- if (r == 'False') {
- var params = {
- error: response,
- message: _t("The user google credentials are not set yet. Contact your administrator for help.")
- }
- $(openerp.web.qweb.render("DialogWarning", params)).dialog({
- title: _t("User Google credentials are not yet set."),
- modal: true,
- });
- }
- }).done(function(r){
- window.open(r.url,"_blank");
- view.reload();
- });
- });
- }
- }
- });
-};
diff --git a/addons/google_drive/__init__.py b/addons/google_drive/__init__.py
new file mode 100644
index 00000000000..e1cf2d00f3c
--- /dev/null
+++ b/addons/google_drive/__init__.py
@@ -0,0 +1 @@
+import google_drive
diff --git a/addons/google_docs/__openerp__.py b/addons/google_drive/__openerp__.py
index 2d064fdb8a9..9e6c73d2649 100644
--- a/addons/google_docs/__openerp__.py
+++ b/addons/google_drive/__openerp__.py
@@ -20,22 +20,31 @@
##############################################################################
{
- 'name': 'Google Docs integration',
+ 'name': 'Google Driveā„¢ integration',
'version': '0.2',
'author': 'OpenERP SA',
'website': 'http://openerp.com',
'category': 'Tools',
'installable': True,
'auto_install': False,
- 'js': ['static/src/js/gdocs.js'],
- 'qweb': ['static/src/xml/gdocs.xml'],
+ 'js': [
+ 'static/lib/gapi/client.js',
+ 'static/src/js/gdrive.js',
+ ],
'data': [
'security/ir.model.access.csv',
- 'res_config_user_view.xml'
+ 'res_config_user_view.xml',
+ 'google_drive_data.xml'
+ ],
+ 'demo': [
+ 'google_drive_demo.xml'
],
- 'depends': ['google_base_account','document'],
+ 'depends': ['base_setup', 'google_base_account'],
'description': """
-Module to attach a google document to any model.
-================================================
+Integrate google document to OpenERP record.
+============================================
+
+This module allows you to integrate google documents to any of your OpenERP record quickly and easily using OAuth 2.0 for Installed Applications,
+You can configure your google Authorization Code from Settings > Configuration > General Settings by clicking on "Generate Google Authorization Code"
"""
}
diff --git a/addons/google_drive/google_drive.py b/addons/google_drive/google_drive.py
new file mode 100644
index 00000000000..d5d433787bb
--- /dev/null
+++ b/addons/google_drive/google_drive.py
@@ -0,0 +1,211 @@
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2012 OpenERP SA (<http://www.openerp.com>).
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import logging
+
+from openerp import SUPERUSER_ID
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+from urlparse import urlparse
+
+from httplib2 import Http
+import urllib
+import json
+
+_logger = logging.getLogger(__name__)
+
+
+class config(osv.osv):
+ _name = 'google.drive.config'
+ _description = "Google Drive templates config"
+
+ def get_google_doc_name(self, cr, uid, ids, res_id, template_id, context=None):
+ res = {}
+ for config in self.browse(cr, SUPERUSER_ID, ids, context=context):
+ model = config.model_id
+ filter_name = config.filter_id and config.filter_id.name or False
+ record = self.pool.get(model.model).read(cr, uid, res_id, [], context=context)
+ record.update({'model': model.name, 'filter': filter_name})
+ name_gdocs = config.name_template or "%(name)s_%(model)s_%(filter)s_gdrive"
+ try:
+ name_gdocs = name_gdocs % record
+ except:
+ raise osv.except_osv(_('Key Error!'), _("At least one key cannot be found in your Google Drive name pattern"))
+
+ attach_pool = self.pool.get("ir.attachment")
+ attach_ids = attach_pool.search(cr, uid, [('res_model', '=', model.model), ('name', '=', name_gdocs), ('res_id', '=', res_id)])
+ url = False
+ if attach_ids:
+ attachment = attach_pool.browse(cr, uid, attach_ids[0], context)
+ url = attachment.url
+ else:
+ url = self.copy_doc(cr, uid, ids, res_id, template_id, name_gdocs, model.model, context)
+ res[config.id] = {'url': url}
+ return res
+
+ def copy_doc(self, cr, uid, ids, res_id, template_id, name_gdocs, res_model, context=None):
+ ir_config = self.pool['ir.config_parameter']
+ google_drive_client_id = ir_config.get_param(cr, SUPERUSER_ID, 'google_drive_client_id')
+ google_drive_client_secret = ir_config.get_param(cr, SUPERUSER_ID, 'google_drive_client_secret')
+ google_drive_refresh_token = ir_config.get_param(cr, SUPERUSER_ID, 'google_drive_refresh_token')
+ google_web_base_url = ir_config.get_param(cr, SUPERUSER_ID, 'web.base.url')
+
+ #For Getting New Access Token With help of old Refresh Token
+ headers = {"Content-type": "application/x-www-form-urlencoded"}
+ data = dict(client_id=google_drive_client_id,
+ refresh_token=google_drive_refresh_token,
+ client_secret=google_drive_client_secret,
+ grant_type="refresh_token")
+ data = urllib.urlencode(data)
+ resp, content = Http().request("https://accounts.google.com/o/oauth2/token", "POST", data, headers)
+ content = json.loads(content)
+
+ # Copy template in to drive with help of new access token
+ if 'access_token' in content:
+ request_url = "https://www.googleapis.com/drive/v2/files/%s?fields=parents/id&access_token=%s" % (template_id, content['access_token'])
+ resp, parents = Http().request(request_url, "GET")
+ parents_dict = json.loads(parents)
+
+ headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
+ record_url = "Click on link to open Record in OpenERP\n %s/?db=%s#id=%s&model=%s" % (google_web_base_url, cr.dbname, res_id, res_model)
+ data = {"title": name_gdocs, "description": record_url, "parents": parents_dict['parents']}
+ request_url = "https://www.googleapis.com/drive/v2/files/%s/copy?access_token=%s" % (template_id, content['access_token'])
+ resp, content = Http().request(request_url, "POST", json.dumps(data), headers)
+ content = json.loads(content)
+ res = False
+ if 'alternateLink' in content.keys():
+ attach_pool = self.pool.get("ir.attachment")
+ attach_vals = {'res_model': res_model, 'name': name_gdocs, 'res_id': res_id, 'type': 'url', 'url': content['alternateLink']}
+ attach_pool.create(cr, uid, attach_vals)
+ res = content['alternateLink']
+ else:
+ raise self.pool.get('res.config.settings').get_config_warning(cr, _("You haven't configured 'Authorization Code' generated from google, Please generate and configure it in %(menu:base_setup.menu_general_configuration)s."), context=context)
+ return res
+
+ def get_google_drive_config(self, cr, uid, res_model, res_id, context=None):
+ '''
+ Function called by the js, when no google doc are yet associated with a record, with the aim to create one. It
+ will first seek for a google.docs.config associated with the model `res_model` to find out what's the template
+ of google doc to copy (this is usefull if you want to start with a non-empty document, a type or a name
+ different than the default values). If no config is associated with the `res_model`, then a blank text document
+ with a default name is created.
+ :param res_model: the object for which the google doc is created
+ :param ids: the list of ids of the objects for which the google doc is created. This list is supposed to have
+ a length of 1 element only (batch processing is not supported in the code, though nothing really prevent it)
+ :return: the config id and config name
+ '''
+ if not res_id:
+ raise osv.except_osv(_('Google Drive Error!'), _("Creating google drive may only be done by one at a time."))
+ # check if a model is configured with a template
+ config_ids = self.search(cr, uid, [('model_id', '=', res_model)], context=context)
+ configs = []
+ for config in self.browse(cr, SUPERUSER_ID, config_ids, context=context):
+ if config.filter_id:
+ if (config.filter_id.user_id and config.filter_id.user_id.id != uid):
+ #Private
+ continue
+ google_doc_configs = self._filter(cr, uid, config, config.filter_id, res_id, context=context)
+ if google_doc_configs:
+ configs.append({'id': config.id, 'name': config.name})
+ else:
+ configs.append({'id': config.id, 'name': config.name})
+ return configs
+
+ def _filter(self, cr, uid, action, action_filter, record_ids, context=None):
+ """ filter the list record_ids that satisfy the action filter """
+ if record_ids and action_filter:
+ if not action.model_id.model == action_filter.model_id:
+ raise osv.except_osv(_('Warning!'), _("Something went wrong with the configuration of attachments with google drive.Please contact your Administrator to fix the problem."))
+ model = self.pool.get(action_filter.model_id)
+ domain = [('id', 'in', [record_ids])] + eval(action_filter.domain)
+ ctx = dict(context or {})
+ ctx.update(eval(action_filter.context))
+ record_ids = model.search(cr, uid, domain, context=ctx)
+ return record_ids
+
+ def _resource_get(self, cr, uid, ids, name, arg, context=None):
+ result = {}
+ for data in self.browse(cr, uid, ids, context):
+ template_url = data.google_drive_template_url
+ try:
+ url = urlparse(template_url)
+ res = url.path.split('/')
+ if res[1] == "spreadsheet":
+ key = url.query.split('=')[1]
+ else:
+ key = res[3]
+ result[data.id] = str(key)
+ except:
+ raise osv.except_osv(_('Incorrect URL!'), _("Please enter a valid URL."))
+ return result
+
+ def _client_id_get(self, cr, uid, ids, name, arg, context=None):
+ result = {}
+ for config_id in ids:
+ config = self.pool['ir.config_parameter']
+ result[config_id] = config.get_param(cr, SUPERUSER_ID, 'google_drive_client_id')
+ return result
+
+ _columns = {
+ 'name': fields.char('Template Name', required=True, size=1024),
+ 'model_id': fields.many2one('ir.model', 'Model', ondelete='set null', required=True),
+ 'model': fields.related('model_id', 'model', type='char', string='Model', readonly=True),
+ 'filter_id': fields.many2one('ir.filters', 'Filter', domain="[('model_id', '=', model)]"),
+ 'google_drive_template_url': fields.char('Template URL', required=True, size=1024),
+ 'google_drive_resource_id': fields.function(_resource_get, type="char", string='Resource Id'),
+ 'google_drive_client_id': fields.function(_client_id_get, type="char", string='Google Client '),
+ 'name_template': fields.char('Google Drive Name Pattern', size=64, help='Choose how the new google drive will be named, on google side. Eg. gdoc_%(field_name)s', required=True),
+ }
+
+ def onchange_model_id(self, cr, uid, ids, model_id, context=None):
+ res = {}
+ if model_id:
+ model = self.pool['ir.model'].browse(cr, uid, model_id, context=context)
+ res['value'] = {'model': model.model}
+ else:
+ res['value'] = {'filter_id': False, 'model': False}
+ return res
+
+ _defaults = {
+ 'name_template': '%(name)s_%(model)s_%(filter)s_gdrive',
+ }
+
+ def _check_model_id(self, cr, uid, ids, context=None):
+ config_id = self.browse(cr, uid, ids[0], context=context)
+ if config_id.filter_id and config_id.model_id.model != config_id.filter_id.model_id:
+ return False
+ return True
+
+ _constraints = [
+ (_check_model_id, 'Model of selected filter is not matching with model of current template.', ['model_id', 'filter_id']),
+ ]
+
+config()
+
+
+class base_config_settings(osv.osv):
+ _inherit = "base.config.settings"
+
+ _columns = {
+ 'google_drive_authorization_code': fields.char('Authorization Code', size=124),
+ 'google_drive_uri': fields.char('URI', readonly=True, help="The URL to generate the authorization code from Google"),
+ }
+ _defaults = {
+ 'google_drive_uri': lambda s, cr, uid, c: s._get_google_token_uri(cr, uid, 'drive', context=c),
+ }
diff --git a/addons/google_drive/google_drive_data.xml b/addons/google_drive/google_drive_data.xml
new file mode 100644
index 00000000000..9bb81e2744a
--- /dev/null
+++ b/addons/google_drive/google_drive_data.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<openerp>
+ <data noupdate="1">
+
+ <record id="config_google_drive_client_id" model="ir.config_parameter">
+ <field name="key">google_drive_client_id</field>
+ <field name="value">39623646228-eg3ggo3mk6o40m7rguobi3rkl9frh4tb.apps.googleusercontent.com</field>
+ </record>
+
+ <record id="config_google_drive_client_secret" model="ir.config_parameter">
+ <field name="key">google_drive_client_secret</field>
+ <field name="value">Ul-PtmnSWs3euWs20fdono0e</field>
+ </record>
+
+ <record id="config_google_drive_refresh_token" model="ir.config_parameter">
+ <field name="key">google_drive_refresh_token</field>
+ <field name="value">-</field>
+ </record>
+
+ <record id="config_google_drive_authorization_code" model="ir.config_parameter">
+ <field name="key">google_drive_authorization_code</field>
+ <field name="value">-</field>
+ </record>
+ </data>
+</openerp> \ No newline at end of file
diff --git a/addons/google_drive/google_drive_demo.xml b/addons/google_drive/google_drive_demo.xml
new file mode 100644
index 00000000000..330d1d4a66b
--- /dev/null
+++ b/addons/google_drive/google_drive_demo.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<openerp>
+ <data noupdate="1">
+
+ <!-- filter demo -->
+ <record id="filter_partner" model="ir.filters">
+ <field name="name">Customer</field>
+ <field name="model_id">res.partner</field>
+ <field name="domain">[['customer', '=', 1]]</field>
+ <field name="user_id" eval="False" />
+ </record>
+
+ <!-- template demo -->
+ <record id="template_partner" model="google.drive.config">
+ <field name="name">Customer Doc</field>
+ <field name="model_id" ref="base.model_res_partner"/>
+ <field name="filter_id" ref="filter_partner"/>
+ <field name="google_drive_template_url">https://docs.google.com/spreadsheet/ccc?key=0Ah2qnrLAoZmUdGRvdVdmS1VoSDctWk1kd18taGZ4ckE#gid=0</field>
+ <field name="name_template">%(name)s_%(model)s_%(filter)s_gdrive</field>
+ </record>
+
+ </data>
+</openerp>
+
diff --git a/addons/google_docs/i18n/ar.po b/addons/google_drive/i18n/ar.po
index bf8df78b926..bf8df78b926 100644
--- a/addons/google_docs/i18n/ar.po
+++ b/addons/google_drive/i18n/ar.po
diff --git a/addons/google_docs/i18n/cs.po b/addons/google_drive/i18n/cs.po
index 38cda3eccca..38cda3eccca 100644
--- a/addons/google_docs/i18n/cs.po
+++ b/addons/google_drive/i18n/cs.po
diff --git a/addons/google_docs/i18n/de.po b/addons/google_drive/i18n/de.po
index 017a0860244..017a0860244 100644
--- a/addons/google_docs/i18n/de.po
+++ b/addons/google_drive/i18n/de.po
diff --git a/addons/google_docs/i18n/es.po b/addons/google_drive/i18n/es.po
index 71bb08ece9a..71bb08ece9a 100644
--- a/addons/google_docs/i18n/es.po
+++ b/addons/google_drive/i18n/es.po
diff --git a/addons/google_docs/i18n/fr.po b/addons/google_drive/i18n/fr.po
index cf9ffdaccc8..cf9ffdaccc8 100644
--- a/addons/google_docs/i18n/fr.po
+++ b/addons/google_drive/i18n/fr.po
diff --git a/addons/google_docs/i18n/google_docs.pot b/addons/google_drive/i18n/google_docs.pot
index 8da7c5d2f70..8da7c5d2f70 100644
--- a/addons/google_docs/i18n/google_docs.pot
+++ b/addons/google_drive/i18n/google_docs.pot
diff --git a/addons/google_docs/i18n/hr.po b/addons/google_drive/i18n/hr.po
index ac1a64cd1e2..ac1a64cd1e2 100644
--- a/addons/google_docs/i18n/hr.po
+++ b/addons/google_drive/i18n/hr.po
diff --git a/addons/google_docs/i18n/hu.po b/addons/google_drive/i18n/hu.po
index 344e01173a9..344e01173a9 100644
--- a/addons/google_docs/i18n/hu.po
+++ b/addons/google_drive/i18n/hu.po
diff --git a/addons/google_docs/i18n/it.po b/addons/google_drive/i18n/it.po
index 2eca676d6c7..2eca676d6c7 100644
--- a/addons/google_docs/i18n/it.po
+++ b/addons/google_drive/i18n/it.po
diff --git a/addons/google_docs/i18n/ln.po b/addons/google_drive/i18n/ln.po
index 24c26b98920..24c26b98920 100644
--- a/addons/google_docs/i18n/ln.po
+++ b/addons/google_drive/i18n/ln.po
diff --git a/addons/google_docs/i18n/mk.po b/addons/google_drive/i18n/mk.po
index 1dfd2120b5b..1dfd2120b5b 100644
--- a/addons/google_docs/i18n/mk.po
+++ b/addons/google_drive/i18n/mk.po
diff --git a/addons/google_docs/i18n/mn.po b/addons/google_drive/i18n/mn.po
index 68380d3592f..68380d3592f 100644
--- a/addons/google_docs/i18n/mn.po
+++ b/addons/google_drive/i18n/mn.po
diff --git a/addons/google_docs/i18n/nl.po b/addons/google_drive/i18n/nl.po
index 91b4d963e01..91b4d963e01 100644
--- a/addons/google_docs/i18n/nl.po
+++ b/addons/google_drive/i18n/nl.po
diff --git a/addons/google_docs/i18n/pl.po b/addons/google_drive/i18n/pl.po
index dcd97078a6f..dcd97078a6f 100644
--- a/addons/google_docs/i18n/pl.po
+++ b/addons/google_drive/i18n/pl.po
diff --git a/addons/google_docs/i18n/pt.po b/addons/google_drive/i18n/pt.po
index 1539bcbedb6..1539bcbedb6 100644
--- a/addons/google_docs/i18n/pt.po
+++ b/addons/google_drive/i18n/pt.po
diff --git a/addons/google_docs/i18n/pt_BR.po b/addons/google_drive/i18n/pt_BR.po
index 38e3c400c25..38e3c400c25 100644
--- a/addons/google_docs/i18n/pt_BR.po
+++ b/addons/google_drive/i18n/pt_BR.po
diff --git a/addons/google_docs/i18n/ro.po b/addons/google_drive/i18n/ro.po
index 716b970f190..716b970f190 100644
--- a/addons/google_docs/i18n/ro.po
+++ b/addons/google_drive/i18n/ro.po
diff --git a/addons/google_docs/i18n/ru.po b/addons/google_drive/i18n/ru.po
index 33f24ed557f..33f24ed557f 100644
--- a/addons/google_docs/i18n/ru.po
+++ b/addons/google_drive/i18n/ru.po
diff --git a/addons/google_docs/i18n/sl.po b/addons/google_drive/i18n/sl.po
index a93afe16703..a93afe16703 100644
--- a/addons/google_docs/i18n/sl.po
+++ b/addons/google_drive/i18n/sl.po
diff --git a/addons/google_docs/i18n/sv.po b/addons/google_drive/i18n/sv.po
index 24e7c63ea39..24e7c63ea39 100644
--- a/addons/google_docs/i18n/sv.po
+++ b/addons/google_drive/i18n/sv.po
diff --git a/addons/google_docs/i18n/tr.po b/addons/google_drive/i18n/tr.po
index 349d71a96b8..349d71a96b8 100644
--- a/addons/google_docs/i18n/tr.po
+++ b/addons/google_drive/i18n/tr.po
diff --git a/addons/google_docs/i18n/zh_CN.po b/addons/google_drive/i18n/zh_CN.po
index 3ca3b5cadf9..3ca3b5cadf9 100644
--- a/addons/google_docs/i18n/zh_CN.po
+++ b/addons/google_drive/i18n/zh_CN.po
diff --git a/addons/google_drive/res_config_user_view.xml b/addons/google_drive/res_config_user_view.xml
new file mode 100644
index 00000000000..8dfdee31143
--- /dev/null
+++ b/addons/google_drive/res_config_user_view.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <!-- add google drive config field in user form -->
+
+ <record model="ir.ui.view" id="view_google_drive_config_tree">
+ <field name="name">google_drive.config.tree</field>
+ <field name="model">google.drive.config</field>
+ <field name="arch" type="xml">
+ <tree string="Google Drive Configuration">
+ <field name="name" />
+ <field name="model_id" />
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_google_drive_config_form">
+ <field name="name">google_drive.config.form</field>
+ <field name="model">google.drive.config</field>
+ <field name="arch" type="xml">
+ <form string="Google Drive Configuration" version="7.0">
+ <field name="model" invisible="1" />
+ <group>
+ <field name="name" />
+ <field name="model_id" on_change="onchange_model_id(model_id)" />
+ <label for='filter_id' />
+ <div>
+ <field name='filter_id' />
+ <p class="oe_grey">
+ <b>To create a new filter:</b><br/>
+ - Go to the OpenERP document you want to filter. For instance, go to Opportunities and search on Sales Department.<br/>
+ - In this "Search" view, select the option "Save Current Filter", enter the name (Ex: Sales Department)<br/>
+ - If you select "Share with all users", link of google document in "More" options will appear for all users in opportunities of Sales Department.<br/>
+ - If you don't select "Share with all users", link of google document in "More" options will not appear for other users in opportunities of Sales Department.<br/>
+ - If filter is not specified, link of google document will appear in "More" option for all users for all opportunities.
+ </p>
+ </div>
+ <field name='google_drive_template_url' placeholder="https://docs.google.com/document/d/1vOtpJK9scIQz6taD9tJRIETWbEw3fSiaQHArsJYcua4/edit" required="1" />
+ <field name='google_drive_resource_id' invisible="1" />
+ <label for='name_template' />
+ <div>
+ <field name='name_template' />
+ <p class="oe_grey">
+ The name of the attached document can use fixed or variable data. To distinguish between documents in
+ Google Drive, use fixed words and fields. For instance, in the example above, if you wrote Agrolait_%%(name)s_Sales
+ in the Google Drive name field, the document in your Google Drive and in OpenERP attachment will be named
+ 'Agrolait_SO0001_Sales'.
+ </p>
+ </div>
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record model='ir.actions.act_window' id='action_google_drive_users_config'>
+ <field name='name'>Google Drive Templates</field>
+ <field name='res_model'>google.drive.config</field>
+ <field name='type'>ir.actions.act_window</field>
+ <field name='view_type'>form</field>
+ <field name='view_id' ref='view_google_drive_config_tree' />
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click to add a new template.
+ </p>
+ <p>
+ Link your own google drive templates to any record of OpenERP. If you have really specific documents you want your collaborator fill in, e.g. Use a spreadsheet to control the quality of your product or review the delivery checklist for each order in a foreign country, ... Its very easy to manage them, link them to OpenERP and use them to collaborate with your employees.
+ </p>
+ </field>
+ </record>
+
+ <record id="inherited_google_view_general_configuration" model="ir.ui.view">
+ <field name="name">General Settings</field>
+ <field name="model">base.config.settings</field>
+ <field name="inherit_id" ref="base_setup.view_general_configuration" />
+ <field name="arch" type="xml">
+ <xpath expr="//div[@name='module_google_drive']" position="after">
+ <div attrs="{'invisible': [('module_google_drive','=',False)]}">
+ <div class="oe_inline">
+ <field name="google_drive_uri" widget="url" text="Generate Google Authorization Code" class="oe_inline oe_bold"/>
+ and paste it here
+ <field name="google_drive_authorization_code" on_change="onchange_google_authorization_code('drive', google_drive_authorization_code)" class="oe_inline" />
+ </div>
+ <button type="action" name="%(google_drive.action_google_drive_users_config)d" string="Configure your templates" class="oe_link" />
+ </div>
+ </xpath>
+ </field>
+ </record>
+
+ <menuitem name='Google Drive configuration' id='menu_google_drive_config' parent='base.menu_administration' />
+ <menuitem id='menu_google_drive_model_config' parent='menu_google_drive_config' action='action_google_drive_users_config' />
+ </data>
+</openerp>
diff --git a/addons/google_drive/security/ir.model.access.csv b/addons/google_drive/security/ir.model.access.csv
new file mode 100644
index 00000000000..ae98cb36976
--- /dev/null
+++ b/addons/google_drive/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_google_drive_all,google.drive.config,model_google_drive_config,,1,0,0,0
+access_google_drive,google.drive.config,model_google_drive_config,base.group_system,1,1,1,1 \ No newline at end of file
diff --git a/addons/google_drive/static/lib/gapi/client.js b/addons/google_drive/static/lib/gapi/client.js
new file mode 100644
index 00000000000..7f40ec55ef9
--- /dev/null
+++ b/addons/google_drive/static/lib/gapi/client.js
@@ -0,0 +1,7 @@
+var gapi=window.gapi=window.gapi||{};gapi._bs=new Date().getTime();(function(){var f=null,g=encodeURIComponent,k=window,m=decodeURIComponent,n="push",r="test",t="shift",u="replace",y="length",B="split",C="join";var D=k,E=document,aa=D.location,ba=function(){},ca=/\[native code\]/,G=function(a,b,c){return a[b]=a[b]||c},da=function(a){for(var b=0;b<this[y];b++)if(this[b]===a)return b;return-1},ea=function(a){a=a.sort();for(var b=[],c=void 0,d=0;d<a[y];d++){var e=a[d];e!=c&&b[n](e);c=e}return b},H=function(){var a;if((a=Object.create)&&ca[r](a))a=a(f);else{a={};for(var b in a)a[b]=void 0}return a},I=G(D,"gapi",{});var J;J=G(D,"___jsl",H());G(J,"I",0);G(J,"hel",10);var K=function(){var a=aa.href,b;if(J.dpo)b=J.h;else{b=J.h;var c=RegExp("([#].*&|[#])jsh=([^&#]*)","g"),d=RegExp("([?#].*&|[?#])jsh=([^&#]*)","g");if(a=a&&(c.exec(a)||d.exec(a)))try{b=m(a[2])}catch(e){}}return b},fa=function(a){var b=G(J,"PQ",[]);J.PQ=[];var c=b[y];if(0===c)a();else for(var d=0,e=function(){++d===c&&a()},h=0;h<c;h++)b[h](e)},L=function(a){return G(G(J,"H",H()),a,H())};var M=G(J,"perf",H()),N=G(M,"g",H()),ga=G(M,"i",H());G(M,"r",[]);H();H();var O=function(a,b,c){var d=M.r;"function"===typeof d?d(a,b,c):d[n]([a,b,c])},Q=function(a,b,c){b&&0<b[y]&&(b=P(b),c&&0<c[y]&&(b+="___"+P(c)),28<b[y]&&(b=b.substr(0,28)+(b[y]-28)),c=b,b=G(ga,"_p",H()),G(b,c,H())[a]=(new Date).getTime(),O(a,"_p",c))},P=function(a){return a[C]("__")[u](/\./g,"_")[u](/\-/g,"_")[u](/\,/g,"_")};var S=H(),T=[],U=function(a){throw Error("Bad hint"+(a?": "+a:""));};T[n](["jsl",function(a){for(var b in a)if(Object.prototype.hasOwnProperty.call(a,b)){var c=a[b];"object"==typeof c?J[b]=G(J,b,[]).concat(c):G(J,b,c)}if(b=a.u)a=G(J,"us",[]),a[n](b),(b=/^https:(.*)$/.exec(b))&&a[n]("http:"+b[1])}]);var ha=/^(\/[a-zA-Z0-9_\-]+)+$/,ia=/^[a-zA-Z0-9\-_\.!]+$/,ja=/^gapi\.loaded_[0-9]+$/,ka=/^[a-zA-Z0-9,._-]+$/,oa=function(a,b,c,d){var e=a[B](";"),h=S[e[t]()],l=f;h&&(l=h(e,b,c,d));if(!(b=!l))b=l,c=b.match(la),d=b.match(ma),b=!(d&&1===d[y]&&na[r](b)&&c&&1===c[y]);b&&U(a);return l},qa=function(a,b,c,d){a=pa(a);ja[r](c)||U("invalid_callback");b=V(b);d=d&&d[y]?V(d):f;var e=function(a){return g(a)[u](/%2C/g,",")};return[g(a.d)[u](/%2C/g,",")[u](/%2F/g,"/"),"/k=",e(a.version),"/m=",e(b),d?"/exm="+e(d):
+"","/rt=j/sv=1/d=1/ed=1",a.a?"/am="+e(a.a):"",a.b?"/rs="+e(a.b):"","/cb=",e(c)][C]("")},pa=function(a){"/"!==a.charAt(0)&&U("relative path");for(var b=a.substring(1)[B]("/"),c=[];b[y];){a=b[t]();if(!a[y]||0==a.indexOf("."))U("empty/relative directory");else if(0<a.indexOf("=")){b.unshift(a);break}c[n](a)}a={};for(var d=0,e=b[y];d<e;++d){var h=b[d][B]("="),l=m(h[0]),p=m(h[1]);2!=h[y]||(!l||!p)||(a[l]=a[l]||p)}b="/"+c[C]("/");ha[r](b)||U("invalid_prefix");c=W(a,"k",!0);d=W(a,"am");a=W(a,"rs");return{d:b,
+version:c,a:d,b:a}},V=function(a){for(var b=[],c=0,d=a[y];c<d;++c){var e=a[c][u](/\./g,"_")[u](/-/g,"_");ka[r](e)&&b[n](e)}return b[C](",")},W=function(a,b,c){a=a[b];!a&&c&&U("missing: "+b);if(a){if(ia[r](a))return a;U("invalid: "+b)}return f},na=/^https?:\/\/[a-z0-9_.-]+\.google\.com(:\d+)?\/[a-zA-Z0-9_.,!=\-\/]+$/,ma=/\/cb=/g,la=/\/\//g,ra=function(){var a=K();if(!a)throw Error("Bad hint");return a};S.m=function(a,b,c,d){(a=a[0])||U("missing_hint");return"https://apis.google.com"+qa(a,b,c,d)};var X=decodeURI("%73cript"),Y=function(a,b){for(var c=[],d=0;d<a[y];++d){var e=a[d];e&&0>da.call(b,e)&&c[n](e)}return c},sa=function(a){"loading"!=E.readyState?Z(a):E.write("<"+X+' src="'+encodeURI(a)+'"></'+X+">")},Z=function(a){var b=E.createElement(X);b.setAttribute("src",a);b.async="true";(a=E.getElementsByTagName(X)[0])?a.parentNode.insertBefore(b,a):(E.head||E.body||E.documentElement).appendChild(b)},ta=function(a,b){var c=b&&b._c;if(c)for(var d=0;d<T[y];d++){var e=T[d][0],h=T[d][1];h&&Object.prototype.hasOwnProperty.call(c,
+e)&&h(c[e],a,b)}},ua=function(a,b){$(function(){var c;c=b===K()?G(I,"_",H()):H();c=G(L(b),"_",c);a(c)})},wa=function(a,b){var c=b||{};"function"==typeof b&&(c={},c.callback=b);ta(a,c);var d=a?a[B](":"):[],e=c.h||ra(),h=G(J,"ah",H());if(!h["::"]||!d[y])va(d||[],c,e);else{for(var l=[],p=f;p=d[t]();){var v=p[B]("."),v=h[p]||h[v[1]&&"ns:"+v[0]||""]||e,s=l[y]&&l[l[y]-1]||f,z=s;if(!s||s.hint!=v)z={hint:v,c:[]},l[n](z);z.c[n](p)}var A=l[y];if(1<A){var F=c.callback;F&&(c.callback=function(){0==--A&&F()})}for(;d=
+l[t]();)va(d.c,c,d.hint)}},va=function(a,b,c){a=ea(a)||[];var d=b.callback,e=b.config,h=b.timeout,l=b.ontimeout,p=f,v=!1;if(h&&!l||!h&&l)throw"Timeout requires both the timeout parameter and ontimeout parameter to be set";var s=G(L(c),"r",[]).sort(),z=G(L(c),"L",[]).sort(),A=[].concat(s),F=function(a,b){if(v)return 0;D.clearTimeout(p);z[n].apply(z,q);var d=((I||{}).config||{}).update;d?d(e):e&&G(J,"cu",[])[n](e);if(b){Q("me0",a,A);try{ua(b,c)}finally{Q("me1",a,A)}}return 1};0<h&&(p=D.setTimeout(function(){v=
+!0;l()},h));var q=Y(a,z);if(q[y]){var q=Y(a,s),w=G(J,"CP",[]),x=w[y];w[x]=function(a){if(!a)return 0;Q("ml1",q,A);var b=function(b){w[x]=f;F(q,a)&&fa(function(){d&&d();b()})},c=function(){var a=w[x+1];a&&a()};0<x&&w[x-1]?w[x]=function(){b(c)}:b(c)};if(q[y]){var R="loaded_"+J.I++;I[R]=function(a){w[x](a);I[R]=f};a=oa(c,q,"gapi."+R,s);s[n].apply(s,q);Q("ml0",q,A);b.sync||D.___gapisync?sa(a):Z(a)}else w[x](ba)}else F(q)&&d&&d()};var $=function(a){if(J.hee&&0<J.hel)try{return a()}catch(b){J.hel--,wa("debug_error",function(){k.___jsl.hefn(b)})}else return a()};I.load=function(a,b){return $(function(){return wa(a,b)})};N.bs0=k.gapi._bs||(new Date).getTime();O("bs0");N.bs1=(new Date).getTime();O("bs1");delete k.gapi._bs;})();
+gapi.load("client",{callback:window["gapi_onload"],_c:{"jsl":{"ci":{"services":{},"deviceType":"desktop","lexps":[102,103,100,71,98,96,110,108,79,106,45,17,86,81,112,61,30],"inline":{"css":1},"report":{},"oauth-flow":{"disableOpt":true,"authUrl":"https://accounts.google.com/o/oauth2/auth","proxyUrl":"https://accounts.google.com/o/oauth2/postmessageRelay","persist":true},"isLoggedIn":true,"isPlusUser":true,"iframes":{"additnow":{"methods":["launchurl"],"url":"https://apis.google.com/additnow/additnow.html?bsv"},"shortlists":{"url":"?bsv"},"plus":{"methods":["onauth"],"url":":socialhost:/u/:session_index:/_/pages/badge?bsv"},":socialhost:":"https://plusone.google.com","recobox":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/render/recobox?bsv"},"plus_followers":{"params":{"url":""},"url":":socialhost:/_/im/_/widget/render/plus/followers?bsv"},"autocomplete":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/render/autocomplete?bsv"},"plus_share":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/+1/sharebutton?plusShare\u003dtrue\u0026bsv"},"savetowallet":{"url":"https://clients5.google.com/s2w/o/savetowallet?bsv"},"panoembed":{"url":"https://ssl.gstatic.com/pano/embed/?bsv"},"signin":{"methods":["onauth"],"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/render/signin?bsv"},"appcirclepicker":{"url":":socialhost:/:session_prefix:_/widget/render/appcirclepicker?bsv"},"commentcount":{"url":":socialhost:/:session_prefix:_/widget/render/commentcount?bsv"},"hangout":{"url":"https://talkgadget.google.com/:session_prefix:talkgadget/_/widget?bsv"},"plus_circle":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/widget/plus/circle?bsv"},"savetodrive":{"methods":["save"],"url":"https://drive.google.com/savetodrivebutton?usegapi\u003d1\u0026bsv"},"card":{"url":":socialhost:/:session_prefix:_/hovercard/card?bsv"},"evwidget":{"params":{"url":""},"url":":socialhost:/:session_prefix:_/events/widget?bsv"},"zoomableimage":{"url":"https://ssl.gstatic.com/microscope/embed/?bsv"},":signuphost:":"https://plus.google.com","plusone":{"preloadUrl":["https://ssl.gstatic.com/s2/oz/images/stars/po/Publisher/sprite4-a67f741843ffc4220554c34bd01bb0bb.png"],"params":{"count":"","size":"","url":""},"url":":socialhost:/:session_prefix:_/+1/fastbutton?bsv"},"comments":{"methods":["scroll","openwindow"],"params":{"location":["search","hash"]},"url":":socialhost:/:session_prefix:_/widget/render/comments?bsv"}},"debug":{"host":"https://plusone.google.com","reportExceptionRate":0.05,"rethrowException":true},"csi":{"rate":0.01},"googleapis.config":{"mobilesignupurl":"https://m.google.com/app/plus/oob?"}},"h":"m;/_/scs/apps-static/_/js/k\u003doz.gapi.en.02N985CHyyc.O/m\u003d__features__/am\u003dIQ/rt\u003dj/d\u003d1/rs\u003dAItRSTPZZ0JVQCv9Qljsu0NQlsb1ZzD2zQ","u":"https://apis.google.com/js/client.js","hee":true,"fp":"e2aa6cd0095417dbec61deca3abed1394160dab3","dpo":false},"fp":"e2aa6cd0095417dbec61deca3abed1394160dab3","annotation":["autocomplete","profile","interactivepost"],"bimodal":["signin"]}}); \ No newline at end of file
diff --git a/addons/google_drive/static/src/img/drive_icon.png b/addons/google_drive/static/src/img/drive_icon.png
new file mode 100644
index 00000000000..590dde867f4
--- /dev/null
+++ b/addons/google_drive/static/src/img/drive_icon.png
Binary files differ
diff --git a/addons/google_drive/static/src/img/drive_icon_mono.png b/addons/google_drive/static/src/img/drive_icon_mono.png
new file mode 100644
index 00000000000..9fd836b6988
--- /dev/null
+++ b/addons/google_drive/static/src/img/drive_icon_mono.png
Binary files differ
diff --git a/addons/google_drive/static/src/img/icon.png b/addons/google_drive/static/src/img/icon.png
new file mode 100644
index 00000000000..3989ab57b30
--- /dev/null
+++ b/addons/google_drive/static/src/img/icon.png
Binary files differ
diff --git a/addons/google_drive/static/src/js/gdrive.js b/addons/google_drive/static/src/js/gdrive.js
new file mode 100644
index 00000000000..1bfcc603d51
--- /dev/null
+++ b/addons/google_drive/static/src/js/gdrive.js
@@ -0,0 +1,76 @@
+openerp.google_drive = function (instance, m) {
+ var _t = instance.web._t,
+ QWeb = instance.web.qweb;
+
+ instance.web.Sidebar.include({
+ start: function () {
+ var self = this;
+ var ids
+ this._super.apply(this, arguments);
+ var view = self.getParent();
+ var result;
+ if (view.fields_view.type == "form") {
+ ids = []
+ view.on("load_record", self, function (r) {
+ ids = [r.id]
+ self.add_gdoc_items(view, r.id)
+ });
+ }
+ },
+ add_gdoc_items: function (view, res_id) {
+ var self = this;
+ var gdoc_item = _.indexOf(_.pluck(self.items.other, 'classname'), 'oe_share_gdoc');
+ if (gdoc_item !== -1) {
+ self.items.other.splice(gdoc_item, 1);
+ }
+ if (res_id) {
+ view.sidebar_eval_context().done(function (context) {
+ var ds = new instance.web.DataSet(this, 'google.drive.config', context);
+ ds.call('get_google_drive_config', [view.dataset.model, res_id, context]).done(function (r) {
+ if (!_.isEmpty(r)) {
+ _.each(r, function (res) {
+ var g_item = _.indexOf(_.pluck(self.items.other, 'label'), res.name);
+ if (g_item !== -1) {
+ self.items.other.splice(g_item, 1);
+ }
+ self.add_items('other', [{
+ label: res.name+ '<img style="position:absolute;right:5px;height:20px;width:20px;" title="Google Drive" src="google_drive/static/src/img/drive_icon.png"/>',
+ config_id: res.id,
+ res_id: res_id,
+ res_model: view.dataset.model,
+ callback: self.on_google_doc,
+ classname: 'oe_share_gdoc'
+ },
+ ]);
+ })
+ }
+ });
+ });
+ }
+ },
+
+ fetch: function (model, fields, domain, ctx) {
+ return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all()
+ },
+
+ on_google_doc: function (doc_item) {
+ var self = this;
+ self.config = doc_item;
+ var loaded = self.fetch('google.drive.config', ['google_drive_resource_id', 'google_drive_client_id'], [['id', '=', doc_item.config_id]])
+ .then(function (configs) {
+ var ds = new instance.web.DataSet(self, 'google.drive.config');
+ ds.call('get_google_doc_name', [[doc_item.config_id], doc_item.res_id,configs[0].google_drive_resource_id]).done(function (r) {
+ if (!_.isEmpty(r)) {
+ _.each(r, function (res) {
+ if(res.url)
+ {
+ window.open(res.url, '_blank');
+ }
+ });
+ }
+ });
+ });
+ },
+
+ });
+}; \ No newline at end of file
diff --git a/addons/google_docs/static/src/xml/gdocs.xml b/addons/google_drive/static/src/xml/gdocs.xml
index 0c8d9c3b01f..0c8d9c3b01f 100644
--- a/addons/google_docs/static/src/xml/gdocs.xml
+++ b/addons/google_drive/static/src/xml/gdocs.xml