micro-users 1.2.4__tar.gz → 1.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of micro-users might be problematic. Click here for more details.
- {micro_users-1.2.4 → micro_users-1.3.1}/PKG-INFO +16 -7
- {micro_users-1.2.4 → micro_users-1.3.1}/README.md +16 -7
- {micro_users-1.2.4 → micro_users-1.3.1}/micro_users.egg-info/PKG-INFO +16 -7
- {micro_users-1.2.4 → micro_users-1.3.1}/micro_users.egg-info/SOURCES.txt +1 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/pyproject.toml +1 -1
- {micro_users-1.2.4 → micro_users-1.3.1}/setup.py +1 -1
- {micro_users-1.2.4 → micro_users-1.3.1}/users/filters.py +0 -12
- micro_users-1.3.1/users/migrations/0002_alter_useractivitylog_action.py +18 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/models.py +1 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/tables.py +8 -1
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/users/user_actions.html +7 -8
- {micro_users-1.2.4 → micro_users-1.3.1}/users/views.py +35 -21
- {micro_users-1.2.4 → micro_users-1.3.1}/LICENSE +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/MANIFEST.in +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/micro_users.egg-info/dependency_links.txt +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/micro_users.egg-info/requires.txt +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/micro_users.egg-info/top_level.txt +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/setup.cfg +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/__init__.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/admin.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/apps.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/forms.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/migrations/0001_initial.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/migrations/__init__.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/signals.py +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/static/css/login.css +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/static/img/default_profile.webp +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/registration/login.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/user_activity_log.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/users/manage_users.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/users/profile.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/users/profile_edit.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/users/user_detail.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/templates/users/user_form.html +0 -0
- {micro_users-1.2.4 → micro_users-1.3.1}/users/urls.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: micro_users
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: Arabic Django user management app with abstract user, permissions, and activity logging
|
|
5
5
|
Home-page: https://github.com/debeski/micro-users
|
|
6
6
|
Author: DeBeski
|
|
@@ -99,12 +99,19 @@ python manage.py migrate users
|
|
|
99
99
|
## Structure
|
|
100
100
|
```
|
|
101
101
|
users/
|
|
102
|
-
├──
|
|
103
|
-
├──
|
|
104
|
-
├──
|
|
105
|
-
├──
|
|
106
|
-
├──
|
|
107
|
-
|
|
102
|
+
├── views.py # CRUD operations
|
|
103
|
+
├── urls.py # URL routing
|
|
104
|
+
├── tables.py # User and Activity Log tables
|
|
105
|
+
├── signals.py # Logging signals
|
|
106
|
+
├── models.py # User model, permissions, activity logs
|
|
107
|
+
├── forms.py # Creation, edit,. etc.
|
|
108
|
+
├── filter.py # Search filters
|
|
109
|
+
├── apps.py # Permissions Localization
|
|
110
|
+
├── admin.py # Admin UI integration
|
|
111
|
+
├── __init__.py # Python init
|
|
112
|
+
├── templates/ # HTML templates
|
|
113
|
+
├── static/ # CSS classes
|
|
114
|
+
└── migrations/ # Database migrations
|
|
108
115
|
```
|
|
109
116
|
|
|
110
117
|
## Version History
|
|
@@ -122,3 +129,5 @@ users/
|
|
|
122
129
|
| v1.2.1 | • Fixed a minor import bug |
|
|
123
130
|
| v1.2.3 | • Separated user detail view from table for consistency<br> • Optimized the new detail + log view for optimal compatibiliyy with users |
|
|
124
131
|
| v1.2.4 | • Fixed a couple of visual inconsistencies |
|
|
132
|
+
| v1.3.0 | • Patched a critical security permission issue<br> • Disabled ADMIN from being viewed/edited from other staff members<br> • Fixed an issue when sorting with full_name<br> • Enabled Logging for all actions |
|
|
133
|
+
| v1.3.1 | • replaced a misplaced code that caused a crash when editing profile |
|
|
@@ -68,12 +68,19 @@ python manage.py migrate users
|
|
|
68
68
|
## Structure
|
|
69
69
|
```
|
|
70
70
|
users/
|
|
71
|
-
├──
|
|
72
|
-
├──
|
|
73
|
-
├──
|
|
74
|
-
├──
|
|
75
|
-
├──
|
|
76
|
-
|
|
71
|
+
├── views.py # CRUD operations
|
|
72
|
+
├── urls.py # URL routing
|
|
73
|
+
├── tables.py # User and Activity Log tables
|
|
74
|
+
├── signals.py # Logging signals
|
|
75
|
+
├── models.py # User model, permissions, activity logs
|
|
76
|
+
├── forms.py # Creation, edit,. etc.
|
|
77
|
+
├── filter.py # Search filters
|
|
78
|
+
├── apps.py # Permissions Localization
|
|
79
|
+
├── admin.py # Admin UI integration
|
|
80
|
+
├── __init__.py # Python init
|
|
81
|
+
├── templates/ # HTML templates
|
|
82
|
+
├── static/ # CSS classes
|
|
83
|
+
└── migrations/ # Database migrations
|
|
77
84
|
```
|
|
78
85
|
|
|
79
86
|
## Version History
|
|
@@ -90,4 +97,6 @@ users/
|
|
|
90
97
|
| v1.2.0 | • Added User Details view with specific user activity log |
|
|
91
98
|
| v1.2.1 | • Fixed a minor import bug |
|
|
92
99
|
| v1.2.3 | • Separated user detail view from table for consistency<br> • Optimized the new detail + log view for optimal compatibiliyy with users |
|
|
93
|
-
| v1.2.4 | • Fixed a couple of visual inconsistencies |
|
|
100
|
+
| v1.2.4 | • Fixed a couple of visual inconsistencies |
|
|
101
|
+
| v1.3.0 | • Patched a critical security permission issue<br> • Disabled ADMIN from being viewed/edited from other staff members<br> • Fixed an issue when sorting with full_name<br> • Enabled Logging for all actions |
|
|
102
|
+
| v1.3.1 | • replaced a misplaced code that caused a crash when editing profile |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: micro-users
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: Arabic Django user management app with abstract user, permissions, and activity logging
|
|
5
5
|
Home-page: https://github.com/debeski/micro-users
|
|
6
6
|
Author: DeBeski
|
|
@@ -99,12 +99,19 @@ python manage.py migrate users
|
|
|
99
99
|
## Structure
|
|
100
100
|
```
|
|
101
101
|
users/
|
|
102
|
-
├──
|
|
103
|
-
├──
|
|
104
|
-
├──
|
|
105
|
-
├──
|
|
106
|
-
├──
|
|
107
|
-
|
|
102
|
+
├── views.py # CRUD operations
|
|
103
|
+
├── urls.py # URL routing
|
|
104
|
+
├── tables.py # User and Activity Log tables
|
|
105
|
+
├── signals.py # Logging signals
|
|
106
|
+
├── models.py # User model, permissions, activity logs
|
|
107
|
+
├── forms.py # Creation, edit,. etc.
|
|
108
|
+
├── filter.py # Search filters
|
|
109
|
+
├── apps.py # Permissions Localization
|
|
110
|
+
├── admin.py # Admin UI integration
|
|
111
|
+
├── __init__.py # Python init
|
|
112
|
+
├── templates/ # HTML templates
|
|
113
|
+
├── static/ # CSS classes
|
|
114
|
+
└── migrations/ # Database migrations
|
|
108
115
|
```
|
|
109
116
|
|
|
110
117
|
## Version History
|
|
@@ -122,3 +129,5 @@ users/
|
|
|
122
129
|
| v1.2.1 | • Fixed a minor import bug |
|
|
123
130
|
| v1.2.3 | • Separated user detail view from table for consistency<br> • Optimized the new detail + log view for optimal compatibiliyy with users |
|
|
124
131
|
| v1.2.4 | • Fixed a couple of visual inconsistencies |
|
|
132
|
+
| v1.3.0 | • Patched a critical security permission issue<br> • Disabled ADMIN from being viewed/edited from other staff members<br> • Fixed an issue when sorting with full_name<br> • Enabled Logging for all actions |
|
|
133
|
+
| v1.3.1 | • replaced a misplaced code that caused a crash when editing profile |
|
|
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "micro_users"
|
|
11
|
-
version = "1.
|
|
11
|
+
version = "1.3.1"
|
|
12
12
|
description = "Arabic Django user management app with abstract user, permissions, and activity logging"
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
requires-python = ">=3.11"
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="micro_users",
|
|
8
|
-
version="1.
|
|
8
|
+
version="1.3.1",
|
|
9
9
|
author="DeBeski",
|
|
10
10
|
author_email="debeski1@gmail.com",
|
|
11
11
|
description="Arabic django user management app with abstract user, permissions, and activity logging",
|
|
@@ -14,11 +14,9 @@ class UserFilter(django_filters.FilterSet):
|
|
|
14
14
|
method='filter_keyword',
|
|
15
15
|
label='',
|
|
16
16
|
)
|
|
17
|
-
|
|
18
17
|
class Meta:
|
|
19
18
|
model = User
|
|
20
19
|
fields = []
|
|
21
|
-
|
|
22
20
|
def __init__(self, *args, **kwargs):
|
|
23
21
|
super().__init__(*args, **kwargs)
|
|
24
22
|
self.form.helper = FormHelper()
|
|
@@ -33,7 +31,6 @@ class UserFilter(django_filters.FilterSet):
|
|
|
33
31
|
css_class='form-row'
|
|
34
32
|
),
|
|
35
33
|
)
|
|
36
|
-
|
|
37
34
|
def filter_keyword(self, queryset, name, value):
|
|
38
35
|
"""
|
|
39
36
|
Filter the queryset by matching the keyword in username, email, phone, and occupation.
|
|
@@ -48,37 +45,31 @@ class UserFilter(django_filters.FilterSet):
|
|
|
48
45
|
)
|
|
49
46
|
|
|
50
47
|
|
|
51
|
-
|
|
52
48
|
class UserActivityLogFilter(django_filters.FilterSet):
|
|
53
49
|
keyword = django_filters.CharFilter(
|
|
54
50
|
method='filter_keyword',
|
|
55
51
|
label='',
|
|
56
52
|
)
|
|
57
|
-
|
|
58
53
|
year = django_filters.ChoiceFilter(
|
|
59
54
|
field_name="timestamp__year",
|
|
60
55
|
lookup_expr="exact",
|
|
61
56
|
choices=[],
|
|
62
57
|
empty_label="السنة",
|
|
63
58
|
)
|
|
64
|
-
|
|
65
59
|
class Meta:
|
|
66
60
|
model = UserActivityLog
|
|
67
61
|
fields = {
|
|
68
62
|
'timestamp': ['gte', 'lte'],
|
|
69
63
|
}
|
|
70
|
-
|
|
71
64
|
def __init__(self, *args, **kwargs):
|
|
72
65
|
super().__init__(*args, **kwargs)
|
|
73
66
|
|
|
74
67
|
# Fetch distinct years dynamically
|
|
75
68
|
years = UserActivityLog.objects.dates('timestamp', 'year').distinct()
|
|
76
69
|
self.filters['year'].extra['choices'] = [(year.year, year.year) for year in years]
|
|
77
|
-
|
|
78
70
|
self.filters['year'].field.widget.attrs.update({
|
|
79
71
|
'onchange': 'this.form.submit();'
|
|
80
72
|
})
|
|
81
|
-
|
|
82
73
|
self.form.helper = FormHelper()
|
|
83
74
|
self.form.helper.form_method = 'GET'
|
|
84
75
|
self.form.helper.form_class = 'form-inline'
|
|
@@ -100,7 +91,6 @@ class UserActivityLogFilter(django_filters.FilterSet):
|
|
|
100
91
|
css_class='form-row'
|
|
101
92
|
),
|
|
102
93
|
)
|
|
103
|
-
|
|
104
94
|
def filter_keyword(self, queryset, name, value):
|
|
105
95
|
"""
|
|
106
96
|
Filter the queryset by matching the keyword in username, email, phone, and occupation.
|
|
@@ -116,5 +106,3 @@ class UserActivityLogFilter(django_filters.FilterSet):
|
|
|
116
106
|
Q(ip_address__icontains=value)
|
|
117
107
|
)
|
|
118
108
|
|
|
119
|
-
|
|
120
|
-
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 5.2.8 on 2025-12-08 14:58
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='useractivitylog',
|
|
15
|
+
name='action',
|
|
16
|
+
field=models.CharField(choices=[('LOGIN', 'تسجيل دخـول'), ('LOGOUT', 'تسجيل خـروج'), ('CREATE', 'انشـاء'), ('UPDATE', 'تعديـل'), ('DELETE', 'حــذف'), ('VIEW', 'عـرض'), ('DOWNLOAD', 'تحميل'), ('CONFIRM', 'تأكيـد'), ('REJECT', 'رفــض'), ('RESET', 'اعادة ضبط')], max_length=10, verbose_name='العملية'),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -25,6 +25,7 @@ class UserActivityLog(models.Model):
|
|
|
25
25
|
('DOWNLOAD', 'تحميل'),
|
|
26
26
|
('CONFIRM', 'تأكيـد'),
|
|
27
27
|
('REJECT', 'رفــض'),
|
|
28
|
+
('RESET', 'اعادة ضبط'),
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, verbose_name="اسم المستخدم", null=True, blank=True)
|
|
@@ -10,6 +10,8 @@ class UserTable(tables.Table):
|
|
|
10
10
|
username = tables.Column(verbose_name="اسم المستخدم")
|
|
11
11
|
email = tables.Column(verbose_name="البريد الالكتروني")
|
|
12
12
|
full_name = tables.Column(verbose_name="الاسم بالكامل", orderable=False,)
|
|
13
|
+
is_staff = tables.BooleanColumn(verbose_name="مسؤول")
|
|
14
|
+
is_active = tables.BooleanColumn(verbose_name="نشط")
|
|
13
15
|
last_login = tables.DateColumn(
|
|
14
16
|
format="H:i Y-m-d ", # This is the format you want for the timestamp
|
|
15
17
|
verbose_name="اخر دخول"
|
|
@@ -33,10 +35,15 @@ class UserActivityLogTable(tables.Table):
|
|
|
33
35
|
format="H:i Y-m-d ", # This is the format you want for the timestamp
|
|
34
36
|
verbose_name="وقت العملية"
|
|
35
37
|
)
|
|
38
|
+
full_name = tables.Column(
|
|
39
|
+
verbose_name="الاسم بالكامل",
|
|
40
|
+
accessor='user.full_name',
|
|
41
|
+
order_by='user__first_name'
|
|
42
|
+
)
|
|
36
43
|
class Meta:
|
|
37
44
|
model = UserActivityLog
|
|
38
45
|
template_name = "django_tables2/bootstrap5.html"
|
|
39
|
-
fields = ("timestamp", "user", "
|
|
46
|
+
fields = ("timestamp", "user", "full_name", "action", "model_name", "object_id", "number")
|
|
40
47
|
attrs = {'class': 'table table-hover align-middle'}
|
|
41
48
|
|
|
42
49
|
class UserActivityLogTableNoUser(UserActivityLogTable):
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
<div class="d-flex gap-2 align-items-center justify-content-end no-print">
|
|
2
|
+
{% if user.is_superuser or user.is_staff and not record.is_superuser %}
|
|
2
3
|
<div class="dropdown-center">
|
|
3
4
|
<a href="#" class="action-icon" id="actionDropdown{{ record.id }}" data-bs-toggle="dropdown" aria-expanded="false">
|
|
4
5
|
<i class="bi bi-three-dots-vertical text-dark"></i>
|
|
5
6
|
</a>
|
|
6
7
|
<ul class="dropdown-menu" aria-labelledby="actionDropdown{{ record.id }}">
|
|
7
|
-
|
|
8
|
-
<a class="dropdown-item" href="#" title="عرض">
|
|
9
|
-
<i class="bi bi-person-lines-fill text-dark me-1 h5"> </i> عرض
|
|
10
|
-
</a>
|
|
11
|
-
</li> {% endcomment %}
|
|
8
|
+
|
|
12
9
|
<li>
|
|
13
10
|
<a class="dropdown-item" href="{% url 'user_detail' record.pk %}" title="عرض">
|
|
14
|
-
<i class="bi bi-person-
|
|
11
|
+
<i class="bi bi-person-lines-fill text-dark me-1 h5"> </i> عرض
|
|
15
12
|
</a>
|
|
16
13
|
</li>
|
|
17
|
-
|
|
14
|
+
|
|
15
|
+
{% if user.is_superuser or user.is_staff and not record.is_superuser %}
|
|
18
16
|
<li>
|
|
19
17
|
<a class="dropdown-item" href="{% url 'edit_user' record.pk %}" title="تعديل">
|
|
20
|
-
<i class="bi bi-person-dash-fill text-dark me-1 h5"
|
|
18
|
+
<i class="bi bi-person-dash-fill text-dark me-1 h5"></i> تعديل
|
|
21
19
|
</a>
|
|
22
20
|
</li>
|
|
23
21
|
{% endif %}
|
|
@@ -31,4 +29,5 @@
|
|
|
31
29
|
{% endif %}
|
|
32
30
|
</ul>
|
|
33
31
|
</div>
|
|
32
|
+
{% endif %}
|
|
34
33
|
</div>
|
|
@@ -10,6 +10,7 @@ from django.shortcuts import render, redirect, get_object_or_404
|
|
|
10
10
|
from django_tables2 import RequestConfig, SingleTableView
|
|
11
11
|
from django_filters.views import FilterView
|
|
12
12
|
from django.views.generic.detail import DetailView
|
|
13
|
+
from django.apps import apps
|
|
13
14
|
|
|
14
15
|
# Project imports
|
|
15
16
|
#################
|
|
@@ -22,6 +23,19 @@ from .models import UserActivityLog
|
|
|
22
23
|
|
|
23
24
|
User = get_user_model() # Use custom user model
|
|
24
25
|
|
|
26
|
+
# Helper Function to log actions
|
|
27
|
+
def log_user_action(request, instance, action, model_name):
|
|
28
|
+
UserActivityLog.objects.create(
|
|
29
|
+
user=request.user,
|
|
30
|
+
action=action,
|
|
31
|
+
model_name=model_name,
|
|
32
|
+
object_id=instance.pk,
|
|
33
|
+
number=instance.number if hasattr(instance, 'number') else '',
|
|
34
|
+
timestamp=timezone.now(),
|
|
35
|
+
ip_address=get_client_ip(request),
|
|
36
|
+
user_agent=request.META.get("HTTP_USER_AGENT", ""),
|
|
37
|
+
)
|
|
38
|
+
|
|
25
39
|
#####################################################################
|
|
26
40
|
|
|
27
41
|
# Function to recognize staff
|
|
@@ -48,7 +62,9 @@ class UserListView(LoginRequiredMixin, UserPassesTestMixin, FilterView, SingleTa
|
|
|
48
62
|
def get_queryset(self):
|
|
49
63
|
# Apply the filter and order by any logic you need
|
|
50
64
|
qs = super().get_queryset().order_by('date_joined')
|
|
51
|
-
#
|
|
65
|
+
# Hide superuser entries from non-superusers
|
|
66
|
+
if not self.request.user.is_superuser:
|
|
67
|
+
qs = qs.exclude(is_superuser=True)
|
|
52
68
|
return qs
|
|
53
69
|
|
|
54
70
|
def get_context_data(self, **kwargs):
|
|
@@ -66,11 +82,11 @@ class UserListView(LoginRequiredMixin, UserPassesTestMixin, FilterView, SingleTa
|
|
|
66
82
|
# Function for creating a new User
|
|
67
83
|
@user_passes_test(is_staff)
|
|
68
84
|
def create_user(request):
|
|
69
|
-
|
|
70
85
|
if request.method == "POST":
|
|
71
86
|
form = CustomUserCreationForm(request.POST or None)
|
|
72
87
|
if form.is_valid():
|
|
73
|
-
form.save()
|
|
88
|
+
user = form.save()
|
|
89
|
+
log_user_action(request, user, "CREATE", "مستخدم")
|
|
74
90
|
return redirect("manage_users")
|
|
75
91
|
else:
|
|
76
92
|
return render(request, "users/user_form.html", {"form": form})
|
|
@@ -84,12 +100,19 @@ def create_user(request):
|
|
|
84
100
|
@user_passes_test(is_staff)
|
|
85
101
|
def edit_user(request, pk):
|
|
86
102
|
user = get_object_or_404(User, pk=pk)
|
|
103
|
+
|
|
104
|
+
# 🚫 Block staff users from editing superuser accounts
|
|
105
|
+
if user.is_superuser and not request.user.is_superuser:
|
|
106
|
+
messages.error(request, "لا يمكن تعديل هذا الحساب!")
|
|
107
|
+
return redirect('manage_users')
|
|
108
|
+
|
|
87
109
|
form_reset = ResetPasswordForm(user, data=request.POST or None)
|
|
88
110
|
|
|
89
111
|
if request.method == "POST":
|
|
90
112
|
form = CustomUserChangeForm(request.POST, instance=user)
|
|
91
113
|
if form.is_valid():
|
|
92
|
-
form.save()
|
|
114
|
+
user = form.save()
|
|
115
|
+
log_user_action(request, user, "UPDATE", "مستخدم")
|
|
93
116
|
return redirect("manage_users")
|
|
94
117
|
else:
|
|
95
118
|
# Validation errors will be automatically handled by the form object
|
|
@@ -106,17 +129,8 @@ def edit_user(request, pk):
|
|
|
106
129
|
def delete_user(request, pk):
|
|
107
130
|
user = get_object_or_404(User, pk=pk)
|
|
108
131
|
if request.method == "POST":
|
|
132
|
+
log_user_action(request, user, "DELETE", "مستخدم")
|
|
109
133
|
user.delete()
|
|
110
|
-
UserActivityLog.objects.create(
|
|
111
|
-
user=request.user,
|
|
112
|
-
action="DELETE",
|
|
113
|
-
model_name='مستخدم',
|
|
114
|
-
object_id=user.pk,
|
|
115
|
-
number=user.username, # Save the relevant number
|
|
116
|
-
timestamp=timezone.now(),
|
|
117
|
-
ip_address=get_client_ip(request), # Assuming you have this function
|
|
118
|
-
user_agent=request.META.get("HTTP_USER_AGENT", ""),
|
|
119
|
-
)
|
|
120
134
|
return redirect("manage_users")
|
|
121
135
|
return redirect("manage_users") # Redirect instead of rendering a separate page
|
|
122
136
|
|
|
@@ -172,10 +186,11 @@ def reset_password(request, pk):
|
|
|
172
186
|
form = ResetPasswordForm(user=user, data=request.POST) # ✅ Correct usage with SetPasswordForm
|
|
173
187
|
if form.is_valid():
|
|
174
188
|
form.save()
|
|
175
|
-
|
|
189
|
+
log_user_action(request, user, "RESET", "رمز سري")
|
|
190
|
+
return redirect("manage_users")
|
|
176
191
|
else:
|
|
177
|
-
print("Form errors:", form.errors)
|
|
178
|
-
return redirect("edit_user", pk=pk)
|
|
192
|
+
print("Form errors:", form.errors)
|
|
193
|
+
return redirect("edit_user", pk=pk)
|
|
179
194
|
|
|
180
195
|
return redirect("manage_users") # Fallback redirect
|
|
181
196
|
|
|
@@ -189,6 +204,7 @@ def user_profile(request):
|
|
|
189
204
|
password_form = ArabicPasswordChangeForm(user, request.POST)
|
|
190
205
|
if password_form.is_valid():
|
|
191
206
|
password_form.save()
|
|
207
|
+
log_user_action(request, user, "UPDATE", "رمز سري")
|
|
192
208
|
update_session_auth_hash(request, password_form.user) # Prevent user from being logged out
|
|
193
209
|
messages.success(request, 'تم تغيير كلمة المرور بنجاح!')
|
|
194
210
|
return redirect('user_profile')
|
|
@@ -209,14 +225,12 @@ def edit_profile(request):
|
|
|
209
225
|
if request.method == 'POST':
|
|
210
226
|
form = UserProfileEditForm(request.POST, request.FILES, instance=request.user)
|
|
211
227
|
if form.is_valid():
|
|
212
|
-
form.save()
|
|
228
|
+
user = form.save()
|
|
229
|
+
log_user_action(request, user, "UPDATE", "بيانات شخصية")
|
|
213
230
|
messages.success(request, 'تم حفظ التغييرات بنجاح')
|
|
214
231
|
return redirect('user_profile')
|
|
215
232
|
else:
|
|
216
233
|
messages.error(request, 'حدث خطأ أثناء حفظ التغييرات')
|
|
217
|
-
|
|
218
234
|
else:
|
|
219
235
|
form = UserProfileEditForm(instance=request.user)
|
|
220
|
-
|
|
221
236
|
return render(request, 'users/profile_edit.html', {'form': form})
|
|
222
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|