micro-users 1.5.0__py3-none-any.whl → 1.6.2__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.

users/views.py CHANGED
@@ -11,20 +11,23 @@ from django_tables2 import RequestConfig, SingleTableView, SingleTableMixin
11
11
  from django_filters.views import FilterView
12
12
  from django.views.generic.detail import DetailView
13
13
  from django.apps import apps
14
+ from django.utils.module_loading import import_string
15
+ from django.contrib.auth.views import LoginView
16
+ from django.conf import settings
14
17
 
15
18
  # Project imports
16
19
  #################
17
20
 
18
21
  from .signals import get_client_ip
19
- from .tables import UserTable, UserActivityLogTable, UserActivityLogTableNoUser
22
+ from .tables import UserTable
20
23
  from .forms import CustomUserCreationForm, CustomUserChangeForm, ArabicPasswordChangeForm, ResetPasswordForm, UserProfileEditForm
21
- from .filters import UserFilter, UserActivityLogFilter
22
- from .models import UserActivityLog
24
+ from .filters import UserFilter
23
25
 
24
26
  User = get_user_model() # Use custom user model
25
27
 
26
28
  # Helper Function to log actions
27
29
  def log_user_action(request, instance, action, model_name):
30
+ UserActivityLog = apps.get_model('users', 'UserActivityLog')
28
31
  UserActivityLog.objects.create(
29
32
  user=request.user,
30
33
  action=action,
@@ -38,6 +41,15 @@ def log_user_action(request, instance, action, model_name):
38
41
 
39
42
  #####################################################################
40
43
 
44
+ # Custom Login View with Theme Injection
45
+ class CustomLoginView(LoginView):
46
+ def get_context_data(self, **kwargs):
47
+ context = super().get_context_data(**kwargs)
48
+ # Inject theme configuration from settings
49
+ context['theme'] = getattr(settings, 'MICRO_USERS_THEME', {})
50
+ return context
51
+
52
+
41
53
  # Function to recognize staff
42
54
  def is_staff(user):
43
55
  return user.is_staff
@@ -64,12 +76,14 @@ class UserListView(LoginRequiredMixin, UserPassesTestMixin, FilterView, SingleTa
64
76
  def get_queryset(self):
65
77
  # Apply the filter and order by any logic you need
66
78
  qs = super().get_queryset().order_by('date_joined')
79
+ # Exclude soft-deleted users
80
+ qs = qs.filter(deleted_at__isnull=True)
67
81
  # Hide superuser entries from non-superusers
68
82
  if not self.request.user.is_superuser:
69
83
  qs = qs.exclude(is_superuser=True)
70
- # Restrict to same department
71
- if self.request.user.department:
72
- qs = qs.filter(department=self.request.user.department)
84
+ # Restrict to same scope
85
+ if self.request.user.scope:
86
+ qs = qs.filter(scope=self.request.user.scope)
73
87
  return qs
74
88
 
75
89
  def get_context_data(self, **kwargs):
@@ -91,12 +105,11 @@ def create_user(request):
91
105
  form = CustomUserCreationForm(request.POST or None, user=request.user)
92
106
  if form.is_valid():
93
107
  user = form.save(commit=False)
94
- # Auto-assign department for non-superusers
95
- if not request.user.is_superuser and request.user.department:
96
- user.department = request.user.department
108
+ # Auto-assign scope for non-superusers
109
+ if not request.user.is_superuser and request.user.scope:
110
+ user.scope = request.user.scope
97
111
  user.save()
98
112
  user.user_permissions.set(form.cleaned_data["permissions"])
99
- log_user_action(request, user, "CREATE", "مستخدم")
100
113
  return redirect("manage_users")
101
114
  else:
102
115
  return render(request, "users/user_form.html", {"form": form})
@@ -116,9 +129,9 @@ def edit_user(request, pk):
116
129
  messages.error(request, "لا يمكن تعديل هذا الحساب!")
117
130
 
118
131
 
119
- # Restrict to same department
132
+ # Restrict to same scope
120
133
  if not request.user.is_superuser:
121
- if request.user.department and user.department != request.user.department:
134
+ if request.user.scope and user.scope != request.user.scope:
122
135
  messages.error(request, "ليس لديك صلاحية لتعديل هذا المستخدم!")
123
136
  return redirect('manage_users')
124
137
 
@@ -130,7 +143,6 @@ def edit_user(request, pk):
130
143
  user = form.save(commit=False)
131
144
  user.save()
132
145
  user.user_permissions.set(form.cleaned_data["permissions"])
133
- log_user_action(request, user, "UPDATE", "مستخدم")
134
146
  return redirect("manage_users")
135
147
  else:
136
148
  # Validation errors will be automatically handled by the form object
@@ -147,24 +159,26 @@ def edit_user(request, pk):
147
159
  def delete_user(request, pk):
148
160
  user = get_object_or_404(User, pk=pk)
149
161
 
150
- # Restrict to same department
162
+ # Restrict to same scope
151
163
  if not request.user.is_superuser:
152
- if request.user.department and user.department != request.user.department:
164
+ if request.user.scope and user.scope != request.user.scope:
153
165
  messages.error(request, "ليس لديك صلاحية لحذف هذا المستخدم!")
154
166
  return redirect('manage_users')
155
167
 
156
168
  if request.method == "POST":
157
- log_user_action(request, user, "DELETE", "مستخدم")
158
- user.delete()
169
+ # Soft delete the user
170
+ user.is_active = False
171
+ user.deleted_at = timezone.now()
172
+ user.save()
159
173
  return redirect("manage_users")
160
174
  return redirect("manage_users") # Redirect instead of rendering a separate page
161
175
 
162
176
 
163
177
  # Class Function for the Log
164
178
  class UserActivityLogView(LoginRequiredMixin, UserPassesTestMixin, SingleTableMixin, FilterView):
165
- model = UserActivityLog
166
- table_class = UserActivityLogTable
167
- filterset_class = UserActivityLogFilter
179
+ model = apps.get_model('users', 'UserActivityLog')
180
+ table_class = import_string('users.tables.UserActivityLogTable')
181
+ filterset_class = import_string('users.filters.UserActivityLogFilter')
168
182
  template_name = "users/user_activity_log.html"
169
183
 
170
184
  def test_func(self):
@@ -175,14 +189,14 @@ class UserActivityLogView(LoginRequiredMixin, UserPassesTestMixin, SingleTableMi
175
189
  qs = super().get_queryset().order_by('-timestamp')
176
190
  if not self.request.user.is_superuser:
177
191
  qs = qs.exclude(user__is_superuser=True)
178
- if self.request.user.department:
179
- qs = qs.filter(user__department=self.request.user.department)
192
+ if self.request.user.scope:
193
+ qs = qs.filter(user__scope=self.request.user.scope)
180
194
  return qs
181
195
 
182
196
  def get_table(self, **kwargs):
183
197
  table = super().get_table(**kwargs)
184
- if self.request.user.department:
185
- table.exclude = ('department',)
198
+ if self.request.user.scope:
199
+ table.exclude = ('scope',)
186
200
  return table
187
201
 
188
202
  def get_context_data(self, **kwargs):
@@ -204,9 +218,11 @@ class UserDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
204
218
  context = super().get_context_data(**kwargs)
205
219
 
206
220
  # self.object is the User instance
221
+ UserActivityLog = apps.get_model('users', 'UserActivityLog')
207
222
  logs_qs = UserActivityLog.objects.filter(user=self.object).order_by('-timestamp')
208
223
 
209
224
  # Create table manually
225
+ UserActivityLogTableNoUser = import_string('users.tables.UserActivityLogTableNoUser')
210
226
  table = UserActivityLogTableNoUser(logs_qs)
211
227
  RequestConfig(self.request, paginate={'per_page': 10}).configure(table)
212
228
 
@@ -219,9 +235,9 @@ class UserDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
219
235
  def reset_password(request, pk):
220
236
  user = get_object_or_404(User, id=pk)
221
237
 
222
- # Restrict to same department
238
+ # Restrict to same scope
223
239
  if not request.user.is_superuser:
224
- if request.user.department and user.department != request.user.department:
240
+ if request.user.scope and user.scope != request.user.scope:
225
241
  messages.error(request, "ليس لديك صلاحية لتعديل هذا المستخدم!")
226
242
  return redirect('manage_users')
227
243
 
@@ -278,78 +294,84 @@ def edit_profile(request):
278
294
  form = UserProfileEditForm(instance=request.user)
279
295
  return render(request, 'users/profile/profile_edit.html', {'form': form})
280
296
 
281
- # Department Management Views
297
+ # Scope Management Views
282
298
  # ###########################
283
299
  from django.template.loader import render_to_string
284
- from .forms import DepartmentForm
285
- from .models import Department
286
- from .tables import DepartmentTable
287
300
 
288
301
  @login_required # staff check handled in template or can be added here
289
302
  @user_passes_test(is_staff)
290
- def manage_departments(request):
303
+ def manage_scopes(request):
291
304
  """
292
305
  Returns the initial modal content with the table.
293
306
  """
294
- if request.user.department:
307
+ if request.user.scope:
295
308
  return JsonResponse({'error': 'Permission denied.'}, status=403)
296
309
 
297
- table = DepartmentTable(Department.objects.all())
310
+ Scope = apps.get_model('users', 'Scope')
311
+ ScopeTable = import_string('users.tables.ScopeTable')
312
+ table = ScopeTable(Scope.objects.all())
298
313
  RequestConfig(request, paginate={'per_page': 5}).configure(table)
299
314
 
300
315
  context = {'table': table}
301
- html = render_to_string('users/partials/department_manager.html', context, request=request)
316
+ html = render_to_string('users/partials/scope_manager.html', context, request=request)
302
317
  return JsonResponse({'html': html})
303
318
 
304
319
  @login_required
305
320
  @user_passes_test(is_staff)
306
- def get_department_form(request, pk=None):
321
+ def get_scope_form(request, pk=None):
307
322
  """
308
323
  Returns the Add/Edit form partial.
309
324
  """
310
- if request.user.department:
325
+ if request.user.scope:
311
326
  return JsonResponse({'error': 'Permission denied.'}, status=403)
312
327
 
328
+ ScopeForm = import_string('users.forms.ScopeForm')
329
+ Scope = apps.get_model('users', 'Scope')
330
+
313
331
  if pk:
314
- department = get_object_or_404(Department, pk=pk)
315
- form = DepartmentForm(instance=department)
332
+ scope = get_object_or_404(Scope, pk=pk)
333
+ form = ScopeForm(instance=scope)
316
334
  else:
317
- form = DepartmentForm()
335
+ form = ScopeForm()
318
336
 
319
- html = render_to_string('users/partials/department_form.html', {'form': form, 'department_id': pk}, request=request)
337
+ html = render_to_string('users/partials/scope_form.html', {'form': form, 'scope_id': pk}, request=request)
320
338
  return JsonResponse({'html': html})
321
339
 
322
340
  @login_required
323
341
  @user_passes_test(is_staff)
324
- def save_department(request, pk=None):
342
+ def save_scope(request, pk=None):
325
343
  """
326
344
  Handles form submission. Returns updated table on success, or form with errors on failure.
327
345
  """
328
- if request.user.department:
346
+ if request.user.scope:
329
347
  return JsonResponse({'error': 'Permission denied.'}, status=403)
348
+
349
+ ScopeForm = import_string('users.forms.ScopeForm')
350
+ Scope = apps.get_model('users', 'Scope')
351
+ ScopeTable = import_string('users.tables.ScopeTable')
330
352
 
331
353
  if request.method == "POST":
332
354
  if pk:
333
- department = get_object_or_404(Department, pk=pk)
334
- form = DepartmentForm(request.POST, instance=department)
355
+ scope = get_object_or_404(Scope, pk=pk)
356
+ form = ScopeForm(request.POST, instance=scope)
335
357
  else:
336
- form = DepartmentForm(request.POST)
358
+ form = ScopeForm(request.POST)
337
359
 
338
360
  if form.is_valid():
339
361
  form.save()
340
362
  # Return updated table
341
- table = DepartmentTable(Department.objects.all())
363
+ table = ScopeTable(Scope.objects.all())
342
364
  RequestConfig(request, paginate={'per_page': 5}).configure(table)
343
- html = render_to_string('users/partials/department_manager.html', {'table': table}, request=request)
365
+ html = render_to_string('users/partials/scope_manager.html', {'table': table}, request=request)
344
366
  return JsonResponse({'success': True, 'html': html})
345
367
  else:
346
368
  # Return form with errors
347
- html = render_to_string('users/partials/department_form.html', {'form': form, 'department_id': pk}, request=request)
369
+ html = render_to_string('users/partials/scope_form.html', {'form': form, 'scope_id': pk}, request=request)
348
370
  return JsonResponse({'success': False, 'html': html})
349
371
 
350
372
  return JsonResponse({'success': False, 'error': 'Invalid method'})
351
373
 
352
374
  @login_required
353
375
  @user_passes_test(is_staff)
354
- def delete_department(request, pk):
355
- return JsonResponse({'success': False, 'error': 'تم تعطيل حذف الأقسام لأسباب أمنية.'})
376
+ def delete_scope(request, pk):
377
+ return JsonResponse({'success': False, 'error': 'تم تعطيل حذف النطاقات لأسباب أمنية.'})
@@ -1,33 +0,0 @@
1
- users/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- users/admin.py,sha256=tjaAXU5Un6P6sVF9uGXQP6aqyTguMf_jLquidvaD04Y,622
3
- users/apps.py,sha256=Xb1nGvCl08KaUVqcUG82-jYdG6-KTVjaw_lgr5GIuYY,1133
4
- users/filters.py,sha256=tXEW7W8V_DsttD0h2Dsc5KUjCQJG18hNqY-w02NqZCw,4722
5
- users/forms.py,sha256=DQb2GCk9z6NyMtwoL7oCWD2sbTKQ23dMD3opKHCQkhI,16549
6
- users/models.py,sha256=JZTu3IcsJfE0hkJe8ycFd5ITu-X2KD90Gno6iv0znuY,2479
7
- users/signals.py,sha256=5Kd3KyfPT6740rvwZj4vy1yXsmjVhmaQ__RB8p5R5aE,1336
8
- users/tables.py,sha256=m78Ano8k0CCINQrCn9lk08cag6RXjQqTbYBRyfJP64g,2780
9
- users/urls.py,sha256=4pzhGcwWuLx8rDq_AnkhOhWeRigpOuDgnZCDwTBYi_E,1585
10
- users/views.py,sha256=aN3qnkkdxWRjvmBAJ8Ay6bWrzZcNMvJpQ8wIQXbk5JQ,13602
11
- users/migrations/0001_initial.py,sha256=lx9sSKS-lxHhI6gelVH52NOkwqEMJ32TvOJUn9zaOXM,4709
12
- users/migrations/0002_alter_useractivitylog_action.py,sha256=I7NLxgcPTslCMuADcr1srXS_C_0y_LcZiAFFHBG5NsE,715
13
- users/migrations/0003_department_alter_useractivitylog_options_and_more.py,sha256=RGTawFewYHxCUVIU8nCKWTTZ5OH22iIcfVv1jk_37K4,1289
14
- users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- users/static/css/login.css,sha256=SiJ6jBbWQAP2Nxt7DOTZbTcFYP9JEp557AuQZ9Eirb0,2120
16
- users/static/img/default_profile.webp,sha256=BKUoQHo4z_fZnmc6z6I-KFvLEHahDr98U9LnDQKHLAM,3018
17
- users/templates/registration/login.html,sha256=owbzO_XjqMeSncwWxkTzsvbkhjEZd7LdbblC3HBnld0,4091
18
- users/templates/users/manage_users.html,sha256=rxXZPNXu_cmdnfxFU4K79n4FvjJe4hOZqoT-ybG_JTg,6329
19
- users/templates/users/user_activity_log.html,sha256=41G7Wjv8ehBTSALwLLVzzoIBIo5hSM3FOw36olDINF8,481
20
- users/templates/users/user_detail.html,sha256=yPiuOGF96rV8t2H1Fl2hhIq78N1588ZFbh5gbAezaxw,2053
21
- users/templates/users/user_form.html,sha256=jcyI7OQZOY4ue4DajPtfjAt2SmAYO5ZgHNOqTp2-FO0,1352
22
- users/templates/users/partials/department_actions.html,sha256=sdZr-awxOeXgPuYHYcrJx6msDcG3KcQzQlCpgseszQ4,283
23
- users/templates/users/partials/department_form.html,sha256=WvjX8hZjqbLMmFCoACkcHZFXTlAwEdy0GyrMRaobwV8,777
24
- users/templates/users/partials/department_manager.html,sha256=dB9_sgv2cRr0x3MKM62vDZzPDWZtkpf4ORFYnbYTIYM,399
25
- users/templates/users/partials/user_actions.html,sha256=J44-sn0fMbLUWjdtlcf5YhgT5OYRykr1mFkeVXoI1ew,1543
26
- users/templates/users/profile/profile.html,sha256=Ir8zvYUgDm89BlwVuuCsPJIVvTPa_2wH3HAaitPc4s8,2911
27
- users/templates/users/profile/profile_edit.html,sha256=L9DUHlQHG-PmxwxBbSjgPk1dEmy0spPi6wXzT4hQe-U,4218
28
- users/templates/users/widgets/grouped_permissions.html,sha256=q51WO-xMvg0aAqn6Ey8pMINDbFOHap_BgHcMxOvfLBw,9878
29
- micro_users-1.5.0.dist-info/LICENSE,sha256=Fco89ULLSSxKkC2KKnx57SaT0R7WOkZfuk8IYcGiN50,1063
30
- micro_users-1.5.0.dist-info/METADATA,sha256=V3Nwbu4x03gIRBkO1UIsLLVbPKD3xW-8K2CPqW4aKqs,10721
31
- micro_users-1.5.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
32
- micro_users-1.5.0.dist-info/top_level.txt,sha256=tWT24ZcWau2wrlbpU_h3mP2jRukyLaVYiyHBuOezpLQ,6
33
- micro_users-1.5.0.dist-info/RECORD,,