complete mautic auto installer

This commit is contained in:
Usman Nasir
2020-08-25 15:49:52 +05:00
parent eb93f51be9
commit 7040b3ee9e
9 changed files with 780 additions and 8 deletions

View File

@@ -17,6 +17,7 @@ from databases.models import Databases
from plogical.installUtilities import installUtilities from plogical.installUtilities import installUtilities
import shutil import shutil
from plogical.processUtilities import ProcessUtilities from plogical.processUtilities import ProcessUtilities
from random import randint
class ApplicationInstaller(multi.Thread): class ApplicationInstaller(multi.Thread):
@@ -39,14 +40,6 @@ class ApplicationInstaller(multi.Thread):
self.installWordPress() self.installWordPress()
elif self.installApp == 'joomla': elif self.installApp == 'joomla':
self.installJoomla() 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': elif self.installApp == 'prestashop':
self.installPrestaShop() self.installPrestaShop()
elif self.installApp == 'magento': elif self.installApp == 'magento':
@@ -55,10 +48,215 @@ class ApplicationInstaller(multi.Thread):
self.convertDomainToSite() self.convertDomainToSite()
elif self.installApp == 'updatePackage': elif self.installApp == 'updatePackage':
self.updatePackage() self.updatePackage()
elif self.installApp == 'mautic':
self.installMautic()
except BaseException as msg: except BaseException as msg:
logging.writeToFile(str(msg) + ' [ApplicationInstaller.run]') 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 = """<?php
// Example local.php to test install (to adapt of course)
$parameters = array(
// Do not set db_driver and mailer_from_name as they are used to assume Mautic is installed
'db_host' => '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): def updatePackage(self):
try: try:

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -961,6 +961,7 @@ app.controller('websitePages', function ($scope, $http, $timeout, $window) {
$scope.setupGit = $("#domainNamePage").text() + "/setupGit"; $scope.setupGit = $("#domainNamePage").text() + "/setupGit";
$scope.installPrestaURL = $("#domainNamePage").text() + "/installPrestaShop"; $scope.installPrestaURL = $("#domainNamePage").text() + "/installPrestaShop";
$scope.installMagentoURL = $("#domainNamePage").text() + "/installMagento"; $scope.installMagentoURL = $("#domainNamePage").text() + "/installMagento";
$scope.installMauticURL = $("#domainNamePage").text() + "/installMautic";
$scope.domainAliasURL = "/websites/" + $("#domainNamePage").text() + "/domainAlias"; $scope.domainAliasURL = "/websites/" + $("#domainNamePage").text() + "/domainAlias";
$scope.previewUrl = "/preview/" + $("#domainNamePage").text() + "/"; $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) { app.controller('sshAccess', function ($scope, $http, $timeout) {

View File

@@ -961,6 +961,7 @@ app.controller('websitePages', function ($scope, $http, $timeout, $window) {
$scope.setupGit = $("#domainNamePage").text() + "/setupGit"; $scope.setupGit = $("#domainNamePage").text() + "/setupGit";
$scope.installPrestaURL = $("#domainNamePage").text() + "/installPrestaShop"; $scope.installPrestaURL = $("#domainNamePage").text() + "/installPrestaShop";
$scope.installMagentoURL = $("#domainNamePage").text() + "/installMagento"; $scope.installMagentoURL = $("#domainNamePage").text() + "/installMagento";
$scope.installMauticURL = $("#domainNamePage").text() + "/installMautic";
$scope.domainAliasURL = "/websites/" + $("#domainNamePage").text() + "/domainAlias"; $scope.domainAliasURL = "/websites/" + $("#domainNamePage").text() + "/domainAlias";
$scope.previewUrl = "/preview/" + $("#domainNamePage").text() + "/"; $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) { app.controller('sshAccess', function ($scope, $http, $timeout) {

View File

@@ -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 %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2>{% trans "Install Mautic" %}</h2>
<p>{% trans "One-click Mautic Install!" %}</p>
</div>
<div ng-controller="installMauticCTRL" class="panel">
<div class="panel-body">
<h3 class="title-hero">
<span id="domainNamePage">{{ domainName }}</span> - {% trans "Installation Details" %} <img ng-hide="wpInstallLoading" src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form name="websiteCreationForm" action="/" id="createPackages" class="form-horizontal bordered-row">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Administrator Username" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="adminUserName" required>
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Email" %}</label>
<div class="col-sm-6">
<input type="email" class="form-control" ng-model="email" required>
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" class="form-control" ng-model="password" required>
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-click="installMautic()" class="btn btn-primary btn-lg btn-block">{% trans "Install Now" %}</button>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-7">
<div class="alert alert-success text-center">
<h2>{$ currentStatus $}</h2>
</div>
<div class="progress">
<div id="installProgress" class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width:0%">
<span class="sr-only">70% Complete</span>
</div>
</div>
<div ng-hide="installationFailed" class="alert alert-danger">
<p>{% trans "Installation failed. Error message:" %} {$ errorMessage $}</p>
</div>
<div ng-hide="installationSuccessfull" class="alert alert-success">
<p>{% trans "Installation successful. Visit:" %} {$ installationURL $}</p>
</div>
<div ng-hide="couldNotConnect" class="alert alert-danger">
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
</div>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()" class="btn btn-primary btn-lg btn-block">{% trans "Go Back" %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1048,6 +1048,17 @@
</a> </a>
</div> </div>
<div class="col-md-3 panel-body">
<a href="{$ installMagentoURL $}" target="_blank"
title="{% trans 'Install Magento' %}">
<img src="{% static 'images/icons/mautic.png' %}" width="65" class="mr-10">
</a>
<a href="{$ installMauticURL $}" target="_blank"
title="{% trans 'Install Mautic' %}">
<span class="h4">{% trans "Mautic" %}</span>
</a>
</div>
</div> </div>
</div> </div>

View File

@@ -96,6 +96,11 @@ urlpatterns = [
url(r'^(?P<domain>(.*))/installMagento$', views.installMagento, name='installMagento'), url(r'^(?P<domain>(.*))/installMagento$', views.installMagento, name='installMagento'),
url(r'^magentoInstall$', views.magentoInstall, name='magentoInstall'), url(r'^magentoInstall$', views.magentoInstall, name='magentoInstall'),
## mautic
url(r'^(?P<domain>(.*))/installMautic$', views.installMautic, name='installMautic'),
url(r'^mauticInstall$', views.mauticInstall, name='mauticInstall'),
## Git ## Git
url(r'^(?P<domain>(.*))/setupGit$', views.setupGit, name='setupGit'), url(r'^(?P<domain>(.*))/setupGit$', views.setupGit, name='setupGit'),

View File

@@ -656,6 +656,22 @@ def magentoInstall(request):
except KeyError: except KeyError:
return redirect(loadLoginPage) 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): def prestaShopInstall(request):
try: try:
userID = request.session['userID'] userID = request.session['userID']

View File

@@ -2310,6 +2310,64 @@ StrictHostKeyChecking no
json_data = json.dumps(data_ret) json_data = json.dumps(data_ret)
return HttpResponse(json_data) 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): def prestaShopInstall(self, userID=None, data=None):
try: try: