diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 0fb6af05e58..87e6a66740c 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -221,8 +221,8 @@ class account_invoice(osv.osv): 'type': { }, 'state': { - 'account.mt_invoice_paid': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'paid' and obj['type'] in ('out_invoice', 'out_refund'), - 'account.mt_invoice_validated': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open' and obj['type'] in ('out_invoice', 'out_refund'), + 'account.mt_invoice_paid': lambda self, cr, uid, obj, ctx=None: obj.state == 'paid' and obj.type in ('out_invoice', 'out_refund'), + 'account.mt_invoice_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'open' and obj.type in ('out_invoice', 'out_refund'), }, } _columns = { diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index ce79c4d5ffb..df852181709 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -33,9 +33,9 @@ class account_analytic_account(osv.osv): _description = 'Analytic Account' _track = { 'state': { - 'analytic.mt_account_pending': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'pending', - 'analytic.mt_account_closed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'close', - 'analytic.mt_account_opened': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open', + 'analytic.mt_account_pending': lambda self, cr, uid, obj, ctx=None: obj.state == 'pending', + 'analytic.mt_account_closed': lambda self, cr, uid, obj, ctx=None: obj.state == 'close', + 'analytic.mt_account_opened': lambda self, cr, uid, obj, ctx=None: obj.state == 'open', }, } diff --git a/addons/base_setup/res_config.py b/addons/base_setup/res_config.py index 33c5aa863bf..6b8578bbf47 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 Google documents 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 @@ + + diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 44ada2ffcc0..1070af22b3c 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -77,12 +77,12 @@ class crm_lead(base_stage, format_address, osv.osv): _track = { 'state': { - 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'], - 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', - 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', + 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj.state in ['new', 'draft'], + 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', + 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj.state == 'cancel', }, 'stage_id': { - 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'cancel', 'done'], + 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj.state not in ['new', 'draft', 'cancel', 'done'], }, } 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 eval="[ ref('msg_case18_1'), ref('msg_case18_2')], True, {}" /> + 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..a6cf67bbe62 100644 --- a/addons/google_base_account/google_base_account.py +++ b/addons/google_base_account/google_base_account.py @@ -19,14 +19,48 @@ # ############################################################################## -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 +from openerp.tools.translate import _ + +import urllib +import urllib2 +import simplejson + + +class google_service(osv.osv): + _name = 'google.service' + + def generate_refresh_token(self, cr, uid, service, authorization_code, context=None): + 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) + try: + req = urllib2.Request("https://accounts.google.com/o/oauth2/token", data, headers) + content = urllib2.urlopen(req).read() + 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) + + content = simplejson.loads(content) + return content.get('refresh_token') + + 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 @@ + + + + + google_redirect_uri + urn:ietf:wg:oauth:2.0:oob + + + \ 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 @@ - - - - - res.users.google.form1 - res.users - - - - - - - - - - - - - - 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 (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import 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 (). -# -# 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 . -# -############################################################################## - -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 @@ - - - - - google.login.form - google.login - -
- - - - -
-
-
-
-
- - - - Google Login - ir.actions.act_window - google.login - form - form - new - - -
-
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 (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -import 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/i18n/google_docs.pot b/addons/google_docs/i18n/google_docs.pot deleted file mode 100644 index 8da7c5d2f70..00000000000 --- a/addons/google_docs/i18n/google_docs.pot +++ /dev/null @@ -1,159 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * google_docs -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 7.0alpha\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-12-21 17:05+0000\n" -"PO-Revision-Date: 2012-12-21 17:05+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: google_docs -#: code:addons/google_docs/google_docs.py:139 -#, python-format -msgid "Key Error!" -msgstr "" - -#. module: google_docs -#: view:google.docs.config:0 -msgid "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`" -msgstr "" - -#. module: google_docs -#: view:google.docs.config:0 -msgid "for a text document with url like `https://docs.google.com/a/openerp.com/document/d/123456789/edit`, the ID is `document:123456789`" -msgstr "" - -#. module: google_docs -#: field:google.docs.config,gdocs_resource_id:0 -msgid "Google Resource ID to Use as Template" -msgstr "" - -#. module: google_docs -#: view:google.docs.config:0 -msgid "for a drawing document with url like `https://docs.google.com/a/openerp.com/drawings/d/123456789/edit`, the ID is `drawings:123456789`" -msgstr "" - -#. module: google_docs -#. openerp-web -#: code:addons/google_docs/static/src/xml/gdocs.xml:6 -#, python-format -msgid "Add Google Doc..." -msgstr "" - -#. module: google_docs -#: view:google.docs.config:0 -msgid "This is the id of the template document, on google side. You can find it thanks to its URL:" -msgstr "" - -#. module: google_docs -#: model:ir.model,name:google_docs.model_google_docs_config -msgid "Google Docs templates config" -msgstr "" - -#. module: google_docs -#. openerp-web -#: code:addons/google_docs/static/src/js/gdocs.js:25 -#, python-format -msgid "The user google credentials are not set yet. Contact your administrator for help." -msgstr "" - -#. module: google_docs -#: view:google.docs.config:0 -msgid "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`" -msgstr "" - -#. module: google_docs -#: code:addons/google_docs/google_docs.py:101 -#, python-format -msgid "Your resource id is not correct. You can find the id in the google docs URL." -msgstr "" - -#. module: google_docs -#: code:addons/google_docs/google_docs.py:125 -#, python-format -msgid "Creating google docs may only be done by one at a time." -msgstr "" - -#. module: google_docs -#: code:addons/google_docs/google_docs.py:56 -#: code:addons/google_docs/google_docs.py:101 -#: code:addons/google_docs/google_docs.py:125 -#, python-format -msgid "Google Docs Error!" -msgstr "" - -#. module: google_docs -#: code:addons/google_docs/google_docs.py:56 -#, python-format -msgid "Check your google configuration in Users/Users/Synchronization tab." -msgstr "" - -#. module: google_docs -#: model:ir.ui.menu,name:google_docs.menu_gdocs_config -msgid "Google Docs configuration" -msgstr "" - -#. module: google_docs -#: model:ir.actions.act_window,name:google_docs.action_google_docs_users_config -#: model:ir.ui.menu,name:google_docs.menu_gdocs_model_config -msgid "Models configuration" -msgstr "" - -#. module: google_docs -#: field:google.docs.config,model_id:0 -msgid "Model" -msgstr "" - -#. module: google_docs -#. openerp-web -#: code:addons/google_docs/static/src/js/gdocs.js:28 -#, python-format -msgid "User Google credentials are not yet set." -msgstr "" - -#. module: google_docs -#: code:addons/google_docs/google_docs.py:139 -#, python-format -msgid "Your Google Doc Name Pattern's key does not found in object." -msgstr "" - -#. module: google_docs -#: help:google.docs.config,name_template:0 -msgid "Choose how the new google docs will be named, on google side. Eg. gdoc_%(field_name)s" -msgstr "" - -#. module: google_docs -#: view:google.docs.config:0 -msgid "Google Docs Configuration" -msgstr "" - -#. module: google_docs -#: help:google.docs.config,gdocs_resource_id:0 -msgid "\n" -"This is the id of the template document, on google side. You can find it thanks to its URL: \n" -"*for a text document with url like `https://docs.google.com/a/openerp.com/document/d/123456789/edit`, the ID is `document:123456789`\n" -"*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`\n" -"*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`\n" -"*for a drawing document with url like `https://docs.google.com/a/openerp.com/drawings/d/123456789/edit`, the ID is `drawings:123456789`\n" -"...\n" -"" -msgstr "" - -#. module: google_docs -#: model:ir.model,name:google_docs.model_ir_attachment -msgid "ir.attachment" -msgstr "" - -#. module: google_docs -#: field:google.docs.config,name_template:0 -msgid "Google Doc Name Pattern" -msgstr "" - 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 @@ - - - - - - - - google_docs.config.tree - google.docs.config - - - - - - - - - - google_docs.config.form - google.docs.config - -
- - - -
-
-
- - - Models configuration - google.docs.config - ir.actions.act_window - form - - - - -
-
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 similarity index 63% rename from addons/google_docs/__openerp__.py rename to 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' ], - 'depends': ['google_base_account','document'], + 'demo': [ + 'google_drive_demo.xml' + ], + '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..1dc6b25f27c --- /dev/null +++ b/addons/google_drive/google_drive.py @@ -0,0 +1,211 @@ +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2012 OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import logging + +from openerp import SUPERUSER_ID +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +import urllib +import urllib2 +import json +import re + +_logger = logging.getLogger(__name__) + + +class config(osv.osv): + _name = 'google.drive.config' + _description = "Google Drive templates config" + + 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 + 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 + 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, res_id, template_id, name_gdocs, model.model, context) + return url + + def copy_doc(self, cr, uid, res_id, template_id, name_gdocs, res_model, context=None): + ir_config = self.pool['ir.config_parameter'] + google_drive_refresh_token = ir_config.get_param(cr, SUPERUSER_ID, 'google_drive_refresh_token') + if not google_drive_refresh_token: + 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) + 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_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", "Accept-Encoding": "gzip, deflate"} + 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) + try: + req = urllib2.Request('https://accounts.google.com/o/oauth2/token', data, headers) + content = urllib2.urlopen(req).read() + except urllib2.HTTPError: + raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong during the token generation. Please request again an authorization code in %(menu:base_setup.menu_general_configuration)s."), context=context) + 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']) + try: + req = urllib2.Request(request_url, None, headers) + parents = urllib2.urlopen(req).read() + except urllib2.HTTPError: + raise self.pool.get('res.config.settings').get_config_warning(cr, _("The Google Template cannot be found. Maybe it has been deleted."), context=context) + parents_dict = json.loads(parents) + + 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']) + 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) + 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'] + 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, uid, config_ids, context=context): + if config.filter_id: + if (config.filter_id.user_id and config.filter_id.user_id.id != uid): + #Private + continue + 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) + if google_doc_configs: + configs.append({'id': config.id, 'name': config.name}) + else: + configs.append({'id': config.id, 'name': config.name}) + return configs + + def _resource_get(self, cr, uid, ids, name, arg, context=None): + result = {} + for data in self.browse(cr, uid, ids, context): + mo = re.search("(key=|/d/)([A-Za-z0-9-_]+)", data.google_drive_template_url) + if mo: + result[data.id] = mo.group(2) + else: + raise osv.except_osv(_('Incorrect URL!'), _("Please enter a valid Google Document URL.")) + return result + + def _client_id_get(self, cr, uid, ids, name, arg, context=None): + result = {} + client_id = self.pool['ir.config_parameter'].get_param(cr, SUPERUSER_ID, 'google_drive_client_id') + for config_id in ids: + result[config_id] = 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': 'Document %(name)s', + } + + 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.pool['google.service']._get_google_token_uri(cr, uid, 'drive', context=c), + } + + def set_google_authorization_code(self, cr, uid, ids, context=None): + config = self.browse(cr, uid, ids[0], context) + refresh_token = self.pool['google.service'].generate_refresh_token(cr, uid, 'drive', config.google_drive_authorization_code, context=context) + self.pool['ir.config_parameter'].set_param(cr, uid, 'google_drive_refresh_token', refresh_token) diff --git a/addons/google_drive/google_drive_data.xml b/addons/google_drive/google_drive_data.xml new file mode 100644 index 00000000000..c9266547969 --- /dev/null +++ b/addons/google_drive/google_drive_data.xml @@ -0,0 +1,16 @@ + + + + + + google_drive_client_id + 39623646228-eg3ggo3mk6o40m7rguobi3rkl9frh4tb.apps.googleusercontent.com + + + + google_drive_client_secret + Ul-PtmnSWs3euWs20fdono0e + + + + \ 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..ebe73f40ec2 --- /dev/null +++ b/addons/google_drive/google_drive_demo.xml @@ -0,0 +1,24 @@ + + + + + + + Customer + res.partner + [['customer', '=', 1]] + + + + + + Partner Review + + + https://docs.google.com/spreadsheet/ccc?key=0Ah2qnrLAoZmUdGRvdVdmS1VoSDctWk1kd18taGZ4ckE#gid=0 + Partner Review %(name)s + + + + + diff --git a/addons/google_docs/i18n/ar.po b/addons/google_drive/i18n/ar.po similarity index 100% rename from addons/google_docs/i18n/ar.po rename to addons/google_drive/i18n/ar.po diff --git a/addons/google_docs/i18n/cs.po b/addons/google_drive/i18n/cs.po similarity index 100% rename from addons/google_docs/i18n/cs.po rename to addons/google_drive/i18n/cs.po diff --git a/addons/google_docs/i18n/de.po b/addons/google_drive/i18n/de.po similarity index 100% rename from addons/google_docs/i18n/de.po rename to addons/google_drive/i18n/de.po diff --git a/addons/google_docs/i18n/es.po b/addons/google_drive/i18n/es.po similarity index 100% rename from addons/google_docs/i18n/es.po rename to addons/google_drive/i18n/es.po diff --git a/addons/google_docs/i18n/fr.po b/addons/google_drive/i18n/fr.po similarity index 100% rename from addons/google_docs/i18n/fr.po rename to addons/google_drive/i18n/fr.po diff --git a/addons/google_drive/i18n/google_drive.pot b/addons/google_drive/i18n/google_drive.pot new file mode 100644 index 00000000000..4472e9c0ed1 --- /dev/null +++ b/addons/google_drive/i18n/google_drive.pot @@ -0,0 +1,227 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * google_drive +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 8.0alpha1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-06-27 16:03+0000\n" +"PO-Revision-Date: 2013-06-27 16:03+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: google_drive +#: model:ir.ui.menu,name:google_drive.menu_google_drive_config +msgid "Google Drive configuration" +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:48 +#, python-format +msgid "Key Error!" +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "The name of the attached document can use fixed or variable data. To distinguish between documents in\n" +" Google Drive, use fixed words and fields. For instance, in the example above, if you wrote Agrolait_%(name)s_Sales\n" +" in the Google Drive name field, the document in your Google Drive and in OpenERP attachment will be named\n" +" 'Agrolait_SO0001_Sales'." +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "- If filter is not specified, link of google document will appear in \"More\" option for all users for all opportunities." +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "To create a new filter:" +msgstr "" + +#. module: google_drive +#: model:ir.model,name:google_drive.model_base_config_settings +msgid "base.config.settings" +msgstr "" + +#. module: google_drive +#: model:ir.actions.act_window,help:google_drive.action_google_drive_users_config +msgid "

\n" +" Click to add a new template.\n" +"

\n" +"

\n" +" 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.\n" +"

\n" +" " +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:150 +#, python-format +msgid "Incorrect URL!" +msgstr "" + +#. module: google_drive +#: view:base.config.settings:0 +msgid "Configure your templates" +msgstr "" + +#. module: google_drive +#: help:google.drive.config,name_template:0 +msgid "Choose how the new google drive will be named, on google side. Eg. gdoc_%(field_name)s" +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "- Go to the OpenERP document you want to filter. For instance, go to Opportunities and search on Sales Department." +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "- In this \"Search\" view, select the option \"Save Current Filter\", enter the name (Ex: Sales Department)" +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "- If you select \"Share with all users\", link of google document in \"More\" options will appear for all users in opportunities of Sales Department." +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "- 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." +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:48 +#, python-format +msgid "At least one key cannot be found in your Google Drive name pattern" +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:150 +#, python-format +msgid "Please enter a valid Google Document URL." +msgstr "" + +#. module: google_drive +#: field:google.drive.config,google_drive_client_id:0 +msgid "Google Client " +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "https://docs.google.com/document/d/1vOtpJK9scIQz6taD9tJRIETWbEw3fSiaQHArsJYcua4/edit" +msgstr "" + +#. module: google_drive +#: field:google.drive.config,filter_id:0 +msgid "Filter" +msgstr "" + +#. module: google_drive +#: field:google.drive.config,name_template:0 +msgid "Google Drive Name Pattern" +msgstr "" + +#. module: google_drive +#: help:base.config.settings,google_drive_uri:0 +msgid "The URL to generate the authorization code from Google" +msgstr "" + +#. module: google_drive +#: model:ir.filters,name:google_drive.filter_partner +msgid "Customer" +msgstr "" + +#. module: google_drive +#: field:google.drive.config,google_drive_resource_id:0 +msgid "Resource Id" +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:91 +#, python-format +msgid "The Google Template cannot be found. Maybe it has been deleted." +msgstr "" + +#. module: google_drive +#: model:ir.actions.act_window,name:google_drive.action_google_drive_users_config +#: model:ir.ui.menu,name:google_drive.menu_google_drive_model_config +msgid "Google Drive Templates" +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:81 +#, python-format +msgid "Something went wrong during the token generation. Please request again an authorization code in %(menu:base_setup.menu_general_configuration)s." +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:124 +#, python-format +msgid "Google Drive Error!" +msgstr "" + +#. module: google_drive +#: field:base.config.settings,google_drive_uri:0 +msgid "URI" +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:124 +#, python-format +msgid "Creating google drive may only be done by one at a time." +msgstr "" + +#. module: google_drive +#: field:google.drive.config,model:0 +#: field:google.drive.config,model_id:0 +msgid "Model" +msgstr "" + +#. module: google_drive +#: view:google.drive.config:0 +msgid "Google Drive Configuration" +msgstr "" + +#. module: google_drive +#: field:google.drive.config,name:0 +msgid "Template Name" +msgstr "" + +#. module: google_drive +#: constraint:google.drive.config:0 +msgid "Model of selected filter is not matching with model of current template." +msgstr "" + +#. module: google_drive +#: field:google.drive.config,google_drive_template_url:0 +msgid "Template URL" +msgstr "" + +#. module: google_drive +#: view:base.config.settings:0 +msgid "and paste it here" +msgstr "" + +#. module: google_drive +#: field:base.config.settings,google_drive_authorization_code:0 +msgid "Authorization Code" +msgstr "" + +#. module: google_drive +#: model:ir.model,name:google_drive.model_google_drive_config +msgid "Google Drive templates config" +msgstr "" + +#. module: google_drive +#: code:addons/google_drive/google_drive.py:64 +#, python-format +msgid "You haven't configured 'Authorization Code' generated from google, Please generate and configure it in %(menu:base_setup.menu_general_configuration)s." +msgstr "" + diff --git a/addons/google_docs/i18n/hr.po b/addons/google_drive/i18n/hr.po similarity index 100% rename from addons/google_docs/i18n/hr.po rename to addons/google_drive/i18n/hr.po diff --git a/addons/google_docs/i18n/hu.po b/addons/google_drive/i18n/hu.po similarity index 100% rename from addons/google_docs/i18n/hu.po rename to addons/google_drive/i18n/hu.po diff --git a/addons/google_docs/i18n/it.po b/addons/google_drive/i18n/it.po similarity index 100% rename from addons/google_docs/i18n/it.po rename to addons/google_drive/i18n/it.po diff --git a/addons/google_docs/i18n/ln.po b/addons/google_drive/i18n/ln.po similarity index 100% rename from addons/google_docs/i18n/ln.po rename to addons/google_drive/i18n/ln.po diff --git a/addons/google_docs/i18n/mk.po b/addons/google_drive/i18n/mk.po similarity index 100% rename from addons/google_docs/i18n/mk.po rename to addons/google_drive/i18n/mk.po diff --git a/addons/google_docs/i18n/mn.po b/addons/google_drive/i18n/mn.po similarity index 100% rename from addons/google_docs/i18n/mn.po rename to addons/google_drive/i18n/mn.po diff --git a/addons/google_docs/i18n/nl.po b/addons/google_drive/i18n/nl.po similarity index 100% rename from addons/google_docs/i18n/nl.po rename to addons/google_drive/i18n/nl.po diff --git a/addons/google_docs/i18n/pl.po b/addons/google_drive/i18n/pl.po similarity index 100% rename from addons/google_docs/i18n/pl.po rename to addons/google_drive/i18n/pl.po diff --git a/addons/google_docs/i18n/pt.po b/addons/google_drive/i18n/pt.po similarity index 100% rename from addons/google_docs/i18n/pt.po rename to addons/google_drive/i18n/pt.po diff --git a/addons/google_docs/i18n/pt_BR.po b/addons/google_drive/i18n/pt_BR.po similarity index 100% rename from addons/google_docs/i18n/pt_BR.po rename to addons/google_drive/i18n/pt_BR.po diff --git a/addons/google_docs/i18n/ro.po b/addons/google_drive/i18n/ro.po similarity index 100% rename from addons/google_docs/i18n/ro.po rename to addons/google_drive/i18n/ro.po diff --git a/addons/google_docs/i18n/ru.po b/addons/google_drive/i18n/ru.po similarity index 100% rename from addons/google_docs/i18n/ru.po rename to addons/google_drive/i18n/ru.po diff --git a/addons/google_docs/i18n/sl.po b/addons/google_drive/i18n/sl.po similarity index 100% rename from addons/google_docs/i18n/sl.po rename to addons/google_drive/i18n/sl.po diff --git a/addons/google_docs/i18n/sv.po b/addons/google_drive/i18n/sv.po similarity index 100% rename from addons/google_docs/i18n/sv.po rename to addons/google_drive/i18n/sv.po diff --git a/addons/google_docs/i18n/tr.po b/addons/google_drive/i18n/tr.po similarity index 100% rename from addons/google_docs/i18n/tr.po rename to addons/google_drive/i18n/tr.po diff --git a/addons/google_docs/i18n/zh_CN.po b/addons/google_drive/i18n/zh_CN.po similarity index 100% rename from addons/google_docs/i18n/zh_CN.po rename to 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..728ef03c5dd --- /dev/null +++ b/addons/google_drive/res_config_user_view.xml @@ -0,0 +1,93 @@ + + + + + + + + google_drive.config.tree + google.drive.config + + + + + + + + + + google_drive.config.form + google.drive.config + +
+ + + + + + +
+
+ + + Google Drive Templates + google.drive.config + ir.actions.act_window + form + + +

+ Click to add a new template. +

+

+ 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. +

+
+
+ + + General Settings + base.config.settings + + + +
+
+ + and paste it here + +
+
+
+
+
+ + + +
+
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;bda.call(b,e)&&c[n](e)}return c},sa=function(a){"loading"!=E.readyState?Z(a):E.write("<"+X+' src="'+encodeURI(a)+'">")},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', + 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_drive_url', [doc_item.config_id, doc_item.res_id,configs[0].google_drive_resource_id]).done(function (url) { + if (url){ + window.open(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 similarity index 100% rename from addons/google_docs/static/src/xml/gdocs.xml rename to addons/google_drive/static/src/xml/gdocs.xml diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 8d707baa50e..5d5be0eec97 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -65,9 +65,9 @@ class hr_expense_expense(osv.osv): _order = "id desc" _track = { 'state': { - 'hr_expense.mt_expense_approved': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'accepted', - 'hr_expense.mt_expense_refused': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancelled', - 'hr_expense.mt_expense_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm', + 'hr_expense.mt_expense_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'accepted', + 'hr_expense.mt_expense_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'cancelled', + 'hr_expense.mt_expense_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm', }, } diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 0eb3c0cbf16..f2be4844d27 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -110,9 +110,9 @@ class hr_holidays(osv.osv): _inherit = ['mail.thread', 'ir.needaction_mixin'] _track = { 'state': { - 'hr_holidays.mt_holidays_approved': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'validate', - 'hr_holidays.mt_holidays_refused': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'refuse', - 'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm', + 'hr_holidays.mt_holidays_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate', + 'hr_holidays.mt_holidays_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'refuse', + 'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm', }, } diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index c25583cba77..d8c00c40a31 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -94,11 +94,11 @@ class hr_applicant(base_stage, osv.Model): _inherit = ['mail.thread', 'ir.needaction_mixin'] _track = { 'state': { - 'hr_recruitment.mt_applicant_hired': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', - 'hr_recruitment.mt_applicant_refused': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', + 'hr_recruitment.mt_applicant_hired': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', + 'hr_recruitment.mt_applicant_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'cancel', }, 'stage_id': { - 'hr_recruitment.mt_stage_changed': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['done', 'cancel'], + 'hr_recruitment.mt_stage_changed': lambda self, cr, uid, obj, ctx=None: obj.state not in ['done', 'cancel'], }, } diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 6961b55933f..0c2c96c729c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -336,8 +336,8 @@ class mail_thread(osv.AbstractModel): # Track initial values of tracked fields tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context) if tracked_fields: - initial = self.read(cr, uid, ids, tracked_fields.keys(), context=context) - initial_values = dict((item['id'], item) for item in initial) + records = self.browse(cr, uid, ids, context=context) + initial_values = dict((this.id, dict((key, getattr(this, key)) for key in tracked_fields.keys())) for this in records) # Perform write, update followers result = super(mail_thread, self).write(cr, uid, ids, values, context=context) @@ -396,7 +396,7 @@ class mail_thread(osv.AbstractModel): if not value: return '' if col_info['type'] == 'many2one': - return value[1] + return value.name_get()[0][1] if col_info['type'] == 'selection': return dict(col_info['selection'])[value] return value @@ -415,23 +415,26 @@ class mail_thread(osv.AbstractModel): if not tracked_fields: return True - for record in self.read(cr, uid, ids, tracked_fields.keys(), context=context): - initial = initial_values[record['id']] - changes = [] + for browse_record in self.browse(cr, uid, ids, context=context): + initial = initial_values[browse_record.id] + changes = set() tracked_values = {} # generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} for col_name, col_info in tracked_fields.items(): - if record[col_name] == initial[col_name] and getattr(self._all_columns[col_name].column, 'track_visibility', None) == 'always': + initial_value = initial[col_name] + record_value = getattr(browse_record, col_name) + + if record_value == initial_value and getattr(self._all_columns[col_name].column, 'track_visibility', None) == 'always': tracked_values[col_name] = dict(col_info=col_info['string'], - new_value=convert_for_display(record[col_name], col_info)) - elif record[col_name] != initial[col_name]: + new_value=convert_for_display(record_value, col_info)) + elif record_value != initial_value and (record_value or initial_value): # because browse null != False if getattr(self._all_columns[col_name].column, 'track_visibility', None) in ['always', 'onchange']: tracked_values[col_name] = dict(col_info=col_info['string'], - old_value=convert_for_display(initial[col_name], col_info), - new_value=convert_for_display(record[col_name], col_info)) + old_value=convert_for_display(initial_value, col_info), + new_value=convert_for_display(record_value, col_info)) if col_name in tracked_fields: - changes.append(col_name) + changes.add(col_name) if not changes: continue @@ -441,7 +444,7 @@ class mail_thread(osv.AbstractModel): if field not in changes: continue for subtype, method in track_info.items(): - if method(self, cr, uid, record, context): + if method(self, cr, uid, browse_record, context): subtypes.append(subtype) posted = False @@ -452,11 +455,11 @@ class mail_thread(osv.AbstractModel): _logger.debug('subtype %s not found, giving error "%s"' % (subtype, e)) continue message = format_message(subtype_rec.description if subtype_rec.description else subtype_rec.name, tracked_values) - self.message_post(cr, uid, record['id'], body=message, subtype=subtype, context=context) + self.message_post(cr, uid, browse_record.id, body=message, subtype=subtype, context=context) posted = True if not posted: message = format_message('', tracked_values) - self.message_post(cr, uid, record['id'], body=message, context=context) + self.message_post(cr, uid, browse_record.id, body=message, context=context) return True #------------------------------------------------------ diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py index 91ede8b8449..148b5faba87 100644 --- a/addons/mail/tests/test_mail_features.py +++ b/addons/mail/tests/test_mail_features.py @@ -739,18 +739,21 @@ class test_mail(TestMailBase): self.ir_model_data.create(cr, uid, {'name': 'mt_private', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_private_id}) mt_name_supername_id = self.mail_message_subtype.create(cr, uid, {'name': 'name_supername', 'description': 'Supername name'}) self.ir_model_data.create(cr, uid, {'name': 'mt_name_supername', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_name_supername_id}) + mt_group_public_set_id = self.mail_message_subtype.create(cr, uid, {'name': 'group_public_set', 'description': 'Group set'}) + self.ir_model_data.create(cr, uid, {'name': 'mt_group_public_set', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_group_public_set_id}) mt_group_public_id = self.mail_message_subtype.create(cr, uid, {'name': 'group_public', 'description': 'Group changed'}) self.ir_model_data.create(cr, uid, {'name': 'mt_group_public', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_group_public_id}) # Data: alter mail_group model for testing purposes (test on classic, selection and many2one fields) self.mail_group._track = { 'public': { - 'mail.mt_private': lambda self, cr, uid, obj, ctx=None: obj['public'] == 'private', + 'mail.mt_private': lambda self, cr, uid, obj, ctx=None: obj.public == 'private', }, 'name': { - 'mail.mt_name_supername': lambda self, cr, uid, obj, ctx=None: obj['name'] == 'supername', + 'mail.mt_name_supername': lambda self, cr, uid, obj, ctx=None: obj.name == 'supername', }, 'group_public_id': { + 'mail.mt_group_public_set': lambda self, cr, uid, obj, ctx=None: obj.group_public_id, 'mail.mt_group_public': lambda self, cr, uid, obj, ctx=None: True, }, } @@ -787,21 +790,37 @@ class test_mail(TestMailBase): self.assertIn(u'Public\u2192Private', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect') self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked feature: message body does not hold always tracked field') - # Test: change public as public, group_public_id -> 1 subtype, name always tracked + # Test: change public as public, group_public_id -> 2 subtypes, name always tracked self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'public': 'public', 'group_public_id': group_system_id}) self.group_pigs.refresh() - self.assertEqual(len(self.group_pigs.message_ids), 4, 'tracked: one message should have been produced') - # Test: first produced message: mt_group_public_id, with name always tracked, public tracked on change + self.assertEqual(len(self.group_pigs.message_ids), 5, 'tracked: one message should have been produced') + # Test: first produced message: mt_group_public_set_id, with name always tracked, public tracked on change last_msg = self.group_pigs.message_ids[-4] - self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should not be linked to any subtype') + self.assertEqual(last_msg.subtype_id.id, mt_group_public_set_id, 'tracked: message should be linked to mt_group_public_set_id') + self.assertIn('Group set', last_msg.body, 'tracked: message body does not hold the subtype description') + self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field') + self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') + # Test: second produced message: mt_group_public_id, with name always tracked, public tracked on change + last_msg = self.group_pigs.message_ids[-5] + self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id') self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description') self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field') self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') + # Test: change group_public_id to False -> 1 subtype, name always tracked + self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'group_public_id': False}) + self.group_pigs.refresh() + self.assertEqual(len(self.group_pigs.message_ids), 6, 'tracked: one message should have been produced') + # Test: first produced message: mt_group_public_set_id, with name always tracked, public tracked on change + last_msg = self.group_pigs.message_ids[-6] + self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id') + self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description') + self.assertIn(u'Administration/Settings\u2192', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') + # Test: change not tracked field, no tracking message self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'description': 'Dummy'}) self.group_pigs.refresh() - self.assertEqual(len(self.group_pigs.message_ids), 4, 'tracked: No message should have been produced') + self.assertEqual(len(self.group_pigs.message_ids), 6, 'tracked: No message should have been produced') # Data: removed changes public_col.track_visibility = None diff --git a/addons/project/i18n/zh_CN.po b/addons/project/i18n/zh_CN.po index b0618c0233d..a544153f2d2 100644 --- a/addons/project/i18n/zh_CN.po +++ b/addons/project/i18n/zh_CN.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-21 17:04+0000\n" -"PO-Revision-Date: 2012-12-14 14:12+0000\n" -"Last-Translator: sum1201 \n" +"PO-Revision-Date: 2013-06-27 03:25+0000\n" +"Last-Translator: Alan \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 04:57+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-06-27 05:36+0000\n" +"X-Generator: Launchpad (build 16681)\n" #. module: project #: view:project.project:0 @@ -82,7 +82,7 @@ msgid "Start Task" msgstr "开始任务" #. module: project -#: code:addons/project/project.py:932 +#: code:addons/project/project.py:944 #, python-format msgid "Warning !" msgstr "警告!" @@ -132,8 +132,8 @@ msgid "" msgstr "保存复杂的摘要(消息数量,……等)。为了插入到看板视图,这一摘要直接是是HTML格式。" #. module: project -#: code:addons/project/project.py:432 -#: code:addons/project/project.py:1318 +#: code:addons/project/project.py:444 +#: code:addons/project/project.py:1332 #, python-format msgid "Warning!" msgstr "警告!" @@ -383,7 +383,7 @@ msgid "Assigned to" msgstr "指派到" #. module: project -#: code:addons/project/project.py:1021 +#: code:addons/project/project.py:1033 #, python-format msgid "Delegated User should be specified" msgstr "应该制定委派用户" @@ -433,7 +433,7 @@ msgstr "使用集成的协作记事簿任务" #: model:mail.message.subtype,name:project.mt_project_task_blocked #: model:mail.message.subtype,name:project.mt_task_blocked msgid "Task Blocked" -msgstr "" +msgstr "延期的任务" #. module: project #: model:process.node,note:project.process_node_opentask0 @@ -453,7 +453,7 @@ msgstr "oe_kanban_text_red" #. module: project #: model:mail.message.subtype,description:project.mt_task_blocked msgid "Task blocked" -msgstr "" +msgstr "延期的任务" #. module: project #: view:project.task:0 @@ -866,7 +866,7 @@ msgstr "公司" #. module: project #: field:project.task.type,fold:0 msgid "Folded by Default" -msgstr "" +msgstr "默认折叠" #. module: project #: field:project.task.history,date:0 @@ -1195,9 +1195,9 @@ msgid "Computed as: Time Spent + Remaining Time." msgstr "计算:花费的时间 + 剩余的时间" #. module: project -#: code:addons/project/project.py:356 -#: code:addons/project/project.py:377 -#: code:addons/project/project.py:709 +#: code:addons/project/project.py:368 +#: code:addons/project/project.py:389 +#: code:addons/project/project.py:721 #, python-format msgid "%s (copy)" msgstr "%s (副本)" @@ -1397,7 +1397,7 @@ msgid "" msgstr "做任务的估计时间.通常由项目经理在任务草稿阶段设定" #. module: project -#: code:addons/project/project.py:220 +#: code:addons/project/project.py:230 #, python-format msgid "Attachments" msgstr "附件" @@ -1447,7 +1447,7 @@ msgstr "剩余的小时数" #. module: project #: model:mail.message.subtype,description:project.mt_task_stage msgid "Stage changed" -msgstr "" +msgstr "任务阶段已改变" #. module: project #: constraint:project.task:0 @@ -1524,7 +1524,7 @@ msgid "Overpassed Tasks" msgstr "拖期任务" #. module: project -#: code:addons/project/project.py:932 +#: code:addons/project/project.py:944 #, python-format msgid "" "Child task still open.\n" @@ -1556,7 +1556,7 @@ msgstr "委派你的任务给其它用户" #. module: project #: model:mail.message.subtype,description:project.mt_task_started msgid "Task started" -msgstr "" +msgstr "已开始的任务" #. module: project #: help:project.task.reevaluate,remaining_hours:0 @@ -1585,7 +1585,7 @@ msgid "CHECK: " msgstr "CHECK: " #. module: project -#: code:addons/project/project.py:432 +#: code:addons/project/project.py:444 #, python-format msgid "You must assign members on the project '%s' !" msgstr "您必须为项目“%s”指定成员!" @@ -1811,7 +1811,7 @@ msgid "Starting Date" msgstr "开始日期" #. module: project -#: code:addons/project/project.py:398 +#: code:addons/project/project.py:410 #: model:ir.actions.act_window,name:project.open_view_project_all #: model:ir.ui.menu,name:project.menu_projects #: view:project.project:0 @@ -1972,7 +1972,7 @@ msgid "Reevaluation Task" msgstr "任务重估" #. module: project -#: code:addons/project/project.py:1318 +#: code:addons/project/project.py:1332 #, python-format msgid "Please delete the project linked with this account first." msgstr "请删除与此科目关联的项目" diff --git a/addons/project/project.py b/addons/project/project.py index cadc4d8afb8..587d60ff86f 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -574,15 +574,15 @@ class task(base_stage, osv.osv): _track = { 'state': { - 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'], - 'project.mt_task_started': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open', - 'project.mt_task_closed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', + 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj.state in ['new', 'draft'], + 'project.mt_task_started': lambda self, cr, uid, obj, ctx=None: obj.state == 'open', + 'project.mt_task_closed': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', }, 'stage_id': { - 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'done', 'open'], + 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj.state not in ['new', 'draft', 'done', 'open'], }, 'kanban_state': { # kanban state: tracked, but only block subtype - 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj['kanban_state'] == 'blocked', + 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'blocked', }, } diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 33e4fcb409c..a69d9356002 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -50,15 +50,15 @@ class project_issue(base_stage, osv.osv): _track = { 'state': { - 'project_issue.mt_issue_new': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'], - 'project_issue.mt_issue_closed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', - 'project_issue.mt_issue_started': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open', + 'project_issue.mt_issue_new': lambda self, cr, uid, obj, ctx=None: obj.state in ['new', 'draft'], + 'project_issue.mt_issue_closed': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', + 'project_issue.mt_issue_started': lambda self, cr, uid, obj, ctx=None: obj.state == 'open', }, 'stage_id': { - 'project_issue.mt_issue_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'done', 'open'], + 'project_issue.mt_issue_stage': lambda self, cr, uid, obj, ctx=None: obj.state not in ['new', 'draft', 'done', 'open'], }, 'kanban_state': { - 'project_issue.mt_issue_blocked': lambda self, cr, uid, obj, ctx=None: obj['kanban_state'] == 'blocked', + 'project_issue.mt_issue_blocked': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'blocked', }, } @@ -578,7 +578,7 @@ class project_issue(base_stage, osv.osv): if context is None: context = {} res = super(project_issue, self).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs) - if thread_id: + if thread_id and subtype: self.write(cr, SUPERUSER_ID, thread_id, {'date_action_last': time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)}, context=context) return res diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 707012fc063..77151026765 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -162,9 +162,9 @@ class purchase_order(osv.osv): ] _track = { 'state': { - 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirmed', - 'purchase.mt_rfq_approved': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'approved', - 'purchase.mt_rfq_done': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', + 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirmed', + 'purchase.mt_rfq_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'approved', + 'purchase.mt_rfq_done': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', }, } _columns = { diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 67c6f82ced3..55bd34009bf 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -34,8 +34,8 @@ class sale_order(osv.osv): _description = "Sales Order" _track = { 'state': { - 'sale.mt_order_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['manual'], - 'sale.mt_order_sent': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['sent'] + 'sale.mt_order_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state in ['manual'], + 'sale.mt_order_sent': lambda self, cr, uid, obj, ctx=None: obj.state in ['sent'] }, }