From 7040b3ee9e5eb6f9ddc348bba14f76fa37e0d3fa Mon Sep 17 00:00:00 2001 From: Usman Nasir Date: Tue, 25 Aug 2020 15:49:52 +0500 Subject: [PATCH] complete mautic auto installer --- plogical/applicationInstaller.py | 214 +++++++++++++++++- static/images/icons/mautic.png | Bin 0 -> 21885 bytes static/websiteFunctions/websiteFunctions.js | 186 +++++++++++++++ .../websiteFunctions/websiteFunctions.js | 186 +++++++++++++++ .../websiteFunctions/installMautic.html | 112 +++++++++ .../templates/websiteFunctions/website.html | 11 + websiteFunctions/urls.py | 5 + websiteFunctions/views.py | 16 ++ websiteFunctions/website.py | 58 +++++ 9 files changed, 780 insertions(+), 8 deletions(-) create mode 100644 static/images/icons/mautic.png create mode 100755 websiteFunctions/templates/websiteFunctions/installMautic.html diff --git a/plogical/applicationInstaller.py b/plogical/applicationInstaller.py index 83e97ad36..8d42a05a7 100755 --- a/plogical/applicationInstaller.py +++ b/plogical/applicationInstaller.py @@ -17,6 +17,7 @@ from databases.models import Databases from plogical.installUtilities import installUtilities import shutil from plogical.processUtilities import ProcessUtilities +from random import randint class ApplicationInstaller(multi.Thread): @@ -39,14 +40,6 @@ class ApplicationInstaller(multi.Thread): self.installWordPress() elif self.installApp == 'joomla': self.installJoomla() - elif self.installApp == 'git': - self.setupGit() - elif self.installApp == 'pull': - self.gitPull() - elif self.installApp == 'detach': - self.detachRepo() - elif self.installApp == 'changeBranch': - self.changeBranch() elif self.installApp == 'prestashop': self.installPrestaShop() elif self.installApp == 'magento': @@ -55,10 +48,215 @@ class ApplicationInstaller(multi.Thread): self.convertDomainToSite() elif self.installApp == 'updatePackage': self.updatePackage() + elif self.installApp == 'mautic': + self.installMautic() except BaseException as msg: logging.writeToFile(str(msg) + ' [ApplicationInstaller.run]') + def installMautic(self): + try: + + admin = self.extraArgs['admin'] + domainName = self.extraArgs['domainName'] + home = self.extraArgs['home'] + tempStatusPath = self.extraArgs['tempStatusPath'] + self.tempStatusPath = tempStatusPath + username = self.extraArgs['username'] + password = self.extraArgs['password'] + email = self.extraArgs['email'] + + FNULL = open(os.devnull, 'w') + + ## Open Status File + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines('Setting up paths,0') + statusFile.close() + + finalPath = '' + self.permPath = '' + + try: + website = ChildDomains.objects.get(domain=domainName) + externalApp = website.master.externalApp + self.masterDomain = website.master.domain + + if home == '0': + path = self.extraArgs['path'] + finalPath = website.path.rstrip('/') + "/" + path + "/" + else: + finalPath = website.path + + if website.master.package.dataBases > website.master.databases_set.all().count(): + pass + else: + raise BaseException("Maximum database limit reached for this website.") + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines('Setting up Database,20') + statusFile.close() + + dbName, dbUser, dbPassword = self.dbCreation(tempStatusPath, website.master) + self.permPath = website.path + + except: + website = Websites.objects.get(domain=domainName) + externalApp = website.externalApp + self.masterDomain = website.domain + + if home == '0': + path = self.extraArgs['path'] + finalPath = "/home/" + domainName + "/public_html/" + path + "/" + else: + finalPath = "/home/" + domainName + "/public_html/" + + if website.package.dataBases > website.databases_set.all().count(): + pass + else: + raise BaseException("Maximum database limit reached for this website.") + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines('Setting up Database,20') + statusFile.close() + + dbName, dbUser, dbPassword = self.dbCreation(tempStatusPath, website) + self.permPath = '/home/%s/public_html' % (website.domain) + + ## Security Check + + command = 'chmod 755 %s' % (self.permPath) + ProcessUtilities.executioner(command) + + if finalPath.find("..") > -1: + raise BaseException("Specified path must be inside virtual host home.") + + if not os.path.exists(finalPath): + command = 'mkdir -p ' + finalPath + ProcessUtilities.executioner(command, externalApp) + + ## checking for directories/files + + if self.dataLossCheck(finalPath, tempStatusPath) == 0: + raise BaseException('Directory is not empty.') + + #### + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines('Downloading Mautic Core,30') + statusFile.close() + + command = "wget https://github.com/mautic/mautic/releases/download/3.1.0/3.1.0.zip" + ProcessUtilities.outputExecutioner(command, externalApp, None, finalPath) + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines('Extracting Mautic Core,50') + statusFile.close() + + command = "unzip 3.1.0.zip" + ProcessUtilities.outputExecutioner(command, externalApp, None, finalPath) + + ## + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines('Running Mautic installer,70') + statusFile.close() + + if home == '0': + path = self.extraArgs['path'] + finalURL = domainName + '/' + path + else: + finalURL = domainName + + localDB = "/home/cyberpanel/" + str(randint(1000, 9999)) + + localDBContent = """ 'localhost', + 'db_table_prefix' => null, + 'db_port' => 3306, + 'db_name' => '%s', + 'db_user' => '%s', + 'db_password' => '%s', + 'db_backup_tables' => true, + 'db_backup_prefix' => 'bak_', + 'admin_email' => '%s', + 'admin_password' => '%s', + 'mailer_transport' => null, + 'mailer_host' => null, + 'mailer_port' => null, + 'mailer_user' => null, + 'mailer_password' => null, + 'mailer_api_key' => null, + 'mailer_encryption' => null, + 'mailer_auth_mode' => null, +);""" % (dbName, dbUser, dbPassword, email, password) + + writeToFile = open(localDB, 'w') + writeToFile.write(localDBContent) + writeToFile.close() + + command = 'rm -rf %s/app/config/local.php' % (finalPath) + ProcessUtilities.executioner(command) + + command = 'mv %s %s/app/config/local.php' % (localDB, finalPath) + ProcessUtilities.executioner(command) + + command = "/usr/local/lsws/lsphp72/bin/php bin/console mautic:install http://%s" % (finalURL) + result = ProcessUtilities.outputExecutioner(command, 'root', None, finalPath) + + if result.find('Install complete') == -1: + raise BaseException(result) + + + ## + + from filemanager.filemanager import FileManager + + fm = FileManager(None, None) + fm.fixPermissions(self.masterDomain) + + installUtilities.reStartLiteSpeedSocket() + + statusFile = open(tempStatusPath, 'w') + statusFile.writelines("Successfully Installed. [200]") + statusFile.close() + return 0 + + + except BaseException as msg: + # remove the downloaded files + FNULL = open(os.devnull, 'w') + + homeDir = "/home/" + domainName + "/public_html" + + if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: + groupName = 'nobody' + else: + groupName = 'nogroup' + + if not os.path.exists(homeDir): + command = "chown " + externalApp + ":" + groupName + " " + homeDir + ProcessUtilities.executioner(command, externalApp) + + try: + mysqlUtilities.deleteDatabase(dbName, dbUser) + db = Databases.objects.get(dbName=dbName) + db.delete() + except: + pass + + command = 'chmod 750 %s' % (self.permPath) + ProcessUtilities.executioner(command) + + + statusFile = open(self.tempStatusPath, 'w') + statusFile.writelines(str(msg) + " [404]") + statusFile.close() + return 0 + def updatePackage(self): try: diff --git a/static/images/icons/mautic.png b/static/images/icons/mautic.png new file mode 100644 index 0000000000000000000000000000000000000000..734f1913e046353ad45f08ca7b1f4abbc78b9624 GIT binary patch literal 21885 zcmbTdXEdDA_b@to^cF<#kzk14ThtIG(R-93+UQ0nLA2;Wh?3~N8-r0IL<^#Y$&A4y zYB0L!ck=s}dq2GE{cx>i@tkw^KD+PzoU@-qBLi(pG8Qrb06_UfN8>pFfQS9}dxsb| z5|L;=f&0T8plK0c?ClZ&aqt5H)SSH?L0nI~99%)qK@QF#zM~*T0D!>9&BP+WLjS3} zled?M!@o5m!CpQ%XaGP_CD_No$paL?HDaIUb^Xo z`hko?4NROuJ)Gp6c~q3S6och)2)sZ64qU-ro?w6ZU?raa;FZVy|8H27hwDE}0z8y> z{);FJeIqV)Z$A*1l!!FY>5-TumyDcL?99(-hQs4VsdhFqL0Ky#l?ZR6+r(G zaDYQF5bV$UKNvJX{!V^wJ^^mtV6J}{9UQ#_1C)4hl>QeBUOxKz|BD#x|KEbbiA*%u z!ADd~EiW?;Yspgwux$@BaYfWcPpH=-)yd zHS&gjZaAelcxre%1$u$N0Z%lPcyKc!&Th`~vSM;t8e-z&;%b^|Vq%(-vQl!7HPys5 zWi=!u9!W|)`X3zsCs=W<$5OH~T53`d3$mFN6PYU{|73>K$0%vE|StfX*nq;prqrYM?gn8aS5Q9 zq_~TOth2O?tfUmrf5SWf|B;?3jv�X7c|roBtWY+2X%{|JMm{5C7{%ATZ8%{BTZ! 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 literal 0 HcmV?d00001 diff --git a/static/websiteFunctions/websiteFunctions.js b/static/websiteFunctions/websiteFunctions.js index 2b38c563e..efd6556d3 100644 --- a/static/websiteFunctions/websiteFunctions.js +++ b/static/websiteFunctions/websiteFunctions.js @@ -961,6 +961,7 @@ app.controller('websitePages', function ($scope, $http, $timeout, $window) { $scope.setupGit = $("#domainNamePage").text() + "/setupGit"; $scope.installPrestaURL = $("#domainNamePage").text() + "/installPrestaShop"; $scope.installMagentoURL = $("#domainNamePage").text() + "/installMagento"; + $scope.installMauticURL = $("#domainNamePage").text() + "/installMautic"; $scope.domainAliasURL = "/websites/" + $("#domainNamePage").text() + "/domainAlias"; $scope.previewUrl = "/preview/" + $("#domainNamePage").text() + "/"; @@ -5218,6 +5219,191 @@ app.controller('installPrestaShopCTRL', function ($scope, $http, $timeout) { }; +}); + +app.controller('installMauticCTRL', function ($scope, $http, $timeout) { + + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.installationFailed = true; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = true; + + $scope.databasePrefix = 'ps_'; + + var statusFile; + var domain = $("#domainNamePage").text(); + var path; + + + $scope.goBack = function () { + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.installationFailed = true; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = true; + $("#installProgress").css("width", "0%"); + }; + + function getInstallStatus() { + + url = "/websites/installWordpressStatus"; + + var data = { + statusFile: statusFile, + domainName: domain + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + + if (response.data.abort === 1) { + + if (response.data.installStatus === 1) { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = true; + $scope.installationSuccessfull = false; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = false; + + if (typeof path !== 'undefined') { + $scope.installationURL = "http://" + domain + "/" + path; + } else { + $scope.installationURL = domain; + } + + + $("#installProgress").css("width", "100%"); + $scope.installPercentage = "100"; + $scope.currentStatus = response.data.currentStatus; + $timeout.cancel(); + + } else { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = false; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = false; + + $scope.errorMessage = response.data.error_message; + + $("#installProgress").css("width", "0%"); + $scope.installPercentage = "0"; + + } + + } else { + $("#installProgress").css("width", response.data.installationProgress + "%"); + $scope.installPercentage = response.data.installationProgress; + $scope.currentStatus = response.data.currentStatus; + + $timeout(getInstallStatus, 1000); + + + } + + } + + function cantLoadInitialDatas(response) { + + $scope.canNotFetch = true; + $scope.couldNotConnect = false; + + + } + + + } + + $scope.installMautic = function () { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = true; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = false; + $scope.goBackDisable = true; + $scope.currentStatus = "Starting installation.."; + + path = $scope.installPath; + + + url = "/websites/mauticInstall"; + + var home = "1"; + + if (typeof path !== 'undefined') { + home = "0"; + } + + + var data = { + domain: domain, + home: home, + path: path, + username: $scope.adminUserName, + email: $scope.email, + passwordByPass: $scope.password + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + if (response.data.installStatus === 1) { + statusFile = response.data.tempStatusPath; + getInstallStatus(); + } else { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = false; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = false; + + $scope.errorMessage = response.data.error_message; + + } + + + } + + function cantLoadInitialDatas(response) { + } + + }; + + }); app.controller('sshAccess', function ($scope, $http, $timeout) { diff --git a/websiteFunctions/static/websiteFunctions/websiteFunctions.js b/websiteFunctions/static/websiteFunctions/websiteFunctions.js index 2b38c563e..efd6556d3 100755 --- a/websiteFunctions/static/websiteFunctions/websiteFunctions.js +++ b/websiteFunctions/static/websiteFunctions/websiteFunctions.js @@ -961,6 +961,7 @@ app.controller('websitePages', function ($scope, $http, $timeout, $window) { $scope.setupGit = $("#domainNamePage").text() + "/setupGit"; $scope.installPrestaURL = $("#domainNamePage").text() + "/installPrestaShop"; $scope.installMagentoURL = $("#domainNamePage").text() + "/installMagento"; + $scope.installMauticURL = $("#domainNamePage").text() + "/installMautic"; $scope.domainAliasURL = "/websites/" + $("#domainNamePage").text() + "/domainAlias"; $scope.previewUrl = "/preview/" + $("#domainNamePage").text() + "/"; @@ -5218,6 +5219,191 @@ app.controller('installPrestaShopCTRL', function ($scope, $http, $timeout) { }; +}); + +app.controller('installMauticCTRL', function ($scope, $http, $timeout) { + + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.installationFailed = true; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = true; + + $scope.databasePrefix = 'ps_'; + + var statusFile; + var domain = $("#domainNamePage").text(); + var path; + + + $scope.goBack = function () { + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.installationFailed = true; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = true; + $("#installProgress").css("width", "0%"); + }; + + function getInstallStatus() { + + url = "/websites/installWordpressStatus"; + + var data = { + statusFile: statusFile, + domainName: domain + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + + if (response.data.abort === 1) { + + if (response.data.installStatus === 1) { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = true; + $scope.installationSuccessfull = false; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = false; + + if (typeof path !== 'undefined') { + $scope.installationURL = "http://" + domain + "/" + path; + } else { + $scope.installationURL = domain; + } + + + $("#installProgress").css("width", "100%"); + $scope.installPercentage = "100"; + $scope.currentStatus = response.data.currentStatus; + $timeout.cancel(); + + } else { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = false; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = false; + + $scope.errorMessage = response.data.error_message; + + $("#installProgress").css("width", "0%"); + $scope.installPercentage = "0"; + + } + + } else { + $("#installProgress").css("width", response.data.installationProgress + "%"); + $scope.installPercentage = response.data.installationProgress; + $scope.currentStatus = response.data.currentStatus; + + $timeout(getInstallStatus, 1000); + + + } + + } + + function cantLoadInitialDatas(response) { + + $scope.canNotFetch = true; + $scope.couldNotConnect = false; + + + } + + + } + + $scope.installMautic = function () { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = true; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = false; + $scope.goBackDisable = true; + $scope.currentStatus = "Starting installation.."; + + path = $scope.installPath; + + + url = "/websites/mauticInstall"; + + var home = "1"; + + if (typeof path !== 'undefined') { + home = "0"; + } + + + var data = { + domain: domain, + home: home, + path: path, + username: $scope.adminUserName, + email: $scope.email, + passwordByPass: $scope.password + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + if (response.data.installStatus === 1) { + statusFile = response.data.tempStatusPath; + getInstallStatus(); + } else { + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.installationFailed = false; + $scope.installationSuccessfull = true; + $scope.couldNotConnect = true; + $scope.wpInstallLoading = true; + $scope.goBackDisable = false; + + $scope.errorMessage = response.data.error_message; + + } + + + } + + function cantLoadInitialDatas(response) { + } + + }; + + }); app.controller('sshAccess', function ($scope, $http, $timeout) { diff --git a/websiteFunctions/templates/websiteFunctions/installMautic.html b/websiteFunctions/templates/websiteFunctions/installMautic.html new file mode 100755 index 000000000..dbda62697 --- /dev/null +++ b/websiteFunctions/templates/websiteFunctions/installMautic.html @@ -0,0 +1,112 @@ +{% extends "baseTemplate/index.html" %} +{% load i18n %} +{% block title %}{% trans "Install Mautic - CyberPanel" %}{% endblock %} +{% block content %} + +{% load static %} +{% get_current_language as LANGUAGE_CODE %} + + +

+
+

{% trans "Install Mautic" %}

+

{% trans "One-click Mautic Install!" %}

+
+ + +
+
+

+ {{ domainName }} - {% trans "Installation Details" %} +

+
+ + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + + +
+ +
+ +
+

{$ currentStatus $}

+
+ +
+
+ 70% Complete +
+
+ + + + +
+

{% trans "Installation failed. Error message:" %} {$ errorMessage $}

+
+ +
+

{% trans "Installation successful. Visit:" %} {$ installationURL $}

+
+ + + +
+

{% trans "Could not connect to server. Please refresh this page." %}

+
+ + +
+
+ +
+ +
+ +
+
+ + +
+ +
+
+
+ + + +
+ + +{% endblock %} \ No newline at end of file diff --git a/websiteFunctions/templates/websiteFunctions/website.html b/websiteFunctions/templates/websiteFunctions/website.html index 8363ce89a..e42060fc6 100755 --- a/websiteFunctions/templates/websiteFunctions/website.html +++ b/websiteFunctions/templates/websiteFunctions/website.html @@ -1048,6 +1048,17 @@ + + diff --git a/websiteFunctions/urls.py b/websiteFunctions/urls.py index ce5861be1..d1edcb6a4 100755 --- a/websiteFunctions/urls.py +++ b/websiteFunctions/urls.py @@ -96,6 +96,11 @@ urlpatterns = [ url(r'^(?P(.*))/installMagento$', views.installMagento, name='installMagento'), url(r'^magentoInstall$', views.magentoInstall, name='magentoInstall'), + ## mautic + + url(r'^(?P(.*))/installMautic$', views.installMautic, name='installMautic'), + url(r'^mauticInstall$', views.mauticInstall, name='mauticInstall'), + ## Git url(r'^(?P(.*))/setupGit$', views.setupGit, name='setupGit'), diff --git a/websiteFunctions/views.py b/websiteFunctions/views.py index ee1a1ec5f..a6d656449 100755 --- a/websiteFunctions/views.py +++ b/websiteFunctions/views.py @@ -656,6 +656,22 @@ def magentoInstall(request): except KeyError: return redirect(loadLoginPage) +def installMautic(request, domain): + try: + userID = request.session['userID'] + wm = WebsiteManager(domain) + return wm.installMautic(request, userID) + except KeyError: + return redirect(loadLoginPage) + +def mauticInstall(request): + try: + userID = request.session['userID'] + wm = WebsiteManager() + return wm.mauticInstall(userID, json.loads(request.body)) + except KeyError: + return redirect(loadLoginPage) + def prestaShopInstall(request): try: userID = request.session['userID'] diff --git a/websiteFunctions/website.py b/websiteFunctions/website.py index 3c77f9425..c8acbf778 100755 --- a/websiteFunctions/website.py +++ b/websiteFunctions/website.py @@ -2310,6 +2310,64 @@ StrictHostKeyChecking no json_data = json.dumps(data_ret) return HttpResponse(json_data) + def installMautic(self, request=None, userID=None, data=None): + try: + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + if ACLManager.checkOwnership(self.domain, admin, currentACL) == 1: + pass + else: + return ACLManager.loadError() + + return render(request, 'websiteFunctions/installMautic.html', {'domainName': self.domain}) + except BaseException as msg: + return HttpResponse(str(msg)) + + def mauticInstall(self, userID=None, data=None): + try: + + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + self.domain = data['domain'] + + if ACLManager.checkOwnership(self.domain, admin, currentACL) == 1: + pass + else: + return ACLManager.loadErrorJson('installStatus', 0) + + mailUtilities.checkHome() + + extraArgs = {} + extraArgs['admin'] = admin + extraArgs['domainName'] = data['domain'] + extraArgs['home'] = data['home'] + extraArgs['username'] = data['username'] + extraArgs['email'] = data['email'] + extraArgs['password'] = data['passwordByPass'] + extraArgs['tempStatusPath'] = "/home/cyberpanel/" + str(randint(1000, 9999)) + + if data['home'] == '0': + extraArgs['path'] = data['path'] + + background = ApplicationInstaller('mautic', extraArgs) + background.start() + + time.sleep(2) + + data_ret = {'status': 1, 'installStatus': 1, 'error_message': 'None', + 'tempStatusPath': extraArgs['tempStatusPath']} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + ## Installation ends + + except BaseException as msg: + data_ret = {'status': 0, 'installStatus': 0, 'error_message': str(msg)} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + def prestaShopInstall(self, userID=None, data=None): try: