Fakultas Ilmu Komputer UI

Commit 31b4486b authored by Azhar Rais's avatar Azhar Rais
Browse files

Merge branch 'master' into '1706074865-24'

# Conflicts:
#   app/templates/app/katalog_materi.html
#   authentication/tests.py
parents 06742143 4a7c10fe
Pipeline #58006 passed with stages
in 14 minutes and 11 seconds
......@@ -134,3 +134,15 @@ class PeriodForm(forms.Form):
if start_date > end_date:
self.add_error("end_date",
"waktu selesai sebelum waktu mulai")
class EditAdminStatusForm(forms.ModelForm):
is_active = forms.BooleanField(required=False)
class Meta:
model = User
fields = ["is_active"]
def __init__(self, *args, **kwargs):
super(EditAdminStatusForm, self).__init__(*args, **kwargs)
......@@ -50,4 +50,12 @@
<!-- Divider -->
<hr class="sidebar-divider my-0">
<li class="nav-item">
<a class="nav-link" href="/administration/news/list">
<span>Kelola Berita</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider my-0">
</ul>
\ No newline at end of file
{% extends 'administration/base_administrasi.html' %}
{% load static %}
{% block content %}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
Edit {{ page_title }}
</h6>
</div>
<div class="card-body">
<form method="POST">
{% csrf_token %}
{{ item.name }}
<div class="status d-flex align-items-baseline">
{{ form.is_active }} <br> Active
</div>
<div class="">
<button class="btn-sm btn-primary rounded p-12" type="submit">
<i class="far fa-save" aria-hidden="true"></i>
Simpan
</button>
</div>
</form>
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -27,30 +27,39 @@
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Nama</th>
<th>NIK</th>
<th>Instansi</th>
<th>Pilihan</th>
<th scope="col">Nama</th>
<th scope="col">NIK</th>
<th scope="col">Instansi</th>
<th scope="col">Status</th>
<th scope="col">Pilihan</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Nama</th>
<th>NIK</th>
<th>Instansi</th>
<th>Pilihan</th>
<th scope="col">Nama</th>
<th scope="col">NIK</th>
<th scope="col">Instansi</th>
<th scope="col">Status</th>
<th scope="col">Pilihan</th>
</tr>
</tr>
</tfoot>
<tbody>
{% for current in users_list %}
<tr>
<td>{{ current.name }}</td>
<td>{% if current.name %} {{ current.name }} {% else %} Superadmin {% endif %}</td>
<td>{{ current.nik }}</td>
<td>{{ current.instansi }}</td>
<td>{% if current.is_active %} Active {% else %} Inactive {% endif %}</td>
<td class="verif-buttons">
<span>
{% if user.is_superuser %}
{% if not current.is_superuser %}
<a href="/administration/setting/admin/{{ current.id }}/edit" class="accept-button button-decoration" role="button">Edit</a>
{% endif %}
{% endif %}
<a href="/administration/profil-admin/{{ current.id }}/" class="accept-button button-decoration">Detail</a>
{% if not current.is_superuser %}
<button type="button" class="reject-button button-decoration" data-toggle="modal" data-target="#confirmModal{{ current.id }}">Hapus</button>
<div class="modal fade" id="confirmModal{{ current.id }}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
......@@ -62,15 +71,25 @@
</button>
</div>
<div class="modal-body">
<p>Sila konfirmasi penghapusan akun dengan tekan tombol hapus di bawah</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<a href="/administration/hapus-admin/{{current.id}}/" type="button" class="btn btn-danger">Hapus</a>
<p>Anda akan menghapus akun admin dengan email
<span class="bg-danger pr-1 pl-1 text-white" style="text-transform: lowercase">{{current.email}}</span>
. Lakukan konfirmasi penghapusan dengan mengetik: </p>
<p class="bg-danger text-white p-1"> {{current.email}} </p>
</div>
<form/>
{% csrf_token %}
<div class="form-group pl-3 pr-3">
<input name="{{ current.email }}" class="form-control" id="{{ current.id }}"required pattern="{{current.email}}" oninput="checkValue(this)">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<a href="/administration/hapus-admin/{{current.id}}/" id="btn-hapus-{{current.id}}" type="button" class="btn btn-danger disabled">Hapus</a>
</div>
</form>
</div>
</div>
</div>
{% endif %}
</span>
</td>
</tr>
......@@ -118,4 +137,14 @@
</div>
</div>
</div>
<script type="text/javascript">
function checkValue(element){
buttonElement = document.getElementById("btn-hapus-" + element.id)
if(element.value != element.name) {
buttonElement.classList.add("disabled");
} else {
buttonElement.classList.remove("disabled");
}
}
</script>
{% endblock %}
\ No newline at end of file
......@@ -5,9 +5,12 @@ from django.urls import resolve
from administration import models, views
from administration.utils import id_generator
from administration.forms import EditAdminStatusForm
from app.models import Category, Materi
from authentication.models import User
from bs4 import BeautifulSoup
EDIT_ENDPOINT = "/edit"
class VerifikasiMateriTest(TestCase):
def setUp(self):
......@@ -809,7 +812,7 @@ class EditVerificationTest(TestCase):
**self.admin_credential, name="Admin", is_admin=True)
models.VerificationSetting(title='test', description="desc test").save()
self.verification = models.VerificationSetting.objects.first()
self.url = "/administration/setting/verification/" + str(self.verification.id) +"/edit"
self.url = "/administration/setting/verification/" + str(self.verification.id) + EDIT_ENDPOINT
def test_edit_verification_admin_access(self):
self.client.login(**self.admin_credential)
......@@ -891,7 +894,7 @@ class EditCategoryTest(TestCase):
**self.admin_credential, name="Admin", is_admin=True)
Category(name='test', description="desc test").save()
self.category = Category.objects.first()
self.url = "/administration/setting/category/" + str(self.category.id) +"/edit"
self.url = "/administration/setting/category/" + str(self.category.id) + EDIT_ENDPOINT
def test_edit_category_admin_access(self):
self.client.login(**self.admin_credential)
......@@ -961,3 +964,150 @@ class EditCategoryTest(TestCase):
self.assertTemplateUsed(response, self.template_name)
self.assertContains(response, "Edit Kategori Materi")
class EditAdminStatusFormTests(TestCase):
def test_set_active_admin(self):
form = EditAdminStatusForm(data={"is_active": True})
self.assertEqual(form.is_valid(), True)
def test_set_inactive_admin(self):
form = EditAdminStatusForm(data={"is_active": False})
self.assertEqual(form.is_valid(), True)
class DeleteAdminTests(TestCase):
def setUp(self):
self.client = Client()
self.user_credential = {
"email": "user@gov.id",
"password": id_generator()
}
self.admin_credential = {
"email": "admin@gov.id",
"password": id_generator()
}
self.superuser_credential = {
"email": "superuser@gov.id",
"password": id_generator()
}
self.user = get_user_model().objects.create_user(
**self.user_credential, name="User", is_contributor=True)
self.admin = get_user_model().objects.create_user(
**self.admin_credential, name="Admin", is_admin=True)
self.superuser = get_user_model().objects.create_user(
**self.superuser_credential, name="Superuser", is_admin=True, is_superuser=True)
User(name='dummy_admin', is_admin=True, email="dummy_admin@example.com").save()
User(name='dummy_superuser', is_admin=True, is_superuser=True, email="dummy_superuser@example.com").save()
self.dummy_admin = User.objects.filter(name="dummy_admin").get()
self.dummy_superuser = User.objects.filter(name="dummy_superuser").get()
self.delete_url_dummy_admin = self.url_generator_delete_admin(self.dummy_admin.id)
self.delete_url_dummy_superuser = self.url_generator_delete_admin(self.dummy_superuser.id)
def url_generator_delete_admin(self, id):
return "/administration/hapus-admin/" + str(id) + "/"
def test_delete_admin_as_admin_user(self):
self.client.login(**self.admin_credential)
self.assertEqual(User.objects.filter(
name="dummy_admin").count(), 1)
self.client.get(self.delete_url_dummy_admin)
self.assertEqual(User.objects.filter(
name="dummy_admin").count(), 0)
def test_cannot_delete_admin_as_user(self):
self.client.login(**self.user_credential)
response = self.client.get(self.delete_url_dummy_admin)
self.assertEqual(response.status_code, 403)
def test_cannot_delete_superuser_as_user_or_admin(self):
self.client.login(**self.user_credential)
response = self.client.get(self.delete_url_dummy_superuser)
self.assertEqual(response.status_code, 403)
self.client.login(**self.admin_credential)
response = self.client.get(self.delete_url_dummy_superuser)
self.assertEqual(response.status_code, 403)
def test_redirect_after_delet_admin(self):
self.client.login(**self.admin_credential)
response = self.client.get(self.delete_url_dummy_admin)
self.assertEqual(response.status_code, 302)
class EditAdminStatusTests(TestCase):
def setUp(self):
self.client = Client()
self.admin_credential = {
"email": "admin@gov.id",
"password": id_generator()
}
self.superuser_credential = {
"email": "superuser@gov.id",
"password": id_generator()
}
self.admin = get_user_model().objects.create_user(
**self.admin_credential, name="Admin", is_admin=True)
self.superuser = get_user_model().objects.create_user(
**self.superuser_credential, name="Superuser", is_admin=True, is_superuser=True)
User(name='dummy_admin', is_admin=True, email="dummy_admin@example.com").save()
User(name='dummy_superuser', is_admin=True, is_superuser=True, email="dummy_superuser@example.com").save()
self.dummy_admin = User.objects.filter(name="dummy_admin").get()
self.dummy_superuser = User.objects.filter(name="dummy_superuser").get()
self.edit_url_dummy_admin = self.url_generator_edit_admin(self.dummy_admin.id)
self.edit_url_dummy_superuser = self.url_generator_edit_admin(self.dummy_superuser.id)
def url_generator_edit_admin(self, id):
return "/administration/setting/admin/" + str(id) + EDIT_ENDPOINT
def test_get_edit_admin_html_content(self):
self.client.login(**self.superuser_credential)
response = self.client.get(self.edit_url_dummy_admin)
self.assertContains(response, self.dummy_admin.name)
response = self.client.get(self.edit_url_dummy_superuser)
self.assertContains(response, self.dummy_superuser.name)
def test_cannot_edit_set_active_admin_as_admin(self):
self.client.login(**self.admin_credential)
self.client.get(self.edit_url_dummy_admin)
response = self.client.post(self.edit_url_dummy_admin, {"is_active": "on"})
self.assertEqual(response.status_code, 403)
def test_edit_set_active_admin_as_superuser(self):
self.dummy_admin.is_active = False
self.client.login(**self.superuser_credential)
self.client.get(self.edit_url_dummy_admin)
response = self.client.post(self.edit_url_dummy_admin, {"is_active": "on"})
self.dummy_admin.refresh_from_db()
self.assertEqual(response.status_code, 302)
self.assertEqual(self.dummy_admin.is_active, True)
def test_edit_set_inactive_admin_as_superuser(self):
self.dummy_admin.is_active = True
self.client.login(**self.superuser_credential)
self.client.get(self.edit_url_dummy_admin)
response = self.client.post(self.edit_url_dummy_admin, {"is_active": "false"})
self.dummy_admin.refresh_from_db()
self.assertEqual(response.status_code, 302)
self.assertEqual(self.dummy_admin.is_active, False)
def test_cannot_edit_set_active_superuser_as_superuser(self):
self.client.login(**self.superuser_credential)
self.client.get(self.edit_url_dummy_superuser)
response = self.client.post(self.edit_url_dummy_superuser, {"is_active": "on"})
self.assertEqual(response.status_code, 403)
def test_cannot_access_edit_page_as_user(self):
self.client.login(**self.admin_credential)
response = self.client.get(self.edit_url_dummy_superuser)
self.assertEqual(response.status_code, 403)
from django.urls import path
from administration.views import VerificationView, DetailVerificationView, VerificationSettingView, CategorySettingView, KelolaKontributorView, ProfileContributorAdminView, ProfileAdminAdministrationView, KelolaAdminView, RegistrasiAdminView, EditVerificationView, delete_admin, delete_contributor, delete_verification, StatisticsView, StatisticApiView, EditCategoryView, delete_category, generatedummy
from administration.views import VerificationView, DetailVerificationView, VerificationSettingView, CategorySettingView, KelolaKontributorView, ProfileContributorAdminView, ProfileAdminAdministrationView, KelolaAdminView, RegistrasiAdminView, EditVerificationView, delete_admin, delete_contributor, delete_verification, StatisticsView, StatisticApiView, EditCategoryView, EditAdminStatusView, delete_category, generatedummy
app_name = "administration"
......@@ -22,6 +22,8 @@ urlpatterns = [
path("profil-admin/<int:pk>/", ProfileAdminAdministrationView.as_view()),
path("kelola-admin/", KelolaAdminView.as_view()),
path("kelola-admin/tambah/", RegistrasiAdminView.as_view()),
path("setting/admin/<int:pk>/edit",
EditAdminStatusView.as_view(), name="edit-admin-status"),
path("hapus-admin/<int:pk>/", delete_admin),
path("hapus-kontributor/<int:pk>/", delete_contributor),
path("generate-dummy", generatedummy),
......
......@@ -10,7 +10,7 @@ from django.contrib import messages
from django.utils import timezone, lorem_ipsum
from dateutil.relativedelta import relativedelta
from administration.models import VerificationReport, VerificationSetting, DeletionHistory
from administration.forms import CategoryForm, VerificationSettingForm, RegistrasiAdminForm, PeriodForm
from administration.forms import CategoryForm, VerificationSettingForm, RegistrasiAdminForm, PeriodForm, EditAdminStatusForm
from app.models import Category, Materi, ViewStatistics, DownloadStatistics, Comment, Like, getRandomColor
from authentication.models import User
from datetime import datetime
......@@ -19,6 +19,7 @@ from administration.utils import generate_time_step
from django.core import management
ADMINISTRATION_MANAGEMENT = "/administration/kelola-admin/"
def get_start_end_date(period):
if period == 'ALL_TIME':
......@@ -450,7 +451,7 @@ class RegistrasiAdminView(TemplateView):
new_user.password = make_password(data["password"])
new_user.is_admin = True
new_user.save()
return HttpResponseRedirect("/administration/kelola-admin/")
return HttpResponseRedirect(ADMINISTRATION_MANAGEMENT)
else:
context = self.get_context_data(**kwargs)
context["form"] = form
......@@ -520,12 +521,42 @@ class EditCategoryView(TemplateView):
context["form"] = form
return self.render_to_response(context)
class EditAdminStatusView(TemplateView):
template_name = "edit_admin.html"
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_superuser:
raise PermissionDenied(request)
return super(EditAdminStatusView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(EditAdminStatusView, self).get_context_data(**kwargs)
context["page_title"] = "Admin Status"
context["item"] = User.objects.get(id=kwargs["pk"])
context["form"] = EditAdminStatusForm(instance=context["item"])
return context
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context=context)
def post(self, request, *args, **kwargs):
user_object = self.get_context_data(**kwargs)["item"]
if user_object.is_superuser:
raise PermissionDenied(request)
form = EditAdminStatusForm(
request.POST, instance=user_object)
form.save()
return HttpResponseRedirect(ADMINISTRATION_MANAGEMENT)
def delete_admin(request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_admin:
raise PermissionDenied(request)
user_to_be_deleted = User.objects.get(pk=kwargs["pk"])
if (user_to_be_deleted.is_superuser):
raise PermissionDenied(request)
DeletionHistory.objects.create(
deleted_user_name=user_to_be_deleted.name,
deleted_user_role="Admin",
......@@ -533,7 +564,7 @@ def delete_admin(request, *args, **kwargs):
)
User.objects.filter(pk=kwargs["pk"]).delete()
return HttpResponseRedirect("/administration/kelola-admin/")
return HttpResponseRedirect(ADMINISTRATION_MANAGEMENT)
def delete_contributor(request, *args, **kwargs):
......
from django import forms
from app.models import Materi,Category
from app.models import Materi, Category, RatingContributor
from authentication.models import User
......@@ -43,3 +43,21 @@ class SuntingProfilForm(forms.ModelForm):
self.fields["email"].widget.attrs["readonly"] = True
class RatingContributorForm(forms.ModelForm):
class Meta:
model = RatingContributor
fields = ['score', 'user']
SCORE_CHOICE = (
('', 'Select score'),
('1', '1'), # First one is the value of select option and second is the displayed value in option
('2', '2'),
('3', '3'),
('4', '4'),
('5', '5'),
)
widgets = {
'score': forms.Select(choices=SCORE_CHOICE, attrs={'class': 'form-control'},),
'user': forms.HiddenInput()
}
# Generated by Django 3.0.4 on 2020-09-30 04:50
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('app', '0014_rating'),
]
operations = [
migrations.AddField(
model_name='downloadstatistics',
name='downloader',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='riwayat_unduh', to=settings.AUTH_USER_MODEL),
),
]
# Generated by Django 3.1.1 on 2020-10-05 14:41
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('app', '0015_reqmaterial'),
]
operations = [
migrations.CreateModel(
name='RatingContributor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timestamp', models.DateTimeField(auto_now=True)),
('score', models.IntegerField()),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
# Generated by Django 3.1.1 on 2020-10-05 14:45
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0016_ratingcontributor'),
]
operations = [
migrations.AlterField(
model_name='ratingcontributor',
name='score',
field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)]),
),
]
# Generated by Django 3.1 on 2020-10-09 00:00
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('app', '0017_auto_20201005_2145'),
('app', '0015_downloadstatistics_downloader'),
]
operations = [
]
import random
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.utils import timezone
......@@ -97,6 +98,8 @@ class ViewStatistics(models.Model):
class DownloadStatistics(models.Model):
materi = models.ForeignKey(
Materi, models.SET_NULL, null=True, related_name="unduh")
downloader = models.ForeignKey(
User, models.SET_NULL, blank=True, null=True, related_name="riwayat_unduh")
timestamp = models.DateTimeField(default=timezone.now)
......@@ -133,3 +136,15 @@ class Rating(models.Model):
class Meta:
unique_together = ["materi", "user"]
class RatingContributor(models.Model):
timestamp = models.DateTimeField(auto_now=True)
score = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
user = models.ForeignKey(User, on_delete=models.CASCADE)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if 1 <= self.score <= 5:
super().save(force_insert, force_update, using, update_fields)
else:
raise ValidationError("Rating score must be integer between 1-5")
\ No newline at end of file
.img-profile {
width: 10vw;
height: 10vw;
}
.profile-data {
color: #615CFD;
padding-right: 75px;
}
\ No newline at end of file
......@@ -62,6 +62,21 @@ body{
background-color: #615CFD;
}