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
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 _
2012-12-09 18:22:37 +00:00
2014-01-29 12:13:16 +00:00
import werkzeug . urls
2013-06-27 14:37:13 +00:00
import urllib2
2013-05-23 10:41:55 +00:00
import json
2013-06-26 15:07:19 +00:00
import re
2014-06-25 06:09:36 +00:00
import openerp
2013-05-23 10:41:55 +00:00
2012-12-09 18:22:37 +00:00
_logger = logging . getLogger ( __name__ )
2013-06-26 09:57:36 +00:00
2013-10-16 16:41:35 +00:00
class config ( osv . Model ) :
2013-06-26 09:30:19 +00:00
_name = ' google.drive.config '
2013-05-10 06:35:01 +00:00
_description = " Google Drive templates config "
2013-06-26 09:57:36 +00:00
2013-06-26 14:52:13 +00:00
def get_google_drive_url ( self , cr , uid , config_id , res_id , template_id , context = None ) :
config = self . browse ( cr , SUPERUSER_ID , config_id , context = context )
model = config . model_id
filter_name = config . filter_id and config . filter_id . name or False
2014-07-06 14:44:26 +00:00
record = self . pool . get ( model . model ) . read ( cr , uid , [ res_id ] , context = context ) [ 0 ]
2013-06-26 14:52:13 +00:00
record . update ( { ' model ' : model . name , ' filter ' : filter_name } )
2013-06-26 17:00:35 +00:00
name_gdocs = config . name_template
2013-06-26 14:52:13 +00:00
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 :
2013-07-22 16:45:08 +00:00
url = self . copy_doc ( cr , uid , res_id , template_id , name_gdocs , model . model , context ) . get ( ' url ' )
2013-06-26 14:52:13 +00:00
return url
2013-06-26 09:57:36 +00:00
2013-07-22 14:30:34 +00:00
def get_access_token ( self , cr , uid , scope = None , context = None ) :
2013-06-26 09:57:36 +00:00
ir_config = self . pool [ ' ir.config_parameter ' ]
2013-06-27 14:37:13 +00:00
google_drive_refresh_token = ir_config . get_param ( cr , SUPERUSER_ID , ' google_drive_refresh_token ' )
2013-11-27 14:20:49 +00:00
user_is_admin = self . pool [ ' res.users ' ] . has_group ( cr , uid , ' base.group_erp_manager ' )
2013-06-27 14:37:13 +00:00
if not google_drive_refresh_token :
2013-11-27 14:20:49 +00:00
if user_is_admin :
2014-06-25 06:09:36 +00:00
model , action_id = self . pool [ ' ir.model.data ' ] . get_object_reference ( cr , uid , ' base_setup ' , ' action_general_configuration ' )
msg = _ ( " You haven ' t configured ' Authorization Code ' generated from google, Please generate and configure it . " )
raise openerp . exceptions . RedirectWarning ( msg , action_id , _ ( ' Go to the configuration panel ' ) )
2013-07-24 13:05:34 +00:00
else :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " Google Drive is not yet configured. Please contact your administrator. " ) )
2013-06-26 09:30:19 +00:00
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 ' )
2013-06-26 09:57:36 +00:00
#For Getting New Access Token With help of old Refresh Token
2013-06-26 17:00:35 +00:00
2014-01-29 12:13:16 +00:00
data = werkzeug . url_encode ( dict ( client_id = google_drive_client_id ,
2013-07-22 14:30:34 +00:00
refresh_token = google_drive_refresh_token ,
client_secret = google_drive_client_secret ,
grant_type = " refresh_token " ,
scope = scope or ' https://www.googleapis.com/auth/drive ' ) )
headers = { " Content-type " : " application/x-www-form-urlencoded " , " Accept-Encoding " : " gzip, deflate " }
2013-06-27 15:01:45 +00:00
try :
req = urllib2 . Request ( ' https://accounts.google.com/o/oauth2/token ' , data , headers )
content = urllib2 . urlopen ( req ) . read ( )
2013-07-24 13:05:34 +00:00
except urllib2 . HTTPError :
2013-11-27 14:20:49 +00:00
if user_is_admin :
2014-06-25 06:09:36 +00:00
model , action_id = self . pool [ ' ir.model.data ' ] . get_object_reference ( cr , uid , ' base_setup ' , ' action_general_configuration ' )
msg = _ ( " Something went wrong during the token generation. Please request again an authorization code . " )
raise openerp . exceptions . RedirectWarning ( msg , action_id , _ ( ' Go to the configuration panel ' ) )
2013-07-24 13:05:34 +00:00
else :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " Google Drive is not yet configured. Please contact your administrator. " ) )
2013-05-23 10:41:55 +00:00
content = json . loads ( content )
2013-07-22 14:30:34 +00:00
return content . get ( ' access_token ' )
2013-06-26 09:57:36 +00:00
2013-07-22 14:30:34 +00:00
def copy_doc ( self , cr , uid , res_id , template_id , name_gdocs , res_model , context = None ) :
ir_config = self . pool [ ' ir.config_parameter ' ]
google_web_base_url = ir_config . get_param ( cr , SUPERUSER_ID , ' web.base.url ' )
access_token = self . get_access_token ( cr , uid , context = context )
2013-05-23 10:41:55 +00:00
# Copy template in to drive with help of new access token
2013-07-22 14:30:34 +00:00
request_url = " https://www.googleapis.com/drive/v2/files/ %s ?fields=parents/id&access_token= %s " % ( template_id , access_token )
headers = { " Content-type " : " application/x-www-form-urlencoded " , " Accept-Encoding " : " gzip, deflate " }
try :
req = urllib2 . Request ( request_url , None , headers )
parents = urllib2 . urlopen ( req ) . read ( )
except urllib2 . HTTPError :
2014-06-25 06:09:36 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( " The Google Template cannot be found. Maybe it has been deleted. " ) )
2013-07-22 14:30:34 +00:00
parents_dict = json . loads ( parents )
2014-07-09 11:39:38 +00:00
record_url = " Click on link to open Record in Odoo \n %s /?db= %s #id= %s &model= %s " % ( google_web_base_url , cr . dbname , res_id , res_model )
2013-07-22 14:30:34 +00:00
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 , access_token )
headers = { ' Content-type ' : ' application/json ' , ' Accept ' : ' text/plain ' }
data_json = json . dumps ( data )
# resp, content = Http().request(request_url, "POST", data_json, headers)
req = urllib2 . Request ( request_url , data_json , headers )
content = urllib2 . urlopen ( req ) . read ( )
content = json . loads ( content )
2013-07-22 16:45:08 +00:00
res = { }
if content . get ( ' alternateLink ' ) :
2013-07-22 14:30:34 +00:00
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 ' ] }
2013-07-22 16:45:08 +00:00
res [ ' id ' ] = attach_pool . create ( cr , uid , attach_vals )
2013-10-17 14:25:45 +00:00
# Commit in order to attach the document to the current object instance, even if the permissions has not been written.
cr . commit ( )
2013-07-22 16:45:08 +00:00
res [ ' url ' ] = content [ ' alternateLink ' ]
2013-10-17 13:47:44 +00:00
key = self . _get_key_from_url ( res [ ' url ' ] )
2014-07-09 11:39:38 +00:00
request_url = " https://www.googleapis.com/drive/v2/files/ %s /permissions?emailMessage=This+is+a+drive+file+created+by+Odoo&sendNotificationEmails=false&access_token= %s " % ( key , access_token )
2014-01-15 09:35:28 +00:00
data = { ' role ' : ' writer ' , ' type ' : ' anyone ' , ' value ' : ' ' , ' withLink ' : True }
2013-10-17 13:47:44 +00:00
try :
req = urllib2 . Request ( request_url , json . dumps ( data ) , headers )
2013-10-17 14:25:45 +00:00
urllib2 . urlopen ( req )
2013-10-17 13:47:44 +00:00
except urllib2 . HTTPError :
2013-10-17 14:25:45 +00:00
raise self . pool . get ( ' res.config.settings ' ) . get_config_warning ( cr , _ ( " The permission ' reader ' for ' anyone with the link ' has not been written on the document " ) , context = context )
user = self . pool [ ' res.users ' ] . browse ( cr , uid , uid , context = context )
if user . email :
data = { ' role ' : ' writer ' , ' type ' : ' user ' , ' value ' : user . email }
try :
req = urllib2 . Request ( request_url , json . dumps ( data ) , headers )
urllib2 . urlopen ( req )
except urllib2 . HTTPError :
2014-01-15 09:35:28 +00:00
pass
2013-10-17 14:25:45 +00:00
return res
2013-06-26 09:57:36 +00:00
2013-06-26 09:30:19 +00:00
def get_google_drive_config ( self , cr , uid , res_model , res_id , context = None ) :
2012-05-23 09:51:36 +00:00
'''
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
2013-06-26 09:57:36 +00:00
of google doc to copy ( this is usefull if you want to start with a non - empty document , a type or a name
2012-05-23 09:51:36 +00:00
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 )
2013-04-01 08:03:23 +00:00
: return : the config id and config name
2012-05-23 09:51:36 +00:00
'''
2013-04-01 08:03:23 +00:00
if not res_id :
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. " ) )
2013-04-01 08:03:23 +00:00
# check if a model is configured with a template
2013-05-10 06:35:01 +00:00
config_ids = self . search ( cr , uid , [ ( ' model_id ' , ' = ' , res_model ) ] , context = context )
2013-04-01 08:03:23 +00:00
configs = [ ]
2013-06-26 14:52:13 +00:00
for config in self . browse ( cr , uid , config_ids , context = context ) :
2013-04-01 08:03:23 +00:00
if config . filter_id :
2013-04-04 07:05:23 +00:00
if ( config . filter_id . user_id and config . filter_id . user_id . id != uid ) :
#Private
continue
2013-06-26 14:52:13 +00:00
domain = [ ( ' id ' , ' in ' , [ res_id ] ) ] + eval ( config . filter_id . domain )
local_context = context and context . copy ( ) or { }
local_context . update ( eval ( config . filter_id . context ) )
google_doc_configs = self . pool . get ( config . filter_id . model_id ) . search ( cr , uid , domain , context = local_context )
2013-06-26 09:57:36 +00:00
if google_doc_configs :
2013-04-01 08:03:23 +00:00
configs . append ( { ' id ' : config . id , ' name ' : config . name } )
2013-03-08 13:30:39 +00:00
else :
2013-04-01 08:03:23 +00:00
configs . append ( { ' id ' : config . id , ' name ' : config . name } )
return configs
2013-06-26 09:57:36 +00:00
2013-10-17 13:47:44 +00:00
def _get_key_from_url ( self , url ) :
mo = re . search ( " (key=|/d/)([A-Za-z0-9-_]+) " , url )
if mo :
return mo . group ( 2 )
return None
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 ) :
2013-10-17 13:47:44 +00:00
mo = self . _get_key_from_url ( data . google_drive_template_url )
2013-06-26 15:07:19 +00:00
if mo :
2013-10-17 13:47:44 +00:00
result [ data . id ] = mo
2013-06-26 15:07:19 +00:00
else :
2013-06-26 14:52:13 +00:00
raise osv . except_osv ( _ ( ' Incorrect URL! ' ) , _ ( " Please enter a valid Google Document URL. " ) )
2013-03-05 13:17:57 +00:00
return result
2013-06-26 09:57:36 +00:00
2013-05-09 14:10:01 +00:00
def _client_id_get ( self , cr , uid , ids , name , arg , context = None ) :
result = { }
2013-06-26 14:52:13 +00:00
client_id = self . pool [ ' ir.config_parameter ' ] . get_param ( cr , SUPERUSER_ID , ' google_drive_client_id ' )
2013-05-09 14:10:01 +00:00
for config_id in ids :
2013-06-26 14:52:13 +00:00
result [ config_id ] = client_id
2013-05-09 14:10:01 +00:00
return result
2012-02-29 18:45:20 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Template Name ' , required = True ) ,
2013-06-26 13:35:52 +00:00
' 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)] " ) ,
2013-06-26 09:30:19 +00:00
' google_drive_template_url ' : fields . char ( ' Template URL ' , required = True , size = 1024 ) ,
2013-06-26 09:57:36 +00:00
' 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 ' ) ,
2014-05-21 09:52:05 +00:00
' name_template ' : fields . char ( ' Google Drive Name Pattern ' , help = ' Choose how the new google drive will be named, on google side. Eg. gdoc_ %(field_name)s ' , required = True ) ,
2013-07-23 13:04:53 +00:00
' active ' : fields . boolean ( ' Active ' ) ,
2013-05-09 14:10:01 +00:00
}
2012-02-29 18:45:20 +00:00
2013-03-22 12:28:59 +00:00
def onchange_model_id ( self , cr , uid , ids , model_id , context = None ) :
2013-06-26 13:35:52 +00:00
res = { }
2013-06-26 09:57:36 +00:00
if model_id :
2013-06-26 13:35:52 +00:00
model = self . pool [ ' ir.model ' ] . browse ( cr , uid , model_id , context = context )
res [ ' value ' ] = { ' model ' : model . model }
2013-06-26 09:57:36 +00:00
else :
2013-06-26 13:35:52 +00:00
res [ ' value ' ] = { ' filter_id ' : False , ' model ' : False }
2013-06-26 09:57:36 +00:00
return res
2013-03-04 05:20:58 +00:00
2012-02-29 18:45:20 +00:00
_defaults = {
2013-06-26 17:00:35 +00:00
' name_template ' : ' Document %(name)s ' ,
2013-07-23 13:04:53 +00:00
' active ' : True ,
2012-02-29 18:45:20 +00:00
}
2013-06-26 09:57:36 +00:00
2013-03-22 12:28:59 +00:00
def _check_model_id ( self , cr , uid , ids , context = None ) :
2013-03-28 07:12:12 +00:00
config_id = self . browse ( cr , uid , ids [ 0 ] , context = context )
2013-06-26 13:35:52 +00:00
if config_id . filter_id and config_id . model_id . model != config_id . filter_id . model_id :
2013-03-28 07:12:12 +00:00
return False
2013-03-22 12:28:59 +00:00
return True
_constraints = [
2013-05-10 06:35:01 +00:00
( _check_model_id , ' Model of selected filter is not matching with model of current template. ' , [ ' model_id ' , ' filter_id ' ] ) ,
2013-03-22 12:28:59 +00:00
]
2013-07-24 15:37:54 +00:00
def get_google_scope ( self ) :
2013-10-17 13:47:44 +00:00
return ' https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file '
2013-07-24 15:37:54 +00:00
2013-05-23 10:41:55 +00:00
2013-10-16 16:41:35 +00:00
class base_config_settings ( osv . TransientModel ) :
2013-05-23 13:38:48 +00:00
_inherit = " base.config.settings "
2013-05-23 10:41:55 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' google_drive_authorization_code ' : fields . char ( ' Authorization Code ' ) ,
2013-06-26 09:30:19 +00:00
' google_drive_uri ' : fields . char ( ' URI ' , readonly = True , help = " The URL to generate the authorization code from Google " ) ,
2013-06-25 16:19:22 +00:00
}
_defaults = {
2013-07-24 15:37:54 +00:00
' google_drive_uri ' : lambda s , cr , uid , c : s . pool [ ' google.service ' ] . _get_google_token_uri ( cr , uid , ' drive ' , scope = s . pool [ ' google.drive.config ' ] . get_google_scope ( ) , context = c ) ,
2014-06-23 14:52:40 +00:00
' google_drive_authorization_code ' : lambda s , cr , uid , c : s . pool [ ' ir.config_parameter ' ] . get_param ( cr , SUPERUSER_ID , ' google_drive_authorization_code ' , context = c ) ,
2013-06-25 10:58:19 +00:00
}
2013-06-27 14:37:13 +00:00
def set_google_authorization_code ( self , cr , uid , ids , context = None ) :
2013-10-16 16:39:08 +00:00
ir_config_param = self . pool [ ' ir.config_parameter ' ]
2013-06-27 14:37:13 +00:00
config = self . browse ( cr , uid , ids [ 0 ] , context )
2013-10-16 16:39:08 +00:00
auth_code = config . google_drive_authorization_code
if auth_code and auth_code != ir_config_param . get_param ( cr , uid , ' google_drive_authorization_code ' , context = context ) :
refresh_token = self . pool [ ' google.service ' ] . generate_refresh_token ( cr , uid , ' drive ' , config . google_drive_authorization_code , context = context )
2014-06-23 14:52:40 +00:00
ir_config_param . set_param ( cr , uid , ' google_drive_authorization_code ' , auth_code , groups = [ ' base.group_system ' ] )
ir_config_param . set_param ( cr , uid , ' google_drive_refresh_token ' , refresh_token , groups = [ ' base.group_system ' ] )