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

users/tables.py CHANGED
@@ -1,13 +1,13 @@
1
1
  import django_tables2 as tables
2
2
  from django.contrib.auth import get_user_model
3
- from .models import UserActivityLog, Department
3
+ from django.apps import apps
4
4
 
5
5
  User = get_user_model() # Use custom user model
6
6
 
7
7
  class UserTable(tables.Table):
8
8
  username = tables.Column(verbose_name="اسم المستخدم")
9
9
  email = tables.Column(verbose_name="البريد الالكتروني")
10
- department = tables.Column(verbose_name="القسم", accessor='department.name', default='-')
10
+ scope = tables.Column(verbose_name="النطاق", accessor='scope.name', default='-')
11
11
  full_name = tables.Column(
12
12
  verbose_name="الاسم الكامل",
13
13
  accessor='user.full_name',
@@ -28,7 +28,7 @@ class UserTable(tables.Table):
28
28
  class Meta:
29
29
  model = User
30
30
  template_name = "django_tables2/bootstrap5.html"
31
- fields = ("username", "email", "full_name", "phone", "department", "is_staff", "is_active","last_login", "actions")
31
+ fields = ("username", "email", "full_name", "phone", "scope", "is_staff", "is_active","last_login", "actions")
32
32
  attrs = {'class': 'table table-hover align-middle'}
33
33
 
34
34
  class UserActivityLogTable(tables.Table):
@@ -41,30 +41,34 @@ class UserActivityLogTable(tables.Table):
41
41
  accessor='user.full_name',
42
42
  order_by='user__first_name'
43
43
  )
44
- department = tables.Column(
45
- verbose_name="القسم",
46
- accessor='user.department.name',
44
+ scope = tables.Column(
45
+ verbose_name="النطاق",
46
+ accessor='user.scope.name',
47
47
  default='عام'
48
48
  )
49
49
  class Meta:
50
- model = UserActivityLog
50
+ model = apps.get_model('users', 'UserActivityLog')
51
51
  template_name = "django_tables2/bootstrap5.html"
52
- fields = ("timestamp", "user", "full_name", "department", "action", "model_name", "object_id", "number")
52
+ fields = ("timestamp", "user", "full_name", "model_name", "action", "object_id", "number", "scope")
53
+ exclude = ("id", "ip_address", "user_agent")
53
54
  attrs = {'class': 'table table-hover align-middle'}
55
+ row_attrs = {
56
+ "class": lambda record: "row-deleted" if record.user and getattr(record.user, "deleted_at", None) else ""
57
+ }
54
58
 
55
59
  class UserActivityLogTableNoUser(UserActivityLogTable):
56
60
  class Meta(UserActivityLogTable.Meta):
57
- # Remove the 'user', 'user.full_name' and 'department' columns
58
- exclude = ("user", "user.full_name", "department")
61
+ # Remove the 'user', 'user.full_name' and 'scope' columns
62
+ exclude = ("user", "user.full_name", "scope")
59
63
 
60
- class DepartmentTable(tables.Table):
64
+ class ScopeTable(tables.Table):
61
65
  actions = tables.TemplateColumn(
62
- template_name='users/partials/department_actions.html',
66
+ template_name='users/partials/scope_actions.html',
63
67
  orderable=False,
64
68
  verbose_name=''
65
69
  )
66
70
  class Meta:
67
- model = Department
71
+ model = apps.get_model('users', 'Scope')
68
72
  template_name = "django_tables2/bootstrap5.html"
69
73
  fields = ("name", "actions")
70
74
  attrs = {'class': 'table table-hover align-middle'}
@@ -3,13 +3,33 @@
3
3
  {% block title %}تسجيل الدخول{% endblock %}
4
4
 
5
5
  {% block content %}
6
- <link rel="stylesheet" href="{% static 'css/login.css' %}">
6
+ <link rel="stylesheet" href="{% static 'users/css/login.css' %}">
7
+ {% if theme %}
8
+ <style>
9
+ :root {
10
+ {% if theme.selection_bg %}--selection-bg: {{ theme.selection_bg }} !important;{% endif %}
11
+ {% if theme.selection_moz_bg %}--selection-moz-bg: {{ theme.selection_moz_bg }} !important;{% endif %}
12
+ {% if theme.left_bg %}--left-bg: {{ theme.left_bg }} !important;{% endif %}
13
+ {% if theme.left_shadow %}--left-shadow: {{ theme.left_shadow }} !important;{% endif %}
14
+ {% if theme.right_bg %}--right-bg: {{ theme.right_bg }} !important;{% endif %}
15
+ {% if theme.right_shadow %}--right-shadow: {{ theme.right_shadow }} !important;{% endif %}
16
+ {% if theme.right_text %}--right-text: {{ theme.right_text }} !important;{% endif %}
17
+ {% if theme.label_color %}--label-color: {{ theme.label_color }} !important;{% endif %}
18
+ {% if theme.input_text %}--input-text: {{ theme.input_text }} !important;{% endif %}
19
+ {% if theme.submit_color %}--submit-color: {{ theme.submit_color }} !important;{% endif %}
20
+ {% if theme.submit_focus %}--submit-focus: {{ theme.submit_focus }} !important;{% endif %}
21
+ {% if theme.submit_active %}--submit-active: {{ theme.submit_active }} !important;{% endif %}
22
+ {% if theme.gradient_start %}--gradient-start: {{ theme.gradient_start }} !important;{% endif %}
23
+ {% if theme.gradient_end %}--gradient-end: {{ theme.gradient_end }} !important;{% endif %}
24
+ }
25
+ </style>
26
+ {% endif %}
7
27
 
8
28
  <div class="page">
9
29
  <div class="container d-flex">
10
- <div class="right p-2 d-felx align-content-center">
30
+ <div class="right p-3 align-content-center">
11
31
  <div class="">
12
- <img src="{% static 'img/pdf_logo.webp' %}" alt="Ministry Logo" class="img-fluid mx-auto d-block "style="width: 75%;">
32
+ <img src="{% static 'img/login_logo.webp' %}" alt="Login Logo" class="img-fluid mx-auto d-block logo-img">
13
33
  </div>
14
34
  {% comment %} <div class="login mb-3 text-secondary">تسجيل الدخــول</div> {% endcomment %}
15
35
  </div>
@@ -25,11 +45,11 @@
25
45
  y2="193.49992"
26
46
  gradientUnits="userSpaceOnUse">
27
47
  <stop
28
- style="stop-color:#c9aa5e;"
48
+ style="stop-color:var(--gradient-end);"
29
49
  offset="0"
30
50
  id="stop876" />
31
51
  <stop
32
- style="stop-color:#cb9447;"
52
+ style="stop-color:var(--gradient-start);"
33
53
  offset="1"
34
54
  id="stop878" />
35
55
  </linearGradient>
@@ -39,77 +59,17 @@
39
59
  <form action="{% url 'login' %}" method="POST">
40
60
  {% csrf_token %}
41
61
  <div class="form">
42
- <input type="text" style="font-size: 28px;" id="username" name="username" autofocus placeholder="اسم المستخدم" required>
62
+ <input type="text" class="login-input" id="username" name="username" autofocus placeholder="اسم المستخدم" required>
43
63
 
44
- <input type="password" style="font-size: 28px;" id="password" name="password" placeholder="كلمة المرور" required>
64
+ <input type="password" class="login-input" id="password" name="password" placeholder="كلمة المرور" required>
45
65
 
46
- <input type="submit" style="font-size: 25px;" class="mt-5 pe-3" value="دخول" id="submit">
66
+ <input type="submit" class="login-submit mt-5 pe-3" value="دخول" id="submit">
47
67
  </div>
48
68
  </form>
49
69
  </div>
50
70
  </div>
51
71
  </div>
52
72
 
53
- <script nonce="{{ request.csp_nonce }}">
54
- var current = null;
55
- document.querySelector('#username').addEventListener('focus', function(e) {
56
- if (current) current.pause();
57
- current = anime({
58
- targets: 'path',
59
- strokeDashoffset: {
60
- value: 0,
61
- duration: 700,
62
- easing: 'easeOutQuart'
63
- },
64
- strokeDasharray: {
65
- value: '240 1386',
66
- duration: 700,
67
- easing: 'easeOutQuart'
68
- }
69
- });
70
- });
71
- document.querySelector('#password').addEventListener('focus', function(e) {
72
- if (current) current.pause();
73
- current = anime({
74
- targets: 'path',
75
- strokeDashoffset: {
76
- value: -336,
77
- duration: 700,
78
- easing: 'easeOutQuart'
79
- },
80
- strokeDasharray: {
81
- value: '240 1386',
82
- duration: 700,
83
- easing: 'easeOutQuart'
84
- }
85
- });
86
- });
87
- document.querySelector('#submit').addEventListener('focus', function(e) {
88
- if (current) current.pause();
89
- current = anime({
90
- targets: 'path',
91
- strokeDashoffset: {
92
- value: -730,
93
- duration: 700,
94
- easing: 'easeOutQuart'
95
- },
96
- strokeDasharray: {
97
- value: '530 1386',
98
- duration: 700,
99
- easing: 'easeOutQuart'
100
- }
101
- });
102
- });
103
-
104
- // New script: Hide the login button in the title bar if present.
105
- document.addEventListener("DOMContentLoaded", function() {
106
- // Adjust the selector to match the login button in your title bar.
107
- var loginTitleButton = document.querySelector(".login-title-btn");
108
- if (loginTitleButton) {
109
- loginTitleButton.style.display = "none";
110
- }
111
- });
112
- </script>
113
-
114
73
  <script src="{% static 'js/anime.min.js' %}"></script>
74
+ <script src="{% static 'users/js/login.js' %}"></script>
115
75
  {% endblock %}
@@ -1,10 +1,12 @@
1
1
  {% extends "base.html" %}
2
2
  {% load django_tables2 %}
3
+ {% load static %}
3
4
  {% load crispy_forms_tags %}
4
5
 
5
6
  {% block title %}الاعدادت - إدارة المستخدمين{% endblock %}
6
7
 
7
8
  {% block content %}
9
+ <link rel="stylesheet" href="{% static 'users/css/style.css' %}">
8
10
 
9
11
  <form method="get" class="py-3 g-2 no-print">
10
12
  {% crispy filter.form %}
@@ -18,9 +20,9 @@
18
20
  </div>
19
21
 
20
22
  <div class="mt-3">
21
- {% if not request.user.department %}
22
- <button type="button" class="btn btn-info me-2 no-print" onclick="loadDepartmentManager()">
23
- <i class="bi bi-list me-1"></i> إدارة الأقسام
23
+ {% if not request.user.scope %}
24
+ <button type="button" class="btn btn-info no-print" onclick="loadScopeManager()">
25
+ <i class="bi bi-list me-1 h4"></i> إدارة النطاقات
24
26
  </button>
25
27
  {% endif %}
26
28
  <a href="{% url 'create_user' %}" class="btn btn-secondary no-print" title="إضافة مستخدم جديد">
@@ -52,14 +54,14 @@
52
54
  </form>
53
55
  {% endif %}
54
56
 
55
- <!-- Department Management Modal -->
56
- <div class="modal fade" id="departmentModal" tabindex="-1" aria-hidden="true">
57
+ <!-- Scope Management Modal -->
58
+ <div class="modal fade" id="scopeModal" tabindex="-1" aria-hidden="true">
57
59
  <div class="modal-dialog modal-lg">
58
60
  <div class="modal-content">
59
61
  <div class="modal-header">
60
62
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
61
63
  </div>
62
- <div class="modal-body" id="departmentModalBody">
64
+ <div class="modal-body" id="scopeModalBody">
63
65
  <!-- Content loaded via AJAX -->
64
66
  <div class="text-center py-5">
65
67
  <div class="spinner-border text-primary" role="status">
@@ -71,17 +73,17 @@
71
73
  </div>
72
74
  </div>
73
75
 
74
- <!-- Script for Department Modal -->
76
+ <!-- Script for Scope Modal -->
75
77
  <script>
76
78
  // Defined globally so they can be called from injected HTML
77
- function loadDepartmentManager() {
78
- const modal = new bootstrap.Modal(document.getElementById('departmentModal'));
79
+ function loadScopeManager() {
80
+ const modal = new bootstrap.Modal(document.getElementById('scopeModal'));
79
81
  modal.show();
80
- loadDepartmentForm("{% url 'manage_departments' %}");
82
+ loadScopeForm("{% url 'manage_scopes' %}");
81
83
  }
82
84
 
83
- function loadDepartmentForm(url) {
84
- // Default to manage_departments if no URL (e.g. back button cases)
85
+ function loadScopeForm(url) {
86
+ // Default to manage_scopes if no URL (e.g. back button cases)
85
87
  // But actually we want distinct behavior:
86
88
  // 1. Initial Load (Table)
87
89
  // 2. Load Form (Add/Edit)
@@ -94,12 +96,12 @@
94
96
  })
95
97
  .then(response => response.json())
96
98
  .then(data => {
97
- document.getElementById('departmentModalBody').innerHTML = data.html;
99
+ document.getElementById('scopeModalBody').innerHTML = data.html;
98
100
  })
99
101
  .catch(err => console.error('Error loading content:', err));
100
102
  }
101
103
 
102
- function submitDepartmentForm(e, url) {
104
+ function submitScopeForm(e, url) {
103
105
  e.preventDefault();
104
106
  const form = e.target;
105
107
  const formData = new FormData(form);
@@ -115,22 +117,22 @@
115
117
  .then(data => {
116
118
  // Whether success or error, we replace the body with the returned HTML
117
119
  // (Updated table or Form with errors)
118
- document.getElementById('departmentModalBody').innerHTML = data.html;
120
+ document.getElementById('scopeModalBody').innerHTML = data.html;
119
121
  })
120
122
  .catch(err => console.error('Error submitting form:', err));
121
123
  }
122
124
 
123
- function deleteDepartment(url) {
125
+ function deleteScope(url) {
124
126
  fetch(url, {
125
127
  headers: { 'X-Requested-With': 'XMLHttpRequest' }
126
128
  })
127
129
  .then(response => response.json())
128
130
  .then(data => {
129
131
  if (data.success) {
130
- document.getElementById('departmentModalBody').innerHTML = data.html;
132
+ document.getElementById('scopeModalBody').innerHTML = data.html;
131
133
  }
132
134
  })
133
- .catch(err => console.error('Error deleting department:', err));
135
+ .catch(err => console.error('Error deleting scope:', err));
134
136
  }
135
137
  </script>
136
138
  {% endblock %}
@@ -1,6 +1,6 @@
1
1
  <div class="d-flex gap-2 justify-content-center">
2
2
  <button class="btn btn-sm btn-primary"
3
- onclick="loadDepartmentForm('{% url 'get_department_form' record.id %}')"
3
+ onclick="loadScopeForm('{% url 'get_scope_form' record.id %}')"
4
4
  title="تعديل">
5
5
  <i class="bi bi-pencil-square"></i>
6
6
  </button>
@@ -1,13 +1,13 @@
1
1
  {% load crispy_forms_tags %}
2
2
 
3
3
  <div class="d-flex justify-content-between mb-3">
4
- <h5 class="modal-title">{% if department_id %}تعديل قسم{% else %}إضافة قسم جديد{% endif %}</h5>
5
- <button class="btn btn-secondary" onclick="loadDepartmentForm('{% url 'manage_departments' %}')">
4
+ <h5 class="modal-title">{% if scope_id %}تعديل نطاق{% else %}إضافة نطاق جديد{% endif %}</h5>
5
+ <button class="btn btn-secondary" onclick="loadScopeForm('{% url 'manage_scopes' %}')">
6
6
  <i class="bi bi-arrow-right me-1"></i> عودة للقائمة
7
7
  </button>
8
8
  </div>
9
9
 
10
- <form id="departmentForm" onsubmit="submitDepartmentForm(event, '{% url 'save_department' %}{% if department_id %}/{{ department_id }}{% endif %}')">
10
+ <form id="scopeForm" onsubmit="submitScopeForm(event, '{% url 'save_scope' %}{% if scope_id %}/{{ scope_id }}{% endif %}')">
11
11
  {% csrf_token %}
12
12
  {% crispy form %}
13
13
 
@@ -1,9 +1,9 @@
1
1
  {% load django_tables2 %}
2
2
  <div class="d-flex justify-content-between mb-3">
3
- <h5 class="modal-title">إدارة الأقسام</h5>
3
+ <h5 class="modal-title">إدارة النطاقات</h5>
4
4
  <button class="btn btn-success"
5
- onclick="loadDepartmentForm('{% url 'get_department_form' %}')">
6
- <i class="bi bi-plus-lg me-1"></i> إضافة قسم
5
+ onclick="loadScopeForm('{% url 'get_scope_form' %}')">
6
+ <i class="bi bi-plus-lg me-1"></i> إضافة نطاق
7
7
  </button>
8
8
  </div>
9
9
 
@@ -1,10 +1,12 @@
1
1
  {% extends "base.html" %}
2
2
  {% load django_tables2 %}
3
+ {% load static %}
3
4
  {% load crispy_forms_tags %}
4
5
 
5
6
  {% block title %}الاعدادت - السجل{% endblock %}
6
7
 
7
8
  {% block content %}
9
+ <link rel="stylesheet" href="{% static 'users/css/style.css' %}">
8
10
 
9
11
  <form method="get" class="py-3 g-2 no-print m-0">
10
12
  {% crispy filter.form %}
users/urls.py CHANGED
@@ -5,7 +5,7 @@ from . import views
5
5
  from django.contrib.auth import views as auth_views
6
6
 
7
7
  urlpatterns = [
8
- path('login/', auth_views.LoginView.as_view(), name='login'),
8
+ path('login/', views.CustomLoginView.as_view(), name='login'),
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'),
@@ -17,11 +17,11 @@ urlpatterns = [
17
17
  path('reset_password/<int:pk>/', views.reset_password, name="reset_password"),
18
18
  path("users/<int:pk>/", views.UserDetailView.as_view(), name="user_detail"),
19
19
 
20
- # Department Management URLs
21
- path("departments/manage/", views.manage_departments, name="manage_departments"),
22
- path("departments/form/", views.get_department_form, name="get_department_form"),
23
- path("departments/form/<int:pk>/", views.get_department_form, name="get_department_form"),
24
- path("departments/save/", views.save_department, name="save_department"),
25
- path("departments/save/<int:pk>/", views.save_department, name="save_department"),
26
- path("departments/delete/<int:pk>/", views.delete_department, name="delete_department"),
20
+ # Scope Management URLs
21
+ path("scopes/manage/", views.manage_scopes, name="manage_scopes"),
22
+ path("scopes/form/", views.get_scope_form, name="get_scope_form"),
23
+ path("scopes/form/<int:pk>/", views.get_scope_form, name="get_scope_form"),
24
+ path("scopes/save/", views.save_scope, name="save_scope"),
25
+ path("scopes/save/<int:pk>/", views.save_scope, name="save_scope"),
26
+ path("scopes/delete/<int:pk>/", views.delete_scope, name="delete_scope"),
27
27
  ]