* [enhancement] integration borker and controller * [minor][fix] parameter naming changes * [fix] common scheduler for integration service * [fix] integrations * [patch] patch to move payment gateway in Integration Service * [fix] add payment success, cancel and failuer handling pages * [enhancment] dropbox integration merged in integration services * provision to add custom parameters * [fixes] patch fix to setup dropbox settings * [fixes] removed dropbox backup, api usage note for paypal and razorpay * [minor][fix] deprecate service events * [fix] return type fix for checkout urls * [fix] add custom settings via patch for dropbox backup frequency * [fix] remove gride editting * [enhance] ldap based login * [enhance] login via ldap credentails * [commit] remove parameter table, save params as json dict
329 lines
10 KiB
Python
329 lines
10 KiB
Python
import frappe
|
|
from frappe import _
|
|
from frappe.integration_broker.integration_controller import IntegrationController
|
|
from frappe.utils import (cint, split_emails, get_request_site_address, cstr,
|
|
get_files_path, get_backups_path, encode)
|
|
import os, json
|
|
from frappe.utils.backups import new_backup
|
|
from frappe.utils.background_jobs import enqueue
|
|
|
|
ignore_list = [".DS_Store"]
|
|
|
|
class Controller(IntegrationController):
|
|
service_name = 'Dropbox Integration'
|
|
parameters_template = [
|
|
{
|
|
"label": "App Access Key",
|
|
'fieldname': 'app_access_key',
|
|
'reqd': 1,
|
|
'default': '',
|
|
'fieldtype': "Data"
|
|
},
|
|
{
|
|
"label": "App Secret Key",
|
|
'fieldname': 'app_secret_key',
|
|
'reqd': 1,
|
|
'default': '',
|
|
'fieldtype': "Password"
|
|
},
|
|
{
|
|
"label": "Dropbox Access Key",
|
|
'fieldname': 'dropbox_access_key',
|
|
'reqd': 1,
|
|
'default': '****',
|
|
'fieldtype': "Password"
|
|
},
|
|
{
|
|
"label": "Dropbox Secret Key",
|
|
'fieldname': 'dropbox_access_secret',
|
|
'reqd': 1,
|
|
'default': '****',
|
|
'fieldtype': "Password"
|
|
},
|
|
{
|
|
"label": "Backup Frequency",
|
|
"fieldname": "backup_frequency",
|
|
"options": "\nDaily\nWeekly",
|
|
"default": "",
|
|
"reqd": 1,
|
|
"fieldtype":'Select'
|
|
}
|
|
]
|
|
|
|
js = "assets/frappe/js/integrations/dropbox_integration.js"
|
|
|
|
# do also changes in dropbox_integration.js scheduler job helper
|
|
scheduled_jobs = [
|
|
{
|
|
"daily_long": [
|
|
"frappe.integrations.dropbox_integration.take_backups_daily"
|
|
],
|
|
"weekly_long": [
|
|
"frappe.integrations.dropbox_integration.take_backups_weekly"
|
|
]
|
|
}
|
|
]
|
|
|
|
def enable(self, parameters, use_test_account=0):
|
|
""" enable service """
|
|
self.parameters = parameters
|
|
self.validate_dropbox_credentails()
|
|
|
|
def validate_dropbox_credentails(self):
|
|
try:
|
|
self.get_dropbox_session()
|
|
except Exception, e:
|
|
frappe.throw(e.message)
|
|
|
|
def get_dropbox_session(self):
|
|
try:
|
|
from dropbox import session
|
|
except:
|
|
raise Exception(_("Please install dropbox python module"))
|
|
parameters = frappe._dict(self.parameters)
|
|
if not (parameters["app_access_key"] or parameters["app_secret_key"]):
|
|
raise Exception(_("Please set Dropbox access keys in your site config"))
|
|
|
|
sess = session.DropboxSession(parameters["app_access_key"], parameters["app_secret_key"], "app_folder")
|
|
|
|
return sess
|
|
|
|
#get auth token
|
|
@frappe.whitelist()
|
|
def get_dropbox_authorize_url():
|
|
doc = frappe.get_doc("Integration Service", "Dropbox Integration")
|
|
Controller.parameters = json.loads(doc.custom_settings_json)
|
|
|
|
sess = Controller().get_dropbox_session()
|
|
request_token = sess.obtain_request_token()
|
|
|
|
setup_parameter({
|
|
"dropbox_access_key": request_token.key,
|
|
"dropbox_access_secret": request_token.secret
|
|
}, doc)
|
|
|
|
return_address = get_request_site_address(True) \
|
|
+ "?cmd=frappe.integrations.dropbox_integration.dropbox_callback"
|
|
|
|
url = sess.build_authorize_url(request_token, return_address)
|
|
|
|
return {
|
|
"url": url,
|
|
"dropbox_access_key": request_token.key,
|
|
"dropbox_access_secret": request_token.secret
|
|
}
|
|
|
|
def setup_parameter(request_token, doc):
|
|
parameters = Controller().parameters
|
|
|
|
for key in ["dropbox_access_key", "dropbox_access_secret"]:
|
|
for parameter in Controller().parameters:
|
|
if key == parameter:
|
|
parameters[key] = request_token[key]
|
|
|
|
doc.custom_settings_json = json.dumps(parameters)
|
|
doc.save(ignore_permissions=True)
|
|
frappe.db.commit()
|
|
|
|
@frappe.whitelist(allow_guest=True)
|
|
def dropbox_callback(oauth_token=None, not_approved=False):
|
|
doc = frappe.get_doc("Integration Service", "Dropbox Integration")
|
|
Controller.parameters = json.loads(doc.custom_settings_json)
|
|
|
|
parameters = Controller().get_parameters()
|
|
if not not_approved:
|
|
if parameters["dropbox_access_key"]==oauth_token:
|
|
sess = Controller().get_dropbox_session()
|
|
sess.set_request_token(parameters["dropbox_access_key"], parameters["dropbox_access_secret"])
|
|
access_token = sess.obtain_access_token()
|
|
|
|
setup_parameter({
|
|
"dropbox_access_key": access_token.key,
|
|
"dropbox_access_secret": access_token.secret
|
|
}, doc)
|
|
|
|
frappe.db.commit()
|
|
else:
|
|
frappe.respond_as_web_page(_("Dropbox Approval"), _("Illegal Access Token Please try again. <p>Please close this window.</p"),
|
|
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
|
|
else:
|
|
frappe.respond_as_web_page(_("Dropbox Approval"), _("Dropbox Access not approved. <p>Please close this window.</p"),
|
|
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
|
|
|
|
frappe.respond_as_web_page(_("Dropbox Approval"), _("Dropbox access allowed. <p>Please close this window.</p"),
|
|
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
|
|
|
|
# backup process
|
|
@frappe.whitelist()
|
|
def take_backup():
|
|
"Enqueue longjob for taking backup to dropbox"
|
|
enqueue("frappe.integrations.dropbox_integration.take_backup_to_dropbox", queue='long')
|
|
frappe.msgprint(_("Queued for backup. It may take a few minutes to an hour."))
|
|
|
|
def take_backups_daily():
|
|
take_backups_if("Daily")
|
|
|
|
def take_backups_weekly():
|
|
take_backups_if("Weekly")
|
|
|
|
def take_backups_if(freq):
|
|
custom_settings_json = frappe.db.get_value("Dropbox Backup", None, "custom_settings_json")
|
|
if custom_settings_json:
|
|
custom_settings_json = json.loads(custom_settings_json)
|
|
if custom_settings_json["backup_frequency"] == freq:
|
|
take_backup_to_dropbox()
|
|
|
|
def take_backup_to_dropbox():
|
|
did_not_upload, error_log = [], []
|
|
try:
|
|
if cint(frappe.db.get_value("Integration Service", "Dropbox Integration", "enabled")):
|
|
did_not_upload, error_log = backup_to_dropbox()
|
|
if did_not_upload: raise Exception
|
|
|
|
send_email(True, "Dropbox")
|
|
except Exception:
|
|
file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)]
|
|
error_message = ("\n".join(file_and_error) + "\n" + frappe.get_traceback())
|
|
frappe.errprint(error_message)
|
|
send_email(False, "Dropbox", error_message)
|
|
|
|
def send_email(success, service_name, error_status=None):
|
|
if success:
|
|
subject = "Backup Upload Successful"
|
|
message ="""<h3>Backup Uploaded Successfully</h3><p>Hi there, this is just to inform you
|
|
that your backup was successfully uploaded to your %s account. So relax!</p>
|
|
""" % service_name
|
|
|
|
else:
|
|
subject = "[Warning] Backup Upload Failed"
|
|
message ="""<h3>Backup Upload Failed</h3><p>Oops, your automated backup to %s
|
|
failed.</p>
|
|
<p>Error message: <br>
|
|
<pre><code>%s</code></pre>
|
|
</p>
|
|
<p>Please contact your system manager for more information.</p>
|
|
""" % (service_name, error_status)
|
|
|
|
if not frappe.db:
|
|
frappe.connect()
|
|
|
|
recipients = split_emails(frappe.db.get_value("Dropbox Backup", None, "send_notifications_to"))
|
|
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
|
|
|
def backup_to_dropbox():
|
|
if not frappe.db:
|
|
frappe.connect()
|
|
|
|
dropbox_client = get_dropbox_client()
|
|
# upload database
|
|
backup = new_backup(ignore_files=True)
|
|
filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db))
|
|
dropbox_client = upload_file_to_dropbox(filename, "/database", dropbox_client)
|
|
|
|
frappe.db.close()
|
|
|
|
# upload files to files folder
|
|
did_not_upload = []
|
|
error_log = []
|
|
|
|
dropbox_client = upload_from_folder(get_files_path(), "/files", dropbox_client, did_not_upload, error_log)
|
|
dropbox_client = upload_from_folder(get_files_path(is_private=1), "/private/files", dropbox_client, did_not_upload, error_log)
|
|
|
|
frappe.connect()
|
|
|
|
return did_not_upload, list(set(error_log))
|
|
|
|
def get_dropbox_client(previous_dropbox_client=None):
|
|
from dropbox import client
|
|
doc = frappe.get_doc("Integration Service", "Dropbox Integration")
|
|
Controller.parameters = json.loads(doc.custom_settings_json)
|
|
|
|
sess = Controller().get_dropbox_session()
|
|
|
|
parameters = Controller().get_parameters()
|
|
sess.set_token(parameters["dropbox_access_key"], parameters["dropbox_access_secret"])
|
|
|
|
dropbox_client = client.DropboxClient(sess)
|
|
|
|
# upgrade to oauth2
|
|
token = dropbox_client.create_oauth2_access_token()
|
|
dropbox_client = client.DropboxClient(token)
|
|
if previous_dropbox_client:
|
|
dropbox_client.connection_reset_count = previous_dropbox_client.connection_reset_count + 1
|
|
else:
|
|
dropbox_client.connection_reset_count = 0
|
|
return dropbox_client
|
|
|
|
def upload_file_to_dropbox(filename, folder, dropbox_client):
|
|
from dropbox import rest
|
|
size = os.stat(encode(filename)).st_size
|
|
|
|
with open(filename, 'r') as f:
|
|
# if max packet size reached, use chunked uploader
|
|
max_packet_size = 4194304
|
|
|
|
if size > max_packet_size:
|
|
uploader = dropbox_client.get_chunked_uploader(f, size)
|
|
while uploader.offset < size:
|
|
try:
|
|
uploader.upload_chunked()
|
|
uploader.finish(folder + "/" + os.path.basename(filename), overwrite=True)
|
|
|
|
except rest.ErrorResponse, e:
|
|
# if "[401] u'Access token not found.'",
|
|
# it means that the user needs to again allow dropbox backup from the UI
|
|
# so re-raise
|
|
exc_message = cstr(e)
|
|
if (exc_message.startswith("[401]")
|
|
and dropbox_client.connection_reset_count < 10
|
|
and exc_message != "[401] u'Access token not found.'"):
|
|
|
|
|
|
# session expired, so get a new connection!
|
|
# [401] u"The given OAuth 2 access token doesn't exist or has expired."
|
|
dropbox_client = get_dropbox_client(dropbox_client)
|
|
|
|
else:
|
|
raise
|
|
else:
|
|
dropbox_client.put_file(folder + "/" + os.path.basename(filename), f, overwrite=True)
|
|
|
|
return dropbox_client
|
|
|
|
def upload_from_folder(path, dropbox_folder, dropbox_client, did_not_upload, error_log):
|
|
import dropbox.rest
|
|
|
|
if not os.path.exists(path):
|
|
return
|
|
|
|
try:
|
|
response = dropbox_client.metadata(dropbox_folder)
|
|
except dropbox.rest.ErrorResponse, e:
|
|
# folder not found
|
|
if e.status==404:
|
|
response = {"contents": []}
|
|
else:
|
|
raise
|
|
|
|
for filename in os.listdir(path):
|
|
filename = cstr(filename)
|
|
|
|
if filename in ignore_list:
|
|
continue
|
|
|
|
found = False
|
|
filepath = os.path.join(path, filename)
|
|
for file_metadata in response["contents"]:
|
|
if (os.path.basename(filepath) == os.path.basename(file_metadata["path"])
|
|
and os.stat(encode(filepath)).st_size == int(file_metadata["bytes"])):
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
try:
|
|
dropbox_client = upload_file_to_dropbox(filepath, dropbox_folder, dropbox_client)
|
|
except Exception:
|
|
did_not_upload.append(filename)
|
|
error_log.append(frappe.get_traceback())
|
|
|
|
return dropbox_client
|