minor update
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-08-26 08:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='banned_until',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='is_banned',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
BIN
Binary file not shown.
@@ -1,7 +1,9 @@
|
|||||||
|
from datetime import timedelta
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.core.validators import FileExtensionValidator
|
from django.core.validators import FileExtensionValidator
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
is_citizen = models.BooleanField(default=False)
|
is_citizen = models.BooleanField(default=False)
|
||||||
@@ -25,6 +27,24 @@ class User(AbstractUser):
|
|||||||
related_name='core_user_permissions',
|
related_name='core_user_permissions',
|
||||||
related_query_name='core_user',
|
related_query_name='core_user',
|
||||||
)
|
)
|
||||||
|
is_banned = models.BooleanField(default=False)
|
||||||
|
banned_until = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
|
def ban(self, days=1):
|
||||||
|
"""Ban user for given days (default 7 days)."""
|
||||||
|
self.is_banned = True
|
||||||
|
self.banned_until = timezone.now() + timedelta(days=days)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def unban(self):
|
||||||
|
self.is_banned = False
|
||||||
|
self.banned_until = None
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def is_currently_banned(self):
|
||||||
|
if self.is_banned and self.banned_until:
|
||||||
|
return timezone.now() < self.banned_until
|
||||||
|
return False
|
||||||
|
|
||||||
class Department(models.Model):
|
class Department(models.Model):
|
||||||
name = models.CharField(max_length=100, unique=True)
|
name = models.CharField(max_length=100, unique=True)
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{% extends "core/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container my-5">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h3><i class="fas fa-users me-2"></i> Manage Citizens</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if citizens %}
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead class="table-dark">
|
||||||
|
<tr>
|
||||||
|
<th>No.</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Phone No.</th>
|
||||||
|
<th>Date Joined</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for citizen in citizens %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ forloop.counter }}</td>
|
||||||
|
<td>{{ citizen.username }}</td>
|
||||||
|
<td>{{ citizen.email }}</td>
|
||||||
|
<td>{{ citizen.phone }}</td>
|
||||||
|
<td>{{ citizen.date_joined|date:"M d, Y" }}</td>
|
||||||
|
<td>
|
||||||
|
{% if citizen.is_currently_banned %}
|
||||||
|
<span class="badge bg-danger">Banned until {{ citizen.banned_until|date:"M d, Y" }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-success">Active</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if citizen.is_currently_banned %}
|
||||||
|
<a href="{% url 'unban_user' citizen.id %}" class="btn btn-sm btn-success">
|
||||||
|
<i class="fas fa-unlock"></i> Unban
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'ban_user' citizen.id %}" class="btn btn-sm btn-danger">
|
||||||
|
<i class="fas fa-ban"></i> Ban
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted">No citizen users found.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<i class="fas fa-users me-2 text-primary"></i>
|
<i class="fas fa-users me-2 text-primary"></i>
|
||||||
<a href="#">Manage Users</a>
|
<a href="{% url 'manage_users' %}">Manage Users</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<i class="fas fa-building me-2 text-success"></i>
|
<i class="fas fa-building me-2 text-success"></i>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<th>Reported By</th>
|
<th>Reported By</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Created At</th>
|
<th>Created At</th>
|
||||||
<th>Assign Department</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -41,22 +41,29 @@
|
|||||||
<td>{{ issue.created_at|date:"M d, Y H:i" }}</td>
|
<td>{{ issue.created_at|date:"M d, Y H:i" }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if issue.department %}
|
{% if issue.department %}
|
||||||
<span class="fw-bold text-primary">{{ issue.department.name }}</span>
|
<span class="fw-bold text-primary">{{ issue.department.name }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<form method="post" action="">
|
<!-- Assign Department Form -->
|
||||||
{% csrf_token %}
|
<form method="post" action="" class="d-inline">
|
||||||
<input type="hidden" name="issue_id" value="{{ issue.id }}">
|
{% csrf_token %}
|
||||||
<select name="department" class="form-select form-select-sm">
|
<input type="hidden" name="issue_id" value="{{ issue.id }}">
|
||||||
<option value="">— Select Department —</option>
|
<select name="department" class="form-select form-select-sm d-inline w-auto">
|
||||||
{% for dept in departments %}
|
<option value="">— Select Department —</option>
|
||||||
<option value="{{ dept.id }}">{{ dept.name }}</option>
|
{% for dept in departments %}
|
||||||
{% endfor %}
|
<option value="{{ dept.id }}">{{ dept.name }}</option>
|
||||||
</select>
|
{% endfor %}
|
||||||
<button type="submit" class="btn btn-sm btn-primary mt-1">Assign</button>
|
</select>
|
||||||
</form>
|
<button type="submit" class="btn btn-sm btn-primary">Assign</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Report Fake Button -->
|
||||||
|
<a href="{% url 'delete_fake_issue' issue.id %}"
|
||||||
|
class="btn btn-sm btn-danger ms-1"
|
||||||
|
onclick="return confirm('Are you sure you want to delete this issue as FAKE?');">
|
||||||
|
<i class="fas fa-ban"></i> Report Fake
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -21,4 +21,8 @@ urlpatterns = [
|
|||||||
path('vote/<int:issue_id>/', views.vote_issue, name='vote_issue'),
|
path('vote/<int:issue_id>/', views.vote_issue, name='vote_issue'),
|
||||||
path("department/", views.department_dashboard, name="department_dashboard"),
|
path("department/", views.department_dashboard, name="department_dashboard"),
|
||||||
path("update-issue-status/<int:issue_id>/", views.update_issue_status, name="update_issue_status"),
|
path("update-issue-status/<int:issue_id>/", views.update_issue_status, name="update_issue_status"),
|
||||||
|
path('manage-users/', views.manage_users, name='manage_users'),
|
||||||
|
path('ban-user/<int:user_id>/', views.ban_user, name='ban_user'),
|
||||||
|
path('unban-user/<int:user_id>/', views.unban_user, name='unban_user'),
|
||||||
|
path('issues/<int:issue_id>/delete_fake/', views.delete_fake_issue, name='delete_fake_issue'),
|
||||||
]
|
]
|
||||||
@@ -291,6 +291,40 @@ def assign_department(request, issue_id):
|
|||||||
|
|
||||||
return redirect("manage_issues")
|
return redirect("manage_issues")
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def manage_users(request):
|
||||||
|
citizens = User.objects.filter(is_citizen=True).order_by('-date_joined')
|
||||||
|
return render(request, 'core/manage_users.html', {'citizens': citizens})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def ban_user(request, user_id):
|
||||||
|
citizen = get_object_or_404(User, id=user_id, is_citizen=True)
|
||||||
|
citizen.ban(days=7) # default ban 7 days
|
||||||
|
messages.warning(request, f"{citizen.username} has been banned for 7 days.")
|
||||||
|
return redirect('manage_users')
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def unban_user(request, user_id):
|
||||||
|
citizen = get_object_or_404(User, id=user_id, is_citizen=True)
|
||||||
|
citizen.unban()
|
||||||
|
messages.success(request, f"{citizen.username} has been unbanned.")
|
||||||
|
return redirect('manage_users')
|
||||||
|
|
||||||
|
# core/views.py
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def delete_fake_issue(request, issue_id):
|
||||||
|
issue = get_object_or_404(Issue, id=issue_id)
|
||||||
|
reporter = issue.reporter
|
||||||
|
issue.delete()
|
||||||
|
messages.error(request, f"Issue by {reporter.username} was reported fake and deleted.")
|
||||||
|
return redirect('manage_issues')
|
||||||
|
|
||||||
|
|
||||||
def resolver_check(user):
|
def resolver_check(user):
|
||||||
return user.is_resolver
|
return user.is_resolver
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user