Fakultas Ilmu Komputer UI

Commit 8ba8e2da authored by arif teguh wangi's avatar arif teguh wangi
Browse files

Merge branch 'master' of...

Merge branch 'master' of https://gitlab.cs.ui.ac.id/pmpl/class-project/marjinal-digipus into 1706040012-30
parents 59309e71 4a8b9a90
Pipeline #57865 passed with stages
in 8 minutes and 46 seconds
......@@ -62,12 +62,21 @@
</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>
......@@ -118,4 +127,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
.img-profile {
width: 10vw;
height: 10vw;
}
.profile-data {
color: #615CFD;
padding-right: 75px;
}
\ No newline at end of file
This diff is collapsed.
{% extends "base.html" %}
{% load static %}
{% block title %}Digipus - {% endblock %}
{% block header %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Digipus Home</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../../static/app/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="../../static/app/css/heroic-features.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="{% static 'app/css/katalog_materi.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'app/css/katalog_kontri.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/util.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'fonts/font-awesome-4.7.0/css/font-awesome.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'vendor/animsition/css/animsition.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'vendor/daterangepicker/daterangepicker.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'vendor/css-hamburgers/hamburgers.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'vendor/select2/select2.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'vendor/animate/animate.css' %}">
<link rel="icon" type="image/png" href="{% static 'images/icons/logo.ico' %}" />
{% endblock header %}
{% block content %}
</head>
<body style="background-color: #f8f8f8;">
<div class="container main">
<header class="jumbotron my-4" id="profile">
<div class="container sub">
<div class="row">
<div class="col-lg-3">
<div class="img-squared">
{% if not contributor.default_profile_picture %}
<img class="img-profile rounded-circle" src="https://i.ibb.co/9wgPzyZ/default-image.png"
alt="Contributor profile picture">
{% else %}
<img class="img-profile rounded-circle" src="{{ contributor.profile_picture.url }}"
alt="Contributor profile picture">
{% endif %}
</div>
</div>
<div class="col-lg-9">
<table aria-describedby="profile">
<th colspan="2" id="contributor-data">
<h3 class="profile-data">{{ contributor.name }}</h3>
<br>
</th>
<tr>
<td class="profile-data">Instansi</td>
<td>{{ contributor.instansi }}</td>
</tr>
<tr>
<td class="profile-data">LinkedIn</td>
<td><a href="https://linkedin.com">{{ contributor.linkedin }}</a></td>
</tr>
<tr>
<td class="profile-data">Facebook</td>
<td><a href="https://facebook.com">{{ contributor.facebook }}</a></td>
</tr>
<tr>
<td class="profile-data">Twitter</td>
<td><a href="https://twitter.com">{{ contributor.twitter }}</a></td>
</tr>
<tr>
<td class="profile-data">Instagram</td>
<td><a href="https://instagram.com">{{ contributor.instagram }}</a></td>
</tr>
</table>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row content">
<div class="col books">
{% for materi in materi_list %}
<div class="card book">
<img src={{materi.cover.url}} class="card-img-top" alt="cover"
style="height:200px; widows: 200px; overflow: hidden;"></img>
<div class="card-body">
<h5 class="card-title">{{materi.title}}</h5>
<p class="card-text">{{materi.author}}</p>
<a href="{% url 'view-materi' materi.id %}" class="btn btn-book">Baca</a>
<a href="{% url 'detail-materi' materi.id %}" class="btn btn-book">Detail</a>
</div>
</div>
{% endfor %}
</div>
<div class="center">
<div class="pagination">
<span class="step-links">
<span class="current">
Page {{ materi_list.number }} of {{ materi_list.paginator.num_pages }}
</span>
<br>
{% if materi_list.has_previous %}
<a href="?page=1{{url}}">&laquo; first</a>
<a href="?page={{ materi_list.previous_page_number }}{{url}}">previous</a>
{% endif %}
{% if materi_list.has_next %}
<a href="?page={{ materi_list.next_page_number }}{{url}}">next</a>
<a href="?page={{ materi_list.paginator.num_pages }}{{url}}">last &raquo;</a>
{% endif %}
</span>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
{% endblock %}
\ No newline at end of file
......@@ -41,13 +41,13 @@
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
<!--===============================================================================================-->
{% endblock header %}
{% block content %}
{% endblock header %}
{% block content %}
</head>
<body style="background-color: #f8f8f8;">
<!-- Page Content -->
<!-- Page Content -->
<div class="container">
<header class="jumbotron my-4">
......@@ -56,18 +56,20 @@
<div class="col">
<h2 class="pageTitle">Temukan Materi Yang Kamu Mau!</h2>
<p class="description">Cari dengan judul buku, penerbit, atau penulis</p>
<form class="searchBar" action=''>
<form class="searchBar" action=''>
<div class="col-6 form-group">
<input type="text" name='search' class="form-control" placeholder="Tulis di sini" value='{{request.GET.search}}'>
<input type="text" name='search' class="form-control" placeholder="Tulis di sini"
value='{{request.GET.search}}'>
</div>
<button type="submit" class="btn btn-cari">Cari</button>
</form>
<p class="pageTitle">Tidak menemukan materi yang kamu cari ? ajukan permintaan materi kami <a href="/req-materi">disini</a></p>
<p class="pageTitle">Tidak menemukan materi yang kamu cari ? ajukan permintaan materi kami <a
href="/req-materi">disini</a></p>
</div>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row content">
<div class="col-3 sidebar">
......@@ -75,26 +77,20 @@
<div class="card">
<div class="card-header" id="headingOne">
<h2 class="mb-0">
<button class="btn collapsed"
type="button"
data-toggle="collapse"
data-target="#collapseOne"
aria-expanded="true"
aria-controls="collapseOne">
Filter
<button class="btn collapsed" type="button" data-toggle="collapse"
data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Filter
</button>
</h2>
</div>
<div id="collapseOne"
class="collapse"
aria-labelledby="headingOne"
data-parent="#accordionExample">
<div id="collapseOne" class="collapse" aria-labelledby="headingOne"
data-parent="#accordionExample">
<div class="card-body">
{% for itemKategori in kategori_list %}
<li>
<a href="?kategori={{itemKategori.pk}}">{{itemKategori.name}}</a>
</li>
<li>
<a href="?kategori={{itemKategori.pk}}">{{itemKategori.name}}</a>
</li>
{% endfor %}
</div>
</div>
......@@ -102,33 +98,35 @@
<div class="card">
<div class="card-header" id="headingTwo">
<h2 class="mb-0">
<button class="btn collapsed" type="button" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
Sort
</button>
</h2>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordionExample">
<div class="card-body">
<li>
<a href="?sort=terbaru">terbaru</a>
</li>
<li>
<a href="?sort=terlama">terlama</a>
</li>
<li>
<a href="?sort=terpopuler">terpopuler</a>
</li>
<li>
<a href="?sort=judul">judul</a>
</li>
<li>
<a href="?sort=penulis">penulis</a>
</li>
<li>
<a href="?sort=pengunggah">pengunggah</a>
</li>
<h2 class="mb-0">
<button class="btn collapsed" type="button" data-toggle="collapse"
data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
Sort
</button>
</h2>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo"
data-parent="#accordionExample">
<div class="card-body">
<li>
<a href="?sort=terbaru">terbaru</a>
</li>
<li>
<a href="?sort=terlama">terlama</a>
</li>
<li>
<a href="?sort=terpopuler">terpopuler</a>
</li>
<li>
<a href="?sort=judul">judul</a>
</li>
<li>
<a href="?sort=penulis">penulis</a>
</li>
<li>
<a href="?sort=pengunggah">pengunggah</a>
</li>
</div>
</div>
</div>
</div>
......@@ -137,11 +135,16 @@
<div class="col-9 books">
{% for item in materi_list %}
<div class="card book">
<img src={{item.cover.url}} class="card-img-top" alt="cover"
<img src={{item.cover.url}} class="card-img-top" alt="cover"
style="height:200px; widows: 200px;; overflow: hidden;"></img>
<div class="card-body">
<h5 class="card-title">{{item.title}}</h5>
<p class="card-text">{{item.author}}</p>
<p class="card-text">Diunggah oleh
<a class="card-link" href="{% url 'katalog-per-kontributor' item.uploader.email %}">
{{item.uploader.name}}
</a>
</p>
<a href="{% url 'view-materi' item.id %}" class="btn btn-book">Baca</a>
<a href="{% url 'detail-materi' item.id %}" class="btn btn-book">Detail</a>
</div>
......@@ -158,14 +161,14 @@
<br>
</br>
{% if materi_list.has_previous %}
<a href="?page=1{{url}}" >&laquo; first</a>
<a href="?page=1{{url}}">&laquo; first</a>
<a href="?page={{ materi_list.previous_page_number }}{{url}}">previous</a>
{% endif %}
{% if materi_list.has_next %}
<a href="?page={{ materi_list.next_page_number }}{{url}}">next</a>
<a href="?page={{ materi_list.paginator.num_pages }}{{url}}">last &raquo;</a>
......@@ -173,16 +176,14 @@
</span>
</div>
</div>
</div>
</div>
</div>
<!-- /.container -->
<!-- /.container -->
</body>
</html>
{% endblock %}
\ No newline at end of file
{% endblock %}
\ No newline at end of file
......@@ -108,7 +108,7 @@ $('#btn_req_submit').click(function () {
$.ajax({
type: 'POST',
url: "{% url 'post-req-materi' %}",
url: "{% url 'req-materi' %}",
data: {
'title': title.value,
},
......
This diff is collapsed.
......@@ -4,7 +4,8 @@ from app import views
from app.views import (DashboardKontributorView, ProfilKontributorView,
SuksesLoginAdminView, SuksesLoginKontributorView,
SuntingProfilView, UploadMateriHTML, UploadMateriView,
ProfilAdminView, PostsView, SuntingProfilAdminView, ReqMateriView)
ProfilAdminView, PostsView, SuntingProfilAdminView,
ReqMateriView, KatalogPerKontributorView)
urlpatterns = [
path("", views.DaftarKatalog.as_view(), name="daftar_katalog"),
......@@ -27,5 +28,7 @@ urlpatterns = [
path("posts/", PostsView.as_view(), name='posts'),
path("sunting-admin/", SuntingProfilAdminView.as_view(), name="sunting-admin"),
path("req-materi/", ReqMateriView.as_view(), name="req-materi"),
path("post-req-materi/", views.post_req_materi, name="post-req-materi"),
path("profil/<str:email>/", KatalogPerKontributorView.as_view(),
name="katalog-per-kontributor"),
path("materi/rate/", views.add_rating_materi, name="rate-materi"),
]
import tempfile, random, datetime, string, hashlib,os
import PIL.Image as Image
def get_random_filename(f_name):
ext = f_name.split(".")[-1]
name = ''.join(random.choices(string.ascii_lowercase , k=4))
name += hashlib.md5((datetime.datetime.now().isoformat() + f_name).encode()).hexdigest()
name = name + "." + ext
return name
def remove_image_exifdata(f_path):
img = Image.open(f_path)
img.save(f_path)
return
......@@ -2,23 +2,21 @@ import mimetypes
import os
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib import messages
from django.core import serializers
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.paginator import Paginator
from django.db.models import Q, Count
from django.http import (Http404, HttpResponse, HttpResponseRedirect,
JsonResponse)
from django.shortcuts import get_object_or_404, redirect, render
from django.shortcuts import get_object_or_404
from django.template import loader
from django.urls import reverse
from django.views.generic import TemplateView, ListView
from .models import Category, Comment, Materi
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views.generic import TemplateView
from administration.models import VerificationReport
from app.forms import SuntingProfilForm, UploadMateriForm
from app.models import Category, Comment, Materi, Like, ViewStatistics, DownloadStatistics, ReqMaterial
from app.models import Category, Comment, Materi, Like, ViewStatistics, DownloadStatistics, ReqMaterial, Rating
from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
from authentication.models import User
import django
......@@ -66,10 +64,10 @@ class DaftarKatalog(TemplateView):
elif(getSort == "terbaru"):
lstMateri = lstMateri.order_by('-date_created')
elif(getSort == "terlama"):
lstMateri = lstMateri.order_by('date_created')
lstMateri = lstMateri.order_by('date_created')
elif(getSort == "terpopuler"):
lstMateri = lstMateri.annotate(count=Count('like__id')).order_by('-count')
context["materi_list"] = lstMateri
paginator = Paginator(context["materi_list"], 15)
page_number = request.GET.get('page')
......@@ -79,6 +77,26 @@ class DaftarKatalog(TemplateView):
context["url"] = url
return self.render_to_response(context=context)
class KatalogPerKontributorView(TemplateView):
template_name = "app/katalog_kontri.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
contributor = get_object_or_404(User, email=kwargs["email"])
context["contributor"] = contributor
return context
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
materi_list = Materi.objects.filter(status="APPROVE", uploader=context["contributor"]).order_by("date_modified")
paginator = Paginator(materi_list, 15)
page_number = request.GET.get('page')
materi_list_by_page = paginator.get_page(page_number)
context["materi_list"] = materi_list_by_page
return self.render_to_response(context=context)
class DetailMateri(TemplateView):
template_name = "app/detail_materi.html"
......@@ -100,6 +118,13 @@ class DetailMateri(TemplateView):
publishedDate = materi.published_date
citationAPA = materi.author+' . (' + publishedDate +') . ' + materi.title +' . '+materi.publisher
context["citationAPA"] = citationAPA
context['materi_rating_score'] = 0
if self.request.user.is_authenticated:
materi_rating = Rating.objects.filter(materi=materi, user=self.request.user).first()
if materi_rating is not None:
context['materi_rating_score'] = materi_rating.score
return context
def get(self, request, *args, **kwargs):
......@@ -166,6 +191,41 @@ def delete_comment(request, pk_materi, pk_comment):
return HttpResponseRedirect(url)
def add_rating_materi(request):
if request.method == 'POST' and request.user.is_authenticated:
materi_id = request.POST.get('materi_id', None)
rating_score = request.POST.get('rating_score', None)
if materi_id is None or rating_score is None:
return JsonResponse({"success": False, "msg": "Missing param"}, status=422)
try:
rating_score = int(rating_score)
except ValueError:
return JsonResponse({"success": False, "msg": "rating_score must be an integer"}, status=422)
try:
materi_id = int(materi_id)
except ValueError:
return JsonResponse({"success": False, "msg": "materi_id must be an integer"}, status=422)
if rating_score not in range(1, 6):
return JsonResponse({"success": False, "msg": "Rating must be an integer from 1 to 5"}, status=422)
materi = Materi.objects.filter(pk=materi_id).first()
if materi is None:
return JsonResponse({"success": False, "msg": "Materi does not exist"}, status=422)
if Rating.objects.filter(materi=materi, user=request.user).first() is not None:
return JsonResponse({"success": False, "msg": "Rating already exist"}, status=409)
Rating(materi=materi, user=request.user, score=rating_score).save()
return JsonResponse({"success": True, "msg": "Rating successfully created", "rating_score": rating_score},
status=201)
return JsonResponse({"success": False, "msg": "Forbidden"}, status=403)
def download_materi(request, pk):
materi = get_object_or_404(Materi, pk=pk)
path = materi.content.path
......@@ -244,7 +304,7 @@ class UploadMateriView(TemplateView):
context = self.get_context_data(**kwargs)
context["form"] = UploadMateriForm
return self.render_to_response(context)
def validate_file_extension(self, value):
ext = os.path.splitext(value.name)[1] # [0] returns path+filename
valid_extensions = ['.pdf', '.doc', '.docx', '.jpg', '.png', '.xlsx', '.xls', '.mp4', '.mp3']
......@@ -347,11 +407,26 @@ class SuntingProfilView(TemplateView):
raise PermissionDenied(request)
current_user = self.request.user
form = SuntingProfilForm(
request.POST, request.FILES, instance=current_user)
if form.is_valid():
current_user.default_profile_picture = True
form.save()
# Removing exifdata from profile picture on upload
if request.FILES:
f_name = request.FILES['profile_picture'].name
f_name = get_random_filename(f_name)
f_path = settings.MEDIA_ROOT + "/" + f_name
request.FILES['profile_picture'].name = f_name
form = SuntingProfilForm(
request.POST, request.FILES, instance=current_user)
form.save()
remove_image_exifdata(f_path)
else:
form.save()
return HttpResponseRedirect("/profil/")
else:
context = self.get_context_data(**kwargs)
......@@ -386,11 +461,26 @@ class SuntingProfilAdminView(TemplateView):
raise PermissionDenied(request)