Browse Source

Mar 15 [UPDT] : Updated 'auto_database_backup'

13.0
AjmalCybro 1 year ago
parent
commit
f627ea78f6
  1. 2
      auto_database_backup/README.rst
  2. 2
      auto_database_backup/__init__.py
  3. 7
      auto_database_backup/__manifest__.py
  4. 1
      auto_database_backup/controllers/__init__.py
  5. 33
      auto_database_backup/controllers/auto_database_backup.py
  6. 20
      auto_database_backup/doc/RELEASE_NOTES.md
  7. 888
      auto_database_backup/models/db_backup_configure.py
  8. BIN
      auto_database_backup/static/description/assets/screenshots/add.png
  9. BIN
      auto_database_backup/static/description/assets/screenshots/amazon_s3_7.png
  10. BIN
      auto_database_backup/static/description/assets/screenshots/amazon_s3_8.png
  11. BIN
      auto_database_backup/static/description/assets/screenshots/amazon_s3_pci1.png
  12. BIN
      auto_database_backup/static/description/assets/screenshots/amazon_s3_pic 2.png
  13. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3-1.png
  14. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_4.png
  15. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_5.png
  16. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_6.png
  17. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_7.png
  18. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_access.png
  19. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_pick 3.png
  20. BIN
      auto_database_backup/static/description/assets/screenshots/amazons3_signup.png
  21. BIN
      auto_database_backup/static/description/assets/screenshots/backup7.png
  22. BIN
      auto_database_backup/static/description/assets/screenshots/backup8.png
  23. BIN
      auto_database_backup/static/description/assets/screenshots/backup9.png
  24. BIN
      auto_database_backup/static/description/assets/screenshots/drive1.png
  25. BIN
      auto_database_backup/static/description/assets/screenshots/drive2.png
  26. BIN
      auto_database_backup/static/description/assets/screenshots/drive3.png
  27. BIN
      auto_database_backup/static/description/assets/screenshots/drive4.png
  28. BIN
      auto_database_backup/static/description/assets/screenshots/drive5.png
  29. BIN
      auto_database_backup/static/description/assets/screenshots/drop1.png
  30. BIN
      auto_database_backup/static/description/assets/screenshots/drop2.png
  31. BIN
      auto_database_backup/static/description/assets/screenshots/drop3.png
  32. BIN
      auto_database_backup/static/description/assets/screenshots/drop4.png
  33. BIN
      auto_database_backup/static/description/assets/screenshots/dropbox-1.png
  34. BIN
      auto_database_backup/static/description/assets/screenshots/dropbox-2.png
  35. BIN
      auto_database_backup/static/description/assets/screenshots/hero.gif
  36. BIN
      auto_database_backup/static/description/assets/screenshots/hero.png
  37. BIN
      auto_database_backup/static/description/assets/screenshots/newcloud1.png
  38. BIN
      auto_database_backup/static/description/assets/screenshots/newcloud2.png
  39. BIN
      auto_database_backup/static/description/assets/screenshots/newcloud2_ds.png
  40. BIN
      auto_database_backup/static/description/assets/screenshots/next_cloud-1.png
  41. BIN
      auto_database_backup/static/description/assets/screenshots/next_cloud2.png
  42. BIN
      auto_database_backup/static/description/assets/screenshots/next_cloud_9.png
  43. BIN
      auto_database_backup/static/description/assets/screenshots/nextcloud_4.png
  44. BIN
      auto_database_backup/static/description/assets/screenshots/nextcloud_5.png
  45. BIN
      auto_database_backup/static/description/assets/screenshots/nextcloud_6.png
  46. BIN
      auto_database_backup/static/description/assets/screenshots/nextcloud_create_3.png
  47. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive-1.png
  48. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive1.png
  49. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive2.png
  50. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive3.png
  51. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive4.png
  52. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive5.png
  53. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive6.png
  54. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive7.png
  55. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive8.png
  56. BIN
      auto_database_backup/static/description/assets/screenshots/onedrive9.png
  57. 1700
      auto_database_backup/static/description/index.html
  58. 179
      auto_database_backup/views/db_backup_configure_views.xml
  59. 22
      auto_database_backup/wizard/__init__.py
  60. 60
      auto_database_backup/wizard/dropbox_auth_code.py
  61. 27
      auto_database_backup/wizard/dropbox_auth_code_views.xml

2
auto_database_backup/README.rst

@ -22,7 +22,7 @@ Company
Credits Credits
======= =======
* Developer: (v14) Midilaj @ Cybrosys * Developer: (v14) Midilaj @ Cybrosys, Farhana Jahan PT @ Cybrosys
Contacts Contacts
======== ========

2
auto_database_backup/__init__.py

@ -19,4 +19,6 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from . import controllers
from . import models from . import models
from . import wizard

7
auto_database_backup/__manifest__.py

@ -21,7 +21,7 @@
############################################################################# #############################################################################
{ {
'name': "Automatic Database Backup", 'name': "Automatic Database Backup",
'version': '13.0.1.0.0', 'version': '13.0.1.0.1',
'summary': """Generate automatic backup of databases and store to local, 'summary': """Generate automatic backup of databases and store to local,
google drive or remote server""", google drive or remote server""",
'description': """This module has been developed for creating database 'description': """This module has been developed for creating database
@ -35,8 +35,11 @@
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'data/ir_cron_data.xml', 'data/ir_cron_data.xml',
'views/db_backup_configure_views.xml' 'views/db_backup_configure_views.xml',
'wizard/dropbox_auth_code_views.xml'
], ],
'external_dependencies': {
'python': ['dropbox', 'pyncclient', 'boto3', 'nextcloud-api-wrapper','paramiko']},
'license': 'LGPL-3', 'license': 'LGPL-3',
'images': ['static/description/banner.jpg'], 'images': ['static/description/banner.jpg'],
'installable': True, 'installable': True,

1
auto_database_backup/controllers/__init__.py

@ -0,0 +1 @@
from . import auto_database_backup

33
auto_database_backup/controllers/auto_database_backup.py

@ -0,0 +1,33 @@
import json
from odoo import http
from odoo.http import request
class OnedriveAuth(http.Controller):
"""Controller for handling authentication with OneDrive and Google Drive."""
@http.route('/onedrive/authentication', type='http', auth="public")
def oauth2callback(self, **kw):
"""
Callback function for OneDrive authentication.
:param kw: A dictionary of keyword arguments.
:return: A redirect response.
"""
state = json.loads(kw['state'])
backup_config = request.env['db.backup.configure'].sudo().browse(
state.get('backup_config_id'))
backup_config.get_onedrive_tokens(kw.get('code'))
backup_config.hide_active = True
backup_config.active = True
return http.local_redirect(state.get('url_return'))
@http.route('/google_drive/authentication', type='http', auth="public")
def gdrive_oauth2callback(self, **kw):
"""Callback function for Google Drive authentication."""
state = json.loads(kw['state'])
backup_config = request.env['db.backup.configure'].sudo().browse(
state.get('backup_config_id'))
backup_config.get_gdrive_tokens(kw.get('code'))
backup_config.hide_active = True
backup_config.active = True
return http.local_redirect(state.get('url_return'))

20
auto_database_backup/doc/RELEASE_NOTES.md

@ -4,3 +4,23 @@
#### Version 13.0.1.0.0 #### Version 13.0.1.0.0
#### ADD #### ADD
- Initial commit for Automatic Database Backup - Initial commit for Automatic Database Backup
#### 16.02.2024
#### Version 13.0.1.0.1
#### ADD
- Dropbox integration added. Backup can be stored in to dropbox.
#### 16.02.2024
#### Version 13.0.1.0.1
#### ADD
- Onedrive integration added. Backup can be stored in to onedrive.
#### 16.02.2024
#### Version 13.0.1.0.1
#### ADD
- Google Drive authentication updated.
#### 16.02.2024
#### Version 13.0.1.0.1
#### ADD
- Nextcloud and Amazon S3 integration added. Backup can be stored into Nextcloud and Amazon S3.

888
auto_database_backup/models/db_backup_configure.py

@ -21,19 +21,33 @@
############################################################################# #############################################################################
from odoo import api, fields, models, _ from odoo import api, fields, models, _
import datetime import datetime
from odoo.http import request
from odoo.service import db from odoo.service import db
from datetime import timedelta
import boto3
import errno import errno
import dropbox
import ftplib import ftplib
import json import json
import logging import logging
import nextcloud_client
import odoo import odoo
import os import os
import paramiko import paramiko
import requests import requests
from nextcloud import NextCloud
from requests.auth import HTTPBasicAuth
import tempfile import tempfile
from werkzeug import urls
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
ONEDRIVE_SCOPE = ['offline_access openid Files.ReadWrite.All']
MICROSOFT_GRAPH_END_POINT = "https://graph.microsoft.com"
GOOGLE_AUTH_ENDPOINT = 'https://accounts.google.com/o/oauth2/auth'
GOOGLE_TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token'
GOOGLE_API_BASE_URL = 'https://www.googleapis.com'
class AutoDatabaseBackup(models.Model): class AutoDatabaseBackup(models.Model):
@ -56,7 +70,11 @@ class AutoDatabaseBackup(models.Model):
('local', 'Local Storage'), ('local', 'Local Storage'),
('google_drive', 'Google Drive'), ('google_drive', 'Google Drive'),
('ftp', 'FTP'), ('ftp', 'FTP'),
('sftp', 'SFTP') ('sftp', 'SFTP'),
('dropbox', 'Dropbox'),
('onedrive', 'Onedrive'),
('next_cloud', 'Next Cloud'),
('amazon_s3', 'Amazon S3')
], string='Backup Destination', help='The location for the backup.') ], string='Backup Destination', help='The location for the backup.')
backup_path = fields.Char(string='Backup Path', backup_path = fields.Char(string='Backup Path',
help='Local storage directory path') help='Local storage directory path')
@ -71,6 +89,19 @@ class AutoDatabaseBackup(models.Model):
ftp_user = fields.Char(string='FTP User', help='FTP user name') ftp_user = fields.Char(string='FTP User', help='FTP user name')
ftp_password = fields.Char(string='FTP Password', help='FTP password') ftp_password = fields.Char(string='FTP Password', help='FTP password')
ftp_path = fields.Char(string='FTP Path', help='FTP Path') ftp_path = fields.Char(string='FTP Path', help='FTP Path')
dropbox_client_key = fields.Char(string='Dropbox Client ID', copy=False,
help='Client id of the dropbox')
dropbox_client_secret = fields.Char(string='Dropbox Client Secret',
copy=False,
help='Client secret id of the dropbox')
dropbox_refresh_token = fields.Char(string='Dropbox Refresh Token',
copy=False,
help='Refresh token for the dropbox')
is_dropbox_token_generated = fields.Boolean(
string='Dropbox Token Generated',
compute='_compute_is_dropbox_token_generated',
copy=False, help='Is the dropbox token generated or not?')
dropbox_folder = fields.Char(string='Dropbox Folder', help='Dropbox folder')
active = fields.Boolean(default=True, help='Checking the configuration' active = fields.Boolean(default=True, help='Checking the configuration'
' is active or not') ' is active or not')
save_to_drive = fields.Boolean(help='Checking whether the backup need ' save_to_drive = fields.Boolean(help='Checking whether the backup need '
@ -91,11 +122,487 @@ class AutoDatabaseBackup(models.Model):
generated_exception = fields.Char(string='Exception', generated_exception = fields.Char(string='Exception',
help='Exception Encountered while Backup ' help='Exception Encountered while Backup '
'generation') 'generation')
gdrive_refresh_token = fields.Char(string='Google drive Refresh Token',
copy=False,
help='Refresh token for google drive')
gdrive_access_token = fields.Char(string='Google Drive Access Token',
copy=False,
help='Access token for google drive')
is_google_drive_token_generated = fields.Boolean(
string='Google drive Token Generated',
compute='_compute_is_google_drive_token_generated', copy=False,
help='Google drive token generated or not')
gdrive_client_key = fields.Char(string='Google Drive Client ID', copy=False,
help='Client id of the google drive')
gdrive_client_secret = fields.Char(string='Google Drive Client Secret',
copy=False,
help='Client secret id of the google'
' drive')
gdrive_token_validity = fields.Datetime(
string='Google Drive Token Validity', copy=False,
help='Token validity of the google drive')
gdrive_redirect_uri = fields.Char(string='Google Drive Redirect URI',
compute='_compute_redirect_uri',
help='Redirect URI of the google drive')
domain = fields.Char(string='Domain Name', help="Field used to store the "
"name of a domain")
next_cloud_user_name = fields.Char(string='User Name',
help="Field used to store the user name"
" for a Nextcloud account.")
next_cloud_password = fields.Char(string='Password',
help="Field used to store the password"
" for a Nextcloud account.")
nextcloud_folder_key = fields.Char(string='Next Cloud Folder Id',
help="Field used to store the unique "
"identifier for a Nextcloud "
"folder.")
onedrive_client_key = fields.Char(string='Onedrive Client ID', copy=False,
help='Client ID of the onedrive')
onedrive_client_secret = fields.Char(string='Onedrive Client Secret',
copy=False, help='Client secret id of'
' the onedrive')
onedrive_access_token = fields.Char(string='Onedrive Access Token',
copy=False,
help='Access token for one drive')
onedrive_refresh_token = fields.Char(string='Onedrive Refresh Token',
copy=False,
help='Refresh token for one drive')
onedrive_token_validity = fields.Datetime(string='Onedrive Token Validity',
copy=False,
help='Token validity date')
onedrive_folder_key = fields.Char(string='Folder ID',
help='Folder id of the onedrive')
is_onedrive_token_generated = fields.Boolean(
string='onedrive Tokens Generated',
compute='_compute_is_onedrive_token_generated',
copy=False, help='Whether to generate onedrive token?')
onedrive_redirect_uri = fields.Char(string='Onedrive Redirect URI',
compute='_compute_redirect_uri',
help='Redirect URI of the onedrive')
aws_access_key = fields.Char(string="Amazon S3 Access Key",
help="Field used to store the Access Key"
" for an Amazon S3 bucket.")
aws_secret_access_key = fields.Char(string='Amazon S3 Secret Key',
help="Field used to store the Secret"
" Key for an Amazon S3 bucket.")
bucket_file_name = fields.Char(string='Bucket Name',
help="Field used to store the name of an"
" Amazon S3 bucket.")
aws_folder_name = fields.Char(string='File Name',
help="field used to store the name of a"
" folder in an Amazon S3 bucket.")
gdrive_backup_error_test = fields.Boolean(string="Google Drive Error Test")
onedrive_backup_error_test = fields.Boolean(string="OneDrive Error Test")
@api.depends('gdrive_access_token', 'gdrive_refresh_token')
def _compute_is_google_drive_token_generated(self):
"""Set True if the Google Drive refresh token is generated"""
for rec in self:
rec.is_google_drive_token_generated = bool(
rec.gdrive_access_token) and bool(rec.gdrive_refresh_token)
@api.onchange('backup_destination')
def _onchange_backup_destination(self):
self.write({
"gdrive_backup_error_test": False,
"onedrive_backup_error_test": False
})
@api.onchange('gdrive_client_key', 'gdrive_client_secret',
'google_drive_folder', 'onedrive_client_key',
'onedrive_client_secret', 'onedrive_folder_key')
def _onchange_gdrive_backup_error_test(self):
if self.backup_destination == 'google_drive':
if self.gdrive_backup_error_test:
self.write({
"gdrive_backup_error_test": False
})
if self.backup_destination == 'onedrive':
if self.onedrive_backup_error_test:
self.write({
"onedrive_backup_error_test": False
})
def action_s3cloud(self):
"""If it has aws_secret_access_key, which will perform s3_cloud
operations for connection test"""
if self.aws_access_key and self.aws_secret_access_key:
try:
bo3 = boto3.client(
's3',
aws_access_key_id=self.aws_access_key,
aws_secret_access_key=self.aws_secret_access_key)
response = bo3.list_buckets()
for bucket in response['Buckets']:
if self.bucket_file_name == bucket['Name']:
self.active = True
self.hide_active = True
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'success',
'title': _("Connection Test Succeeded!"),
'message': _(
"Everything seems properly set up!"),
'sticky': False,
}
}
raise UserError(
_("Bucket not found. Please check the bucket name and"
" try again."))
except Exception:
self.active = False
self.hide_active = False
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'danger',
'title': _("Connection Test Failed!"),
'message': _("An error occurred while testing the "
"connection."),
'sticky': False,
}
}
def action_nextcloud(self):
"""If it has next_cloud_password, domain, and next_cloud_user_name
which will perform an action for nextcloud connection test"""
if self.domain and self.next_cloud_password and \
self.next_cloud_user_name:
try:
ncx = NextCloud(self.domain,
auth=HTTPBasicAuth(self.next_cloud_user_name,
self.next_cloud_password))
data = ncx.list_folders('/').__dict__
if data['raw'].status_code == 207:
self.active = True
self.hide_active = True
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'success',
'title': _("Connection Test Succeeded!"),
'message': _("Everything seems properly set up!"),
'sticky': False,
}
}
else:
self.active = False
self.hide_active = False
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'danger',
'title': _("Connection Test Failed!"),
'message': _("An error occurred while testing the "
"connection."),
'sticky': False,
}
}
except Exception:
self.active = False
self.hide_active = False
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'danger',
'title': _("Connection Test Failed!"),
'message': _("An error occurred while testing the "
"connection."),
'sticky': False,
}
}
def _compute_redirect_uri(self):
"""Compute the redirect URI for onedrive and Google Drive"""
for rec in self:
base_url = request.env['ir.config_parameter'].get_param(
'web.base.url')
rec.onedrive_redirect_uri = base_url + '/onedrive/authentication'
rec.gdrive_redirect_uri = base_url + '/google_drive/authentication'
@api.depends('onedrive_access_token', 'onedrive_refresh_token')
def _compute_is_onedrive_token_generated(self):
"""Set true if onedrive tokens are generated"""
for rec in self:
rec.is_onedrive_token_generated = bool(
rec.onedrive_access_token) and bool(rec.onedrive_refresh_token)
@api.depends('dropbox_refresh_token')
def _compute_is_dropbox_token_generated(self):
"""Set True if the dropbox refresh token is generated"""
for rec in self:
rec.is_dropbox_token_generated = bool(rec.dropbox_refresh_token)
def action_get_dropbox_auth_code(self):
"""Open a wizards to set up dropbox Authorization code"""
return {
'type': 'ir.actions.act_window',
'name': 'Dropbox Authorization Wizard',
'res_model': 'dropbox.auth.code',
'view_mode': 'form',
'target': 'new',
'context': {'dropbox_auth': True}
}
def action_get_onedrive_auth_code(self):
"""Generate onedrive authorization code"""
AUTHORITY = \
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
action = self.env.ref(
"auto_database_backup.db_backup_configure_action")
action_data = {
'id': action.id,
'name': action.name,
'type': action.type,
'xml_id': action.xml_id,
'help': action.help,
'binding_model_id': action.binding_model_id,
'binding_type': action.binding_type,
'binding_view_types': action.binding_view_types,
'display_name': action.display_name,
'res_model': action.res_model,
'target': action.target,
'view_mode': action.view_mode,
'views': action.views,
'groups_id': [(6, 0, action.groups_id.ids)],
'search_view_id': action.search_view_id.id if action.search_view_id else False,
'filter': action.filter,
'search_view': action.search_view,
'limit': action.limit,
}
base_url = request.env['ir.config_parameter'].get_param('web.base.url')
url_return = base_url + \
'/web#id=%d&action=%d&view_type=form&model=%s' % (
self.id, action_data['id'], 'db.backup.configure')
state = {
'backup_config_id': self.id,
'url_return': url_return
}
encoded_params = urls.url_encode({
'response_type': 'code',
'client_id': self.onedrive_client_key,
'state': json.dumps(state),
'scope': ONEDRIVE_SCOPE,
'redirect_uri': base_url + '/onedrive/authentication',
'prompt': 'consent',
'access_type': 'offline'
})
auth_url = "%s?%s" % (AUTHORITY, encoded_params)
return {
'type': 'ir.actions.act_url',
'target': 'self',
'url': auth_url,
}
def action_get_gdrive_auth_code(self):
"""Generate google drive authorization code"""
action = self.env.ref(
"auto_database_backup.db_backup_configure_action")
action_data = {
'id': action.id,
'name': action.name,
'type': action.type,
'xml_id': action.xml_id,
'help': action.help,
'binding_model_id': action.binding_model_id,
'binding_type': action.binding_type,
'binding_view_types': action.binding_view_types,
'display_name': action.display_name,
'res_model': action.res_model,
'target': action.target,
'view_mode': action.view_mode,
'views': action.views,
'groups_id': [(6, 0, action.groups_id.ids)],
'search_view_id': action.search_view_id.id if action.search_view_id else False,
'filter': action.filter,
'search_view': action.search_view,
'limit': action.limit,
}
base_url = request.env['ir.config_parameter'].get_param('web.base.url')
url_return = base_url + \
'/web#id=%d&action=%d&view_type=form&model=%s' % (
self.id, action_data['id'], 'db.backup.configure')
state = {
'backup_config_id': self.id,
'url_return': url_return
}
encoded_params = urls.url_encode({
'response_type': 'code',
'client_id': self.gdrive_client_key,
'scope': 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file',
'redirect_uri': base_url + '/google_drive/authentication',
'access_type': 'offline',
'state': json.dumps(state),
'approval_prompt': 'force',
})
auth_url = "%s?%s" % (GOOGLE_AUTH_ENDPOINT, encoded_params)
return {
'type': 'ir.actions.act_url',
'target': 'self',
'url': auth_url,
}
def get_gdrive_tokens(self, authorize_code):
"""Generate onedrive tokens from authorization code."""
base_url = request.env['ir.config_parameter'].get_param('web.base.url')
headers = {"content-type": "application/x-www-form-urlencoded"}
data = {
'code': authorize_code,
'client_id': self.gdrive_client_key,
'client_secret': self.gdrive_client_secret,
'grant_type': 'authorization_code',
'redirect_uri': base_url + '/google_drive/authentication'
}
try:
res = requests.post(GOOGLE_TOKEN_ENDPOINT, params=data,
headers=headers)
res.raise_for_status()
response = res.content and res.json() or {}
if response:
expires_in = response.get('expires_in')
self.write({
'gdrive_access_token': response.get('access_token'),
'gdrive_refresh_token': response.get('refresh_token'),
'gdrive_token_validity': fields.Datetime.now() + timedelta(
seconds=expires_in) if expires_in else False,
})
if self.gdrive_backup_error_test:
self.write({
'gdrive_backup_error_test': False
})
except Exception:
if not self.gdrive_backup_error_test:
self.write({"gdrive_backup_error_test": True})
def generate_onedrive_refresh_token(self):
"""Generate onedrive access token from refresh token if expired"""
base_url = request.env['ir.config_parameter'].get_param('web.base.url')
headers = {"Content-type": "application/x-www-form-urlencoded"}
data = {
'client_id': self.onedrive_client_key,
'client_secret': self.onedrive_client_secret,
'scope': ONEDRIVE_SCOPE,
'grant_type': "refresh_token",
'redirect_uri': base_url + '/onedrive/authentication',
'refresh_token': self.onedrive_refresh_token
}
try:
res = requests.post(
"https://login.microsoftonline.com/common/oauth2/v2.0/token",
data=data, headers=headers)
res.raise_for_status()
response = res.content and res.json() or {}
if response:
expires_in = response.get('expires_in')
self.write({
'onedrive_access_token': response.get('access_token'),
'onedrive_refresh_token': response.get('refresh_token'),
'onedrive_token_validity': fields.Datetime.now() + timedelta(
seconds=expires_in) if expires_in else False,
})
except requests.HTTPError as error:
_logger.exception("Bad microsoft onedrive request : %s !",
error.response.content)
raise error
def get_onedrive_tokens(self, authorize_code):
"""Generate onedrive tokens from authorization code."""
headers = {"content-type": "application/x-www-form-urlencoded"}
base_url = request.env['ir.config_parameter'].get_param('web.base.url')
data = {
'code': authorize_code,
'client_id': self.onedrive_client_key,
'client_secret': self.onedrive_client_secret,
'grant_type': 'authorization_code',
'scope': ONEDRIVE_SCOPE,
'redirect_uri': base_url + '/onedrive/authentication'
}
try:
res = requests.post(
"https://login.microsoftonline.com/common/oauth2/v2.0/token",
data=data, headers=headers)
res.raise_for_status()
response = res.content and res.json() or {}
if response:
expires_in = response.get('expires_in')
self.write({
'onedrive_access_token': response.get('access_token'),
'onedrive_refresh_token': response.get('refresh_token'),
'onedrive_token_validity': fields.Datetime.now() + timedelta(
seconds=expires_in) if expires_in else False,
})
if self.onedrive_backup_error_test:
self.write({
'onedrive_backup_error_test': False
})
except Exception:
if not self.onedrive_backup_error_test:
self.write({"onedrive_backup_error_test": True})
def generate_gdrive_refresh_token(self):
"""Generate Google Drive access token from refresh token if expired"""
headers = {"content-type": "application/x-www-form-urlencoded"}
data = {
'refresh_token': self.gdrive_refresh_token,
'client_id': self.gdrive_client_key,
'client_secret': self.gdrive_client_secret,
'grant_type': 'refresh_token',
}
try:
res = requests.post(GOOGLE_TOKEN_ENDPOINT, data=data,
headers=headers)
res.raise_for_status()
response = res.content and res.json() or {}
if response:
expires_in = response.get('expires_in')
self.write({
'gdrive_access_token': response.get('access_token'),
'gdrive_token_validity': fields.Datetime.now() + timedelta(
seconds=expires_in) if expires_in else False,
})
except requests.HTTPError as error:
error_key = error.response.json().get("error", "nc")
error_msg = _(
"An error occurred while generating the token. Your"
"authorization code may be invalid or has already expired [%s]."
"You should check your Client ID and secret on the Google APIs"
" platform or try to stop and restart your calendar"
" synchronisation.",
error_key)
raise UserError(error_msg)
def get_dropbox_auth_url(self):
"""Return dropbox authorization url"""
dbx_auth = dropbox.oauth.DropboxOAuth2FlowNoRedirect(
self.dropbox_client_key,
self.dropbox_client_secret,
token_access_type='offline')
return dbx_auth.start()
def set_dropbox_refresh_token(self, auth_code):
"""Generate and set the dropbox refresh token from authorization code"""
try:
dbx_auth = dropbox.oauth.DropboxOAuth2FlowNoRedirect(
self.dropbox_client_key,
self.dropbox_client_secret,
token_access_type='offline')
outh_result = dbx_auth.finish(auth_code)
self.dropbox_refresh_token = outh_result.refresh_token
except Exception:
raise ValidationError(
'Please Enter Valid Authentication Code')
@api.constrains('db_name', 'master_pwd') @api.constrains('db_name', 'master_pwd')
def _check_db_credentials(self): def _check_db_credentials(self):
""" """
Validate enetered database name and master password Validate entered database name and master password
""" """
database_list = db.list_dbs() database_list = db.list_dbs()
if self.db_name not in database_list: if self.db_name not in database_list:
@ -119,8 +626,12 @@ class AutoDatabaseBackup(models.Model):
port=self.sftp_port) port=self.sftp_port)
sftp = client.open_sftp() sftp = client.open_sftp()
sftp.close() sftp.close()
except Exception as e: except Exception:
raise UserError(_("SFTP Exception: %s", e)) raise UserError(
_("It seems there was an issue with the connection, "
"possibly due to incorrect information provided. "
"Please double-check all the information you provided "
"for the connection to ensure it is correct."))
finally: finally:
client.close() client.close()
elif self.backup_destination == 'ftp': elif self.backup_destination == 'ftp':
@ -129,16 +640,18 @@ class AutoDatabaseBackup(models.Model):
ftp_server.connect(self.ftp_host, int(self.ftp_port)) ftp_server.connect(self.ftp_host, int(self.ftp_port))
ftp_server.login(self.ftp_user, self.ftp_password) ftp_server.login(self.ftp_user, self.ftp_password)
ftp_server.quit() ftp_server.quit()
except Exception as e: except Exception:
raise UserError(_("FTP Exception: %s", e)) raise UserError(
title = _("Connection Test Succeeded!") _("It seems there was an issue with the connection, "
message = _("Everything seems properly set up!") "possibly due to incorrect information provided. "
"Please double-check all the information you provided "
"for the connection to ensure it is correct."))
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'display_notification', 'tag': 'display_notification',
'params': { 'params': {
'title': title, 'title': _("Connection Test Succeeded!"),
'message': message, 'message': _("Everything seems properly set up!"),
'sticky': False, 'sticky': False,
} }
} }
@ -270,57 +783,314 @@ class AutoDatabaseBackup(models.Model):
client.close() client.close()
# Google Drive backup # Google Drive backup
elif rec.backup_destination == 'google_drive': elif rec.backup_destination == 'google_drive':
temp = tempfile.NamedTemporaryFile(suffix='.%s' %
rec.backup_format)
with open(temp.name, "wb+") as tmp:
odoo.service.db.dump_db(rec.db_name, tmp, rec.backup_format)
try: try:
access_token = self.env['google.drive.config'].sudo(). \ if rec.gdrive_token_validity <= fields.Datetime.now():
get_access_token() rec.generate_gdrive_refresh_token()
headers = {"Authorization": "Bearer %s" % access_token} temp = tempfile.NamedTemporaryFile(
para = { suffix='.%s' % rec.backup_format)
"name": backup_filename, with open(temp.name, "wb+") as tmp:
"parents": [rec.google_drive_folder], odoo.service.db.dump_db(rec.db_name, tmp,
} rec.backup_format)
files = { try:
'data': ('metadata', json.dumps(para), headers = {
'application/json; charset=UTF-8'), "Authorization": "Bearer %s" % rec.gdrive_access_token}
'file': open(temp.name, "rb") para = {
} "name": backup_filename,
requests.post( "parents": [rec.google_drive_folder],
"https://www.googleapis.com/upload/drive/v3/" }
"files?uploadType=multipart", files = {
headers=headers, 'data': ('metadata', json.dumps(para),
files=files 'application/json; charset=UTF-8'),
) 'file': open(temp.name, "rb")
if rec.auto_remove: }
query = "parents = '%s'" % rec.google_drive_folder requests.post(
files_req = requests.get( "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
"https://www.googleapis.com/drive/v3/files?q=%s" % headers=headers,
query, files=files
headers=headers) )
files = files_req.json()['files'] if rec.auto_remove:
for file in files: query = "parents = '%s'" % rec.google_drive_folder_key
file_date_req = requests.get( files_req = requests.get(
"https://www.googleapis.com/drive/v3/files/%s?" "https://www.googleapis.com/drive/v3/files?q=%s" % query,
"fields=createdTime" % headers=headers)
file['id'], headers=headers) files = files_req.json()['files']
create_time = file_date_req.json()['createdTime'][ for file in files:
:19].replace('T', ' ') file_date_req = requests.get(
diff_days = ( "https://www.googleapis.com/drive/v3/files/%s?fields=createdTime" %
datetime.datetime.now() - datetime.
datetime.strptime(create_time,
'%Y-%m-%d %H:%M:%S'))\
.days
if diff_days >= rec.days_to_remove:
requests.delete(
"https://www.googleapis.com/drive/"
"v3/files/%s" %
file['id'], headers=headers) file['id'], headers=headers)
create_time = file_date_req.json()[
'createdTime'][
:19].replace('T', ' ')
diff_days = (
fields.datetime.now() - fields.datetime.strptime(
create_time, '%Y-%m-%d %H:%M:%S')).days
if diff_days >= rec.days_to_remove:
requests.delete(
"https://www.googleapis.com/drive/v3/files/%s" %
file['id'], headers=headers)
if rec.notify_user:
mail_template_success.send_mail(rec.id,
force_send=True)
except Exception as e:
rec.generated_exception = e
_logger.info('Google Drive Exception: %s', e)
if rec.notify_user:
mail_template_failed.send_mail(rec.id,
force_send=True)
except Exception:
if rec.notify_user: if rec.notify_user:
mail_template_success.send_mail(rec.id, force_send=True) mail_template_failed.send_mail(rec.id, force_send=True)
except Exception as e: raise ValidationError(
rec.generated_exception = e 'Please check the credentials before activation')
_logger.info('Google Drive Exception: %s', e) else:
raise ValidationError('Please check connection')
# Dropbox backup
elif rec.backup_destination == 'dropbox':
try:
temp = tempfile.NamedTemporaryFile(
suffix='.%s' % rec.backup_format)
with open(temp.name, "wb+") as tmp:
odoo.service.db.dump_db(rec.db_name, tmp,
rec.backup_format)
try:
dbx = dropbox.Dropbox(
app_key=rec.dropbox_client_key,
app_secret=rec.dropbox_client_secret,
oauth2_refresh_token=rec.dropbox_refresh_token)
dropbox_destination = (rec.dropbox_folder + '/' +
backup_filename)
dbx.files_upload(temp.read(), dropbox_destination)
if rec.auto_remove:
files = dbx.files_list_folder(
rec.dropbox_folder)
file_entries = files.entries
expired_files = list(filter(
lambda fl: (fields.datetime.now() -
fl.client_modified).days >=
rec.days_to_remove,
file_entries))
for file in expired_files:
dbx.files_delete_v2(file.path_display)
if rec.notify_user:
mail_template_success.send_mail(rec.id,
force_send=True)
except Exception as error:
rec.generated_exception = error
_logger.info('Dropbox Exception: %s', error)
if rec.notify_user:
mail_template_failed.send_mail(rec.id,
force_send=True)
except Exception:
if rec.notify_user: if rec.notify_user:
mail_template_failed.send_mail(rec.id, force_send=True) mail_template_failed.send_mail(rec.id, force_send=True)
raise ValidationError(
'Please check the credentials before activation')
else:
raise ValidationError('Please check connection')
# Onedrive Backup
elif rec.backup_destination == 'onedrive':
try:
if rec.onedrive_token_validity <= fields.Datetime.now():
rec.generate_onedrive_refresh_token()
temp = tempfile.NamedTemporaryFile(
suffix='.%s' % rec.backup_format)
with open(temp.name, "wb+") as tmp:
odoo.service.db.dump_db(rec.db_name, tmp,
rec.backup_format)
headers = {
'Authorization': 'Bearer %s' % rec.onedrive_access_token,
'Content-Type': 'application/json'}
upload_session_url = MICROSOFT_GRAPH_END_POINT + "/v1.0/me/drive/items/%s:/%s:/createUploadSession" % (
rec.onedrive_folder_key, backup_filename)
try:
upload_session = requests.post(upload_session_url,
headers=headers)
upload_url = upload_session.json().get('uploadUrl')
requests.put(upload_url, data=temp.read())
if rec.auto_remove:
list_url = MICROSOFT_GRAPH_END_POINT + "/v1.0/me/drive/items/%s/children" % rec.onedrive_folder_key
response = requests.get(list_url, headers=headers)
files = response.json().get('value')
for file in files:
create_time = file['createdDateTime'][
:19].replace(
'T',
' ')
diff_days = (
fields.datetime.now() - fields.datetime.strptime(
create_time, '%Y-%m-%d %H:%M:%S')).days
if diff_days >= rec.days_to_remove:
delete_url = MICROSOFT_GRAPH_END_POINT + "/v1.0/me/drive/items/%s" % \
file['id']
requests.delete(delete_url, headers=headers)
if rec.notify_user:
mail_template_success.send_mail(rec.id,
force_send=True)
except Exception as error:
rec.generated_exception = error
_logger.info('Onedrive Exception: %s', error)
if rec.notify_user:
mail_template_failed.send_mail(rec.id,
force_send=True)
except Exception:
if rec.notify_user:
mail_template_failed.send_mail(rec.id, force_send=True)
raise ValidationError(
'Please check the credentials before activation')
else:
raise ValidationError('Please check connection')
# NextCloud Backup
elif rec.backup_destination == 'next_cloud':
try:
if rec.domain and rec.next_cloud_password and \
rec.next_cloud_user_name:
try:
# Connect to NextCloud using the provided username
# and password
ncx = NextCloud(rec.domain,
auth=HTTPBasicAuth(
rec.next_cloud_user_name,
rec.next_cloud_password))
# Connect to NextCloud again to perform additional
# operations
nc = nextcloud_client.Client(rec.domain)
nc.login(rec.next_cloud_user_name,
rec.next_cloud_password)
# Get the folder name from the NextCloud folder ID
folder_name = rec.nextcloud_folder_key
# If auto_remove is enabled, remove backup files
# older than specified days
if rec.auto_remove:
folder_path = "/" + folder_name
for item in nc.list(folder_path):
backup_file_name = item.path.split("/")[-1]
backup_date_str = \
backup_file_name.split("_")[
2]
backup_date = fields.datetime.strptime(
backup_date_str, '%Y-%m-%d').date()
if (fields.date.today() - backup_date).days \
>= rec.days_to_remove:
nc.delete(item.path)
# If notify_user is enabled, send a success email
# notification
if rec.notify_user:
mail_template_success.send_mail(rec.id,
force_send=True)
except Exception as error:
rec.generated_exception = error
_logger.info('NextCloud Exception: %s', error)
if rec.notify_user:
# If an exception occurs, send a failed email
# notification
mail_template_failed.send_mail(rec.id,
force_send=True)
# Get the list of folders in the root directory of NextCloud
data = ncx.list_folders('/').__dict__
folders = [
[file_name['href'].split('/')[-2],
file_name['file_id']]
for file_name in data['data'] if
file_name['href'].endswith('/')]
# If the folder name is not found in the list of folders,
# create the folder
if folder_name not in [file[0] for file in folders]:
nc.mkdir(folder_name)
# Dump the database to a temporary file
temp = tempfile.NamedTemporaryFile(
suffix='.%s' % rec.backup_format)
with open(temp.name, "wb+") as tmp:
odoo.service.db.dump_db(rec.db_name, tmp,
rec.backup_format)
backup_file_path = temp.name
remote_file_path = f"/{folder_name}/{rec.db_name}_" \
f"{backup_time}.{rec.backup_format}"
nc.put_file(remote_file_path, backup_file_path)
else:
# Dump the database to a temporary file
temp = tempfile.NamedTemporaryFile(
suffix='.%s' % rec.backup_format)
with open(temp.name, "wb+") as tmp:
odoo.service.db.dump_db(rec.db_name, tmp,
rec.backup_format)
backup_file_path = temp.name
remote_file_path = f"/{folder_name}/{rec.db_name}_" \
f"{backup_time}.{rec.backup_format}"
nc.put_file(remote_file_path, backup_file_path)
except Exception:
raise ValidationError('Please check connection')
# Amazon S3 Backup
elif rec.backup_destination == 'amazon_s3':
if rec.aws_access_key and rec.aws_secret_access_key:
try:
# Create a boto3 client for Amazon S3 with provided
# access key id and secret access key
bo3 = boto3.client(
's3',
aws_access_key_id=rec.aws_access_key,
aws_secret_access_key=rec.aws_secret_access_key)
# If auto_remove is enabled, remove the backups that
# are older than specified days from the S3 bucket
if rec.auto_remove:
folder_path = rec.aws_folder_name
response = bo3.list_objects(
Bucket=rec.bucket_file_name,
Prefix=folder_path)
today = fields.date.today()
for file in response['Contents']:
file_path = file['Key']
last_modified = file['LastModified']
date = last_modified.date()
age_in_days = (today - date).days
if age_in_days >= rec.days_to_remove:
bo3.delete_object(
Bucket=rec.bucket_file_name,
Key=file_path)
# Create a boto3 resource for Amazon S3 with provided
# access key id and secret access key
s3 = boto3.resource(
's3',
aws_access_key_id=rec.aws_access_key,
aws_secret_access_key=rec.aws_secret_access_key)
# Create a folder in the specified bucket, if it
# doesn't already exist
s3.Object(rec.bucket_file_name,
rec.aws_folder_name + '/').put()
bucket = s3.Bucket(rec.bucket_file_name)
# Get all the prefixes in the bucket
prefixes = set()
for obj in bucket.objects.all():
key = obj.key
if key.endswith('/'):
prefix = key[:-1] # Remove the trailing slash
prefixes.add(prefix)
# If the specified folder is present in the bucket,
# take a backup of the database and upload it to the
# S3 bucket
if rec.aws_folder_name in prefixes:
temp = tempfile.NamedTemporaryFile(
suffix='.%s' % rec.backup_format)
with open(temp.name, "wb+") as tmp:
odoo.service.db.dump_db(rec.db_name, tmp,
rec.backup_format)
backup_file_path = temp.name
remote_file_path = f"{rec.aws_folder_name}/{rec.db_name}_" \
f"{backup_time}.{rec.backup_format}"
s3.Object(rec.bucket_file_name,
remote_file_path).upload_file(
backup_file_path)
# If notify_user is enabled, send email to the
# user notifying them about the successful backup
if rec.notify_user:
mail_template_success.send_mail(rec.id,
force_send=True)
except Exception as error:
# If any error occurs, set the 'generated_exception'
# field to the error message and log the error
rec.generated_exception = error
_logger.info('Amazon S3 Exception: %s', error)
# If notify_user is enabled, send email to the user
# notifying them about the failed backup
if rec.notify_user:
mail_template_failed.send_mail(rec.id,
force_send=True)

BIN
auto_database_backup/static/description/assets/screenshots/add.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazon_s3_7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazon_s3_8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazon_s3_pci1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazon_s3_pic 2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_access.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_pick 3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
auto_database_backup/static/description/assets/screenshots/amazons3_signup.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
auto_database_backup/static/description/assets/screenshots/backup7.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 77 KiB

BIN
auto_database_backup/static/description/assets/screenshots/backup8.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 39 KiB

BIN
auto_database_backup/static/description/assets/screenshots/backup9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 63 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drive1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drive2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drive3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drive4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drive5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drop1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drop2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drop3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
auto_database_backup/static/description/assets/screenshots/drop4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
auto_database_backup/static/description/assets/screenshots/dropbox-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
auto_database_backup/static/description/assets/screenshots/dropbox-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
auto_database_backup/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
auto_database_backup/static/description/assets/screenshots/hero.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

BIN
auto_database_backup/static/description/assets/screenshots/newcloud1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
auto_database_backup/static/description/assets/screenshots/newcloud2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
auto_database_backup/static/description/assets/screenshots/newcloud2_ds.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
auto_database_backup/static/description/assets/screenshots/next_cloud-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
auto_database_backup/static/description/assets/screenshots/next_cloud2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
auto_database_backup/static/description/assets/screenshots/next_cloud_9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
auto_database_backup/static/description/assets/screenshots/nextcloud_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

BIN
auto_database_backup/static/description/assets/screenshots/nextcloud_5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
auto_database_backup/static/description/assets/screenshots/nextcloud_6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
auto_database_backup/static/description/assets/screenshots/nextcloud_create_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
auto_database_backup/static/description/assets/screenshots/onedrive9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

1700
auto_database_backup/static/description/index.html

File diff suppressed because it is too large

179
auto_database_backup/views/db_backup_configure_views.xml

@ -20,6 +20,8 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<sheet> <sheet>
<field name="gdrive_backup_error_test" invisible="1"/>
<field name="onedrive_backup_error_test" invisible="1"/>
<div class="oe_title"> <div class="oe_title">
<h1> <h1>
<field name="name" placeholder="Name..."/> <field name="name" placeholder="Name..."/>
@ -34,12 +36,6 @@
</group> </group>
<group> <group>
<field name="backup_destination" required="1"/> <field name="backup_destination" required="1"/>
<div class="text-muted"
attrs="{'invisible': [('backup_destination', '!=', 'google_drive')]}">
Enable and
configure Google Drive option from General
Settings
</div>
<field name="backup_path" <field name="backup_path"
attrs="{'invisible': [('backup_destination', '!=', 'local')], 'required': [('backup_destination', '=', 'local')]}"/> attrs="{'invisible': [('backup_destination', '!=', 'local')], 'required': [('backup_destination', '=', 'local')]}"/>
<field name="ftp_host" <field name="ftp_host"
@ -64,8 +60,69 @@
password="True"/> password="True"/>
<field name="sftp_path" <field name="sftp_path"
attrs="{'invisible': [('backup_destination', '!=', 'sftp')], 'required': [('backup_destination', '=', 'sftp')]}"/> attrs="{'invisible': [('backup_destination', '!=', 'sftp')], 'required': [('backup_destination', '=', 'sftp')]}"/>
<field name="google_drive_folderid" <field name="gdrive_client_key" string="Client ID"
attrs="{'invisible': [('backup_destination', '!=', 'google_drive')], 'required': [('backup_destination', '=', 'google_drive')]}"/>
<field name="gdrive_client_secret"
string="Client Secret"
attrs="{'invisible': [('backup_destination', '!=', 'google_drive')], 'required': [('backup_destination', '=', 'google_drive')]}"
password="True"/>
<field name="gdrive_redirect_uri"
string="Redirect URI"
attrs="{'invisible': [('backup_destination', '!=', 'google_drive')]}"/>
<field name="gdrive_access_token" password="True"
invisible="1"/>
<field name="gdrive_refresh_token" password="True"
invisible="1"/>
<field name="gdrive_token_validity" invisible="1"/>
<field name="google_drive_folder"
attrs="{'invisible': [('backup_destination', '!=', 'google_drive')], 'required': [('backup_destination', '=', 'google_drive')]}"/> attrs="{'invisible': [('backup_destination', '!=', 'google_drive')], 'required': [('backup_destination', '=', 'google_drive')]}"/>
<field name="is_google_drive_token_generated"
invisible="1"/>
<field name="domain" string="Domain Name"
attrs="{'invisible': [('backup_destination', '!=', 'next_cloud')]}"/>
<field name="next_cloud_user_name"
string="User Name"
attrs="{'invisible': [('backup_destination', '!=', 'next_cloud')]}"/>
<field name="next_cloud_password" string="Password"
attrs="{'invisible': [('backup_destination', '!=', 'next_cloud')]}"/>
<field name="nextcloud_folder_key" string="Folder ID"
attrs="{'invisible': [('backup_destination', '!=', 'next_cloud')]}"/>
<field name="aws_access_key"
attrs="{'invisible': [('backup_destination', '!=', 'amazon_s3')]}"/>
<field name="aws_secret_access_key"
attrs="{'invisible': [('backup_destination', '!=', 'amazon_s3')]}"/>
<field name="bucket_file_name"
attrs="{'invisible': [('backup_destination', '!=', 'amazon_s3')]}"/>
<field name="aws_folder_name"
attrs="{'invisible': [('backup_destination', '!=', 'amazon_s3')]}"/>
<field name="onedrive_client_key" string="Client ID"
attrs="{'invisible': [('backup_destination', '!=', 'onedrive')], 'required': [('backup_destination', '=', 'onedrive')]}"/>
<field name="onedrive_client_secret"
string="Client Secret"
attrs="{'invisible': [('backup_destination', '!=', 'onedrive')], 'required': [('backup_destination', '=', 'onedrive')]}"
password="True"/>
<field name="onedrive_redirect_uri"
string="Redirect URI"
attrs="{'invisible': [('backup_destination', '!=', 'onedrive')]}"/>
<field name="onedrive_folder_key" string="Folder ID"
attrs="{'invisible': [('backup_destination', '!=', 'onedrive')], 'required': [('backup_destination', '=', 'onedrive')]}"/>
<field name="onedrive_access_token"
string="Access Token"
invisible="1" password="True"/>
<field name="onedrive_refresh_token"
string="Refresh Token" invisible="1"
password="True"/>
<field name="onedrive_token_validity"
string="Token Validity" invisible="1"/>
<field name="is_onedrive_token_generated"
invisible="1"/>
<field name="dropbox_client_key" string="App Key"
attrs="{'invisible': [('backup_destination', '!=', 'dropbox')], 'required': [('backup_destination', '=', 'dropbox')]}"
password="True"/>
<field name="dropbox_client_secret"
string="App Secret"
attrs="{'invisible': [('backup_destination', '!=', 'dropbox')], 'required': [('backup_destination', '=', 'dropbox')]}"
password="True"/>
<field name="auto_remove"/> <field name="auto_remove"/>
<label for="days_to_remove" class="oe_inline" <label for="days_to_remove" class="oe_inline"
attrs="{'invisible': [('auto_remove', '=', False)]}"/> attrs="{'invisible': [('auto_remove', '=', False)]}"/>
@ -74,16 +131,120 @@
attrs="{'required': [('auto_remove', '=', True)]}"/> attrs="{'required': [('auto_remove', '=', True)]}"/>
Days Days
</div> </div>
<div attrs="{'invisible': [('backup_destination', '!=', 'dropbox')]}">
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'dropbox'), ('is_dropbox_token_generated', '=', False)]}">
<i class="text-success fa fa-check"/>
Refresh token set
</div>
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'dropbox'), ('is_dropbox_token_generated', '=', True)]}">
<i class="fa fa-exclamation-triangle text-warning"/>
No refresh token set
</div>
</div>
<div attrs="{'invisible': [('backup_destination', '!=', 'dropbox')]}">
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'dropbox'), ('is_dropbox_token_generated', '=', True)]}">
<button class="btn btn-link"
name="action_get_dropbox_auth_code"
type="object">
<i class="fa fa-arrow-right"/>
Setup Token
</button>
</div>
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'dropbox'), ('is_dropbox_token_generated', '=', False)]}">
<button class="btn btn-link"
name="action_get_dropbox_auth_code"
type="object">
<i class="fa fa-arrow-right"/>
Reset Token
</button>
</div>
</div>
<div attrs="{'invisible': [('backup_destination', '!=', 'google_drive')]}">
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'google_drive'), ('is_google_drive_token_generated', '=', False)]}">
<i class="text-success fa fa-check"/>
Refresh token set
</div>
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'google_drive'), ('is_google_drive_token_generated', '=', True)]}">
<i class="fa fa-exclamation-triangle text-warning"/>
No refresh token set
</div>
</div>
<div attrs="{'invisible': [('backup_destination', '!=', 'google_drive')]}">
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'google_drive'), ('is_google_drive_token_generated', '=', True)]}">
<button class="btn btn-link"
name="action_get_gdrive_auth_code"
type="object">
<i class="fa fa-arrow-right"/>
Setup Token
</button>
</div>
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'google_drive'), ('is_google_drive_token_generated', '=', False)]}">
<button class="btn btn-link"
name="action_get_gdrive_auth_code"
type="object">
<i class="fa fa-arrow-right"/>
Reset Token
</button>
</div>
</div>
<div class="alert alert-danger" role="alert" style="margin-bottom:0px;width: 229%;" attrs="{'invisible': [('gdrive_backup_error_test', '=', False)]}">
Something went wrong during your token generation. Maybe your Authorization Code is invalid
</div>
<div attrs="{'invisible': [('backup_destination', '!=', 'onedrive')]}">
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'onedrive'), ('is_onedrive_token_generated', '=', False)]}">
<i class="text-success fa fa-check"/>
Refresh token set
</div>
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'onedrive'), ('is_onedrive_token_generated', '=', True)]}">
<i class="fa fa-exclamation-triangle text-warning"/>
No refresh token set
</div>
</div>
<div attrs="{'invisible': [('backup_destination', '!=', 'onedrive')]}">
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'onedrive'), ('is_onedrive_token_generated', '=', True)]}">
<button class="btn btn-link"
name="action_get_onedrive_auth_code"
type="object">
<i class="fa fa-arrow-right"/>
Setup Token
</button>
</div>
<div attrs="{'invisible': ['|', ('backup_destination', '!=', 'onedrive'), ('is_onedrive_token_generated', '=', False)]}">
<button class="btn btn-link"
name="action_get_onedrive_auth_code"
type="object">
<i class="fa fa-arrow-right"/>
Reset Token
</button>
</div>
</div>
<div class="alert alert-danger" role="alert" style="margin-bottom:0px;width: 91%%;" attrs="{'invisible': [('onedrive_backup_error_test', '=', False)]}">
Bad microsoft onedrive request. Maybe your Authorization Code is invalid
</div>
<button name="test_connection" type="object" <button name="test_connection" type="object"
string="Test Connection" string="Test Connection"
icon="fa-television" icon="fa-television"
attrs="{'invisible': [('backup_destination', 'not in', ('ftp', 'sftp'))]}"/> attrs="{'invisible': [('backup_destination', 'not in', ('ftp', 'sftp'))]}"/>
<button name="action_nextcloud" type="object"
string="Test Connection"
icon="fa-television"
attrs="{'invisible': [('backup_destination', '!=', 'next_cloud')]}"/>
<button name="action_s3cloud" type="object"
string="Test Connection"
icon="fa-television"
attrs="{'invisible': [('backup_destination', '!=', 'amazon_s3')]}"/>
</group> </group>
<group> <group>
<field name="notify_user"/> <field name="notify_user"/>
<field name="user_id" <field name="user_id"
attrs="{'invisible': [('notify_user', '=', False)]}"/> attrs="{'invisible': [('notify_user', '=', False)]}"/>
</group> </group>
<group>
<field name="dropbox_refresh_token" invisible="1"/>
<field name="is_dropbox_token_generated" invisible="1"/>
<field name="dropbox_folder"
attrs="{'invisible': [('backup_destination', '!=', 'dropbox')], 'required': [('backup_destination', '=', 'dropbox')]}"/>
</group>
</group> </group>
</sheet> </sheet>
</form> </form>
@ -108,7 +269,7 @@
</search> </search>
</field> </field>
</record> </record>
<record id="action_db_backup_configure" model="ir.actions.act_window"> <record id="db_backup_configure_action" model="ir.actions.act_window">
<field name="name">Database Backup</field> <field name="name">Database Backup</field>
<field name="res_model">db.backup.configure</field> <field name="res_model">db.backup.configure</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
@ -123,5 +284,5 @@
parent="base.menu_custom" sequence="10"/> parent="base.menu_custom" sequence="10"/>
<menuitem id="menu_db_backup_configuration" parent="menu_db_backup" <menuitem id="menu_db_backup_configuration" parent="menu_db_backup"
name="Backup Configuration" name="Backup Configuration"
action="action_db_backup_configure"/> action="db_backup_configure_action"/>
</odoo> </odoo>

22
auto_database_backup/wizard/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import dropbox_auth_code

60
auto_database_backup/wizard/dropbox_auth_code.py

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import api, fields, models
class DropboxAuthCode(models.TransientModel):
"""Model for managing the Dropbox authentication code and URL.
Methods:
_compute_dropbox_auth_url:
Compute method to retrieve the Dropbox authentication URL
action_setup_dropbox_token:
Method to set up the Dropbox token using the provided authorization
code.
"""
_name = 'dropbox.auth.code'
_description = 'Dropbox Authentication Code Wizard'
dropbox_authorization_code = fields.Char(string='Authorization Code',
help='Authorization code received'
'from Dropbox')
dropbox_auth_url = fields.Char(string='Dropbox Authentication URL',
compute='_compute_dropbox_auth_url',
help='URL for Dropbox authentication')
@api.depends('dropbox_authorization_code')
def _compute_dropbox_auth_url(self):
"""Compute method to retrieve the Dropbox authentication URL"""
backup_config = self.env['db.backup.configure'].browse(
self.env.context.get('active_id'))
dropbox_auth_url = backup_config.get_dropbox_auth_url()
for rec in self:
rec.dropbox_auth_url = dropbox_auth_url
def action_setup_dropbox_token(self):
"""Method to set up the Dropbox token using the provided authorization
code."""
backup_config = self.env['db.backup.configure'].browse(
self.env.context.get('active_id'))
backup_config.hide_active = True
backup_config.active = True
backup_config.set_dropbox_refresh_token(self.dropbox_authorization_code)

27
auto_database_backup/wizard/dropbox_auth_code_views.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- dropbox_auth_code form View-->
<record id="dropbox_auth_code_view_form" model="ir.ui.view">
<field name="name">dropbox.auth.code.view.form</field>
<field name="model">dropbox.auth.code</field>
<field name="arch" type="xml">
<form>
<group>
<span>Get an authorization code and set it in the field below.</span>
<field name="dropbox_auth_url"
class="fa fa-arrow-right"
widget="url"
text="Get Authorization Code"
nolabel="1"/>
</group>
<group>
<field name="dropbox_authorization_code" required="1"/>
</group>
<footer>
<button string="Confirm" type="object" name="action_setup_dropbox_token" class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>
Loading…
Cancel
Save