Add user notification preferences and related API endpoints

- Introduced a new model `UserNotificationPreferences` to store user-specific notification dismissal settings.
- Added context processor to include notification preferences in templates.
- Implemented API endpoints to dismiss backup and AI scanner notifications permanently.
- Updated front-end logic to handle notification dismissal via server-side checks and API calls.
- Enhanced documentation to include a new Custom CSS Guide for theme customization.
This commit is contained in:
Master3395
2025-09-15 02:01:11 +02:00
parent 792d91461a
commit 96226989b1
13 changed files with 1122 additions and 25 deletions

View File

@@ -107,6 +107,7 @@ TEMPLATES = [
'django.contrib.messages.context_processors.messages',
'baseTemplate.context_processors.version_context',
'baseTemplate.context_processors.cosmetic_context',
'baseTemplate.context_processors.notification_preferences_context',
],
},
},

View File

@@ -30,6 +30,7 @@ CyberPanel comes with comprehensive documentation and step-by-step guides:
- 🐳 **[Docker Command Execution](guides/Docker_Command_Execution_Guide.md)** - Execute commands in Docker containers
- 🤖 **[AI Scanner Setup](guides/AIScannerDocs.md)** - Configure AI-powered security scanning
- 📧 **[Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup
- 🎨 **[Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md)** - Create custom themes for CyberPanel 2.5.5-dev
---
@@ -109,7 +110,6 @@ Install CyberPanel easily with the following command:
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
```
---
## 📊 Upgrading CyberPanel
@@ -125,6 +125,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
## 🆕 Recent Updates & Fixes
### **Bandwidth Reset Issue Fixed** (January 2025)
- **Issue**: Monthly bandwidth usage was not resetting, causing cumulative values to grow indefinitely
- **Solution**: Implemented automatic monthly bandwidth reset for all websites and child domains
- **Affected OS**: All supported operating systems (Ubuntu, AlmaLinux, RockyLinux, RHEL, CloudLinux, CentOS)
@@ -132,6 +133,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
- **Documentation**: See [Bandwidth Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md)
### **New Operating System Support Added** (January 2025)
- **Ubuntu 24.04.3**: Full compatibility with latest Ubuntu LTS
- **AlmaLinux 10**: Full compatibility with latest AlmaLinux release
- **Long-term Support**: Both supported until 2029-2030
@@ -157,17 +159,19 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
- 🐳 [Docker Command Execution](guides/Docker_Command_Execution_Guide.md) - Execute commands in Docker containers
- 🤖 [AI Scanner Setup](guides/AIScannerDocs.md) - Configure AI-powered security scanning
- 📧 [Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md) - Email marketing platform setup
- 🎨 [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) - Create custom themes for CyberPanel 2.5.5+
- 📚 [All Guides Index](guides/INDEX.md) - Complete documentation hub
### 🔗 **Direct Guide Links**
| Feature | Guide | Description |
| ----------- | ---------------------------------------------------------- | ------------------------------ |
| 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers |
| 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning |
| 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform |
| 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues |
| 📚 All | [Complete Index](guides/INDEX.md) | Browse all available guides |
| Feature | Guide | Description |
| ------------ | ---------------------------------------------------------- | ---------------------------------- |
| 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers |
| 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning |
| 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform |
| 🎨 Design | [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) | Create custom themes for 2.5.5-dev |
| 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues |
| 📚 All | [Complete Index](guides/INDEX.md) | Browse all available guides |
---
@@ -176,12 +180,13 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
### **Common Issues & Solutions**
#### **Bandwidth Not Resetting Monthly**
- **Issue**: Bandwidth usage shows cumulative values instead of monthly usage
- **Solution**: Run the bandwidth reset script: `/usr/local/CyberCP/scripts/reset_bandwidth.sh`
- **Prevention**: Ensure monthly cron job is running: `0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup`
#### **General Support**
- Check logs: `/usr/local/lscp/logs/error.log`
- Verify cron jobs: `crontab -l`
- Test manual reset: Use provided scripts in `/usr/local/CyberCP/scripts/`

View File

@@ -6,3 +6,6 @@ from django.apps import AppConfig
class BasetemplateConfig(AppConfig):
name = 'baseTemplate'
def ready(self):
import baseTemplate.signals

View File

@@ -23,4 +23,30 @@ def cosmetic_context(request):
cosmetic.save()
return {
'cosmetic': cosmetic
}
}
def notification_preferences_context(request):
"""Add user notification preferences to all templates"""
try:
if 'userID' in request.session:
from .models import UserNotificationPreferences
from loginSystem.models import Administrator
user = Administrator.objects.get(pk=request.session['userID'])
try:
preferences = UserNotificationPreferences.objects.get(user=user)
return {
'backup_notification_dismissed': preferences.backup_notification_dismissed,
'ai_scanner_notification_dismissed': preferences.ai_scanner_notification_dismissed
}
except UserNotificationPreferences.DoesNotExist:
return {
'backup_notification_dismissed': False,
'ai_scanner_notification_dismissed': False
}
except:
pass
return {
'backup_notification_dismissed': False,
'ai_scanner_notification_dismissed': False
}

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2024-01-01 00:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('baseTemplate', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='UserNotificationPreferences',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('backup_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the backup notification')),
('ai_scanner_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the AI scanner notification')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'User Notification Preferences',
'verbose_name_plural': 'User Notification Preferences',
},
),
]

View File

@@ -2,6 +2,7 @@
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
@@ -11,4 +12,19 @@ class version(models.Model):
build = models.IntegerField()
class CyberPanelCosmetic(models.Model):
MainDashboardCSS = models.TextField(default='')
MainDashboardCSS = models.TextField(default='')
class UserNotificationPreferences(models.Model):
"""Model to store user notification dismissal preferences"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='notification_preferences')
backup_notification_dismissed = models.BooleanField(default=False, help_text="Whether user has dismissed the backup notification")
ai_scanner_notification_dismissed = models.BooleanField(default=False, help_text="Whether user has dismissed the AI scanner notification")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "User Notification Preferences"
verbose_name_plural = "User Notification Preferences"
def __str__(self):
return f"Notification Preferences for {self.user.username}"

23
baseTemplate/signals.py Normal file
View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import UserNotificationPreferences
@receiver(post_save, sender=User)
def create_user_notification_preferences(sender, instance, created, **kwargs):
"""Create default notification preferences when a new user is created"""
if created:
UserNotificationPreferences.objects.create(
user=instance,
backup_notification_dismissed=False,
ai_scanner_notification_dismissed=False
)
@receiver(post_save, sender=User)
def save_user_notification_preferences(sender, instance, **kwargs):
"""Save notification preferences when user is saved"""
if hasattr(instance, 'notification_preferences'):
instance.notification_preferences.save()

View File

@@ -1841,10 +1841,10 @@
// Backup notification banner logic
function checkBackupStatus() {
// Check if user has dismissed the notification in this session
if (sessionStorage.getItem('backupNotificationDismissed') === 'true') {
return;
}
// Check if user has dismissed the notification permanently (from server-side context)
{% if backup_notification_dismissed %}
return; // Notification already dismissed permanently
{% endif %}
// Check if user has backup configured (you'll need to implement this API)
// For now, we'll show it by default unless they have a backup plan
@@ -1871,16 +1871,34 @@
const body = document.body;
banner.classList.remove('show');
body.classList.remove('notification-shown');
// Remember dismissal for this session
sessionStorage.setItem('backupNotificationDismissed', 'true');
// Dismiss permanently via API
fetch('/base/dismiss_backup_notification', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
}
})
.then(response => response.json())
.then(data => {
if (data.status === 1) {
console.log('Backup notification dismissed permanently');
} else {
console.error('Failed to dismiss backup notification:', data.error);
}
})
.catch(error => {
console.error('Error dismissing backup notification:', error);
});
}
// AI Scanner Notification Functions
function checkAIScannerStatus() {
// Check if user has dismissed the notification in this session
if (sessionStorage.getItem('aiScannerNotificationDismissed') === 'true') {
return;
}
// Check if user has dismissed the notification permanently (from server-side context)
{% if ai_scanner_notification_dismissed %}
return; // Notification already dismissed permanently
{% endif %}
// Check if we're not already on the AI Scanner page
if (!window.location.href.includes('aiscanner')) {
@@ -1900,8 +1918,26 @@
const body = document.body;
banner.classList.remove('show');
body.classList.remove('ai-scanner-shown');
// Remember dismissal for this session
sessionStorage.setItem('aiScannerNotificationDismissed', 'true');
// Dismiss permanently via API
fetch('/base/dismiss_ai_scanner_notification', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
}
})
.then(response => response.json())
.then(data => {
if (data.status === 1) {
console.log('AI scanner notification dismissed permanently');
} else {
console.error('Failed to dismiss AI scanner notification:', data.error);
}
})
.catch(error => {
console.error('Error dismissing AI scanner notification:', error);
});
}
// Check both notification statuses when page loads

View File

@@ -24,4 +24,7 @@ urlpatterns = [
re_path(r'^getSSHUserActivity$', views.getSSHUserActivity, name='getSSHUserActivity'),
re_path(r'^getTopProcesses$', views.getTopProcesses, name='getTopProcesses'),
re_path(r'^analyzeSSHSecurity$', views.analyzeSSHSecurity, name='analyzeSSHSecurity'),
re_path(r'^dismiss_backup_notification$', views.dismiss_backup_notification, name='dismiss_backup_notification'),
re_path(r'^dismiss_ai_scanner_notification$', views.dismiss_ai_scanner_notification, name='dismiss_ai_scanner_notification'),
re_path(r'^get_notification_preferences$', views.get_notification_preferences, name='get_notification_preferences'),
]

View File

@@ -6,7 +6,7 @@ from django.http import HttpResponse
from plogical.getSystemInformation import SystemInformation
import json
from loginSystem.views import loadLoginPage
from .models import version
from .models import version, UserNotificationPreferences
import requests
import subprocess
import shlex
@@ -1305,3 +1305,88 @@ def getTopProcesses(request):
except Exception as e:
return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)
@csrf_exempt
@require_POST
def dismiss_backup_notification(request):
"""API endpoint to permanently dismiss the backup notification for the current user"""
try:
user_id = request.session.get('userID')
if not user_id:
return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403)
# Get or create user notification preferences
user = Administrator.objects.get(pk=user_id)
preferences, created = UserNotificationPreferences.objects.get_or_create(
user=user,
defaults={
'backup_notification_dismissed': False,
'ai_scanner_notification_dismissed': False
}
)
# Mark backup notification as dismissed
preferences.backup_notification_dismissed = True
preferences.save()
return HttpResponse(json.dumps({'status': 1, 'message': 'Backup notification dismissed permanently'}), content_type='application/json')
except Exception as e:
return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500)
@csrf_exempt
@require_POST
def dismiss_ai_scanner_notification(request):
"""API endpoint to permanently dismiss the AI scanner notification for the current user"""
try:
user_id = request.session.get('userID')
if not user_id:
return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403)
# Get or create user notification preferences
user = Administrator.objects.get(pk=user_id)
preferences, created = UserNotificationPreferences.objects.get_or_create(
user=user,
defaults={
'backup_notification_dismissed': False,
'ai_scanner_notification_dismissed': False
}
)
# Mark AI scanner notification as dismissed
preferences.ai_scanner_notification_dismissed = True
preferences.save()
return HttpResponse(json.dumps({'status': 1, 'message': 'AI scanner notification dismissed permanently'}), content_type='application/json')
except Exception as e:
return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500)
@csrf_exempt
@require_GET
def get_notification_preferences(request):
"""API endpoint to get current user's notification preferences"""
try:
user_id = request.session.get('userID')
if not user_id:
return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403)
# Get user notification preferences
user = Administrator.objects.get(pk=user_id)
try:
preferences = UserNotificationPreferences.objects.get(user=user)
return HttpResponse(json.dumps({
'status': 1,
'backup_notification_dismissed': preferences.backup_notification_dismissed,
'ai_scanner_notification_dismissed': preferences.ai_scanner_notification_dismissed
}), content_type='application/json')
except UserNotificationPreferences.DoesNotExist:
# Return default values if preferences don't exist yet
return HttpResponse(json.dumps({
'status': 1,
'backup_notification_dismissed': False,
'ai_scanner_notification_dismissed': False
}), content_type='application/json')
except Exception as e:
return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500)

829
guides/CUSTOM_CSS_GUIDE.md Normal file
View File

@@ -0,0 +1,829 @@
# CyberPanel 2.5.5-dev Custom CSS Guide
A comprehensive guide for creating custom CSS that fully works with the new design system of CyberPanel 2.5.5-dev.
## Table of Contents
1. [Overview](#overview)
2. [Design System Architecture](#design-system-architecture)
3. [CSS Variables Reference](#css-variables-reference)
4. [Component Structure](#component-structure)
5. [Customization Examples](#customization-examples)
6. [Best Practices](#best-practices)
7. [Troubleshooting](#troubleshooting)
8. [Advanced Techniques](#advanced-techniques)
## Overview
CyberPanel 2.5.5-dev features a modern, CSS-variable-based design system that supports both light and dark themes. The system is built with:
- **CSS Custom Properties (Variables)** for consistent theming
- **Modern CSS Grid and Flexbox** layouts
- **Responsive design** principles
- **Dark mode support** with automatic theme switching
- **Component-based architecture** for easy customization
## Design System Architecture
### Core Structure
The design system is built around CSS custom properties defined in `:root` and `[data-theme="dark"]` selectors:
```css
:root {
/* Light Theme Variables */
--bg-primary: #f0f0ff;
--bg-secondary: white;
--text-primary: #2f3640;
--accent-color: #5856d6;
/* ... more variables */
}
[data-theme="dark"] {
/* Dark Theme Variables */
--bg-primary: #0f0f23;
--bg-secondary: #1a1a3e;
--text-primary: #e4e4e7;
--accent-color: #7c7ff3;
/* ... more variables */
}
```
### Key Components
1. **Header** (`#header`) - Top navigation bar
2. **Sidebar** (`#sidebar`) - Left navigation panel
3. **Main Content** (`#main-content`) - Page content area
4. **Cards** (`.content-card`) - Content containers
5. **Buttons** (`.btn`) - Interactive elements
6. **Forms** (`.form-*`) - Input elements
## CSS Variables Reference
### Color Variables
#### Background Colors
```css
--bg-primary /* Main background color */
--bg-secondary /* Card/container background */
--bg-sidebar /* Sidebar background */
--bg-sidebar-item /* Sidebar menu item background */
--bg-hover /* Hover state background */
```
#### Text Colors
```css
--text-primary /* Main text color */
--text-secondary /* Secondary text color */
--text-heading /* Heading text color */
```
#### Accent Colors
```css
--accent-color /* Primary accent color */
--accent-hover /* Accent hover state */
--danger-color /* Error/danger color */
--success-color /* Success color */
```
#### Border & Shadow
```css
--border-color /* Default border color */
--shadow-color /* Default shadow color */
```
### Special Variables
#### Gradients
```css
--warning-bg /* Warning banner gradient */
--ai-banner-bg /* AI scanner banner gradient */
```
#### Status Colors
```css
--success-bg /* Success background */
--success-border /* Success border */
--danger-bg /* Danger background */
--danger-border /* Danger border */
--warning-bg /* Warning background */
--info-bg /* Info background */
```
## Component Structure
### Header Component
```css
#header {
background: var(--bg-secondary);
height: 80px;
display: flex;
align-items: center;
padding: 0 30px;
box-shadow: 0 2px 12px var(--shadow-color);
position: fixed;
top: 0;
left: 260px;
right: 0;
z-index: 1000;
}
```
**Customization Example:**
```css
/* Change header height and add custom styling */
#header {
height: 100px;
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
border-bottom: 3px solid var(--accent-color);
}
#header .logo-text .brand {
font-size: 32px;
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
```
### Sidebar Component
```css
#sidebar {
width: 260px;
background: var(--bg-sidebar);
height: 100vh;
position: fixed;
left: 0;
top: 0;
overflow-y: auto;
z-index: 1001;
}
```
**Customization Example:**
```css
/* Make sidebar wider with custom styling */
#sidebar {
width: 300px;
background: linear-gradient(180deg, var(--bg-sidebar), var(--bg-secondary));
border-right: 2px solid var(--accent-color);
}
#sidebar .menu-item {
margin: 4px 20px;
border-radius: 12px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
#sidebar .menu-item:hover {
transform: translateX(5px);
box-shadow: 0 4px 12px var(--shadow-color);
}
```
### Content Cards
```css
.content-card {
background: var(--bg-secondary);
border-radius: 12px;
padding: 30px;
box-shadow: 0 2px 8px var(--shadow-color);
border: 1px solid var(--border-color);
margin-bottom: 25px;
}
```
**Customization Example:**
```css
/* Add glassmorphism effect to cards */
.content-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] .content-card {
background: rgba(26, 26, 62, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
}
```
## Customization Examples
### 1. Complete Theme Override
```css
/* Custom Purple Theme */
:root {
--bg-primary: #f8f4ff;
--bg-secondary: #ffffff;
--bg-sidebar: #f3f0ff;
--bg-hover: #e8e0ff;
--text-primary: #2d1b69;
--text-secondary: #6b46c1;
--accent-color: #8b5cf6;
--accent-hover: #7c3aed;
--border-color: #e0d7ff;
--shadow-color: rgba(139, 92, 246, 0.1);
}
[data-theme="dark"] {
--bg-primary: #1a0b2e;
--bg-secondary: #2d1b69;
--bg-sidebar: #1e0a3e;
--bg-hover: #3d2a7a;
--text-primary: #f3f0ff;
--text-secondary: #c4b5fd;
--accent-color: #a78bfa;
--accent-hover: #8b5cf6;
--border-color: #4c1d95;
--shadow-color: rgba(139, 92, 246, 0.3);
}
```
### 2. Custom Button Styles
```css
/* Custom button variants */
.btn-custom {
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
border: none;
border-radius: 20px;
padding: 12px 24px;
color: white;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
transition: all 0.3s ease;
}
.btn-custom:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
}
.btn-custom:active {
transform: translateY(0);
}
```
### 3. Custom Sidebar Menu Items
```css
/* Animated sidebar menu items */
#sidebar .menu-item {
position: relative;
overflow: hidden;
}
#sidebar .menu-item::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
#sidebar .menu-item:hover::before {
left: 100%;
}
#sidebar .menu-item .icon-wrapper {
position: relative;
z-index: 1;
}
```
### 4. Custom Form Styling
```css
/* Modern form inputs */
.form-control {
border: 2px solid var(--border-color);
border-radius: 12px;
padding: 12px 16px;
font-size: 14px;
transition: all 0.3s ease;
background: var(--bg-secondary);
}
.form-control:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.1);
transform: translateY(-1px);
}
.form-control::placeholder {
color: var(--text-secondary);
opacity: 0.7;
}
```
### 5. Custom Notifications
```css
/* Custom notification banners */
.notification-banner {
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
border-radius: 16px;
padding: 20px;
margin: 20px;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.3);
position: relative;
overflow: hidden;
}
.notification-banner::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4);
animation: rainbow 3s linear infinite;
}
@keyframes rainbow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
```
## Best Practices
### 1. Use CSS Variables
Always use CSS variables instead of hardcoded values:
```css
/* ✅ Good */
.custom-element {
background: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--border-color);
}
/* ❌ Bad */
.custom-element {
background: white;
color: #2f3640;
border: 1px solid #e8e9ff;
}
```
### 2. Support Both Themes
Always provide both light and dark theme support:
```css
.custom-element {
background: var(--bg-secondary);
color: var(--text-primary);
}
/* Dark theme specific adjustments */
[data-theme="dark"] .custom-element {
/* Additional dark theme styling if needed */
}
```
### 3. Use Semantic Class Names
```css
/* ✅ Good */
.primary-button { }
.content-container { }
.navigation-item { }
/* ❌ Bad */
.red-button { }
.big-box { }
.item1 { }
```
### 4. Maintain Responsive Design
```css
.custom-element {
padding: 20px;
font-size: 16px;
}
@media (max-width: 768px) {
.custom-element {
padding: 15px;
font-size: 14px;
}
}
```
### 5. Use Modern CSS Features
```css
.custom-element {
/* Use CSS Grid for layouts */
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
/* Use Flexbox for alignment */
align-items: center;
justify-content: space-between;
/* Use CSS custom properties for calculations */
--element-height: 60px;
height: var(--element-height);
/* Use modern selectors */
&:hover { }
&:focus-within { }
}
```
## Troubleshooting
### Common Issues
#### 1. Custom CSS Not Applying
**Problem:** Custom CSS doesn't appear to be working.
**Solution:**
- Check CSS specificity - use more specific selectors
- Ensure CSS is placed after the base styles
- Use `!important` sparingly and only when necessary
```css
/* Increase specificity */
#main-content .content-card .custom-element {
background: var(--bg-secondary);
}
```
#### 2. Dark Mode Not Working
**Problem:** Custom styles don't adapt to dark mode.
**Solution:**
- Always use CSS variables
- Test both light and dark themes
- Provide dark mode specific overrides when needed
```css
.custom-element {
background: var(--bg-secondary);
color: var(--text-primary);
}
[data-theme="dark"] .custom-element {
/* Dark mode specific adjustments */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
```
#### 3. Responsive Issues
**Problem:** Custom styles break on mobile devices.
**Solution:**
- Use relative units (rem, em, %)
- Test on different screen sizes
- Use CSS Grid and Flexbox for responsive layouts
```css
.custom-element {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
}
@media (max-width: 768px) {
.custom-element {
padding: 0.5rem;
}
}
```
## Advanced Techniques
### 1. CSS Animations
```css
/* Smooth page transitions */
.page-transition {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Hover animations */
.interactive-element {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.interactive-element:hover {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 8px 25px var(--shadow-color);
}
```
### 2. CSS Grid Layouts
```css
/* Advanced grid layouts */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.dashboard-card {
grid-column: span 1;
background: var(--bg-secondary);
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 8px var(--shadow-color);
}
.dashboard-card.featured {
grid-column: span 2;
}
@media (max-width: 768px) {
.dashboard-card.featured {
grid-column: span 1;
}
}
```
### 3. CSS Custom Properties with JavaScript
```css
/* Dynamic theming with CSS variables */
:root {
--custom-accent: #5856d6;
--custom-accent-hover: #4644c0;
}
.custom-theme {
--accent-color: var(--custom-accent);
--accent-hover: var(--custom-accent-hover);
}
```
### 4. Advanced Selectors
```css
/* Complex selectors for specific styling */
#sidebar .menu-item:not(.active):hover {
background: var(--bg-hover);
transform: translateX(5px);
}
.content-card > *:first-child {
margin-top: 0;
}
.content-card > *:last-child {
margin-bottom: 0;
}
/* Attribute selectors */
[data-status="success"] {
border-left: 4px solid var(--success-color);
}
[data-status="error"] {
border-left: 4px solid var(--danger-color);
}
```
### 5. CSS Functions and Calculations
```css
/* Using CSS functions */
.responsive-text {
font-size: clamp(14px, 2.5vw, 18px);
line-height: calc(1.5em + 0.5vw);
}
.dynamic-spacing {
padding: calc(1rem + 2vw);
margin: calc(0.5rem + 1vw);
}
/* CSS custom properties with calculations */
:root {
--base-size: 16px;
--scale-factor: 1.2;
--large-size: calc(var(--base-size) * var(--scale-factor));
}
```
## Implementation Guide
### Step 1: Access the Design Page
1. Log into CyberPanel
2. Navigate to **Design** in the sidebar
3. Scroll down to the **Custom CSS** section
### Step 2: Add Your Custom CSS
1. Paste your custom CSS into the textarea
2. Click **Save Changes**
3. Refresh the page to see your changes
### Step 3: Test Your Changes
1. Test in both light and dark modes
2. Test on different screen sizes
3. Verify all interactive elements work correctly
### Step 4: Iterate and Refine
1. Make adjustments as needed
2. Test thoroughly before finalizing
3. Document your customizations
## Example: Complete Custom Theme
Here's a complete example of a custom theme that you can use as a starting point:
```css
/* Custom CyberPanel Theme - Ocean Blue */
/* Light Theme */
:root {
--bg-primary: #f0f9ff;
--bg-secondary: #ffffff;
--bg-sidebar: #e0f2fe;
--bg-sidebar-item: #ffffff;
--bg-hover: #bae6fd;
--text-primary: #0c4a6e;
--text-secondary: #0369a1;
--text-heading: #0c4a6e;
--border-color: #bae6fd;
--shadow-color: rgba(6, 105, 161, 0.1);
--accent-color: #0284c7;
--accent-hover: #0369a1;
--danger-color: #dc2626;
--success-color: #059669;
--warning-bg: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
--ai-banner-bg: linear-gradient(135deg, #0284c7 0%, #0369a1 50%, #0c4a6e 100%);
}
/* Dark Theme */
[data-theme="dark"] {
--bg-primary: #0c4a6e;
--bg-secondary: #075985;
--bg-sidebar: #0c4a6e;
--bg-sidebar-item: #075985;
--bg-hover: #0369a1;
--text-primary: #e0f2fe;
--text-secondary: #bae6fd;
--text-heading: #f0f9ff;
--border-color: #0369a1;
--shadow-color: rgba(0, 0, 0, 0.3);
--accent-color: #38bdf8;
--accent-hover: #0ea5e9;
--danger-color: #f87171;
--success-color: #34d399;
--warning-bg: linear-gradient(135deg, #78350f 0%, #92400e 100%);
--ai-banner-bg: linear-gradient(135deg, #0c4a6e 0%, #075985 50%, #0369a1 100%);
}
/* Custom Header Styling */
#header {
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
box-shadow: 0 4px 20px var(--shadow-color);
}
#header .logo-text .brand {
color: white;
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Custom Sidebar Styling */
#sidebar {
background: linear-gradient(180deg, var(--bg-sidebar), var(--bg-secondary));
border-right: 3px solid var(--accent-color);
}
#sidebar .menu-item {
border-radius: 12px;
margin: 4px 20px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
#sidebar .menu-item:hover {
transform: translateX(8px);
box-shadow: 0 4px 15px var(--shadow-color);
}
#sidebar .menu-item.active {
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
box-shadow: 0 4px 15px rgba(2, 132, 199, 0.3);
}
/* Custom Content Cards */
.content-card {
background: var(--bg-secondary);
border-radius: 16px;
box-shadow: 0 4px 20px var(--shadow-color);
border: 1px solid var(--border-color);
position: relative;
overflow: hidden;
}
.content-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--accent-color), var(--accent-hover));
}
/* Custom Buttons */
.btn {
border-radius: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-primary {
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
box-shadow: 0 4px 15px rgba(2, 132, 199, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(2, 132, 199, 0.4);
}
/* Custom Form Elements */
.form-control {
border: 2px solid var(--border-color);
border-radius: 12px;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 4px rgba(2, 132, 199, 0.1);
transform: translateY(-1px);
}
/* Custom Animations */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.content-card {
animation: slideIn 0.5s ease-out;
}
/* Responsive Design */
@media (max-width: 768px) {
#sidebar {
width: 100%;
height: auto;
position: relative;
}
#header {
left: 0;
}
.content-card {
margin: 10px;
padding: 20px;
}
}
```
This guide provides everything you need to create beautiful, functional custom CSS for CyberPanel 2.5.5+. Remember to always test your changes thoroughly and use the CSS variables for consistency across themes.

View File

@@ -14,6 +14,9 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
### 📧 Email & Marketing
- **[Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)** - Step-by-step guide for installing and configuring Mautic email marketing platform
### 🎨 Customization & Design
- **[Custom CSS Guide](CUSTOM_CSS_GUIDE.md)** - Complete guide for creating custom CSS that works with CyberPanel 2.5.5-dev design system
### 📖 General Documentation
- **[README](../README.md)** - Main CyberPanel documentation with installation instructions and feature overview
- **[Contributing Guide](CONTRIBUTING.md)** - Guidelines for contributing to the CyberPanel project
@@ -23,7 +26,8 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
1. **New to CyberPanel?** Start with the [README](../README.md) for installation and basic setup
2. **Need Docker help?** Check the [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
3. **Setting up email marketing?** Follow the [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
4. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md)
4. **Want to customize the interface?** Check the [Custom CSS Guide](CUSTOM_CSS_GUIDE.md)
5. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md)
## 🔍 Finding What You Need
@@ -31,6 +35,7 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
- **Docker Features**: [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
- **Security Features**: [AI Scanner Documentation](AIScannerDocs.md)
- **Email Marketing**: [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
- **Customization & Design**: [Custom CSS Guide](CUSTOM_CSS_GUIDE.md)
- **Development**: [Contributing Guide](CONTRIBUTING.md)
## 📝 Guide Categories
@@ -45,6 +50,11 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
- Third-party applications
- Custom configurations
### 🎨 **Customization**
- Custom CSS theming
- Design system integration
- Interface personalization
### 📖 **Documentation**
- Installation guides
- Configuration tutorials

28
run_migration.py Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Migration script for UserNotificationPreferences model
Run this script to apply the database migration for notification preferences
"""
import os
import sys
import django
# Add the project directory to Python path
sys.path.append('/usr/local/CyberCP')
# Set up Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CyberCP.settings')
django.setup()
from django.core.management import execute_from_command_line
if __name__ == '__main__':
print("Running migration for UserNotificationPreferences...")
try:
execute_from_command_line(['manage.py', 'migrate', 'baseTemplate'])
print("Migration completed successfully!")
except Exception as e:
print(f"Migration failed: {e}")
sys.exit(1)