[IMP] google oAuth2 + start synchro calendar (insert ok, bug with recurrent)

bzr revid: jke@openerp.com-20131202143133-u6vi1xw1oa9w8qpf
This commit is contained in:
jke-openerp 2013-12-02 15:31:33 +01:00
parent 74d698f42f
commit 27a42cc6eb
24 changed files with 1018 additions and 63 deletions

View File

@ -21,5 +21,6 @@
import base_calendar
import controllers
import res_config
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -43,7 +43,8 @@ If you need to manage your meetings, you should install the CRM module.
'security/calendar_security.xml',
'security/ir.model.access.csv',
'crm_meeting_view.xml',
'base_calendar_data.xml'
'base_calendar_data.xml',
'res_config_view.xml',
],
'js': [
'static/src/js/*.js'

View File

@ -1509,7 +1509,6 @@ class crm_meeting(osv.Model):
self.message_post(cr, uid, event.id, body=_("An invitation email has been sent to attendee(s)"), subtype="base_calendar.subtype_invitation", context=context)
return;
def get_attendee(self, cr, uid, meeting_id, context=None):
#Used for view in controller
invitation = {'meeting':{}, 'attendee': [], 'logo': ''}
@ -1650,13 +1649,11 @@ class crm_meeting(osv.Model):
end_date = self._get_recurrency_end_date(data, context=context)
super(crm_meeting, self).write(cr, uid, [data['id']], {'end_date': end_date}, context=context)
attendees_create = False
attendees_create = False
if values.get('partner_ids', False):
attendees_create = self.create_attendees(cr, uid, ids, context)
print values
if values.get('date', False):
if values.get('date', False) and not context.get('install_mode',False): #Not send mail when install data
print 'Id notified (ids|new_id) : ',ids,"|",new_id
the_id = new_id or (ids and int(ids[0]));
@ -1799,7 +1796,8 @@ class crm_meeting(osv.Model):
res = super(crm_meeting, self).unlink(cr, uid, ids, context=context)
self.unlink_events(cr, uid, ids, context=context)
return res
# class mail_mail(osv.osv):
# _inherit = "mail.mail"

View File

@ -17,35 +17,30 @@
<record model="calendar.alarm" id="alarm_notif_1">
<field name="name">15 min notif</field>
<field name="active" eval="1" />
<field name="duration" eval="15" />
<field name="interval">minutes</field>
<field name="type">notification</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_2">
<field name="name">30 min notif</field>
<field name="active" eval="1" />
<field name="duration" eval="30" />
<field name="interval">minutes</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_3">
<field name="name">1 hour notif</field>
<field name="active" eval="1" />
<field name="duration" eval="1" />
<field name="interval">hours</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_4">
<field name="name">2 hours notif</field>
<field name="active" eval="1" />
<field name="duration" eval="2" />
<field name="interval">hours</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_5">
<field name="name">1 day notif</field>
<field name="active" eval="1" />
<field name="duration" eval="1" />
<field name="interval">days</field>
<field name="type">notification</field>
@ -54,35 +49,30 @@
<record model="calendar.alarm" id="alarm_mail_1">
<field name="name">15 min mail</field>
<field name="active" eval="1" />
<field name="duration" eval="15" />
<field name="interval">minutes</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_2">
<field name="name">30 min mail</field>
<field name="active" eval="1" />
<field name="duration" eval="30" />
<field name="interval">minutes</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_3">
<field name="name">1 hour mail</field>
<field name="active" eval="1" />
<field name="duration" eval="1" />
<field name="interval">hours</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_4">
<field name="name">2 hours mail</field>
<field name="active" eval="1" />
<field name="duration" eval="2" />
<field name="interval">hours</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_5">
<field name="name">1 day mail</field>
<field name="active" eval="1" />
<field name="duration" eval="1" />
<field name="interval">days</field>
<field name="type">email</field>
@ -349,10 +339,10 @@
<body>
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
<div style="height:auto;text-align: center;font-size : 30px;color: #8A89BA;">
<strong>REMINDER : ${object.name}</strong>
<strong>${object.name}</strong>
</div>
<div style="height: 50px;text-align: left;font-size : 14px;border-collapse: separate;margin-top:10px">
<strong style="margin-left:12px">Hello ${ctx['att_obj'].cn}</strong> ,<br/><p style="margin-left:12px">${object.organizer} invited you for the ${object.name} meeting of ${object.user_id.company_id.name}.</p>
<strong style="margin-left:12px">Hello ${ctx['att_obj'].cn}</strong> ,<br/><p style="margin-left:12px">${object.user_id.partner_id.name} invited you for the ${object.name} meeting of ${object.user_id.company_id.name}.</p>
</div>
<div style="height: auto;margin-left:12px;margin-top:30px;">
<table>
@ -367,7 +357,7 @@
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">
% if object.location :
% if object.location:
<tr style=" height: 30px;">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
@ -383,20 +373,6 @@
</td>
</tr>
% endif
% if not object.location :
<tr style=" height: 30px;color:#909090">
<td>
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px;" >
: -
</div>
</td>
</tr>
% endif
% if object.description :
<tr style=" height:auto;">
<td style="vertical-align:top;">
@ -410,21 +386,7 @@
</div>
</td>
</tr>
% endif
% if not object.description :
<tr style=" height: 30px;color:#909090">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: -
</div>
</td>
</tr>
% endif
% endif
<tr style=" height: 30px;">
<td style="height: 25px;width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
<div>
@ -448,6 +410,13 @@
</tr>
</table>
</div>
<div style="height: auto;width:300px; margin:0 auto;padding-top:20px;">
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#8A89BA;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;" href="${ctx['base_url']}/meeting_invitation/accept?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">Accept</a>
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#808080;text-decoration: none;color:#FFFFFF;" href="${ctx['base_url']}/meeting_invitation/decline?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">Decline</a>
</div>
<div style="padding-top:10px;">
-- </br> Sent by ${object.user_id.name} from ${object.user_id.company_id.name}. View this meeting detail <a href="${ctx['base_url']}/meeting_invitation/view?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">directly in OpenERP.</a>
</div>
</div>
</body>
</html>

View File

@ -9,6 +9,7 @@
<record id="crm_meeting_1" model="crm.meeting">
<field eval="1" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root')])]"/>
<field name="name">Follow-up for Project proposal</field>
<field name="description">Meeting to discuss project plan and hash out the details of implementation.</field>
<field eval="time.strftime('%Y-%m-03 10:20:00')" name="date"/>
@ -20,7 +21,7 @@
<record id="crm_meeting_2" model="crm.meeting">
<field eval="1" name="active"/>
<field name="partner_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root')])]"/>
<field name="name">Initial discussion</field>
<field name="description">Discussion with partner for product.</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet3')])]"/>
@ -32,7 +33,7 @@
<record id="crm_meeting_3" model="crm.meeting">
<field eval="1" name="active"/>
<field name="partner_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root')])]"/>
<field name="name">Pricing Discussion</field>
<field name="description">Internal meeting for discussion for new pricing for product and services.</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
@ -44,7 +45,7 @@
<record id="crm_meeting_4" model="crm.meeting">
<field eval="1" name="active"/>
<field name="partner_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root')])]"/>
<field name="name">Requirements review</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet3')])]"/>
<field eval="time.strftime('%Y-%m-20 8:00:00')" name="date"/>
@ -55,7 +56,7 @@
<record id="crm_meeting_5" model="crm.meeting">
<field eval="1" name="active"/>
<field name="partner_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root')])]"/>
<field name="name">Changes in Designing</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1')])]"/>
<field eval="time.strftime('%Y-%m-22 11:05:00')" name="date"/>
@ -66,7 +67,7 @@
<record id="crm_meeting_6" model="crm.meeting">
<field eval="1" name="active"/>
<field name="partner_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root')])]"/>
<field name="name">Presentation for new Services</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
<field eval="time.strftime('%Y-%m-18 2:00:00')" name="date"/>

View File

@ -27,7 +27,7 @@
<record model="ir.ui.view" id="view_crm_meeting_form">
<field name="name">CRM - Meetings Form</field>
<field name="model">crm.meeting</field>
<field name="sequence">1</field>
<field name="priority" eval="1"/>
<field name="arch" type="xml">
<form string="Meetings" version="7.0">
<sheet>
@ -158,7 +158,7 @@
<record model="ir.ui.view" id="view_crm_meeting_form_popup">
<field name="name">Meetings Popup</field>
<field name="model">crm.meeting</field>
<field name="sequence">5</field>
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<form string="Meetings" version="7.0">
<field name="state" invisible="1"/>
@ -310,7 +310,7 @@
<field name="view_mode">form,calendar,tree,gantt</field>
<field name="view_id" ref="action_view_crm_meeting_form"/>
</record>
</data>
</openerp>

View File

@ -18,8 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import google_base_account
import controllers
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1 @@
import main

View File

@ -0,0 +1,119 @@
import simplejson
import urllib
import openerp
import openerp.addons.web.http as http
from openerp.addons.web.http import request
import openerp.addons.web.controllers.main as webmain
import json
from openerp.addons.web.http import SessionExpiredException
from werkzeug.exceptions import BadRequest
import werkzeug.utils
class google_auth(http.Controller):
@http.route('/googleauth/oauth2callback', type='http', auth="none")
def oauth2callback(self, **kw):
state = simplejson.loads(kw['state'])
#action = state.get('a')
#menu = state.get('m')
dbname = state.get('d')
#service = state.get('s')
url_return = state.get('from')
registry = openerp.modules.registry.RegistryManager.get(dbname)
with registry.cursor() as cr:
#TODO CHECK IF REQUEST OK
registry.get('google.calendar').set_all_tokens(cr,request.session.uid,kw['code'])
registry.get('google.calendar').set_primary_id(cr,request.session.uid)
return werkzeug.utils.redirect(url_return)
#@openerp.addons.web.http.route('/web_calendar_sync/sync_calendar/sync_data', type='json', auth='user')
@http.route('/web_calendar_sync/sync_calendar/sync_data', type='json', auth='user')
def sync_data(self, arch, fields, model,**kw):
calendar_info = {
'field_data':{},
'date_start':arch['attrs'].get('date_start'),
'date_stop':arch['attrs'].get('date_stop'),
'calendar_string':arch['attrs'].get('string'),
'model':model
}
for field, data in fields.items():
calendar_info['field_data'][field] = {
'type': data.get('type'),
'string': data.get('string')
}
if model == 'crm.meeting':
model_obj = request.registry.get('crm.meeting.synchronize')
gc_obj = request.registry.get('google.calendar')
#We check that user has already accepted openerp to acces his calendar !
if not gc_obj.get_refresh_token(request.cr, request.uid,context=kw.get('LocalContext')):
url = gc_obj.authorize_google_uri(request.cr, request.uid, from_url=kw.get('fromurl'),context=kw.get('LocalContext'))
return {
"status" : "NeedAuth",
"url" : url
}
#We lunch th synchronization
print "ORI COONTEXT = ",kw.get('LocalContext')
model_obj.synchronize_events(request.cr, request.uid, [], kw.get('LocalContext'))
else:
model_obj = request.registry.get('google.calendar')
model_obj.synchronize_calendar(request.cr, request.uid, calendar_info, kw.get('LocalContext'))
return { "status" : "SUCCESS" }
@http.route('/googleauth/AuthorizeMe', type='http', auth="none")
def authorize_app(self,**val):
if val.get('done'):
return;
registry = openerp.modules.registry.RegistryManager.get(request.session.get('db'))
gs_pool = registry.get('google.service')
with registry.cursor() as cr:
url = gs_pool._get_authorize_uri(cr,request.session.uid,service='calendar',from_url='')
return werkzeug.utils.redirect(url) ##REDIRECT WHERE THE USER WAS BEFORE (with state)
@http.route('/googleauth/GiveMeAToken', type='http', auth="none")
def authorize_me(self,**val):
registry = openerp.modules.registry.RegistryManager.get(request.session.get('db'))
gs_pool = registry.get('google.service')
with registry.cursor() as cr:
token = gs_pool._get_google_token_json(cr, request.session.uid, 'api_code')
print '#####################################'
print '## YOUR TOKEN : ',token, " ##"
print '#####################################'
#return werkzeug.utils.redirect(url)
return

View File

@ -22,6 +22,7 @@
from openerp.osv import osv
from openerp import SUPERUSER_ID
from openerp.tools.translate import _
from openerp.addons.web.http import request
import urllib
import urllib2
@ -57,9 +58,135 @@ class google_service(osv.osv_memory):
'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),
'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:
#If no scope is passed, we use service by default to get a default scope
def _get_authorize_uri(self, cr, uid, from_url, service, scope = False, context=None): #authorize_API
if not service:
print 'please specify a service (Calendar for example)!'
state_obj = {}
state_obj['d'] = cr.dbname
state_obj['a'] = request and request.params.get('action') or False
state_obj['m'] = request and request.params.get('menu_id') or False
state_obj['s'] = service
state_obj['from'] = from_url
base_url = self.get_base_url(cr, uid, context)
client_id = self.get_client_id(cr, uid, service, context)
params = {
'response_type': 'code',
'client_id': client_id,
'state' : simplejson.dumps(state_obj),
'scope': scope or 'https://www.googleapis.com/auth/%s' % (service,),
'redirect_uri': base_url + '/googleauth/oauth2callback',
'approval_prompt':'force',
'access_type':'offline'
}
uri = self.get_uri_oauth(a='auth') + "?%s" % urllib.urlencode(params)
return uri
def _get_google_token_json(self, cr, uid, authorize_code, service, context=None): #exchange_AUTHORIZATION vs Token (service = calendar)
res = False
base_url = self.get_base_url(cr, uid, context)
client_id = self.get_client_id(cr, uid, service, context)
client_secret = self.get_client_secret(cr, uid, service, context)
params = {
'code': authorize_code,
'client_id': client_id,
'client_secret': client_secret,
'grant_type' : 'authorization_code',
'redirect_uri': base_url + '/googleauth/oauth2callback'
}
headers = {"content-type": "application/x-www-form-urlencoded"}
try:
data = urllib.urlencode(params)
req = urllib2.Request(self.get_uri_oauth(a='token'), data, headers)
content = urllib2.urlopen(req).read()
res = simplejson.loads(content)
except urllib2.HTTPError,e:
print e
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired"), context=context)
return res
def _refresh_google_token_json(self, cr, uid, refresh_token, service, context=None): #exchange_AUTHORIZATION vs Token (service = calendar)
res = False
base_url = self.get_base_url(cr, uid, context)
client_id = self.get_client_id(cr, uid, service, context)
client_secret = self.get_client_secret(cr, uid, service, context)
params = {
'refresh_token': refresh_token,
'client_id': client_id,
'client_secret': client_secret,
'grant_type' : 'refresh_token'
}
headers = {"content-type": "application/x-www-form-urlencoded"}
try:
data = urllib.urlencode(params)
req = urllib2.Request(self.get_uri_oauth(a='token'), data, headers)
content = urllib2.urlopen(req).read()
res = simplejson.loads(content)
except urllib2.HTTPError:
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired"), context=context)
return res
def _do_request(self,cr,uid,uri,params={},headers={},type='POST', context=None):
res = False
print "#########################################"
print "### URI : %s ###" % (uri)
print "### HEADERS : %s ###" % (headers)
print "### METHOD : %s ###" % (type)
if type=='GET':
print "### PARAMS : %s ###" % urllib.urlencode(params)
else:
print "### PARAMS : %s ###" % (params)
print "#########################################"
try:
if type.upper() == 'GET':
data = urllib.urlencode(params)
req = urllib2.Request(self.get_uri_api() + uri + "?" + data)#,headers)
elif type.upper() == 'POST':
req = urllib2.Request(self.get_uri_api() + uri, params, headers)
else:
raise ('Method not supported [GET or POST] not in [%s]!' % (type))
content = urllib2.urlopen(req).read()
res = simplejson.loads(content)
except urllib2.HTTPError,e:
print e.read()
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during your token generation. Maybe your Authorization Code is invalid or already expired"), context=context)
return res
def get_base_url(self, cr, uid, context=None):
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url',default='http://www.openerp.com?NoBaseUrl',context=context)
def get_client_id(self, cr, uid, service, context=None):
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'google_%s_client_id' % (service,),context=context)
def get_client_secret(self, cr, uid, service, context=None):
return self.pool.get('ir.config_parameter').get_param(cr, uid, 'google_%s_client_secret' % (service,),context=context)
def get_uri_oauth(self,a=''): #a = optionnal action
return "https://accounts.google.com/o/oauth2/%s" % (a,)
def get_uri_api(self):
return 'https://www.googleapis.com'

View File

@ -0,0 +1,2 @@
import google_calendar
import wizard

Binary file not shown.

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# 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/>.
#
##############################################################################
{
'name': 'Google Calendar',
'version': '1.0',
'category': 'Tools',
'description': """
The module adds the possibility to synchronize Google Calendar with OpenERP
========================================
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['base_calendar'],
'js': ['static/src/js/*.js'],
'qweb': ['static/src/xml/*.xml'],
'data': [
'google_calendar_view.xml',
'google_calendar_data.xml',
'wizard/crm_meeting_view.xml',
],
'demo': [],
'installable': True,
'auto_install': False,
}

View File

@ -0,0 +1,250 @@
##############################################################################
#
# 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 simplejson
import re
import urllib
import urllib2
from openerp import tools
from openerp.tools.translate import _
from openerp.addons.web.http import request
import werkzeug.utils
from datetime import datetime, timedelta, date
from openerp.osv import fields, osv
from openerp.osv import osv
google_state_mapping = {
'needs-action':'needsAction',
'declined': 'declined',
'tentative':'tentative',
'accepted':'accepted',
'delegated':'declined',
}
oe_state_mapping = {
'needsAction':'needs-action',
'declined': 'declined',
'tentative':'tentative',
'accepted':'accepted',
}
class google_calendar(osv.osv):
_name = 'google.calendar'
STR_SERVICE = 'calendar'
def authorize_google_uri(self,cr,uid,from_url='http://www.openerp.com',context=None):
url = self.pool.get('google.service')._get_authorize_uri(cr,uid,from_url,self.STR_SERVICE,scope=self.get_calendar_scope(),context=context)
return url
def set_all_tokens(self,cr,uid,authorization_code,context=None):
gs_pool = self.pool.get('google.service')
all_token = gs_pool._get_google_token_json(cr, uid, authorization_code,self.STR_SERVICE,context=context)
vals = {}
vals['google_%s_rtoken' % self.STR_SERVICE] = all_token.get('refresh_token')
vals['google_%s_token_validity' % self.STR_SERVICE] = datetime.now() + timedelta(seconds=all_token.get('expires_in')) #NEED A CALCUL
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
self.pool.get('res.users').write(cr,uid,uid,vals,context=context)
def set_primary_id(self,cr,uid,context=None):
gs_pool = self.pool.get('google.service')
params = {
'fields': 'id',
'access_token': self.get_token(cr, uid, context=context)
}
cal = gs_pool._do_request(cr, uid, "/calendar/v3/calendars/primary/", params, type='GET', context=context)
if cal.get('id',False):
vals = {}
vals['google_calendar_id']= cal.get('id')
self.pool.get('res.users').write(cr,uid,uid,vals,context=context)
return True
else:
return False
def generate_data(self, cr, uid, event, context=None):
if event.allday:
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=0), context=context).isoformat('T').split('T')[0]
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=24), context=context).isoformat('T').split('T')[0]
type = 'date'
else:
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
type = 'dateTime'
attendee_list = []
for attendee in event.attendee_ids:
attendee_list.append({
'email':attendee.email or 'NoEmail@mail.com',
'displayName':attendee.partner_id.name,
'responseStatus':google_state_mapping.get(attendee.state, 'needsAction'),
})
data = {
"summary": event.name or '',
"description": event.description or '',
"start":{
type:start_date,
#'timeZone':context.get('tz') or 'UTC'
},
"end":{
type:end_date,
#'timeZone':context.get('tz') or 'UTC'
},
"attendees":attendee_list,
"location":event.location or '',
"visibility":event['class'] or 'public',
}
if event.recurrency and event.rrule:
data["recurrence"]= []
if event.exdate:
data["recurrence"].append("EXDATE:"+event.exdate)
data["recurrence"]+=["RRULE:"+event.rrule]
#if not recurrency and event.recurrent_id and event.recurrent_id != 0: ###" IMMUTABLE
# data["recurringEventId"] = event.recurrent_id
print data
return data
def create_event(self, cr, uid,event, context=None):
gs_pool = self.pool.get('google.service')
print "CONTEXT : ",context
data = self.generate_data(cr, uid,event, context=context)
url = "/calendar/v3/calendars/%s/events?fields=%s&access_token=%s" % ('primary',urllib.quote('id,updated'),self.get_token(cr,uid,context))
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
data_json = simplejson.dumps(data)
response = gs_pool._do_request(cr, uid, url, data_json, headers, type='POST', context=context)
#TODO Check response result
return response
def get_token(self,cr,uid,context=None):
current_user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
if datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], "%Y-%m-%d %H:%M:%S") < datetime.now() - timedelta(minutes=5):
print "NEED TO RENEW TOKEN",current_user.google_calendar_token_validity , "<", datetime.now() - timedelta(minutes=5)
self.do_refresh_token(cr,uid,context=context)
current_user.refresh()
else:
print "KEEP OLD TOKEN",current_user.google_calendar_token_validity , "<", datetime.now() - timedelta(minutes=5)
return current_user.google_calendar_token
def do_refresh_token(self,cr,uid,context=None):
current_user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
gs_pool = self.pool.get('google.service')
refresh = current_user.google_calendar_rtoken
all_token = gs_pool._refresh_google_token_json(cr, uid, current_user.google_calendar_rtoken,self.STR_SERVICE,context=context)
vals = {}
vals['google_%s_token_validity' % self.STR_SERVICE] = datetime.now() + timedelta(seconds=all_token.get('expires_in'))
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
self.pool.get('res.users').write(cr,uid,uid,vals,context=context)
def get_refresh_token(self,cr,uid,context=None):
current_user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
return current_user.google_calendar_rtoken
def get_calendar_scope(self,RO=False):
readonly = RO and '.readonly' or ''
return 'https://www.googleapis.com/auth/calendar%s' % (readonly)
class res_users(osv.osv):
_inherit = 'res.users'
_columns = {
'google_calendar_id': fields.char('Primary Calendar ID'),
'google_calendar_rtoken': fields.char('Refresh Token'),
'google_calendar_token': fields.char('User token'),
'google_calendar_token_validity': fields.datetime('Token Validity'),
}
def get_cal_token_info(self, cr, uid, partner_id=False, context=None):
if partner_id:
user = self.pool.get('res.partner').browse(cr,uid,partner_id,context=context).user_id
else:
user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
return dict(authcode=user.google_cal_authcode, token=user.google_cal_token, validity=user.google_cal_token_validity,)
def update_cal_token(self,cr,uid,jsontoken,context=None):
print jsontoken
import ipdb; ipdb.set_trace();
self.write(cr,uid,uid,{'xxx' : datetime.now() } ,context=context)
return "OK"
class crm_meeting(osv.osv):
_inherit = "crm.meeting"
def write(self, cr, uid, ids, vals, context=None):
sync_fields = set(['name', 'description', 'date', 'date_closed', 'date_deadline', 'attendee_ids', 'location', 'class'])
if (set(vals.keys()) & sync_fields) and 'oe_update_date' not in vals.keys():
vals['oe_update_date'] = datetime.now()
return super(crm_meeting, self).write(cr, uid, ids, vals, context=context)
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
default['attendee_ids'] = False
if default.get('write_type', False):
del default['write_type']
elif default.get('recurrent_id', False):
default['oe_update_date'] = datetime.now()
default['google_internal_event_id'] = False
else:
default['google_internal_event_id'] = False
default['oe_update_date'] = False
return super(crm_meeting, self).copy(cr, uid, id, default, context)
_columns = {
'google_internal_event_id': fields.char('Google Calendar Event Id', size=124),
'oe_update_date': fields.datetime('OpenERP Update Date'),
}
# If attendees are updated, we need to specify that next synchro need an action
class calendar_attendee(osv.osv):
_inherit = 'calendar.attendee'
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, list):
cr.execute("SELECT crmmeeting_id FROM crmmeeting_attendee_rel WHERE attendee_id = %s"%(ids[0]))
else:
cr.execute("SELECT crmmeeting_id FROM crmmeeting_attendee_rel WHERE attendee_id = %s"%(ids))
event_id = cr.fetchone()[0]
if event_id:
self.pool.get('crm.meeting').write(cr, uid, event_id, {'oe_update_date':datetime.now()})
return super(calendar_attendee, self).write(cr, uid, ids, vals, context=context)

Binary file not shown.

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--
<record id="google_calendar_template" model="google.drive.config">
<field name="name">Base Spreadsheet Template</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="google_drive_template_url">https://docs.google.com/spreadsheet/ccc?key=0ApGVjjwUC-ygdDZ0TG5EQnRlLVFQNlFGdFN5b1ZrY1E</field>
<field name="name_template">Reporting %(name)s</field>
<field name="active" eval="0" />
</record>
-->
</data>
</openerp>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- add google drive config field in user form -->
<record model="ir.ui.view" id="view_ir_attachment_google_spreadsheet_tree">
<field name="name">ir.attachment.google.spreadsheet.tree</field>
<field name="model">ir.attachment</field>
<field name="arch" type="xml">
<tree string="Google Spreadsheets" version="7.0">
<field name="name" string="Name"/>
<field name="url" />
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_ir_attachment_google_spreadsheet_form">
<field name="name">ir.attachment.google.spreadsheet.form</field>
<field name="model">ir.attachment</field>
<field name="arch" type="xml">
<form string="Google Spreadsheets" version="7.0">
<sheet>
<group>
<group>
<field name="name" string="Name"/>
<field name="url" widget="url"/>
</group>
<group colspan="2">
<label for="description" colspan="2"/>
<field name="description" nolabel="1" colspan="2"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_ir_attachment_google_spreadsheet_tree" model="ir.actions.act_window">
<field name="name">Google Spreadsheets</field>
<field name="res_model">ir.attachment</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{}</field>
<field name="domain">[('url', 'like', '/spreadsheet/')]</field>
<field name="help">Google Spreadsheets</field>
</record>
<record id="action_ir_attachment_google_spreadsheet_tree_view" model="ir.actions.act_window.view">
<field eval="1" name="sequence"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_ir_attachment_google_spreadsheet_tree"/>
<field name="act_window_id" ref="action_ir_attachment_google_spreadsheet_tree"/>
</record>
<record id="action_ir_attachment_google_spreadsheet_form_view" model="ir.actions.act_window.view">
<field eval="2" name="sequence"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_ir_attachment_google_spreadsheet_form"/>
<field name="act_window_id" ref="action_ir_attachment_google_spreadsheet_tree"/>
</record>
<menuitem id="menu_reporting_dashboard_google_spreadsheets" parent="base.menu_reporting_dashboard" action="action_ir_attachment_google_spreadsheet_tree"/>
</data>
</openerp>

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,40 @@
/*---------------------------------------------------------
* OpenERP web_calendar
*---------------------------------------------------------*/
openerp.google_calendar = function(instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
instance.web_calendar.FullCalendarView.include({
view_loading: function(r) {
var self = this;
this.$el.on('click', 'div.oe_dhx_cal_sync_button', function() {
self.sync_calendar(r);
});
return this._super(r);
},
sync_calendar: function(res) {
var self = this;
var context = instance.web.pyeval.eval('context');
console.log("Model is..." + res.model);
self.rpc('/web_calendar_sync/sync_calendar/sync_data', {
arch: res.arch,
fields: res.fields,
model:res.model,
fromurl: window.location.href,
LocalContext:context
}).always(function(o) {
if (o.status == "NeedAuth") {
alert(_t("You will be redirected on gmail to authorize your OpenErp to access your calendar !"));
window.location = o.url;
}
console.log(o);
//self.reload();
});
}
});
};

View File

@ -0,0 +1,17 @@
<template>
<t t-extend="CalendarView.sidebar">
<t t-jquery="div.oe_calendar_mini" t-operation="after">
<div id="sync" class="oe_dhx_cal_sync_button dhx_cal_tab " >
<br/>
<center>
<button class="oe_button">
<span>
<img src="/google_calendar/static/src/img/calendar_32.png"/> Sync with <b>Google</b>
</span>
</button>
</center>
</div>
</t>
</t>
</template>

View File

@ -0,0 +1 @@
import crm_meeting

View File

@ -0,0 +1,269 @@
# -*- 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 osv, fields
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.tools.translate import _
from datetime import datetime
from dateutil import parser
import pytz
import urllib
import urllib2
import json
import werkzeug.utils
class crm_meeting_synchronize(osv.osv_memory):
_name = 'crm.meeting.synchronize'
def synchronize_events(self, cr, uid, ids, context=None):
gc_obj = self.pool.get('google.calendar')
self.create_new_events(cr, uid, context=context)
#self.bind_recurring_events_to_google(cr, uid, context)
#self.update_events(cr, uid, access_token, context)
return True
#
# def generate_data(self, cr, uid, event, context):
# if event.allday:
# start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0]
# end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0]
# type = 'date'
# else:
# start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
# end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
# type = 'dateTime'
# attendee_list = []
# for attendee in event.attendee_ids:
# attendee_list.append({
# 'email':attendee.email,
# 'displayName':attendee.partner_id.name,
# 'responseStatus':google_state_mapping.get(attendee.state, 'needsAction'),
# })
# data = {
# "summary": event.name or '',
# "description": event.description or '',
# "start":{
# type:start_date,
# 'timeZone':context.get('tz')
# },
# "end":{
# type:end_date,
# 'timeZone':context.get('tz')
# },
# "attendees":attendee_list,
# "colorId":4,
# "location":event.location or '',
# "visibility":event['class'] or 'public',
# }
# if event.rrule:
# data["recurrence"]=["RRULE:"+event.rrule]
# return data
#
def create_new_events(self, cr, uid, context):
gc_pool = self.pool.get('google.calendar')
crm_meeting = self.pool['crm.meeting']
user_obj = self.pool['res.users']
print "TO CHECK RESULT HERE >>>>>>>>>>>>>>>>>>>>"
context_norecurrent = context.copy()
context_norecurrent['virtual_id'] = False
new_events_ids = crm_meeting.search(cr, uid,[('partner_ids', 'in', user_obj.browse(cr,uid,uid,context=context).partner_id.id),('google_internal_event_id', '=', False)], context=context_norecurrent)
#new_events_ids = [str(i).split('-')[0] for i in new_events_ids]
#new_events_ids = base_calendar_id2real_id(new_events_ids,False); #[str(i).split('-')[0] for i in new_events_ids]
print 'Events ids [new_events_ids] : ', new_events_ids
for event in crm_meeting.browse(cr, uid, list(set(new_events_ids)), context):
response = gc_pool.create_event(cr,uid,event,context=context)
update_date = datetime.strptime(response['updated'],"%Y-%m-%dT%H:%M:%S.%fz")
crm_meeting.write(cr, uid, event.id, {'google_internal_event_id': response['id'], 'oe_update_date':update_date})
#Check that response OK and return according to that
return True
#
# def bind_recurring_events_to_google(self, cr, uid, context):
# crm_meeting = self.pool['crm.meeting']
# new_events_ids = crm_meeting.search(cr, uid,[('user_id', '=', uid),('google_internal_event_id', '=', False),('recurrent_id', '>', 0)], context)
# new_events_ids = base_calendar_id2real_id(new_events_ids,False); #[str(i).split('-')[0] for i in new_events_ids]
#
# for event in crm_meeting.browse(cr, uid, list(set(new_events_ids)), context):
# source_record = crm_meeting.read(cr, uid ,event.recurrent_id,['allday', 'google_internal_event_id'],context)
# new_google_internal_event_id = False
# if event.recurrent_id_date and source_record.get('allday') and source_record.get('google_internal_event_id'):
# new_google_internal_event_id = source_record.get('google_internal_event_id') +'_'+ event.recurrent_id_date.split(' ')[0].replace('-','') + 'Z'
# elif event.recurrent_id_date and source_record.get('google_internal_event_id'):
# new_google_internal_event_id = source_record.get('google_internal_event_id') +'_'+ event.recurrent_id_date.replace('-','').replace(' ','T').replace(':','') + 'Z'
# if new_google_internal_event_id:
# crm_meeting.write(cr, uid, [event.id], {'google_internal_event_id': new_google_internal_event_id})
def get_event_dict(self,access_token, nextPageToken):
request_url = "https://www.googleapis.com/calendar/v3/calendars/%s/events?fields=%s&access_token=%s" % ('primary',urllib.quote('items,nextPageToken') ,access_token)
if nextPageToken:
request_url += "&pageToken=%s" %(nextPageToken)
try:
req = urllib2.Request(request_url)
content = urllib2.urlopen(req).read()
except urllib2.HTTPError,e:
error_message = e.read()
print error_message
content = json.loads(content)
google_events_dict = {}
for google_event in content['items']:
if google_event.get('updated',False):
google_events_dict[google_event['id']] = google_event
if content.get('nextPageToken', False):
google_events_dict.update(self.get_event_dict(access_token,content['nextPageToken']))
return google_events_dict
def update_events(self, cr, uid, access_token, context):
crm_meeting = self.pool['crm.meeting']
google_event_dict = self.get_event_dict(access_token, False)
updated_events_ids = crm_meeting.search(cr, uid,[('partner_ids', 'in', user_obj.browse(cr,uid,uid,context=context)),('google_internal_event_id', '!=', False),('oe_update_date','!=', False)], context)
for event in crm_meeting.browse(cr, uid, list(set(updated_events_ids)), context):
if event.google_internal_event_id in google_event_dict:
self.check_and_sync(cr, uid, access_token, event, google_event_dict[event.google_internal_event_id], context)
del google_event_dict[event.google_internal_event_id]
else:
request_url = "https://www.googleapis.com/calendar/v3/calendars/%s/events/%s?access_token=%s" % ('primary', event.google_internal_event_id, access_token)
content = {}
try:
req = urllib2.Request(request_url)
content = urllib2.urlopen(req).read()
except urllib2.HTTPError,e:
error_message = e.read()
print error_message
if e.code == 404:
print "Need DELETE"
if content:
content = json.loads(content)
self.check_and_sync(cr, uid, access_token, event, content, context)
for new_google_event in google_event_dict.values():
if new_google_event.get('recurringEventId',False):
reccurent_event = crm_meeting.search(cr, uid, [('google_internal_event_id', '=', new_google_event['recurringEventId'])])
new_google_event_id = new_google_event['id'].split('_')[1].replace('T','')[:-1]
for event_id in reccurent_event:
if isinstance(event_id, str) and len(event_id.split('-'))>1 and event_id.split('-')[1] == new_google_event_id:
reccurnt_event_id = int(event_id.split('-')[0].strip())
parent_event = crm_meeting.read(cr,uid, reccurnt_event_id, [], context)
parent_event['id'] = event_id
#reccurent update from google
self.update_from_google(cr, uid, parent_event, new_google_event, "copy", context)
else:
#new event from google
self.update_from_google(cr, uid, False, new_google_event, "create", context)
# del google_events_dict[new_google_event['id']]
def check_and_sync(self, cr, uid, access_token, oe_event, google_event, context):
if datetime.strptime(oe_event.oe_update_date,"%Y-%m-%d %H:%M:%S.%f") > datetime.strptime(google_event['updated'],"%Y-%m-%dT%H:%M:%S.%fz"):
#update to google
self.update_to_google(cr, uid, access_token, oe_event, google_event, context)
elif datetime.strptime(oe_event.oe_update_date,"%Y-%m-%d %H:%M:%S.%f") < datetime.strptime(google_event['updated'],"%Y-%m-%dT%H:%M:%S.%fz"):
#update from google
self.update_from_google(cr, uid, oe_event, google_event, 'write', context)
pass
def update_to_google(self, cr, uid, access_token, oe_event, google_event, context):
crm_meeting = self.pool['crm.meeting']
request_url = "https://www.googleapis.com/calendar/v3/calendars/%s/events/%s?fields=%s" % ('primary', google_event['id'], urllib.quote('id,updated'))
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
data = self.generate_data(cr,uid ,oe_event, context)
data['sequence'] = google_event.get('sequence', 0)
data_json = json.dumps(data)
try:
req = urllib2.Request(request_url, data_json, headers)
req.get_method = lambda: 'PATCH'
content = urllib2.urlopen(req).read()
except urllib2.HTTPError,e:
error_message = json.loads(e.read())
content = json.loads(content)
update_date = datetime.strptime(content['updated'],"%Y-%m-%dT%H:%M:%S.%fz")
crm_meeting.write(cr, uid, [oe_event.id], {'oe_update_date':update_date})
def update_from_google(self, cr, uid, event, single_event_dict, type, context):
crm_meeting = self.pool['crm.meeting']
res_partner_obj = self.pool['res.partner']
calendar_attendee_obj = self.pool['calendar.attendee']
attendee_record= []
result = {}
if single_event_dict.get('attendees',False):
for google_attendee in single_event_dict['attendees']:
if type == "write":
for oe_attendee in event['attendee_ids']:
if calendar_attendee_obj.browse(cr, uid ,oe_attendee,context=context).email == google_attendee['email']:
calendar_attendee_obj.write(cr, uid,[oe_attendee] ,{'state' : oe_state_mapping[google_attendee['responseStatus']]})
google_attendee['found'] = True
if google_attendee.get('found',False):
continue
attendee_id = res_partner_obj.search(cr, uid,[('email', '=', google_attendee['email'])], context=context)
if not attendee_id:
attendee_id = [res_partner_obj.create(cr, uid,{'email': google_attendee['email'], 'name': google_attendee.get("displayName",False) or google_attendee['email'] }, context=context)]
attendee = res_partner_obj.read(cr, uid, attendee_id[0], ['email'], context=context)
attendee['partner_id'] = attendee.pop('id')
attendee['state'] = oe_state_mapping[google_attendee['responseStatus']]
attendee_record.append((0, 0, attendee))
if single_event_dict['start'].get('dateTime',False) and single_event_dict['end'].get('dateTime',False):
UTC = pytz.timezone('UTC')
date = parser.parse(single_event_dict['start']['dateTime'])
date_deadline = parser.parse(single_event_dict['end']['dateTime'])
delta = date_deadline.astimezone(UTC) - date.astimezone(UTC)
result['duration'] = (delta.seconds / 60) / 60.0 + delta.days *24
date = str(date.astimezone(UTC))[:-6]
date_deadline = str(date_deadline.astimezone(UTC))[:-6]
allday = False
else:
date = single_event_dict['start']['date'] + ' 12:00:00'
date_deadline = single_event_dict['start']['date'] + ' 12:00:00'
allday = True
update_date = datetime.strptime(single_event_dict['updated'],"%Y-%m-%dT%H:%M:%S.%fz")
result.update({
'attendee_ids': attendee_record,
'date': date,
'date_deadline': date_deadline,
'allday': allday,
'name': single_event_dict.get('summary','Event'),
'description': single_event_dict.get('description',''),
'location':single_event_dict.get('location',''),
'class':single_event_dict.get('visibility','public'),
'oe_update_date':update_date,
'google_internal_event_id': single_event_dict.get('id',''),
})
if single_event_dict.get("recurrence",False):
rrule = [rule for rule in single_event_dict["recurrence"] if rule.startswith("RRULE:")][0][6:]
result['rrule']=rrule
if type == "write":
crm_meeting.write(cr, uid, event['id'], result, context=context)
elif type == "copy":
result['write_type'] = "copy"
crm_meeting.write(cr, uid, event['id'], result, context=context)
elif type == "create":
crm_meeting.create(cr, uid, result, context=context)

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_crm_meeting_google_synchronize" model="ir.ui.view">
<field name="name">Synchronize Events to Google</field>
<field name="model">crm.meeting.synchronize</field>
<field name="arch" type="xml">
<form string="Synchronize Events to Google" version="7.0">
<group string="This wizard will synchronize Events to Google Calendar">
</group>
<footer>
<button name="synchronize_events" string="Synchronize" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_crm_meeting_google_synchronize" model="ir.actions.act_window">
<field name="name">Synchronize Events to Google</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">crm.meeting.synchronize</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_crm_meeting_google_synchronize"/>
<field name="context">{}</field>
<field name="target">new</field>
</record>
<menuitem name="Synchronize with Google"
id="mail_menu_calendar_synch" parent="mail.mail_my_stuff"
sequence="10" action="action_crm_meeting_google_synchronize"/>
</data>
</openerp>