Implement RBAC migration and role management enhancements
- Added AJAX functionality to fetch role permissions for copying. - Introduced system role management with permission checks for updates. - Implemented role deletion with confirmation modal and backend handling. - Enhanced user role assignment migration scripts to transition from legacy profiles to RBAC. - Created SQL migration scripts for user roles and permissions mapping. - Updated user interface to support new role management features including copy permissions and system role indicators.
This commit is contained in:
@@ -249,15 +249,19 @@ class MarketingFileManager {
|
||||
emptyState.style.display = 'none';
|
||||
|
||||
try {
|
||||
// Use proper folder ID (null for root, or the folder ID)
|
||||
const folderId = this.currentFolder ? this.currentFolder : 'null';
|
||||
// Add cache busting to prevent browser caching
|
||||
let url = `index.php?page=marketing&action=marketing_files&folder_id=${folderId}&limit=50&_t=${Date.now()}`;
|
||||
|
||||
let url = `index.php?page=marketing&action=marketing_files&limit=50&_t=${Date.now()}`;
|
||||
|
||||
// Only filter by folder if no tag filter is active (tag search is across all folders)
|
||||
if (!this.filters.tag) {
|
||||
const folderId = this.currentFolder ? this.currentFolder : 'null';
|
||||
url += `&folder_id=${folderId}`;
|
||||
}
|
||||
|
||||
if (this.filters.search) {
|
||||
url += `&search=${encodeURIComponent(this.filters.search)}`;
|
||||
}
|
||||
|
||||
|
||||
if (this.filters.tag) {
|
||||
url += `&tag=${encodeURIComponent(this.filters.tag)}`;
|
||||
}
|
||||
@@ -289,21 +293,33 @@ class MarketingFileManager {
|
||||
|
||||
if (data && data.length > 0) {
|
||||
let files = data;
|
||||
|
||||
|
||||
// Client-side file type filtering
|
||||
if (this.filters.fileTypes.length > 0) {
|
||||
files = files.filter(file =>
|
||||
files = files.filter(file =>
|
||||
this.filters.fileTypes.includes(file.file_type.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (files.length === 0) {
|
||||
emptyState.style.display = 'block';
|
||||
// No files after filtering, check for subfolders
|
||||
const subfolders = this.getSubfolders(this.currentFolder);
|
||||
if (subfolders.length > 0) {
|
||||
this.renderFolderTiles(subfolders);
|
||||
} else {
|
||||
emptyState.style.display = 'block';
|
||||
}
|
||||
} else {
|
||||
this.renderFiles(files);
|
||||
}
|
||||
} else {
|
||||
emptyState.style.display = 'block';
|
||||
// No files, check for subfolders
|
||||
const subfolders = this.getSubfolders(this.currentFolder);
|
||||
if (subfolders.length > 0) {
|
||||
this.renderFolderTiles(subfolders);
|
||||
} else {
|
||||
emptyState.style.display = 'block';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading files:', error);
|
||||
@@ -372,12 +388,73 @@ class MarketingFileManager {
|
||||
renderFiles(files) {
|
||||
const container = document.getElementById('filesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
|
||||
files.forEach(file => {
|
||||
const fileElement = this.createFileElement(file);
|
||||
container.appendChild(fileElement);
|
||||
});
|
||||
}
|
||||
|
||||
getSubfolders(folderId) {
|
||||
// Find immediate children of the specified folder
|
||||
if (!folderId || folderId === '') {
|
||||
// Root folder - return top-level folders
|
||||
return this.folders;
|
||||
}
|
||||
|
||||
// Recursively search for the folder and return its children
|
||||
const findFolder = (folders, targetId) => {
|
||||
for (const folder of folders) {
|
||||
if (folder.id === targetId) {
|
||||
return folder.children || [];
|
||||
}
|
||||
if (folder.children && folder.children.length > 0) {
|
||||
const found = findFolder(folder.children, targetId);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
return findFolder(this.folders, folderId);
|
||||
}
|
||||
|
||||
renderFolderTiles(subfolders) {
|
||||
const container = document.getElementById('filesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
subfolders.forEach(folder => {
|
||||
const folderElement = this.createFolderTileElement(folder);
|
||||
container.appendChild(folderElement);
|
||||
});
|
||||
}
|
||||
|
||||
createFolderTileElement(folder) {
|
||||
const folderElement = document.createElement('div');
|
||||
folderElement.className = `folder-tile ${this.viewMode}-item`;
|
||||
folderElement.setAttribute('data-folder-id', folder.id);
|
||||
|
||||
folderElement.innerHTML = `
|
||||
<div class="folder-tile-icon">
|
||||
<i class="fa fa-folder"></i>
|
||||
</div>
|
||||
<div class="folder-tile-info">
|
||||
<div class="folder-tile-name" title="${this.escapeHtml(folder.folder_name)}">
|
||||
${this.escapeHtml(folder.folder_name)}
|
||||
</div>
|
||||
<div class="folder-tile-meta">
|
||||
<span class="folder-file-count">${folder.file_count || 0} files</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Click to navigate to folder
|
||||
folderElement.addEventListener('click', () => {
|
||||
this.selectFolder(folder.id);
|
||||
});
|
||||
|
||||
return folderElement;
|
||||
}
|
||||
|
||||
createFileElement(file) {
|
||||
const fileElement = document.createElement('div');
|
||||
@@ -385,7 +462,7 @@ class MarketingFileManager {
|
||||
fileElement.setAttribute('data-file-id', file.id);
|
||||
|
||||
const thumbnail = this.getThumbnail(file);
|
||||
const tags = file.tags.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`).join('');
|
||||
const tags = file.tags.map(tag => `<span class="tag clickable" data-tag="${this.escapeHtml(tag)}">${this.escapeHtml(tag)}</span>`).join('');
|
||||
|
||||
fileElement.innerHTML = `
|
||||
<div class="file-thumbnail">
|
||||
@@ -429,11 +506,20 @@ class MarketingFileManager {
|
||||
fileElement.querySelector('.edit-btn').addEventListener('click', () => {
|
||||
this.editFile(file);
|
||||
});
|
||||
|
||||
|
||||
// Make tags clickable to filter by tag
|
||||
fileElement.querySelectorAll('.tag.clickable').forEach(tagElement => {
|
||||
tagElement.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const tagName = tagElement.getAttribute('data-tag');
|
||||
this.filterByTag(tagName);
|
||||
});
|
||||
});
|
||||
|
||||
fileElement.addEventListener('dblclick', () => {
|
||||
this.previewFile(file);
|
||||
});
|
||||
|
||||
|
||||
return fileElement;
|
||||
}
|
||||
|
||||
@@ -843,15 +929,41 @@ class MarketingFileManager {
|
||||
|
||||
updateFileTypeFilters() {
|
||||
this.filters.fileTypes = [];
|
||||
|
||||
|
||||
document.querySelectorAll('.file-type-filters input[type="checkbox"]:checked').forEach(checkbox => {
|
||||
const types = checkbox.value.split(',');
|
||||
this.filters.fileTypes.push(...types);
|
||||
});
|
||||
|
||||
|
||||
this.loadFiles();
|
||||
}
|
||||
|
||||
|
||||
filterByTag(tagName) {
|
||||
// Set the tag filter
|
||||
this.filters.tag = tagName;
|
||||
|
||||
// Update the dropdown to show the selected tag
|
||||
const tagSelect = document.getElementById('tagFilter');
|
||||
if (tagSelect) {
|
||||
tagSelect.value = tagName;
|
||||
}
|
||||
|
||||
// Clear folder selection to search across all folders
|
||||
this.currentFolder = '';
|
||||
|
||||
// Update folder tree UI to show root as active
|
||||
document.querySelectorAll('.folder-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
const rootFolder = document.querySelector('.folder-item.root');
|
||||
if (rootFolder) {
|
||||
rootFolder.classList.add('active');
|
||||
}
|
||||
|
||||
// Reload files with the tag filter
|
||||
this.loadFiles();
|
||||
}
|
||||
|
||||
populateTagFilter(tags) {
|
||||
const select = document.getElementById('tagFilter');
|
||||
select.innerHTML = '<option value="">All Tags</option>';
|
||||
|
||||
Reference in New Issue
Block a user