mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-03-25 21:40:07 +01:00
FTP: path normalization, post-create directory edit, enable/disable
- Resolve FTP home paths without duplicating /home/domain; support absolute paths under site home - Add changeFTPDirectory API and list UI; improve create form path help - Add setFTPAccountStatus (Status 0/1) with Enable/Disable on list page - Pure-FTPd MySQL: require Status='1' for authentication in install templates - Plugin signals for change directory and account status
This commit is contained in:
@@ -283,6 +283,7 @@ class FTPManager:
|
||||
'custom_quota_enabled': items.custom_quota_enabled,
|
||||
'custom_quota_size': items.custom_quota_size,
|
||||
'package_quota': items.domain.package.diskSpace,
|
||||
'acct_enabled': (str(items.status) == '1'),
|
||||
}
|
||||
|
||||
if checker == 0:
|
||||
@@ -330,6 +331,75 @@ class FTPManager:
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def changeFTPDirectory(self):
|
||||
"""
|
||||
Change FTP account home directory after creation.
|
||||
Uses listFTPAccounts permission (same as password / quota).
|
||||
"""
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if ACLManager.currentContextPermission(currentACL, 'listFTPAccounts') == 0:
|
||||
return ACLManager.loadErrorJson('changeDirectoryStatus', 0)
|
||||
|
||||
data = json.loads(self.request.body)
|
||||
userName = data['ftpUserName']
|
||||
selectedDomain = data['selectedDomain']
|
||||
newPath = data.get('path', '')
|
||||
if newPath is None:
|
||||
newPath = ''
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if ACLManager.checkOwnership(selectedDomain, admin, currentACL) != 1:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
ftp = Users.objects.get(user=userName)
|
||||
if currentACL['admin'] != 1 and ftp.domain.admin != admin:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
result = FTPUtilities.changeFTPDirectory(userName, newPath, selectedDomain)
|
||||
if result[0] == 1:
|
||||
data_ret = {'status': 1, 'changeDirectoryStatus': 1, 'error_message': 'None'}
|
||||
else:
|
||||
data_ret = {'status': 0, 'changeDirectoryStatus': 0, 'error_message': result[1]}
|
||||
return HttpResponse(json.dumps(data_ret), content_type='application/json')
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'changeDirectoryStatus': 0, 'error_message': str(msg)}
|
||||
return HttpResponse(json.dumps(data_ret), content_type='application/json')
|
||||
|
||||
def setFTPAccountStatus(self):
|
||||
"""Enable (enabled=true) or disable (enabled=false) FTP login for a user."""
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if ACLManager.currentContextPermission(currentACL, 'listFTPAccounts') == 0:
|
||||
return ACLManager.loadErrorJson('setFTPStatusResult', 0)
|
||||
|
||||
data = json.loads(self.request.body)
|
||||
userName = data['ftpUserName']
|
||||
selectedDomain = data['selectedDomain']
|
||||
enabled = bool(data.get('enabled', True))
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if ACLManager.checkOwnership(selectedDomain, admin, currentACL) != 1:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
ftp = Users.objects.get(user=userName)
|
||||
if currentACL['admin'] != 1 and ftp.domain.admin != admin:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
result = FTPUtilities.setFTPAccountStatus(userName, enabled, selectedDomain)
|
||||
if result[0] == 1:
|
||||
data_ret = {'status': 1, 'setFTPStatusResult': 1, 'error_message': 'None'}
|
||||
else:
|
||||
data_ret = {'status': 0, 'setFTPStatusResult': 0, 'error_message': result[1]}
|
||||
return HttpResponse(json.dumps(data_ret), content_type='application/json')
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'setFTPStatusResult': 0, 'error_message': str(msg)}
|
||||
return HttpResponse(json.dumps(data_ret), content_type='application/json')
|
||||
|
||||
def updateFTPQuota(self):
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
|
||||
@@ -33,4 +33,20 @@ class pluginManager:
|
||||
|
||||
@staticmethod
|
||||
def postChangePassword(request, response):
|
||||
return pluginManagerGlobal.globalPlug(request, postChangePassword, response)
|
||||
return pluginManagerGlobal.globalPlug(request, postChangePassword, response)
|
||||
|
||||
@staticmethod
|
||||
def preChangeFTPDirectory(request):
|
||||
return pluginManagerGlobal.globalPlug(request, preChangeFTPDirectory)
|
||||
|
||||
@staticmethod
|
||||
def postChangeFTPDirectory(request, response):
|
||||
return pluginManagerGlobal.globalPlug(request, postChangeFTPDirectory, response)
|
||||
|
||||
@staticmethod
|
||||
def preSetFTPAccountStatus(request):
|
||||
return pluginManagerGlobal.globalPlug(request, preSetFTPAccountStatus)
|
||||
|
||||
@staticmethod
|
||||
def postSetFTPAccountStatus(request, response):
|
||||
return pluginManagerGlobal.globalPlug(request, postSetFTPAccountStatus, response)
|
||||
@@ -26,4 +26,11 @@ postSubmitFTPDelete = Signal()
|
||||
preChangePassword = Signal()
|
||||
|
||||
## This event is fired after CyberPanel core finished deletion of child-domain
|
||||
postChangePassword = Signal()
|
||||
postChangePassword = Signal()
|
||||
|
||||
## Before / after changing FTP account home directory (list FTP page)
|
||||
preChangeFTPDirectory = Signal()
|
||||
postChangeFTPDirectory = Signal()
|
||||
|
||||
preSetFTPAccountStatus = Signal()
|
||||
postSetFTPAccountStatus = Signal()
|
||||
@@ -109,15 +109,7 @@ app.controller('createFTPAccount', function ($scope, $http) {
|
||||
$scope.errorMessage = "Invalid path: Path cannot contain '..' or '~'";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if path starts with slash (should be relative)
|
||||
if (path.startsWith("/")) {
|
||||
$scope.ftpLoading = false;
|
||||
resetFtpCreateAlerts();
|
||||
$scope.alertFtpCreateError = true;
|
||||
$scope.errorMessage = "Invalid path: Path must be relative (not starting with '/')";
|
||||
return;
|
||||
}
|
||||
// Absolute paths under /home/... are allowed; server validates they stay inside the site home
|
||||
}
|
||||
|
||||
var url = "/ftp/submitFTPCreation";
|
||||
@@ -375,6 +367,7 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
|
||||
var globalFTPUsername = "";
|
||||
@@ -390,6 +383,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false; // Don't show loading when opening password dialog
|
||||
$scope.changePasswordBox = false;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = ftpUsername;
|
||||
globalFTPUsername = ftpUsername;
|
||||
@@ -454,6 +449,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = true; // Show loading while fetching
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
var selectedDomain = $scope.selectedDomain;
|
||||
|
||||
@@ -479,7 +476,11 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
if (response.data.fetchStatus == 1) {
|
||||
|
||||
$scope.records = JSON.parse(response.data.data);
|
||||
|
||||
angular.forEach($scope.records, function (r) {
|
||||
if (typeof r.acct_enabled === 'undefined') {
|
||||
r.acct_enabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.notificationsBox = false;
|
||||
$scope.recordsFetched = false;
|
||||
@@ -489,6 +490,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading when done
|
||||
$scope.ftpAccounts = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
$scope.domainFeteched = $scope.selectedDomain;
|
||||
|
||||
@@ -501,6 +504,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading on error
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
}
|
||||
@@ -516,6 +521,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading on connection error
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
|
||||
}
|
||||
@@ -542,6 +549,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.quotaManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
@@ -630,6 +639,128 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.manageDirectory = function (record) {
|
||||
$scope.recordsFetched = true;
|
||||
$scope.passwordChanged = true;
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
globalFTPUsername = record.user;
|
||||
$scope.ftpPathEdit = record.dir || '';
|
||||
};
|
||||
|
||||
$scope.changeDirectoryBtn = function () {
|
||||
$scope.ftpLoading = true;
|
||||
var url = "/ftp/changeFTPDirectory";
|
||||
var pathVal = $scope.ftpPathEdit;
|
||||
if (typeof pathVal === 'undefined' || pathVal === null) {
|
||||
pathVal = '';
|
||||
} else {
|
||||
pathVal = String(pathVal).trim();
|
||||
}
|
||||
var data = {
|
||||
ftpUserName: globalFTPUsername,
|
||||
selectedDomain: $scope.selectedDomain,
|
||||
path: pathVal
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
if (response.data.changeDirectoryStatus === 1) {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.directoryManagementBox = true;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'FTP directory updated successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
populateCurrentRecords();
|
||||
} else {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas() {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.couldNotConnect = false;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setFtpAccountStatus = function (record, enabled) {
|
||||
$scope.ftpLoading = true;
|
||||
var url = "/ftp/setFTPAccountStatus";
|
||||
var data = {
|
||||
ftpUserName: record.user,
|
||||
selectedDomain: $scope.selectedDomain,
|
||||
enabled: !!enabled
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
$http.post(url, data, config).then(function (response) {
|
||||
$scope.ftpLoading = false;
|
||||
if (response.data.setFTPStatusResult === 1) {
|
||||
record.acct_enabled = !!enabled;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: enabled ? 'FTP account enabled.' : 'FTP account disabled.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
populateCurrentRecords();
|
||||
} else {
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}, function () {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.couldNotConnect = false;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -569,26 +569,26 @@
|
||||
</div>
|
||||
|
||||
<div ng-hide="ftpDetails" class="form-group">
|
||||
<label class="form-label">{% trans "Path (Relative)" %}</label>
|
||||
<label class="form-label">{% trans "FTP home path" %}</label>
|
||||
<div class="path-info">
|
||||
<i class="fas fa-folder"></i>
|
||||
{% trans "Leave empty to use the website's home directory, or specify a subdirectory" %}
|
||||
{% trans "Leave empty for the website's home directory. Use a subdirectory, or the full path under that home." %}
|
||||
<br>
|
||||
<small style="margin-top: 0.5rem; display: block; color: var(--text-secondary, #64748b);">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>{% trans "Examples:" %}</strong> {% trans "docs, public_html, uploads, api" %}
|
||||
<strong>{% trans "Examples:" %}</strong> {% trans "public_html, uploads, or /home/yourdomain.com/public_html" %}
|
||||
<br>
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
{% trans "Security: Path will be restricted to this subdirectory only" %}
|
||||
{% trans "Security: FTP will be restricted to this directory only (must stay inside the site home)" %}
|
||||
</small>
|
||||
</div>
|
||||
<input placeholder="{% trans 'e.g., docs or public_html (leave empty for home directory)' %}"
|
||||
<input placeholder="{% trans 'e.g. public_html or full path under site home (optional)' %}"
|
||||
type="text" class="form-control" ng-model="ftpPath"
|
||||
pattern="^[a-zA-Z0-9._/-]+$"
|
||||
title="{% trans 'Only letters, numbers, dots, underscores, hyphens, and forward slashes allowed' %}">
|
||||
<small style="color: var(--text-secondary, #64748b); margin-top: 0.5rem; display: block;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{% trans "Do not use: .. ~ / or special characters like ; | & $ ` ' \" < > * ?" %}
|
||||
{% trans "Do not use: .. ~ or special characters like ; | & $ ` ' \" < > * ?" %}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -301,6 +301,10 @@
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ftp-row-disabled td {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
@@ -541,25 +545,58 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change FTP home directory -->
|
||||
<div ng-hide="directoryManagementBox" class="password-section">
|
||||
<h3><i class="fas fa-folder-open"></i> {% trans "FTP home directory for" %} {$ ftpUsername $}</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Path" %}</label>
|
||||
<div class="path-info" style="margin-bottom: 0.75rem;">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
{% trans "Leave empty to use this site's home directory. You may enter a subdirectory or the full path under that home (no path traversal)." %}
|
||||
</div>
|
||||
<input type="text" class="form-control" ng-model="ftpPathEdit"
|
||||
placeholder="{% trans 'e.g. public_html/sub or full path under site home' %}">
|
||||
</div>
|
||||
<div style="margin-top: 1rem;">
|
||||
<button type="button" ng-click="changeDirectoryBtn()" class="btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
{% trans "Update Directory" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FTP Accounts Table -->
|
||||
<div ng-hide="ftpAccounts">
|
||||
<table class="ftp-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 8%;">{% trans "ID" %}</th>
|
||||
<th style="width: 20%;">{% trans "User Name" %}</th>
|
||||
<th style="width: 30%;">{% trans "Directory" %}</th>
|
||||
<th style="width: 15%;">{% trans "Quota" %}</th>
|
||||
<th style="width: 27%; text-align: center;">{% trans "Actions" %}</th>
|
||||
<th style="width: 7%;">{% trans "ID" %}</th>
|
||||
<th style="width: 16%;">{% trans "User Name" %}</th>
|
||||
<th style="width: 10%;">{% trans "Status" %}</th>
|
||||
<th style="width: 24%;">{% trans "Directory" %}</th>
|
||||
<th style="width: 12%;">{% trans "Quota" %}</th>
|
||||
<th style="width: 31%; text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<tr ng-repeat="record in records track by $index" ng-class="{'ftp-row-disabled': !record.acct_enabled}">
|
||||
<td><strong ng-bind="record.id"></strong></td>
|
||||
<td>
|
||||
<i class="fas fa-user-circle" style="color: var(--accent-color, #5b5fcf); margin-right: 0.5rem;"></i>
|
||||
<span ng-bind="record.user"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="quota-badge" ng-show="record.acct_enabled" style="background: rgba(34,197,94,0.15); color: #16a34a;">
|
||||
<i class="fas fa-check-circle"></i> {% trans "Enabled" %}
|
||||
</span>
|
||||
<span class="quota-badge" ng-hide="record.acct_enabled" style="background: rgba(239,68,68,0.12); color: #dc2626;">
|
||||
<i class="fas fa-ban"></i> {% trans "Disabled" %}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="directory-badge">
|
||||
<i class="fas fa-folder"></i>
|
||||
@@ -579,6 +616,18 @@
|
||||
<i class="fas fa-hdd"></i>
|
||||
{% trans "Quota" %}
|
||||
</button>
|
||||
<button type="button" ng-click="manageDirectory(record)" class="btn-action" title="{% trans 'Change which directory this FTP user is locked to' %}">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
{% trans "Directory" %}
|
||||
</button>
|
||||
<button type="button" ng-click="setFtpAccountStatus(record, false)" ng-show="record.acct_enabled" class="btn-action" style="opacity: 0.95;" title="{% trans 'Block FTP login without deleting the account' %}">
|
||||
<i class="fas fa-user-slash"></i>
|
||||
{% trans "Disable" %}
|
||||
</button>
|
||||
<button type="button" ng-click="setFtpAccountStatus(record, true)" ng-hide="record.acct_enabled" class="btn-action" title="{% trans 'Allow FTP login again' %}">
|
||||
<i class="fas fa-user-check"></i>
|
||||
{% trans "Enable" %}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -15,6 +15,8 @@ urlpatterns = [
|
||||
path('listFTPAccounts', views.listFTPAccounts, name='listFTPAccounts'),
|
||||
path('getAllFTPAccounts', views.getAllFTPAccounts, name='getAllFTPAccounts'),
|
||||
path('changePassword', views.changePassword, name='changePassword'),
|
||||
path('changeFTPDirectory', views.changeFTPDirectory, name='changeFTPDirectory'),
|
||||
path('setFTPAccountStatus', views.setFTPAccountStatus, name='setFTPAccountStatus'),
|
||||
path('updateFTPQuota', views.updateFTPQuota, name='updateFTPQuota'),
|
||||
path('getFTPQuotaUsage', views.getFTPQuotaUsage, name='getFTPQuotaUsage'),
|
||||
path('migrateFTPQuotas', views.migrateFTPQuotas, name='migrateFTPQuotas'),
|
||||
|
||||
36
ftp/views.py
36
ftp/views.py
@@ -255,6 +255,42 @@ def changePassword(request):
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def changeFTPDirectory(request):
|
||||
try:
|
||||
result = pluginManager.preChangeFTPDirectory(request)
|
||||
if result != 200:
|
||||
return result
|
||||
|
||||
fm = FTPManager(request)
|
||||
coreResult = fm.changeFTPDirectory()
|
||||
|
||||
result = pluginManager.postChangeFTPDirectory(request, coreResult)
|
||||
if result != 200:
|
||||
return result
|
||||
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def setFTPAccountStatus(request):
|
||||
try:
|
||||
result = pluginManager.preSetFTPAccountStatus(request)
|
||||
if result != 200:
|
||||
return result
|
||||
|
||||
fm = FTPManager(request)
|
||||
coreResult = fm.setFTPAccountStatus()
|
||||
|
||||
result = pluginManager.postSetFTPAccountStatus(request, coreResult)
|
||||
if result != 200:
|
||||
return result
|
||||
|
||||
return coreResult
|
||||
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def updateFTPQuota(request):
|
||||
try:
|
||||
fm = FTPManager(request)
|
||||
|
||||
@@ -3,12 +3,12 @@ MYSQLPort 3306
|
||||
MYSQLSocket /var/lib/mysql/mysql.sock
|
||||
MYSQLDatabase cyberpanel
|
||||
MYSQLCrypt md5
|
||||
MYSQLGetDir SELECT Dir FROM users WHERE User='\L'
|
||||
MYSQLGetGID SELECT Gid FROM users WHERE User='\L'
|
||||
MYSQLGetPW SELECT Password FROM users WHERE User='\L'
|
||||
MYSQLGetUID SELECT Uid FROM users WHERE User='\L'
|
||||
# Quota enforcement queries
|
||||
MYSQLGetQTAFS SELECT QuotaSize FROM users WHERE User='\L'
|
||||
MYSQLGetQTAUS SELECT 0 FROM users WHERE User='\L'
|
||||
# Only accounts with Status='1' can authenticate (disabled = Status '0')
|
||||
MYSQLGetDir SELECT Dir FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetGID SELECT Gid FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetPW SELECT Password FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetUID SELECT Uid FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetQTAFS SELECT QuotaSize FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetQTAUS SELECT 0 FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLPassword 1qaz@9xvps
|
||||
MYSQLUser cyberpanel
|
||||
|
||||
@@ -3,12 +3,13 @@ MYSQLPort 3307
|
||||
MYSQLSocket /var/lib/mysql1/mysql.sock
|
||||
MYSQLDatabase cyberpanel
|
||||
MYSQLCrypt md5
|
||||
MYSQLGetDir SELECT Dir FROM users WHERE User='\L'
|
||||
MYSQLGetGID SELECT Gid FROM users WHERE User='\L'
|
||||
MYSQLGetPW SELECT Password FROM users WHERE User='\L'
|
||||
MYSQLGetUID SELECT Uid FROM users WHERE User='\L'
|
||||
# Only accounts with Status='1' can authenticate (disabled = Status '0')
|
||||
MYSQLGetDir SELECT Dir FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetGID SELECT Gid FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetPW SELECT Password FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetUID SELECT Uid FROM users WHERE User='\L' AND Status='1'
|
||||
# Quota enforcement queries
|
||||
MYSQLGetQTAFS SELECT QuotaSize FROM users WHERE User='\L'
|
||||
MYSQLGetQTAUS SELECT 0 FROM users WHERE User='\L'
|
||||
MYSQLGetQTAFS SELECT QuotaSize FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLGetQTAUS SELECT 0 FROM users WHERE User='\L' AND Status='1'
|
||||
MYSQLPassword 1qaz@9xvps
|
||||
MYSQLUser cyberpanel
|
||||
|
||||
@@ -22,6 +22,57 @@ from plogical.processUtilities import ProcessUtilities
|
||||
|
||||
class FTPUtilities:
|
||||
|
||||
@staticmethod
|
||||
def get_domain_home_directory(domain_name):
|
||||
"""
|
||||
Filesystem root for the selected site: primary domain /home/<domain>,
|
||||
or child domain /home/<master>/<child.path> per CyberPanel vhost layout.
|
||||
"""
|
||||
try:
|
||||
child = ChildDomains.objects.select_related('master').get(domain=domain_name)
|
||||
master_dom = child.master.domain
|
||||
rel = (child.path or '').strip().strip('/')
|
||||
if rel:
|
||||
return os.path.abspath('/home/%s/%s' % (master_dom, rel))
|
||||
except ChildDomains.DoesNotExist:
|
||||
pass
|
||||
return os.path.abspath('/home/' + domain_name)
|
||||
|
||||
@staticmethod
|
||||
def assert_ftp_raw_path_safe(raw):
|
||||
"""Reject shell metacharacters and obvious traversal markers in user input."""
|
||||
if raw is None or not str(raw).strip():
|
||||
return
|
||||
s = str(raw)
|
||||
dangerous_chars = [';', '|', '&', '$', '`', '\'', '"', '<', '>', '*', '?']
|
||||
if any(char in s for char in dangerous_chars):
|
||||
raise BaseException("Invalid path: Path contains dangerous characters")
|
||||
if '..' in s or '~' in s:
|
||||
raise BaseException("Invalid path: Path cannot contain '..' or '~'")
|
||||
|
||||
@staticmethod
|
||||
def resolve_ftp_home_path(domain_name, raw_path):
|
||||
"""
|
||||
Resolve FTP home directory under domain_name.
|
||||
Empty / None / 'None' -> domain document root only.
|
||||
Absolute paths are allowed if they resolve under that root (no /home duplication).
|
||||
"""
|
||||
domain_home = FTPUtilities.get_domain_home_directory(domain_name)
|
||||
if raw_path is None:
|
||||
return domain_home
|
||||
raw = str(raw_path).strip()
|
||||
if raw == '' or raw == 'None':
|
||||
return domain_home
|
||||
FTPUtilities.assert_ftp_raw_path_safe(raw)
|
||||
if raw.startswith('/'):
|
||||
candidate = os.path.abspath(raw)
|
||||
else:
|
||||
candidate = os.path.abspath(os.path.join(domain_home, raw))
|
||||
dh = domain_home
|
||||
if candidate != dh and not candidate.startswith(dh + os.sep):
|
||||
raise BaseException("Security violation: Path must be within domain home directory")
|
||||
return candidate
|
||||
|
||||
@staticmethod
|
||||
def createNewFTPAccount(udb,upass,username,password,path):
|
||||
try:
|
||||
@@ -143,37 +194,14 @@ class FTPUtilities:
|
||||
|
||||
## gid , uid ends
|
||||
|
||||
# Enhanced path validation and handling
|
||||
if path and path.strip() and path != 'None':
|
||||
# Clean the path
|
||||
path = path.strip().lstrip("/")
|
||||
|
||||
# Additional security checks
|
||||
if path.find("..") > -1 or path.find("~") > -1 or path.startswith("/"):
|
||||
raise BaseException("Invalid path: Path must be relative and not contain '..' or '~' or start with '/'")
|
||||
|
||||
# Check for dangerous characters
|
||||
dangerous_chars = [';', '|', '&', '$', '`', '\'', '"', '<', '>', '*', '?']
|
||||
if any(char in path for char in dangerous_chars):
|
||||
raise BaseException("Invalid path: Path contains dangerous characters")
|
||||
|
||||
# Construct full path
|
||||
full_path = "/home/" + domainName + "/" + path
|
||||
|
||||
# Additional security: ensure path is within domain directory
|
||||
domain_home = "/home/" + domainName
|
||||
if not os.path.abspath(full_path).startswith(os.path.abspath(domain_home)):
|
||||
raise BaseException("Security violation: Path must be within domain directory")
|
||||
|
||||
result = FTPUtilities.ftpFunctions(full_path, externalApp)
|
||||
|
||||
if result[0] == 1:
|
||||
path = full_path
|
||||
else:
|
||||
# Path: empty -> domain home; relative or absolute under domain home (no duplicate /home/... prefix)
|
||||
if path and str(path).strip() and str(path).strip() != 'None':
|
||||
path = FTPUtilities.resolve_ftp_home_path(domainName, path)
|
||||
result = FTPUtilities.ftpFunctions(path, externalApp)
|
||||
if result[0] != 1:
|
||||
raise BaseException("Path validation failed: " + result[1])
|
||||
|
||||
else:
|
||||
path = "/home/" + domainName
|
||||
path = FTPUtilities.get_domain_home_directory(domainName)
|
||||
|
||||
# Enhanced symlink handling
|
||||
if os.path.islink(path):
|
||||
@@ -251,6 +279,63 @@ class FTPUtilities:
|
||||
except BaseException as msg:
|
||||
return 0, str(msg)
|
||||
|
||||
@staticmethod
|
||||
def changeFTPDirectory(userName, raw_path, selected_domain):
|
||||
"""
|
||||
Update FTP user home directory after creation. selected_domain must match
|
||||
the master website domain for this account (same as list FTP dropdown).
|
||||
"""
|
||||
try:
|
||||
website = Websites.objects.get(domain=selected_domain)
|
||||
ftp = Users.objects.get(user=userName)
|
||||
if ftp.domain_id != website.id:
|
||||
raise BaseException("FTP user does not belong to the selected domain")
|
||||
|
||||
externalApp = website.externalApp
|
||||
resolved = FTPUtilities.resolve_ftp_home_path(selected_domain, raw_path)
|
||||
|
||||
if os.path.islink(resolved):
|
||||
logging.CyberCPLogFileWriter.writeToFile(
|
||||
"FTP path is symlinked: %s" % resolved)
|
||||
raise BaseException("Cannot set FTP directory: Path is a symbolic link")
|
||||
|
||||
result = FTPUtilities.ftpFunctions(resolved, externalApp)
|
||||
if result[0] != 1:
|
||||
raise BaseException("Path validation failed: " + result[1])
|
||||
|
||||
ftp.dir = resolved
|
||||
ftp.save()
|
||||
return 1, None
|
||||
except Users.DoesNotExist:
|
||||
return 0, "FTP user not found"
|
||||
except Websites.DoesNotExist:
|
||||
return 0, "Domain not found"
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [changeFTPDirectory]")
|
||||
return 0, str(msg)
|
||||
|
||||
@staticmethod
|
||||
def setFTPAccountStatus(userName, enabled, selected_domain):
|
||||
"""
|
||||
Enable or disable FTP login (Status '1' / '0'). Pure-FTPd must use
|
||||
MySQL queries that include AND Status='1' for authentication.
|
||||
"""
|
||||
try:
|
||||
website = Websites.objects.get(domain=selected_domain)
|
||||
ftp = Users.objects.get(user=userName)
|
||||
if ftp.domain_id != website.id:
|
||||
raise BaseException("FTP user does not belong to the selected domain")
|
||||
ftp.status = '1' if enabled else '0'
|
||||
ftp.save()
|
||||
return 1, None
|
||||
except Users.DoesNotExist:
|
||||
return 0, "FTP user not found"
|
||||
except Websites.DoesNotExist:
|
||||
return 0, "Domain not found"
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [setFTPAccountStatus]")
|
||||
return 0, str(msg)
|
||||
|
||||
@staticmethod
|
||||
def changeFTPPassword(userName, password):
|
||||
try:
|
||||
|
||||
@@ -109,15 +109,7 @@ app.controller('createFTPAccount', function ($scope, $http) {
|
||||
$scope.errorMessage = "Invalid path: Path cannot contain '..' or '~'";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if path starts with slash (should be relative)
|
||||
if (path.startsWith("/")) {
|
||||
$scope.ftpLoading = false;
|
||||
resetFtpCreateAlerts();
|
||||
$scope.alertFtpCreateError = true;
|
||||
$scope.errorMessage = "Invalid path: Path must be relative (not starting with '/')";
|
||||
return;
|
||||
}
|
||||
// Absolute paths under /home/... are allowed; server validates they stay inside the site home
|
||||
}
|
||||
|
||||
var url = "/ftp/submitFTPCreation";
|
||||
@@ -375,6 +367,7 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
|
||||
var globalFTPUsername = "";
|
||||
@@ -390,6 +383,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false; // Don't show loading when opening password dialog
|
||||
$scope.changePasswordBox = false;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = ftpUsername;
|
||||
globalFTPUsername = ftpUsername;
|
||||
@@ -454,6 +449,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = true; // Show loading while fetching
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
var selectedDomain = $scope.selectedDomain;
|
||||
|
||||
@@ -479,7 +476,11 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
if (response.data.fetchStatus == 1) {
|
||||
|
||||
$scope.records = JSON.parse(response.data.data);
|
||||
|
||||
angular.forEach($scope.records, function (r) {
|
||||
if (typeof r.acct_enabled === 'undefined') {
|
||||
r.acct_enabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.notificationsBox = false;
|
||||
$scope.recordsFetched = false;
|
||||
@@ -489,6 +490,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading when done
|
||||
$scope.ftpAccounts = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
$scope.domainFeteched = $scope.selectedDomain;
|
||||
|
||||
@@ -501,6 +504,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading on error
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
}
|
||||
@@ -516,6 +521,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading on connection error
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
|
||||
}
|
||||
@@ -542,6 +549,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.quotaManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
@@ -630,6 +639,80 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.manageDirectory = function (record) {
|
||||
$scope.recordsFetched = true;
|
||||
$scope.passwordChanged = true;
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
globalFTPUsername = record.user;
|
||||
$scope.ftpPathEdit = record.dir || '';
|
||||
};
|
||||
|
||||
$scope.changeDirectoryBtn = function () {
|
||||
$scope.ftpLoading = true;
|
||||
var url = "/ftp/changeFTPDirectory";
|
||||
var pathVal = $scope.ftpPathEdit;
|
||||
if (typeof pathVal === 'undefined' || pathVal === null) {
|
||||
pathVal = '';
|
||||
} else {
|
||||
pathVal = String(pathVal).trim();
|
||||
}
|
||||
var data = {
|
||||
ftpUserName: globalFTPUsername,
|
||||
selectedDomain: $scope.selectedDomain,
|
||||
path: pathVal
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
if (response.data.changeDirectoryStatus === 1) {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.directoryManagementBox = true;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'FTP directory updated successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
populateCurrentRecords();
|
||||
} else {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas() {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.couldNotConnect = false;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -109,15 +109,7 @@ app.controller('createFTPAccount', function ($scope, $http) {
|
||||
$scope.errorMessage = "Invalid path: Path cannot contain '..' or '~'";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if path starts with slash (should be relative)
|
||||
if (path.startsWith("/")) {
|
||||
$scope.ftpLoading = false;
|
||||
resetFtpCreateAlerts();
|
||||
$scope.alertFtpCreateError = true;
|
||||
$scope.errorMessage = "Invalid path: Path must be relative (not starting with '/')";
|
||||
return;
|
||||
}
|
||||
// Absolute paths under /home/... are allowed; server validates they stay inside the site home
|
||||
}
|
||||
|
||||
var url = "/ftp/submitFTPCreation";
|
||||
@@ -375,6 +367,7 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
|
||||
var globalFTPUsername = "";
|
||||
@@ -390,6 +383,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false; // Don't show loading when opening password dialog
|
||||
$scope.changePasswordBox = false;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = ftpUsername;
|
||||
globalFTPUsername = ftpUsername;
|
||||
@@ -454,6 +449,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = true; // Show loading while fetching
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
var selectedDomain = $scope.selectedDomain;
|
||||
|
||||
@@ -479,7 +476,11 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
if (response.data.fetchStatus == 1) {
|
||||
|
||||
$scope.records = JSON.parse(response.data.data);
|
||||
|
||||
angular.forEach($scope.records, function (r) {
|
||||
if (typeof r.acct_enabled === 'undefined') {
|
||||
r.acct_enabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.notificationsBox = false;
|
||||
$scope.recordsFetched = false;
|
||||
@@ -489,6 +490,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading when done
|
||||
$scope.ftpAccounts = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
$scope.domainFeteched = $scope.selectedDomain;
|
||||
|
||||
@@ -501,6 +504,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading on error
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
}
|
||||
@@ -516,6 +521,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.ftpLoading = false; // Hide loading on connection error
|
||||
$scope.ftpAccounts = true;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
|
||||
|
||||
}
|
||||
@@ -542,6 +549,8 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.directoryManagementBox = true;
|
||||
$scope.quotaManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
@@ -630,6 +639,80 @@ app.controller('listFTPAccounts', function ($scope, $http) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.manageDirectory = function (record) {
|
||||
$scope.recordsFetched = true;
|
||||
$scope.passwordChanged = true;
|
||||
$scope.canNotChangePassword = true;
|
||||
$scope.couldNotConnect = true;
|
||||
$scope.ftpLoading = false;
|
||||
$scope.changePasswordBox = true;
|
||||
$scope.quotaManagementBox = true;
|
||||
$scope.directoryManagementBox = false;
|
||||
$scope.notificationsBox = true;
|
||||
$scope.ftpUsername = record.user;
|
||||
globalFTPUsername = record.user;
|
||||
$scope.ftpPathEdit = record.dir || '';
|
||||
};
|
||||
|
||||
$scope.changeDirectoryBtn = function () {
|
||||
$scope.ftpLoading = true;
|
||||
var url = "/ftp/changeFTPDirectory";
|
||||
var pathVal = $scope.ftpPathEdit;
|
||||
if (typeof pathVal === 'undefined' || pathVal === null) {
|
||||
pathVal = '';
|
||||
} else {
|
||||
pathVal = String(pathVal).trim();
|
||||
}
|
||||
var data = {
|
||||
ftpUserName: globalFTPUsername,
|
||||
selectedDomain: $scope.selectedDomain,
|
||||
path: pathVal
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
if (response.data.changeDirectoryStatus === 1) {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.directoryManagementBox = true;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'FTP directory updated successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
populateCurrentRecords();
|
||||
} else {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas() {
|
||||
$scope.ftpLoading = false;
|
||||
$scope.couldNotConnect = false;
|
||||
if (typeof PNotify !== 'undefined') {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user