micro-users 1.1.1__py3-none-any.whl → 1.2.1__py3-none-any.whl
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.1.1.dist-info → micro_users-1.2.1.dist-info}/METADATA +15 -1
- {micro_users-1.1.1.dist-info → micro_users-1.2.1.dist-info}/RECORD +11 -10
- users/tables.py +6 -0
- users/templates/users/manage_users.html +1 -1
- users/templates/users/user_actions.html +7 -2
- users/templates/users/user_detail.html +49 -0
- users/urls.py +4 -3
- users/views.py +35 -8
- {micro_users-1.1.1.dist-info → micro_users-1.2.1.dist-info}/LICENSE +0 -0
- {micro_users-1.1.1.dist-info → micro_users-1.2.1.dist-info}/WHEEL +0 -0
- {micro_users-1.1.1.dist-info → micro_users-1.2.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: micro-users
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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
|
|
@@ -105,3 +105,17 @@ users/
|
|
|
105
105
|
├── templates/ # HTML templates
|
|
106
106
|
└── migrations/ # Database migrations
|
|
107
107
|
```
|
|
108
|
+
|
|
109
|
+
## Version History
|
|
110
|
+
|
|
111
|
+
| Version | Changes |
|
|
112
|
+
|----------|---------|
|
|
113
|
+
| v1.0.0 | Initial release as pip package |
|
|
114
|
+
| v1.0.1 | Fixed a couple of new issues as a pip package |
|
|
115
|
+
| v1.0.2 | Fixed the readme and building files |
|
|
116
|
+
| v1.0.3 | Still getting the hang of this pip publish thing |
|
|
117
|
+
| v1.0.4 | Honestly still messing with and trying settings and stuff out |
|
|
118
|
+
| v1.1.0 | OK, finally a working seamless micro-users app |
|
|
119
|
+
| v1.1.1 | Fixed a bug where a staff member can edit the admin details |
|
|
120
|
+
| v1.2.0 | Added User Details view with specific user activity log |
|
|
121
|
+
| v1.2.1 | Fixed a minot import bug |
|
|
@@ -5,20 +5,21 @@ users/filters.py,sha256=9-yrWBF-CdWb1nrAhmifWb1AHI0z4LQma1uR_9jLr2U,4797
|
|
|
5
5
|
users/forms.py,sha256=GHC8pFm2i9PD3MVaakrgMXEszsBrXieHq7DYiAfo8Fw,14977
|
|
6
6
|
users/models.py,sha256=KX_6LoiNJN6PCTFOuuGp5so4CNn5pAh1Vpaigv4fKk4,2060
|
|
7
7
|
users/signals.py,sha256=5Kd3KyfPT6740rvwZj4vy1yXsmjVhmaQ__RB8p5R5aE,1336
|
|
8
|
-
users/tables.py,sha256=
|
|
9
|
-
users/urls.py,sha256=
|
|
10
|
-
users/views.py,sha256=
|
|
8
|
+
users/tables.py,sha256=ZW8_TR6Y3DwchS7Q8DGVdJX01fjIDZfaqoZo0XpZnyU,1920
|
|
9
|
+
users/urls.py,sha256=FwQ9GVOBRQ4iXQ9UyLFI0aEAga0d5qL_miPNpmFPA-Q,1022
|
|
10
|
+
users/views.py,sha256=mruC7kf0_KMiNVxOwZNMyt5pUidu7U3RyIgYHDEFJ_4,8113
|
|
11
11
|
users/migrations/0001_initial.py,sha256=lx9sSKS-lxHhI6gelVH52NOkwqEMJ32TvOJUn9zaOXM,4709
|
|
12
12
|
users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
users/templates/user_activity_log.html,sha256=S_FDN6vVLz_mB826yjeU9vtVGtzk7E_LKBmQIeYtdkQ,611
|
|
14
14
|
users/templates/registration/login.html,sha256=owbzO_XjqMeSncwWxkTzsvbkhjEZd7LdbblC3HBnld0,4091
|
|
15
|
-
users/templates/users/manage_users.html,sha256=
|
|
15
|
+
users/templates/users/manage_users.html,sha256=ZusUTrJcp-xeORBnOcBObi9q1zlQE9yVRurMEvQVeWQ,2940
|
|
16
16
|
users/templates/users/profile.html,sha256=9ahVF6YZUR-6-c8SKc0rN2pVdis2lI9gbcOQZeMaFnY,2909
|
|
17
17
|
users/templates/users/profile_edit.html,sha256=sgO3h9ffVK1vnDNl4E6l5x3xfam3FTQl6Lqkrw5gmlw,4215
|
|
18
|
-
users/templates/users/user_actions.html,sha256=
|
|
18
|
+
users/templates/users/user_actions.html,sha256=oJedI5NElf-bXFBdeuQVNgx2enbwRsJSowBv5i6PFUE,1625
|
|
19
|
+
users/templates/users/user_detail.html,sha256=jU6GIKlXEwnASDL_yosc6V46SjSwIDAgaRn0_iDK50U,1688
|
|
19
20
|
users/templates/users/user_form.html,sha256=jcyI7OQZOY4ue4DajPtfjAt2SmAYO5ZgHNOqTp2-FO0,1352
|
|
20
|
-
micro_users-1.
|
|
21
|
-
micro_users-1.
|
|
22
|
-
micro_users-1.
|
|
23
|
-
micro_users-1.
|
|
24
|
-
micro_users-1.
|
|
21
|
+
micro_users-1.2.1.dist-info/LICENSE,sha256=Fco89ULLSSxKkC2KKnx57SaT0R7WOkZfuk8IYcGiN50,1063
|
|
22
|
+
micro_users-1.2.1.dist-info/METADATA,sha256=Q__3FA_yfJm1I4WY9gLGBeVv8-N7L4fj6tUaLRWGhLE,3421
|
|
23
|
+
micro_users-1.2.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
24
|
+
micro_users-1.2.1.dist-info/top_level.txt,sha256=tWT24ZcWau2wrlbpU_h3mP2jRukyLaVYiyHBuOezpLQ,6
|
|
25
|
+
micro_users-1.2.1.dist-info/RECORD,,
|
users/tables.py
CHANGED
|
@@ -38,3 +38,9 @@ class UserActivityLogTable(tables.Table):
|
|
|
38
38
|
template_name = "django_tables2/bootstrap5.html"
|
|
39
39
|
fields = ("timestamp", "user", "user.full_name", "action", "model_name", "object_id", "number")
|
|
40
40
|
attrs = {'class': 'table table-hover align-middle'}
|
|
41
|
+
|
|
42
|
+
class UserActivityLogTableNoUser(UserActivityLogTable):
|
|
43
|
+
class Meta(UserActivityLogTable.Meta):
|
|
44
|
+
# Remove the 'user' and 'user.full_name' columns
|
|
45
|
+
exclude = ("user", "user.full_name")
|
|
46
|
+
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
|
|
66
66
|
// Update the modal content
|
|
67
67
|
document.getElementById("userName").textContent = userName;
|
|
68
|
-
form.action =
|
|
68
|
+
form.action = "{% url 'delete_user' 0 %}".replace("/0/", `/${userId}/`);
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
</script>
|
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
</li> {% endcomment %}
|
|
12
12
|
{% if not record.is_superuser %}
|
|
13
13
|
<li>
|
|
14
|
-
<a class="dropdown-item" href="{% url '
|
|
14
|
+
<a class="dropdown-item" href="{% url 'user_detail' record.pk %}" title="عرض">
|
|
15
|
+
<i class="bi bi-person-dash-fill text-dark me-1 h5"> </i> عرض
|
|
16
|
+
</a>
|
|
17
|
+
</li>
|
|
18
|
+
<li>
|
|
19
|
+
<a class="dropdown-item" href="{% url 'edit_user' record.pk %}" title="تعديل">
|
|
15
20
|
<i class="bi bi-person-dash-fill text-dark me-1 h5"> </i> تعديل
|
|
16
21
|
</a>
|
|
17
22
|
</li>
|
|
@@ -19,7 +24,7 @@
|
|
|
19
24
|
{% if user.is_superuser and not record.is_staff %}
|
|
20
25
|
<li>
|
|
21
26
|
<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#deleteModal"
|
|
22
|
-
data-user-id="{{ record.
|
|
27
|
+
data-user-id="{{ record.pk }}" data-user-name="{{ record.username }}">
|
|
23
28
|
<i class="bi bi-x-octagon text-danger me-1 h5"> </i> حذف
|
|
24
29
|
</a>
|
|
25
30
|
</li>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% load crispy_forms_tags %}
|
|
3
|
+
{% load django_tables2 %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
<div class="container mt-4">
|
|
7
|
+
<!-- USER INFO CARD -->
|
|
8
|
+
<div class="card mb-4 shadow-sm">
|
|
9
|
+
<div class="card-header bg-primary text-white">
|
|
10
|
+
تفاصيل المستخدم
|
|
11
|
+
</div>
|
|
12
|
+
<div class="card-body">
|
|
13
|
+
<h5>{{ detail_user.get_full_name }} ({{ detail_user.username }})</h5>
|
|
14
|
+
<p class="mb-1"><strong>البريد:</strong> {{ detail_user.email }}</p>
|
|
15
|
+
<p class="mb-1"><strong>تاريخ الإنشاء:</strong> {{ detail_user.date_joined|date:"Y-m-d" }}</p>
|
|
16
|
+
<p class="mb-1"><strong>آخر دخول:</strong> {{ detail_user.last_login|date:"Y-m-d H:i" }}</p>
|
|
17
|
+
<p class="mb-1"><strong>الصلاحيات:</strong>
|
|
18
|
+
{% if detail_user.is_superuser %}
|
|
19
|
+
مدير النظام
|
|
20
|
+
{% elif detail_user.is_staff %}
|
|
21
|
+
مستخدم اداري
|
|
22
|
+
{% else %}
|
|
23
|
+
مستخدم عادي
|
|
24
|
+
{% endif %}
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- ACTIVITY LOG TABLE -->
|
|
30
|
+
<div class="card shadow-sm">
|
|
31
|
+
<div class="card-header bg-secondary text-white">
|
|
32
|
+
سجل نشاط المستخدم
|
|
33
|
+
</div>
|
|
34
|
+
<div class="card-body">
|
|
35
|
+
{% render_table table %}
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- BACK BUTTON -->
|
|
40
|
+
<div class="mb-3">
|
|
41
|
+
<a href="{% url 'manage_users' %}" class="btn btn-outline-secondary">
|
|
42
|
+
<i class="bi bi-arrow-right me-2"></i> العودة إلى إدارة المستخدمين
|
|
43
|
+
</a>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
{% endblock %}
|
users/urls.py
CHANGED
|
@@ -9,10 +9,11 @@ urlpatterns = [
|
|
|
9
9
|
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
|
10
10
|
path("users/", views.UserListView.as_view(), name="manage_users"),
|
|
11
11
|
path('users/create/', views.create_user, name='create_user'),
|
|
12
|
-
path('users/edit/<int:
|
|
13
|
-
path('users/delete/<int:
|
|
12
|
+
path('users/edit/<int:pk>/', views.edit_user, name='edit_user'),
|
|
13
|
+
path('users/delete/<int:pk>/', views.delete_user, name='delete_user'),
|
|
14
14
|
path("profile", views.user_profile, name="user_profile"),
|
|
15
15
|
path('profile/edit/', views.edit_profile, name='edit_profile'),
|
|
16
16
|
path("logs/", views.UserActivityLogView.as_view(), name="user_activity_log"),
|
|
17
|
-
path('reset_password/<int:
|
|
17
|
+
path('reset_password/<int:pk>/', views.reset_password, name="reset_password"),
|
|
18
|
+
path("users/<int:pk>/", views.UserDetailView.as_view(), name="user_detail"),
|
|
18
19
|
]
|
users/views.py
CHANGED
|
@@ -9,12 +9,13 @@ from django.http import JsonResponse
|
|
|
9
9
|
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
|
+
from django.views.generic.detail import DetailView
|
|
12
13
|
|
|
13
14
|
# Project imports
|
|
14
15
|
#################
|
|
15
16
|
|
|
16
17
|
from .signals import get_client_ip
|
|
17
|
-
from .tables import UserTable, UserActivityLogTable
|
|
18
|
+
from .tables import UserTable, UserActivityLogTable, UserActivityLogTableNoUser
|
|
18
19
|
from .forms import CustomUserCreationForm, CustomUserChangeForm, ArabicPasswordChangeForm, ResetPasswordForm, UserProfileEditForm
|
|
19
20
|
from .filters import UserFilter, UserActivityLogFilter
|
|
20
21
|
from .models import UserActivityLog
|
|
@@ -32,6 +33,7 @@ def is_staff(user):
|
|
|
32
33
|
def is_superuser(user):
|
|
33
34
|
return user.is_superuser
|
|
34
35
|
|
|
36
|
+
|
|
35
37
|
# Class Function for managing users
|
|
36
38
|
class UserListView(LoginRequiredMixin, UserPassesTestMixin, FilterView, SingleTableView):
|
|
37
39
|
model = User
|
|
@@ -80,8 +82,8 @@ def create_user(request):
|
|
|
80
82
|
|
|
81
83
|
# Function for editing an existing User
|
|
82
84
|
@user_passes_test(is_staff)
|
|
83
|
-
def edit_user(request,
|
|
84
|
-
user = get_object_or_404(User,
|
|
85
|
+
def edit_user(request, pk):
|
|
86
|
+
user = get_object_or_404(User, pk=pk)
|
|
85
87
|
form_reset = ResetPasswordForm(user, data=request.POST or None)
|
|
86
88
|
|
|
87
89
|
if request.method == "POST":
|
|
@@ -101,8 +103,8 @@ def edit_user(request, user_id):
|
|
|
101
103
|
|
|
102
104
|
# Function for deleting a User
|
|
103
105
|
@user_passes_test(is_superuser)
|
|
104
|
-
def delete_user(request,
|
|
105
|
-
user = get_object_or_404(User,
|
|
106
|
+
def delete_user(request, pk):
|
|
107
|
+
user = get_object_or_404(User, pk=pk)
|
|
106
108
|
if request.method == "POST":
|
|
107
109
|
user.delete()
|
|
108
110
|
UserActivityLog.objects.create(
|
|
@@ -139,10 +141,35 @@ class UserActivityLogView(LoginRequiredMixin, UserPassesTestMixin, SingleTableVi
|
|
|
139
141
|
return context
|
|
140
142
|
|
|
141
143
|
|
|
144
|
+
class UserDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView, SingleTableView):
|
|
145
|
+
model = User
|
|
146
|
+
template_name = "users/user_detail.html"
|
|
147
|
+
context_object_name = "detail_user" # Keep clear naming
|
|
148
|
+
table_class = UserActivityLogTableNoUser
|
|
149
|
+
paginate_by = 10
|
|
150
|
+
|
|
151
|
+
def test_func(self):
|
|
152
|
+
# only staff can view user detail page
|
|
153
|
+
return self.request.user.is_staff
|
|
154
|
+
|
|
155
|
+
def get_queryset(self):
|
|
156
|
+
# This is for the DetailView (only target user)
|
|
157
|
+
return User.objects.all()
|
|
158
|
+
|
|
159
|
+
def get_table_data(self):
|
|
160
|
+
# filter log table to user in URL
|
|
161
|
+
return UserActivityLog.objects.filter(user=self.get_object()).order_by('-timestamp')
|
|
162
|
+
|
|
163
|
+
def get_context_data(self, **kwargs):
|
|
164
|
+
context = super().get_context_data(**kwargs)
|
|
165
|
+
context['table'] = self.get_table() # table instance
|
|
166
|
+
return context
|
|
167
|
+
|
|
168
|
+
|
|
142
169
|
# Function that resets a user password
|
|
143
170
|
@user_passes_test(is_staff)
|
|
144
|
-
def reset_password(request,
|
|
145
|
-
user = get_object_or_404(User, id=
|
|
171
|
+
def reset_password(request, pk):
|
|
172
|
+
user = get_object_or_404(User, id=pk)
|
|
146
173
|
|
|
147
174
|
if request.method == "POST":
|
|
148
175
|
form = ResetPasswordForm(user=user, data=request.POST) # ✅ Correct usage with SetPasswordForm
|
|
@@ -151,7 +178,7 @@ def reset_password(request, user_id):
|
|
|
151
178
|
return redirect("manage_users") # Redirect after successful reset
|
|
152
179
|
else:
|
|
153
180
|
print("Form errors:", form.errors) # Debugging
|
|
154
|
-
return redirect("edit_user",
|
|
181
|
+
return redirect("edit_user", pk=pk) # Redirect to edit user on failure
|
|
155
182
|
|
|
156
183
|
return redirect("manage_users") # Fallback redirect
|
|
157
184
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|