summaryrefslogtreecommitdiff
path: root/addons/google_drive/models
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/google_drive/models
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/google_drive/models')
-rw-r--r--addons/google_drive/models/__init__.py5
-rw-r--r--addons/google_drive/models/google_drive.py230
-rw-r--r--addons/google_drive/models/res_config_settings.py46
3 files changed, 281 insertions, 0 deletions
diff --git a/addons/google_drive/models/__init__.py b/addons/google_drive/models/__init__.py
new file mode 100644
index 00000000..c8728366
--- /dev/null
+++ b/addons/google_drive/models/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import res_config_settings
+from . import google_drive
diff --git a/addons/google_drive/models/google_drive.py b/addons/google_drive/models/google_drive.py
new file mode 100644
index 00000000..6e3eeb9c
--- /dev/null
+++ b/addons/google_drive/models/google_drive.py
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import ast
+import logging
+import json
+import re
+
+import requests
+import werkzeug.urls
+
+from odoo import api, fields, models
+from odoo.exceptions import RedirectWarning, UserError
+from odoo.tools.translate import _
+
+from odoo.addons.google_account.models.google_service import GOOGLE_TOKEN_ENDPOINT, TIMEOUT
+
+_logger = logging.getLogger(__name__)
+
+
+class GoogleDrive(models.Model):
+
+ _name = 'google.drive.config'
+ _description = "Google Drive templates config"
+
+ def get_google_drive_url(self, res_id, template_id):
+ self.ensure_one()
+ self = self.sudo()
+
+ model = self.model_id
+ filter_name = self.filter_id.name if self.filter_id else False
+ record = self.env[model.model].browse(res_id).read()[0]
+ record.update({
+ 'model': model.name,
+ 'filter': filter_name
+ })
+ name_gdocs = self.name_template
+ try:
+ name_gdocs = name_gdocs % record
+ except:
+ raise UserError(_("At least one key cannot be found in your Google Drive name pattern."))
+
+ attachments = self.env["ir.attachment"].search([('res_model', '=', model.model), ('name', '=', name_gdocs), ('res_id', '=', res_id)])
+ url = False
+ if attachments:
+ url = attachments[0].url
+ else:
+ url = self.copy_doc(res_id, template_id, name_gdocs, model.model).get('url')
+ return url
+
+ @api.model
+ def get_access_token(self, scope=None):
+ Config = self.env['ir.config_parameter'].sudo()
+ google_drive_refresh_token = Config.get_param('google_drive_refresh_token')
+ user_is_admin = self.env.is_admin()
+ if not google_drive_refresh_token:
+ if user_is_admin:
+ dummy, action_id = self.env['ir.model.data'].get_object_reference('base_setup', 'action_general_configuration')
+ msg = _("There is no refresh code set for Google Drive. You can set it up from the configuration panel.")
+ raise RedirectWarning(msg, action_id, _('Go to the configuration panel'))
+ else:
+ raise UserError(_("Google Drive is not yet configured. Please contact your administrator."))
+ google_drive_client_id = Config.get_param('google_drive_client_id')
+ google_drive_client_secret = Config.get_param('google_drive_client_secret')
+ #For Getting New Access Token With help of old Refresh Token
+ data = {
+ 'client_id': google_drive_client_id,
+ 'refresh_token': google_drive_refresh_token,
+ 'client_secret': google_drive_client_secret,
+ 'grant_type': "refresh_token",
+ 'scope': scope or 'https://www.googleapis.com/auth/drive'
+ }
+ headers = {"Content-type": "application/x-www-form-urlencoded"}
+ try:
+ req = requests.post(GOOGLE_TOKEN_ENDPOINT, data=data, headers=headers, timeout=TIMEOUT)
+ req.raise_for_status()
+ except requests.HTTPError:
+ if user_is_admin:
+ dummy, action_id = self.env['ir.model.data'].get_object_reference('base_setup', 'action_general_configuration')
+ msg = _("Something went wrong during the token generation. Please request again an authorization code .")
+ raise RedirectWarning(msg, action_id, _('Go to the configuration panel'))
+ else:
+ raise UserError(_("Google Drive is not yet configured. Please contact your administrator."))
+ return req.json().get('access_token')
+
+ @api.model
+ def copy_doc(self, res_id, template_id, name_gdocs, res_model):
+ google_web_base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ access_token = self.get_access_token()
+ # Copy template in to drive with help of new access token
+ request_url = "https://www.googleapis.com/drive/v2/files/%s?fields=parents/id&access_token=%s" % (template_id, access_token)
+ headers = {"Content-type": "application/x-www-form-urlencoded"}
+ try:
+ req = requests.get(request_url, headers=headers, timeout=TIMEOUT)
+ req.raise_for_status()
+ parents_dict = req.json()
+ except requests.HTTPError:
+ raise UserError(_("The Google Template cannot be found. Maybe it has been deleted."))
+
+ record_url = "Click on link to open Record in Odoo\n %s/?db=%s#id=%s&model=%s" % (google_web_base_url, self._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, access_token)
+ headers = {
+ 'Content-type': 'application/json',
+ 'Accept': 'text/plain'
+ }
+ # resp, content = Http().request(request_url, "POST", data_json, headers)
+ req = requests.post(request_url, data=json.dumps(data), headers=headers, timeout=TIMEOUT)
+ req.raise_for_status()
+ content = req.json()
+ res = {}
+ if content.get('alternateLink'):
+ res['id'] = self.env["ir.attachment"].create({
+ 'res_model': res_model,
+ 'name': name_gdocs,
+ 'res_id': res_id,
+ 'type': 'url',
+ 'url': content['alternateLink']
+ }).id
+ # Commit in order to attach the document to the current object instance, even if the permissions has not been written.
+ self._cr.commit()
+ res['url'] = content['alternateLink']
+ key = self._get_key_from_url(res['url'])
+ request_url = "https://www.googleapis.com/drive/v2/files/%s/permissions?emailMessage=This+is+a+drive+file+created+by+Odoo&sendNotificationEmails=false&access_token=%s" % (key, access_token)
+ data = {'role': 'writer', 'type': 'anyone', 'value': '', 'withLink': True}
+ try:
+ req = requests.post(request_url, data=json.dumps(data), headers=headers, timeout=TIMEOUT)
+ req.raise_for_status()
+ except requests.HTTPError:
+ raise self.env['res.config.settings'].get_config_warning(_("The permission 'reader' for 'anyone with the link' has not been written on the document"))
+ if self.env.user.email:
+ data = {'role': 'writer', 'type': 'user', 'value': self.env.user.email}
+ try:
+ requests.post(request_url, data=json.dumps(data), headers=headers, timeout=TIMEOUT)
+ except requests.HTTPError:
+ pass
+ return res
+
+ @api.model
+ def get_google_drive_config(self, res_model, res_id):
+ '''
+ 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
+ '''
+ # TO DO in master: fix my signature and my model
+ if isinstance(res_model, str):
+ res_model = self.env['ir.model'].search([('model', '=', res_model)]).id
+ if not res_id:
+ raise UserError(_("Creating google drive may only be done by one at a time."))
+ # check if a model is configured with a template
+ configs = self.search([('model_id', '=', res_model)])
+ config_values = []
+ for config in configs.sudo():
+ if config.filter_id:
+ if config.filter_id.user_id and config.filter_id.user_id.id != self.env.user.id:
+ #Private
+ continue
+ try:
+ domain = [('id', 'in', [res_id])] + ast.literal_eval(config.filter_id.domain)
+ except:
+ raise UserError(_("The document filter must not include any 'dynamic' part, so it should not be based on the current time or current user, for example."))
+ additionnal_context = ast.literal_eval(config.filter_id.context)
+ google_doc_configs = self.env[config.filter_id.model_id].with_context(**additionnal_context).search(domain)
+ if google_doc_configs:
+ config_values.append({'id': config.id, 'name': config.name})
+ else:
+ config_values.append({'id': config.id, 'name': config.name})
+ return config_values
+
+ name = fields.Char('Template Name', required=True)
+ model_id = fields.Many2one('ir.model', 'Model', required=True, ondelete='cascade')
+ model = fields.Char('Related Model', related='model_id.model', readonly=True)
+ filter_id = fields.Many2one('ir.filters', 'Filter', domain="[('model_id', '=', model)]")
+ google_drive_template_url = fields.Char('Template URL', required=True)
+ google_drive_resource_id = fields.Char('Resource Id', compute='_compute_ressource_id')
+ google_drive_client_id = fields.Char('Google Client', compute='_compute_client_id')
+ name_template = fields.Char('Google Drive Name Pattern', default='Document %(name)s', help='Choose how the new google drive will be named, on google side. Eg. gdoc_%(field_name)s', required=True)
+ active = fields.Boolean('Active', default=True)
+
+ def _get_key_from_url(self, url):
+ word = re.search("(key=|/d/)([A-Za-z0-9-_]+)", url)
+ if word:
+ return word.group(2)
+ return None
+
+ def _compute_ressource_id(self):
+ for record in self:
+ if record.google_drive_template_url:
+ word = self._get_key_from_url(record.google_drive_template_url)
+ if word:
+ record.google_drive_resource_id = word
+ else:
+ raise UserError(_("Please enter a valid Google Document URL."))
+ else:
+ record.google_drive_resource_id = False
+
+ def _compute_client_id(self):
+ google_drive_client_id = self.env['ir.config_parameter'].sudo().get_param('google_drive_client_id')
+ for record in self:
+ record.google_drive_client_id = google_drive_client_id
+
+ @api.onchange('model_id')
+ def _onchange_model_id(self):
+ if self.model_id:
+ self.model = self.model_id.model
+ else:
+ self.filter_id = False
+ self.model = False
+
+ @api.constrains('model_id', 'filter_id')
+ def _check_model_id(self):
+ if self.filter_id and self.model_id.model != self.filter_id.model_id:
+ return False
+ if self.model_id.model and self.filter_id:
+ # force an execution of the filter to verify compatibility
+ self.get_google_drive_config(self.model_id.model, 1)
+ return True
+
+ def get_google_scope(self):
+ return 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file'
diff --git a/addons/google_drive/models/res_config_settings.py b/addons/google_drive/models/res_config_settings.py
new file mode 100644
index 00000000..8107dd8d
--- /dev/null
+++ b/addons/google_drive/models/res_config_settings.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = "res.config.settings"
+
+ google_drive_authorization_code = fields.Char(string='Authorization Code', config_parameter='google_drive_authorization_code')
+ google_drive_uri = fields.Char(compute='_compute_drive_uri', string='URI', help="The URL to generate the authorization code from Google")
+ is_google_drive_token_generated = fields.Boolean(string='Refresh Token Generated')
+
+ @api.depends('google_drive_authorization_code')
+ def _compute_drive_uri(self):
+ google_drive_uri = self.env['google.service']._get_google_token_uri('drive', scope=self.env['google.drive.config'].get_google_scope())
+ for config in self:
+ config.google_drive_uri = google_drive_uri
+
+ def get_values(self):
+ res = super(ResConfigSettings, self).get_values()
+ refresh_token = self.env['ir.config_parameter'].sudo().get_param('google_drive_refresh_token', False)
+ res.update(is_google_drive_token_generated=bool(refresh_token))
+ return res
+
+ def confirm_setup_token(self):
+ params = self.env['ir.config_parameter'].sudo()
+ authorization_code_before = params.get_param('google_drive_authorization_code')
+ authorization_code = self.google_drive_authorization_code
+ if authorization_code != authorization_code_before:
+ refresh_token = (
+ self.env['google.service'].generate_refresh_token('drive', authorization_code)
+ if authorization_code else False
+ )
+ params.set_param('google_drive_refresh_token', refresh_token)
+
+ def action_setup_token(self):
+ self.ensure_one()
+ template = self.env.ref('google_drive.google_drive_auth_code_wizard')
+ return {
+ 'name': _('Set up refresh token'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'res.config.settings',
+ 'views': [(template.id, 'form')],
+ 'target': 'new',
+ }