2012-03-02 10:35:05 +00:00
##############################################################################
#
# 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/>.
#
##############################################################################
2012-12-09 18:22:37 +00:00
import logging
2012-11-01 07:18:46 +00:00
from datetime import datetime
2012-12-09 18:22:37 +00:00
2012-12-06 14:56:32 +00:00
from openerp . tools import DEFAULT_SERVER_DATETIME_FORMAT
2013-03-05 13:17:57 +00:00
from openerp import SUPERUSER_ID
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
2013-03-05 13:17:57 +00:00
from urlparse import urlparse
2012-12-09 18:22:37 +00:00
_logger = logging . getLogger ( __name__ )
2012-03-02 10:35:05 +00:00
try :
import gdata . docs . data
import gdata . docs . client
2013-02-01 11:51:53 +00:00
# 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 )
2012-03-02 10:35:05 +00:00
except ImportError :
2012-10-10 20:47:33 +00:00
_logger . warning ( " Please install latest gdata-python-client from http://code.google.com/p/gdata-python-client/downloads/list " )
2012-02-24 10:52:15 +00:00
2013-02-01 11:51:53 +00:00
2012-02-29 18:45:20 +00:00
class google_docs_ir_attachment ( osv . osv ) :
2012-02-29 12:29:40 +00:00
_inherit = ' ir.attachment '
2012-02-24 10:52:15 +00:00
2012-05-23 09:51:36 +00:00
def _auth ( self , cr , uid , context = None ) :
2012-03-28 08:29:48 +00:00
'''
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 ' )
2012-05-23 09:51:36 +00:00
#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.
2013-02-01 11:51:53 +00:00
user_config = google_pool . default_get ( cr , uid , { ' user ' : ' ' , ' password ' : ' ' } , context = context )
2012-03-28 08:29:48 +00:00
#login gmail account
2013-02-01 11:51:53 +00:00
client = google_pool . google_login ( user_config [ ' user ' ] , user_config [ ' password ' ] , type = ' docs_client ' , context = context )
2012-03-28 09:47:18 +00:00
if not client :
2013-02-01 11:51:53 +00:00
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 ' ] )
2012-02-29 18:45:20 +00:00
return client
2012-02-24 10:52:15 +00:00
2012-05-23 09:51:36 +00:00
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
2012-02-24 14:01:19 +00:00
'''
2012-03-28 08:29:48 +00:00
#login with the base account google module
2012-05-23 09:51:36 +00:00
client = self . _auth ( cr , uid , context = context )
2012-03-01 14:45:07 +00:00
# create the document in google docs
2012-11-01 07:26:31 +00:00
title = " %s %s " % ( context . get ( " name " , " Untitled Document. " ) , datetime . today ( ) . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) )
2013-02-26 05:55:25 +00:00
local_resource = gdata . docs . data . Resource ( gdata . docs . data . SPREADSHEET_LABEL , title = title )
2012-03-28 08:29:48 +00:00
#create a new doc in Google Docs
gdocs_resource = client . post ( entry = local_resource , uri = ' https://docs.google.com/feeds/default/private/full/ ' )
2012-05-23 09:51:36 +00:00
# create an ir.attachment into the db
2012-03-01 14:45:07 +00:00
self . create ( cr , uid , {
2012-05-23 09:51:36 +00:00
' res_model ' : res_model ,
' res_id ' : res_id ,
2012-03-01 14:45:07 +00:00
' type ' : ' url ' ,
2012-11-01 07:18:46 +00:00
' name ' : title ,
2012-03-28 08:29:48 +00:00
' url ' : gdocs_resource . get_alternate_link ( ) . href ,
2012-05-23 09:51:36 +00:00
} , context = context )
2012-11-01 07:18:46 +00:00
return { ' resource_id ' : gdocs_resource . resource_id . text ,
' title ' : title ,
' url ' : gdocs_resource . get_alternate_link ( ) . href }
2012-02-24 10:52:15 +00:00
2012-05-23 09:51:36 +00:00
def copy_gdoc ( self , cr , uid , res_model , res_id , name_gdocs , gdoc_template_id , context = None ) :
2012-03-28 11:41:13 +00:00
'''
copy an existing document in google docs
2012-05-23 09:51:36 +00:00
: 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
2012-03-28 11:41:13 +00:00
'''
2012-05-23 09:51:36 +00:00
#login with the base account google module
2012-03-28 08:29:48 +00:00
client = self . _auth ( cr , uid )
2012-02-29 18:45:20 +00:00
# fetch and copy the original document
2012-03-28 11:41:13 +00:00
try :
2013-02-01 11:51:53 +00:00
doc = client . get_resource_by_id ( gdoc_template_id )
2012-03-28 11:41:13 +00:00
#copy the document you choose in the configuration
2013-02-01 11:51:53 +00:00
copy_resource = client . copy_resource ( doc , name_gdocs )
2012-03-28 11:41:13 +00:00
except :
2013-02-25 09:16:39 +00:00
raise osv . except_osv ( _ ( ' Google Drive Error! ' ) , _ ( " Your resource id is not correct. You can find the id in the google docs URL. " ) )
2012-05-23 09:51:36 +00:00
# create an ir.attachment
2012-03-01 14:45:07 +00:00
self . create ( cr , uid , {
2012-05-23 09:51:36 +00:00
' res_model ' : res_model ,
' res_id ' : res_id ,
2012-03-01 14:45:07 +00:00
' type ' : ' url ' ,
2012-03-30 14:51:49 +00:00
' name ' : name_gdocs ,
2012-03-01 14:45:07 +00:00
' url ' : copy_resource . get_alternate_link ( ) . href
2012-05-23 09:51:36 +00:00
} , context = context )
return copy_resource . resource_id . text
2012-02-29 18:45:20 +00:00
2012-05-23 09:51:36 +00:00
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
'''
2012-10-19 10:40:20 +00:00
if len ( ids ) != 1 :
2013-02-25 09:16:39 +00:00
raise osv . except_osv ( _ ( ' Google Drive Error! ' ) , _ ( " Creating google drive may only be done by one at a time. " ) )
2012-05-23 09:51:36 +00:00
res_id = ids [ 0 ]
pool_ir_attachment = self . pool . get ( ' ir.attachment ' )
2012-03-30 14:51:49 +00:00
pool_gdoc_config = self . pool . get ( ' google.docs.config ' )
2012-05-23 09:51:36 +00:00
name_gdocs = ' '
model_fields_dic = self . pool . get ( 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 )
2012-03-19 10:31:32 +00:00
if google_docs_config :
2012-05-23 09:51:36 +00:00
name_gdocs = pool_gdoc_config . browse ( cr , uid , google_docs_config , context = context ) [ 0 ] . name_template
2012-10-17 06:46:15 +00:00
try :
name_gdocs = name_gdocs % model_fields_dic
except :
2013-02-25 09:16:39 +00:00
raise osv . except_osv ( _ ( ' Key Error! ' ) , _ ( " Your Google Drive Name Pattern ' s key does not found in object. " ) )
2012-05-23 09:51:36 +00:00
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 )
2012-03-19 10:31:32 +00:00
else :
2012-05-23 09:51:36 +00:00
google_document = pool_ir_attachment . create_empty_google_doc ( cr , uid , res_model , res_id , context = context )
return google_document
2012-03-05 16:11:15 +00:00
2012-03-12 14:41:31 +00:00
class config ( osv . osv ) :
_name = ' google.docs.config '
2013-02-25 09:16:39 +00:00
_description = " Google Drive templates config "
2013-03-05 13:17:57 +00:00
def _resource_get ( self , cr , uid , ids , name , arg , context = None ) :
result = { }
for data in self . browse ( cr , uid , ids , context ) :
template_url = data . gdocs_template_url
url = urlparse ( template_url )
res = url . path . split ( ' / ' )
resource = res [ 1 ]
if res [ 1 ] == " spreadsheet " :
key = url . query . split ( ' = ' ) [ 1 ]
else :
key = res [ 3 ]
res_id = resource + " : " + key
result [ data . id ] = str ( res_id )
return result
2012-02-29 18:45:20 +00:00
_columns = {
2012-09-26 14:08:02 +00:00
' model_id ' : fields . many2one ( ' ir.model ' , ' Model ' , required = True ) ,
2013-02-28 07:28:22 +00:00
' filter_id ' : fields . many2one ( ' ir.filters ' , ' Filter ' ) ,
2013-03-05 13:17:57 +00:00
' gdocs_template_url ' : fields . char ( ' Template Url ' , required = True , size = 1024 ) ,
' gdocs_resource_id ' : fields . function ( _resource_get , type = " char " , string = ' Resource Id ' , store = True ) ,
2013-02-25 09:16:39 +00:00
' 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 ) ,
2012-02-29 18:45:20 +00:00
}
2013-03-04 05:20:58 +00:00
def onchange_model_id ( self , cr , uid , ids , model_id ) :
res = { ' domain ' : { ' filter_id ' : [ ] } }
if model_id :
model_name = self . pool . get ( ' ir.model ' ) . read ( cr , uid , model_id , [ ' model ' ] )
if model_name :
mod_name = model_name [ ' model ' ]
res [ ' domain ' ] = { ' filter_id ' : [ ( ' model_id ' , ' = ' , mod_name ) ] }
else :
res [ ' value ' ] = { ' filter_id ' : False }
return res
2012-02-29 18:45:20 +00:00
_defaults = {
2013-03-05 13:17:57 +00:00
' name_template ' : ' %(name)s _model_filter_gdoc ' ,
2012-02-29 18:45:20 +00:00
}
2013-03-05 13:17:57 +00:00
def _wrap_create ( self , old_create , model ) :
""" Return a wrapper around `old_create` calling both `old_create` and
` _process ` , in that order .
"""
def wrapper ( cr , uid , vals , context = None ) :
# avoid loops or cascading actions
if context and context . get ( ' action ' ) :
return old_create ( cr , uid , vals , context = context )
context = dict ( context or { } , action = True )
new_id = old_create ( cr , uid , vals , context = context )
# as it is a new record, we do not consider the actions that have a prefilter
action_dom = [ ( ' model_id ' , ' = ' , model ) ]
config_ids = self . search ( cr , uid , action_dom , context = context )
# # check postconditions, and execute actions on the records that satisfy them
for action in self . browse ( cr , uid , config_ids , context = context ) :
if self . _filter ( cr , uid , action , action . filter_id , [ new_id ] , context = context ) :
self . _process ( cr , uid , action , [ new_id ] , context = context )
return new_id
return wrapper
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 :
assert action . model_id . model == action_filter . model_id , " Filter model different from action rule model "
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 _process ( self , cr , uid , action , record_ids , context = None ) :
""" process the given action on the records """
# execute server actions
model = self . pool . get ( action . model_id . model )
template_url = action . gdocs_template_url
attach_obj = self . pool . get ( ' ir.attachment ' )
for record_id in record_ids :
record_id = int ( record_id )
model_fields_dic = self . pool . get ( action . model_id . model ) . read ( cr , uid , record_id , [ ] , context = context )
name_gdocs = action . name_template
name_gdocs = name_gdocs . replace ( ' model ' , action . model_id . name )
2013-03-06 05:31:38 +00:00
if action . filter_id :
name_gdocs = name_gdocs . replace ( ' filter ' , action . filter_id . name )
2013-03-05 13:17:57 +00:00
try :
name_gdocs = name_gdocs % model_fields_dic
except :
raise osv . except_osv ( _ ( ' Key Error! ' ) , _ ( " Your Google Drive Name Pattern ' s key does not found in object. " ) )
attachments = attach_obj . search ( cr , uid , [ ( ' res_id ' , ' = ' , record_id ) , ( ' name ' , ' = ' , name_gdocs ) ] )
if not attachments :
google_template_id = action . gdocs_resource_id
attach_obj . copy_gdoc ( cr , uid , action . model_id . model , record_id , name_gdocs , google_template_id , context = context )
return True
def _wrap_write ( self , old_write , model ) :
""" Return a wrapper around `old_write` calling both `old_write` and
` _process ` , in that order .
"""
def wrapper ( cr , uid , ids , vals , context = None ) :
# avoid loops or cascading actions
if context and context . get ( ' action ' ) :
return old_write ( cr , uid , ids , vals , context = context )
context = dict ( context or { } , action = True )
ids = [ ids ] if isinstance ( ids , ( int , long , str ) ) else ids
# retrieve the action rules to possibly execute
action_dom = [ ( ' model_id ' , ' = ' , model ) ]
config_ids = self . search ( cr , uid , action_dom , context = context )
gconfigs = self . browse ( cr , uid , config_ids , context = context )
# check preconditions
pre_ids = { }
for gconfig in gconfigs :
pre_ids [ gconfig ] = self . _filter ( cr , uid , gconfig , gconfig . filter_id , ids , context = context )
# execute write
old_write ( cr , uid , ids , vals , context = context )
# check postconditions, and execute actions on the records that satisfy them
for gconfig in gconfigs :
post_ids = self . _filter ( cr , uid , gconfig , gconfig . filter_id , pre_ids [ gconfig ] , context = context )
print ' post_idsss ' , post_ids
if post_ids :
print ' post_idsss ' , post_ids
self . _process ( cr , uid , gconfig , post_ids , context = context )
return True
return wrapper
def _register_hook ( self , cr , ids = None ) :
""" Wrap the methods `create` and `write` of the models specified by
the rules given by ` ids ` ( or all existing rules if ` ids ` is ` None ` . )
"""
if ids is None :
ids = self . search ( cr , SUPERUSER_ID , [ ] )
for config in self . browse ( cr , SUPERUSER_ID , ids ) :
model = config . model_id . model
model_obj = self . pool . get ( model )
model_obj . create = self . _wrap_create ( model_obj . create , model )
model_obj . write = self . _wrap_write ( model_obj . write , model )
return True
def create ( self , cr , uid , vals , context = None ) :
res_id = super ( config , self ) . create ( cr , uid , vals , context = context )
self . _register_hook ( cr , [ res_id ] )
return res_id
def write ( self , cr , uid , ids , vals , context = None ) :
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
super ( config , self ) . write ( cr , uid , ids , vals , context = context )
self . _register_hook ( cr , ids )
return True
2012-03-28 08:29:48 +00:00
config ( )