From 7165433989b7caf41cc54529413880625f1ada64 Mon Sep 17 00:00:00 2001 From: usmannasir <01-134132-158@student.bahria.edu.pk> Date: Fri, 19 Oct 2018 22:47:15 +0500 Subject: [PATCH] Bug fixes to Email Marketing --- .../templates/baseTemplate/index.html | 1 + emailMarketing/emailMarketing.py | 68 +- emailMarketing/emailMarketingManager.py | 25 +- emailMarketing/urls.py | 1 + emailMarketing/views.py | 8 + install/install.py | 8 +- static/emailMarketing/checklist.png | Bin 0 -> 2961 bytes static/emailMarketing/compose.png | Bin 0 -> 2557 bytes static/emailMarketing/emailMarketing.js | 1323 +++++++++++++++++ static/emailMarketing/mailing.png | Bin 0 -> 2124 bytes static/emailMarketing/paper-plane.png | Bin 0 -> 3260 bytes static/emailMarketing/post-office.png | Bin 0 -> 2511 bytes 12 files changed, 1407 insertions(+), 27 deletions(-) create mode 100644 static/emailMarketing/checklist.png create mode 100644 static/emailMarketing/compose.png create mode 100644 static/emailMarketing/emailMarketing.js create mode 100644 static/emailMarketing/mailing.png create mode 100644 static/emailMarketing/paper-plane.png create mode 100644 static/emailMarketing/post-office.png diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index a46330835..40fd953d0 100755 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -554,6 +554,7 @@
  • {% trans "Email Policy Server" %}
  • {% trans "Email Limits" %}
  • {% trans "SpamAssassin" %}
  • +
  • {% trans "Email Marketing" %}
  • diff --git a/emailMarketing/emailMarketing.py b/emailMarketing/emailMarketing.py index ff6485d06..393c8d732 100644 --- a/emailMarketing/emailMarketing.py +++ b/emailMarketing/emailMarketing.py @@ -43,17 +43,21 @@ class emailMarketing(multi.Thread): with open(self.extraArgs['path'], 'r') as emailsList: data = csv.reader(emailsList, delimiter=',') for items in data: - for value in items: - if re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', value) != None: - try: - getEmail = EmailsInList.objects.get(owner=newList, email=value) - except: - newEmail = EmailsInList(owner=newList, email=value, - verificationStatus='NOT CHECKED', - dateCreated=time.strftime("%I-%M-%S-%a-%b-%Y")) - newEmail.save() - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], str(counter) + ' emails read.') - counter = counter + 1 + try: + for value in items: + if re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', value) != None: + try: + getEmail = EmailsInList.objects.get(owner=newList, email=value) + except: + newEmail = EmailsInList(owner=newList, email=value, + verificationStatus='NOT CHECKED', + dateCreated=time.strftime("%I-%M-%S-%a-%b-%Y")) + newEmail.save() + logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], str(counter) + ' emails read.') + counter = counter + 1 + except BaseException, msg: + logging.CyberCPLogFileWriter.writeToFile(str(msg)) + continue elif self.extraArgs['path'].endswith('.txt'): with open(self.extraArgs['path'], 'r') as emailsList: emails = emailsList.readline() @@ -137,13 +141,14 @@ class emailMarketing(multi.Thread): domain = verificationList.owner.domain tempStatusPath = '/home/cyberpanel/' + domain + "/" + self.extraArgs['listName'] logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, str(msg) +'. [404]') + logging.CyberCPLogFileWriter.writeToFile('your error') return 0 def startEmailJob(self): try: try: if self.extraArgs['host'] == 'localhost': - smtpServer = smtplib.SMTP(self.extraArgs['host']) + smtpServer = smtplib.SMTP('127.0.0.1') else: verifyHost = SMTPHosts.objects.get(host=self.extraArgs['host']) smtpServer = smtplib.SMTP(verifyHost.host, int(verifyHost.port)) @@ -169,6 +174,11 @@ class emailMarketing(multi.Thread): sent = 0 failed = 0 + ipFile = "/etc/cyberpanel/machineIP" + f = open(ipFile) + ipData = f.read() + ipAddress = ipData.split('\n', 1)[0] + ## Compose Message from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText @@ -178,17 +188,34 @@ class emailMarketing(multi.Thread): message['Subject'] = emailMessage.subject message['From'] = emailMessage.fromEmail - if re.search('', emailMessage.emailMessage, re.IGNORECASE): - html = MIMEText(emailMessage.emailMessage, 'html') - message.attach(html) - else: - html = MIMEText(emailMessage.emailMessage, 'plain') - message.attach(html) - for items in allEmails: - if items.verificationStatus == 'Verified' or self.extraArgs['verificationCheck']: + if (items.verificationStatus == 'Verified' or self.extraArgs['verificationCheck']) and not items.verificationStatus == 'REMOVED': try: + if re.search('', emailMessage.emailMessage, re.IGNORECASE): + html = MIMEText(emailMessage.emailMessage, 'html') + message.attach(html) + + if self.extraArgs['unsubscribeCheck']: + removalMessage = '

    Unsubscribe from future emails.' + additionalCheck = MIMEText(removalMessage, 'html') + message.attach(additionalCheck) + else: + html = MIMEText(emailMessage.emailMessage, 'plain') + message.attach(html) + if self.extraArgs['unsubscribeCheck']: + finalMessage = '\n\n Unsubscribe by visiting https://' + ipAddress + ':8090/emailMarketing/remove/' + \ + self.extraArgs[ + 'listName'] + "/" + items.email + + html = MIMEText(finalMessage, 'plain') + message.attach(html) + + + + smtpServer.sendmail(message['From'], items.email, message.as_string()) sent = sent + 1 logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], @@ -198,6 +225,7 @@ class emailMarketing(multi.Thread): logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Successfully sent: ' + str( sent) + ', Failed: ' + str(failed)) + logging.CyberCPLogFileWriter.writeToFile(str(msg)) emailJob = EmailJobs(owner=emailMessage, date=time.strftime("%I-%M-%S-%a-%b-%Y"), diff --git a/emailMarketing/emailMarketingManager.py b/emailMarketing/emailMarketingManager.py index 9acfb2a68..dd61dff80 100644 --- a/emailMarketing/emailMarketingManager.py +++ b/emailMarketing/emailMarketingManager.py @@ -662,8 +662,15 @@ class EmailMarketingManager: extraArgs['selectedTemplate'] = data['selectedTemplate'] extraArgs['listName'] = data['listName'] extraArgs['host'] = data['host'] - extraArgs['verificationCheck'] = data['verificationCheck'] - extraArgs['unsubscribeCheck'] = data['unsubscribeCheck'] + try: + extraArgs['verificationCheck'] = data['verificationCheck'] + except: + extraArgs['verificationCheck'] = False + try: + extraArgs['unsubscribeCheck'] = data['unsubscribeCheck'] + except: + extraArgs['unsubscribeCheck'] = False + extraArgs['tempStatusPath'] = "/home/cyberpanel/" + data['selectedTemplate'] + '_pendingJob' currentACL = ACLManager.loadedACL(userID) @@ -730,4 +737,16 @@ class EmailMarketingManager: except BaseException, msg: final_dic = {'status': 0, 'error_message': str(msg)} final_json = json.dumps(final_dic) - return HttpResponse(final_json) \ No newline at end of file + return HttpResponse(final_json) + + + def remove(self, listName, emailAddress): + try: + eList = EmailLists.objects.get(listName=listName) + removeEmail = EmailsInList.objects.get(owner=eList, email=emailAddress) + removeEmail.verificationStatus = 'REMOVED' + removeEmail.save() + except: + pass + + return HttpResponse('Email Address Successfully removed from the list.') \ No newline at end of file diff --git a/emailMarketing/urls.py b/emailMarketing/urls.py index b8bf67151..2f1f56aff 100644 --- a/emailMarketing/urls.py +++ b/emailMarketing/urls.py @@ -23,4 +23,5 @@ urlpatterns = [ url(r'^startEmailJob$', views.startEmailJob, name='startEmailJob'), url(r'^deleteTemplate$', views.deleteTemplate, name='deleteTemplate'), url(r'^deleteJob$', views.deleteJob, name='deleteJob'), + url(r'^remove/(?P[-\w]+)/(?P\w+@.+)$', views.remove, name='remove'), ] \ No newline at end of file diff --git a/emailMarketing/views.py b/emailMarketing/views.py index a5ba2efc4..76be7dd8f 100644 --- a/emailMarketing/views.py +++ b/emailMarketing/views.py @@ -179,5 +179,13 @@ def deleteJob(request): userID = request.session['userID'] emm = EmailMarketingManager(request) return emm.deleteJob() + except KeyError: + return redirect(loadLoginPage) + + +def remove(request, listName, emailAddress): + try: + emm = EmailMarketingManager(request) + return emm.remove(listName, emailAddress) except KeyError: return redirect(loadLoginPage) \ No newline at end of file diff --git a/install/install.py b/install/install.py index 46fa22abb..11163393c 100644 --- a/install/install.py +++ b/install/install.py @@ -687,8 +687,8 @@ class preFlightsChecks: count = 0 while (1): - #command = "wget http://cyberpanel.net/CyberPanel.1.7.2.tar.gz" - command = "wget http://cyberpanel.net/CyberPanelTemp.tar.gz" + command = "wget http://cyberpanel.net/CyberPanel.1.7.2.tar.gz" + #command = "wget http://cyberpanel.net/CyberPanelTemp.tar.gz" res = subprocess.call(shlex.split(command)) if res == 1: @@ -707,8 +707,8 @@ class preFlightsChecks: count = 0 while(1): - #command = "tar zxf CyberPanel.1.7.2.tar.gz" - command = "tar zxf CyberPanelTemp.tar.gz" + command = "tar zxf CyberPanel.1.7.2.tar.gz" + #command = "tar zxf CyberPanelTemp.tar.gz" res = subprocess.call(shlex.split(command)) diff --git a/static/emailMarketing/checklist.png b/static/emailMarketing/checklist.png new file mode 100644 index 0000000000000000000000000000000000000000..bb709fb21690ff7679504ba461ea2c4bf7d746ac GIT binary patch literal 2961 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 T|KVWZ00000NkvXXu0mjfO;z3U literal 0 HcmV?d00001 diff --git a/static/emailMarketing/emailMarketing.js b/static/emailMarketing/emailMarketing.js new file mode 100644 index 000000000..2e32e0a98 --- /dev/null +++ b/static/emailMarketing/emailMarketing.js @@ -0,0 +1,1323 @@ +/** + * Created by usman on 8/1/17. + */ + +var emailListURL = "/emailMarketing/"+ $("#domainNamePage").text() + "/emailLists"; +$("#emailLists").attr("href", emailListURL); +$("#emailListsChild").attr("href", emailListURL); + +var manageListsURL = "/emailMarketing/"+ $("#domainNamePage").text() + "/manageLists"; +$("#manageLists").attr("href", manageListsURL); +$("#manageListsChild").attr("href", manageListsURL); + +var sendEmailsURL = "/emailMarketing/sendEmails"; +$("#sendEmailsPage").attr("href", sendEmailsURL); +$("#sendEmailsPageChild").attr("href", sendEmailsURL); + +var composeEmailURL = "/emailMarketing/composeEmailMessage"; +$("#composeEmails").attr("href", composeEmailURL); +$("#composeEmailsChild").attr("href", composeEmailURL); + +var smtpHostsURL = "/emailMarketing/"+ $("#domainNamePage").text() + "/manageSMTP"; +$("#manageSMTPHosts").attr("href", smtpHostsURL); +$("#manageSMTPHostsChild").attr("href", smtpHostsURL); + + +app.controller('emailMarketing', function($scope,$http) { + + $scope.cyberPanelLoading = true; + $scope.fetchUsers = function(){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/fetchUsers"; + + var data = {}; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + $scope.users = JSON.parse(response.data.data); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + $scope.fetchUsers(); + $scope.enableDisableMarketing = function(status, userName){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/enableDisableMarketing"; + + var data = {userName: userName}; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + $scope.fetchUsers(); + + if(response.data.status === 1){ + new PNotify({ + title: 'Success!', + text: 'Changes successfully saved.', + type:'success' + }); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; +}); + +app.controller('createEmailList', function($scope, $http, $timeout) { + + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = true; + + var statusFile; + var path; + + + $scope.goBack = function () { + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = true; + $("#installProgress").css("width", "0%"); + }; + + $scope.createEmailList = function(){ + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.cyberPanelLoading = false; + $scope.goBackDisable = true; + $scope.currentStatus = "Starting to load email addresses.."; + + + url = "/emailMarketing/submitEmailList"; + + var data = { + domain: $("#domainNamePage").text(), + path:$scope.path, + listName: $scope.listName + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + if (response.data.status === 1) + { + statusFile = response.data.tempStatusPath; + getInstallStatus(); + } + else{ + + $scope.installationDetailsForm = true; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = false; + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + function getInstallStatus(){ + + url = "/websites/installWordpressStatus"; + + var data = { + statusFile: statusFile, + domainName: $("#domainNamePage").text() + }; + + 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.cyberPanelLoading = true; + $scope.goBackDisable = false; + $scope.currentStatus = 'Emails successfully loaded.'; + $timeout.cancel(); + + } + else{ + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = false; + $scope.currentStatus = response.data.error_message; + + + } + + } + else{ + $scope.installPercentage = response.data.installationProgress; + $scope.currentStatus = response.data.currentStatus; + + $timeout(getInstallStatus,1000); + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + + } + + + } + + $scope.fetchEmails = function(){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/fetchEmails"; + + var data = {'listName': $scope.listName}; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + $scope.records = JSON.parse(response.data.data); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + +}); + +app.controller('manageEmailLists', function($scope, $http, $timeout) { + + $scope.installationDetailsForm = true; + $scope.installationProgress = true; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = true; + $scope.verificationStatus = true; + + var statusFile; + var path; + + + $scope.goBack = function () { + $scope.installationDetailsForm = false; + $scope.installationProgress = true; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = true; + $("#installProgress").css("width", "0%"); + }; + + $scope.createEmailList = function(){ + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.cyberPanelLoading = false; + $scope.goBackDisable = true; + $scope.currentStatus = "Starting to load email addresses.."; + + + url = "/emailMarketing/submitEmailList"; + + var data = { + domain: $("#domainNamePage").text(), + path:$scope.path, + listName: $scope.listName + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + if (response.data.status === 1) + { + statusFile = response.data.tempStatusPath; + getInstallStatus(); + } + else{ + + $scope.installationDetailsForm = true; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = false; + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + function getInstallStatus(){ + + url = "/websites/installWordpressStatus"; + + var data = { + statusFile: statusFile, + domainName: $("#domainNamePage").text() + }; + + 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.cyberPanelLoading = true; + $scope.goBackDisable = false; + $scope.currentStatus = 'Emails successfully loaded.'; + $timeout.cancel(); + + } + else{ + + $scope.installationDetailsForm = true; + $scope.installationProgress = false; + $scope.cyberPanelLoading = true; + $scope.goBackDisable = false; + $scope.currentStatus = response.data.error_message; + + + } + + } + else{ + $scope.installPercentage = response.data.installationProgress; + $scope.currentStatus = response.data.currentStatus; + + $timeout(getInstallStatus,1000); + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + + } + + + } + + + /// + + $scope.currentRecords = true; + + $scope.recordstoShow = 50; + var globalPage; + + $scope.fetchRecords = function () { + $scope.fetchEmails(globalPage); + }; + + $scope.fetchEmails = function(page){ + globalPage = page; + listVerificationStatus(); + + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/fetchEmails"; + + var data = { + 'listName': $scope.listName, + 'recordstoShow': $scope.recordstoShow, + 'page': page + + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + $scope.currentRecords = false; + $scope.records = JSON.parse(response.data.data); + $scope.pagination = response.data.pagination; + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + $scope.deleteList = function () { + + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/deleteList"; + + var data = { + listName: $scope.listName + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + if (response.data.status === 1) + { + new PNotify({ + title: 'Success!', + text: 'Emails Successfully Deleted.', + type:'success' + }); + } + else{ + + $scope.cyberPanelLoading = false; + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + $scope.showAddEmails = function () { + $scope.installationDetailsForm = false; + $scope.verificationStatus = true; + }; + + // List Verification + + $scope.startVerification = function () { + + $scope.currentStatusVerification = 'Email verification job started..'; + $scope.installationDetailsForm = true; + $scope.verificationStatus = false; + $scope.verificationButton = true; + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/emailVerificationJob"; + + var data = { + listName: $scope.listName + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + if (response.data.status === 1) + { + listVerificationStatus(); + $scope.verificationButton = true; + } + else{ + + $scope.cyberPanelLoading = true; + $scope.verificationButton = false; + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + $scope.verificationButton = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + + }; + + var globalCounter = 0; + + function listVerificationStatus(){ + + $scope.verificationButton = true; + $scope.cyberPanelLoading = false; + + url = "/websites/installWordpressStatus"; + + var data = { + domain: $("#domainNamePage").text(), + statusFile: "/home/cyberpanel/" + $("#domainNamePage").text() + "/" + $scope.listName + }; + + 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.cyberPanelLoading = true; + $scope.verificationButton = false; + $scope.currentStatusVerification = 'Emails successfully verified.'; + $timeout.cancel(); + + } + else{ + + if(response.data.error_message.search('No such file') > -1){ + $scope.verificationButton = false; + return; + } + $scope.verificationButton = true; + $scope.cyberPanelLoading = false; + $scope.verificationStatus = false; + $scope.currentStatusVerification = response.data.error_message; + + } + + } + else{ + $scope.currentStatusVerification = response.data.currentStatus; + $timeout(listVerificationStatus,1000); + $scope.verificationStatus = false; + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + + } + + + } + + // Delete Email from list + + $scope.deleteEmail = function(id){ + + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/deleteEmail"; + + var data = { + id: id + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + $scope.fetchEmails(globalPage); + + if (response.data.status === 1) + { + $scope.fetchEmails(globalPage); + new PNotify({ + title: 'Success.', + text: 'Email Successfully deleted.', + type:'success' + }); + + } + else{ + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + +}); + +app.controller('manageSMTPHostsCTRL', function($scope, $http) { + + $scope.cyberPanelLoading = true; + $scope.fetchSMTPHosts = function(){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/fetchSMTPHosts"; + + var data = {}; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + $scope.records = JSON.parse(response.data.data); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + $scope.fetchSMTPHosts(); + $scope.saveSMTPHost = function(status, userName){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/saveSMTPHost"; + + var data = { + smtpHost: $scope.smtpHost, + smtpPort: $scope.smtpPort, + smtpUserName: $scope.smtpUserName, + smtpPassword: $scope.smtpPassword + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + $scope.fetchSMTPHosts(); + new PNotify({ + title: 'Success!', + text: 'Successfully saved new SMTP host.', + type:'success' + }); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + $scope.smtpHostOperations = function(operation, id){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/smtpHostOperations"; + + var data = { + id: id, + operation: operation + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + $scope.fetchSMTPHosts(); + + if(response.data.status === 1){ + new PNotify({ + title: 'Success!', + text: response.data.message, + type:'success' + }); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; +}); + +app.controller('composeMessageCTRL', function($scope, $http) { + + $scope.cyberPanelLoading = true; + $scope.saveTemplate = function(status, userName){ + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/saveEmailTemplate"; + + var data = { + name: $scope.name, + subject: $scope.subject, + fromName: $scope.fromName, + fromEmail: $scope.fromEmail, + replyTo: $scope.replyTo, + emailMessage: $scope.emailMessage + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + new PNotify({ + title: 'Success!', + text: 'Template successfully saved.', + type:'success' + }); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; +}); + +app.controller('sendEmailsCTRL', function($scope, $http, $timeout) { + + $scope.cyberPanelLoading = true; + $scope.availableFunctions = true; + $scope.sendEmailsView = true; + $scope.jobStatus = true; + + // Button + + $scope.deleteTemplateBTN = false; + $scope.sendEmailBTN = false; + + $scope.templateSelected = function () { + $scope.availableFunctions = false; + $scope.sendEmailsView = true; + $scope.previewLink = '/emailMarketing/preview/' + $scope.selectedTemplate; + $scope.jobStatus = true; + emailJobStatus(); + + }; + + $scope.sendEmails = function () { + $scope.sendEmailsView = false; + $scope.fetchJobs(); + }; + + $scope.fetchJobs = function(){ + + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/fetchJobs"; + + var data = { + 'selectedTemplate': $scope.selectedTemplate + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + $scope.currentRecords = false; + $scope.records = JSON.parse(response.data.data); + } + else{ + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + + $scope.startEmailJob = function () { + $scope.cyberPanelLoading = false; + $scope.deleteTemplateBTN = true; + $scope.sendEmailBTN = true; + $scope.sendEmailsView = true; + $scope.goBackDisable = true; + + url = "/emailMarketing/startEmailJob"; + + + var data = { + 'selectedTemplate': $scope.selectedTemplate, + 'listName': $scope.listName, + 'host': $scope.host, + 'verificationCheck': $scope.verificationCheck, + 'unsubscribeCheck': $scope.unsubscribeCheck + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if(response.data.status === 1){ + emailJobStatus(); + } + else{ + $scope.cyberPanelLoading = true; + $scope.deleteTemplateBTN = false; + $scope.sendEmailBTN = false; + $scope.sendEmailsView = false; + $scope.jobStatus = true; + $scope.goBackDisable = false; + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + } + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = false; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + + }; + + function emailJobStatus(){ + + $scope.cyberPanelLoading = false; + $scope.deleteTemplateBTN = true; + $scope.sendEmailBTN = true; + $scope.sendEmailsView = true; + $scope.jobStatus = false; + $scope.goBackDisable = true; + + url = "/websites/installWordpressStatus"; + + var data = { + domain: 'example.com', + statusFile: "/home/cyberpanel/" + $scope.selectedTemplate + "_pendingJob" + }; + + 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.cyberPanelLoading = true; + $scope.deleteTemplateBTN = false; + $scope.sendEmailBTN = false; + $scope.sendEmailsView = true; + $scope.jobStatus = false; + $scope.goBackDisable = false; + $scope.currentStatus = 'Emails successfully sent.'; + $scope.fetchJobs(); + $timeout.cancel(); + + } + else{ + + if(response.data.error_message.search('No such file') > -1){ + $scope.cyberPanelLoading = true; + $scope.deleteTemplateBTN = false; + $scope.sendEmailBTN = false; + $scope.sendEmailsView = true; + $scope.jobStatus = true; + $scope.goBackDisable = false; + return; + } + $scope.cyberPanelLoading = true; + $scope.deleteTemplateBTN = false; + $scope.sendEmailBTN = false; + $scope.sendEmailsView = true; + $scope.jobStatus = false; + $scope.goBackDisable = false; + $scope.currentStatus = response.data.error_message; + + } + + } + else{ + $scope.currentStatus = response.data.currentStatus; + $timeout(emailJobStatus,1000); + $scope.cyberPanelLoading = false; + $scope.deleteTemplateBTN = true; + $scope.sendEmailBTN = true; + $scope.sendEmailsView = true; + $scope.jobStatus = false; + $scope.goBackDisable = true; + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page.', + type:'error' + }); + + + } + + + } + + $scope.goBack = function () { + $scope.cyberPanelLoading = true; + $scope.deleteTemplateBTN = false; + $scope.sendEmailBTN = false; + $scope.sendEmailsView = false; + $scope.jobStatus = true; + + }; + + $scope.deleteTemplate = function(){ + + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/deleteTemplate"; + + var data = { + selectedTemplate: $scope.selectedTemplate + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + + if (response.data.status === 1) + { + new PNotify({ + title: 'Success.', + text: 'Template Successfully deleted.', + type:'success' + }); + + } + else{ + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; + $scope.deleteJob = function(id){ + + $scope.cyberPanelLoading = false; + + url = "/emailMarketing/deleteJob"; + + var data = { + id: id + }; + + var config = { + headers : { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data,config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + $scope.cyberPanelLoading = true; + $scope.fetchJobs(); + + if (response.data.status === 1) + { + new PNotify({ + title: 'Success.', + text: 'Template Successfully deleted.', + type:'success' + }); + + } + else{ + + new PNotify({ + title: 'Operation Failed!', + text: response.data.error_message, + type:'error' + }); + + } + + } + function cantLoadInitialDatas(response) { + $scope.cyberPanelLoading = true; + new PNotify({ + title: 'Operation Failed!', + text: 'Could not connect to server, please refresh this page', + type:'error' + }); + + } + + }; +}); + + diff --git a/static/emailMarketing/mailing.png b/static/emailMarketing/mailing.png new file mode 100644 index 0000000000000000000000000000000000000000..b8586aea2c6ae186c8d02b71572fd44ae17f9dc6 GIT binary patch literal 2124 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 Z;GZ;>Ulnqt#w!2-002ovPDHLkV1kgQjobhL literal 0 HcmV?d00001