diff --git a/.gitignore b/.gitignore
index c0739ce4ef0b8293ec8d34f428377bf9fcf5af8d..7e6811adf6066c047eb2fca79f1f49238d300bc2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ media
 /static/
 media/
 .coverage
+virtualenv
 
 # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
 # in your Git repository. Update and uncomment the following line accordingly.
diff --git a/administration/templates/administration/base_administrasi.html b/administration/templates/administration/base_administrasi.html
index 4e8acf203c9f51e8a259f7a65a475bc57450ab56..e92e0277c2ab9096266b43ea3670c0fb41e39ec0 100644
--- a/administration/templates/administration/base_administrasi.html
+++ b/administration/templates/administration/base_administrasi.html
@@ -68,7 +68,7 @@
 
     <!-- Scroll to Top Button-->
     <a class="scroll-to-top rounded" href="#page-top">
-        <i class="fas fa-angle-up"></i>
+        <em class="fas fa-angle-up"></em>
     </a>
 
 
diff --git a/administration/templates/administration/base_administrasi2.html b/administration/templates/administration/base_administrasi2.html
index cf48a013a723adf31ceaea1039ed8c1e76aae3da..a731025ecefb52cbc43d5499d45074907923029c 100644
--- a/administration/templates/administration/base_administrasi2.html
+++ b/administration/templates/administration/base_administrasi2.html
@@ -4,6 +4,7 @@
 <html lang="en">
 <!-- Static assets -->
 <head>
+    <title>Digipus</title>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -67,7 +68,7 @@
 
     <!-- Scroll to Top Button-->
     <a class="scroll-to-top rounded" href="#page-top">
-        <i class="fas fa-angle-up"></i>
+        <em class="fas fa-angle-up"></em>
     </a>
 
 
diff --git a/administration/templates/administration/data_statistik.html b/administration/templates/administration/data_statistik.html
index 2aad8e549f34095ff42972a7c2c5ad91691fa478..17df72575d35c2a457fe0ae1500c4af991e0abdd 100644
--- a/administration/templates/administration/data_statistik.html
+++ b/administration/templates/administration/data_statistik.html
@@ -28,7 +28,7 @@
                         <div class="h5 mb-0 font-weight-bold text-gray-800">{{chart_data.total.0}}</div>
                     </div>
                     <div class="col-auto">
-                        <i class="fas fa-eye" aria-hidden="true"></i>
+                        <em class="fas fa-eye" aria-hidden="true"></em>
                     </div>
                 </div>
             </div>
@@ -45,7 +45,7 @@
                         <div class="h5 mb-0 font-weight-bold text-gray-800">{{chart_data.total.1}}</div>
                     </div>
                     <div class="col-auto">
-                        <i class="fas fa-download" aria-hidden="true"></i>
+                        <em class="fas fa-download" aria-hidden="true"></em>
                     </div>
                 </div>
             </div>
@@ -62,7 +62,7 @@
                         <div class="h5 mb-0 font-weight-bold text-gray-800">{{chart_data.total.2}}</div>
                     </div>
                     <div class="col-auto">
-                        <i class="fas fa-thumbs-up" aria-hidden="true"></i>
+                        <em class="fas fa-thumbs-up" aria-hidden="true"></em>
                     </div>
                 </div>
             </div>
@@ -79,7 +79,7 @@
                         <div class="h5 mb-0 font-weight-bold text-gray-800">{{chart_data.total.3}}</div>
                     </div>
                     <div class="col-auto">
-                        <i class="fas fa-comments fa-2x text-gray-300"></i>
+                        <em class="fas fa-comments fa-2x text-gray-300"></em>
                     </div>
                 </div>
             </div>
@@ -100,7 +100,7 @@
                 <!-- <div class="dropdown no-arrow">
                     <a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown"
                         aria-haspopup="true" aria-expanded="false">
-                        <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
+                        <em class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></em>
                     </a>
                     <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
                         aria-labelledby="dropdownMenuLink">
diff --git a/administration/templates/detail_admin.html b/administration/templates/detail_admin.html
index 2f86f7006c304af25c06382f4433ed408ffabf14..5105c7706cce8755a67c0c411a24579ef161d9db 100644
--- a/administration/templates/detail_admin.html
+++ b/administration/templates/detail_admin.html
@@ -19,31 +19,46 @@
       <h4>{{ user.email }}</h4>
       <h4>{{ user.biography }}</h4>
       <div class="profile-margin"></div>
-      <table>
-          <tr>
-              <td class="profile-data">Instansi</td>
-              <td>{{ user.instansi }}</td>
-          </tr>
-         
-              <td class="profile-data">LinkedIn</td>
-              <td><a href="https://linkedin.com">{{ user.linkedin }}</a></td>
-          </tr>
-          
-          <tr>
-              <td class="profile-data">Facebook</td>
-              <td><a href="https://facebook.com">{{ user.facebook }}</a></td>
-          </tr>
-         
-          <tr>
-              <td class="profile-data">Twitter</td>
-              <td><a href="https://twitter.com">{{ user.twitter }}</a></td>
-          </tr>
-
-          <tr>
-              <td class="profile-data">Instagram</td>
-              <td><a href="https://instagram.com">{{ user.instagram }}</a></td>
-          </tr>
-      </table>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            instansi
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            {{user.instansi}}
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            LinkedIn
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://linkedin.com">{{ user.linkedin }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Facebook
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">  
+            <a href="https://facebook.com">{{ user.facebook }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Twitter
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://twitter.com">{{ user.twitter }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Instagram
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://instagram.com">{{ user.instagram }}</a>
+        </div>
+      </div>
       <a class="btn btn-primary btn-admin" href="/administration/kelola-admin/">Kembali ke Kelola Admin</a>
   </div>
 </div>
diff --git a/administration/templates/detail_kontri_admin.html b/administration/templates/detail_kontri_admin.html
index fb425130b9478304df78d175dcc1dccec0946072..ef4ff27d8bb210b497fc24fa18160e3ae3273d81 100644
--- a/administration/templates/detail_kontri_admin.html
+++ b/administration/templates/detail_kontri_admin.html
@@ -18,31 +18,46 @@
       <h4>{{ user.email }}</h4>
       <h4>{{ user.biography }}</h4>
       <div class="profile-margin"></div>
-      <table>
-          <tr>
-              <td class="profile-data">Instansi</td>
-              <td>{{ user.instansi }}</td>
-          </tr>
-         
-              <td class="profile-data">LinkedIn</td>
-              <td><a href="https://linkedin.com">{{ user.linkedin }}</a></td>
-          </tr>
-          
-          <tr>
-              <td class="profile-data">Facebook</td>
-              <td><a href="https://facebook.com">{{ user.facebook }}</a></td>
-          </tr>
-         
-          <tr>
-              <td class="profile-data">Twitter</td>
-              <td><a href="https://twitter.com">{{ user.twitter }}</a></td>
-          </tr>
-
-          <tr>
-              <td class="profile-data">Instagram</td>
-              <td><a href="https://instagram.com">{{ user.instagram }}</a></td>
-          </tr>
-      </table>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            instansi
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            {{user.instansi}}
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            LinkedIn
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://linkedin.com">{{ user.linkedin }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Facebook
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">  
+            <a href="https://facebook.com">{{ user.facebook }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Twitter
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://twitter.com">{{ user.twitter }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Instagram
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://instagram.com">{{ user.instagram }}</a>
+        </div>
+      </div>
       <a class="btn btn-primary btn-admin" href="/administration/kelola-kontributor/">Kembali ke Kelola Kontributor</a>
   </div>
 </div>
diff --git a/administration/templates/detail_verif.html b/administration/templates/detail_verif.html
index 18b1751c9bec8649249ece282e2e92f14117303d..a5755d3980c9120fdf5c6eec648162d29205b6df 100644
--- a/administration/templates/detail_verif.html
+++ b/administration/templates/detail_verif.html
@@ -8,7 +8,7 @@ Pratinjau Materi
 
     <!-- Sidebar Toggle (Topbar) -->
     <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
-        <i class="fa fa-bars"></i>
+        <em class="fa fa-bars"></em>
     </button>
 
     <div class="sidebar-brand-text mx-3">Pratinjau Materi</div>
@@ -53,7 +53,7 @@ Pratinjau Materi
                 <div class="info-wrapper">
                     <div class="info" id="1">
                         <dt class="col col-4">
-                            <p class="info-name">Verifikatur</p>
+                            <p class="info-name"><strong>Verifikatur</strong></p>
                         </dt>
                         <dd>
                             <p class="info-content">{{verification_report.user.name}}</p>
@@ -61,7 +61,7 @@ Pratinjau Materi
                     </div>
                     <div class="info" id="1">
                         <dt class="col col-4">
-                            <p class="info-name">Waktu Verifikasi</p>
+                            <p class="info-name"><strong>Waktu Verifikasi</strong></p>
                         </dt>
                         <dd>
                             <p class="info-content">{{verification_report.timestamp}}</p>
@@ -69,7 +69,7 @@ Pratinjau Materi
                     </div>
                     <div class="info" id="1">
                         <dt class="col col-4">
-                            <p class="info-name">Status Materi</p>
+                            <p class="info-name"><strong>Status Materi</strong></p>
                         </dt>
                         <dd>
                             <p class="info-content">{{verification_report.status}}</p>
diff --git a/administration/templates/kelola_admin.html b/administration/templates/kelola_admin.html
index 1af6884bf2e244ff5cc323b2f94dfce561f236b9..dd020c9771ca6b73161182317499809216bf6e16 100644
--- a/administration/templates/kelola_admin.html
+++ b/administration/templates/kelola_admin.html
@@ -15,7 +15,7 @@
   <div class="card-header py-3">
     <div class="d-flex">
       <div class="mr-auto p-2">
-        <h6 class="m-0 font-weight-bold text-primary">Tabel Daftar Admin</h6>
+        <h6 id="table-description" class="m-0 font-weight-bold text-primary">Tabel Daftar Admin</h6>
       </div>
       <div class="p-2">
         <a href="/administration/kelola-admin/tambah/" class="accept-button button-decoration button-header">Buat Akun Baru</a>
@@ -24,7 +24,7 @@
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+      <table aria-describedby="table-description" class="table table-bordered" id="dataTable">
         <thead>
           <tr>
             <th scope="col">Nama</th>
diff --git a/administration/templates/kelola_kontributor.html b/administration/templates/kelola_kontributor.html
index 0fdfe526aeb6b32b9e3c57643beb2472a965a75f..21b94fc5bc769c604ed0e78dddcc854b05f8dc8b 100644
--- a/administration/templates/kelola_kontributor.html
+++ b/administration/templates/kelola_kontributor.html
@@ -13,25 +13,25 @@
 <!-- DataTales Example -->
 <div class="card shadow mb-4">
   <div class="card-header py-3">
-    <h6 class="m-0 font-weight-bold text-primary">Tabel Daftar Kontributor</h6>
+    <h6 id="table-description" class="m-0 font-weight-bold text-primary">Tabel Daftar Kontributor</h6>
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+      <table aria-describedby="table-description" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
         <thead>
           <tr>
-            <th>Nama</th>
-            <th>NIK</th>
-            <th>Instansi</th>
-            <th>Detail</th>
+            <th scope="col">Nama</th>
+            <th scope="col">NIK</th>
+            <th scope="col">Instansi</th>
+            <th scope="col">Detail</th>
           </tr>
         </thead>
         <tfoot>
           <tr>
-            <th>Nama</th>
-            <th>NIK</th>
-            <th>Instansi</th>
-            <th>Detail</th>
+            <th scope="col">Nama</th>
+            <th scope="col">NIK</th>
+            <th scope="col">Instansi</th>
+            <th scope="col">Detail</th>
           </tr>
           </tr>
         </tfoot>
diff --git a/administration/templates/laporan_materi.html b/administration/templates/laporan_materi.html
index 5b92f17dce51124d0ff32ba034ac282131d2954e..33e489890c0eaa9979f3fd06567f559e8df7ebdb 100644
--- a/administration/templates/laporan_materi.html
+++ b/administration/templates/laporan_materi.html
@@ -76,11 +76,11 @@
 </div>
 <div class="card shadow mb-4">
   <div class="card-header py-3">
-    <h6 class="m-0 font-weight-bold text-primary" id="titleTabelPending">Materi yang Diblokir</h6>
+    <h6 id="table-description" class="m-0 font-weight-bold text-primary" id="titleTabelPending">Materi yang Diblokir</h6>
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table class="table table-bordered" id="dataTablePending" aria-describedby="titleTabelPending">
+      <table aria-describedby="table-description" class="table table-bordered" id="dataTablePending" aria-describedby="titleTabelPending">
         {% if not materi_diblokir %}
           Tidak ada materi yang diblokir
         {% else %}
diff --git a/administration/templates/registrasi_admin.html b/administration/templates/registrasi_admin.html
index 7d3a0151bb82231d953cbcbc96034ec44a3a8710..8da8fc6f3271dd7dd02a0e2374c1cfb3b4d50499 100644
--- a/administration/templates/registrasi_admin.html
+++ b/administration/templates/registrasi_admin.html
@@ -103,7 +103,7 @@
 
           <!-- Sidebar Toggle (Topbar) -->
           <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
-            <i class="fa fa-bars"></i>
+            <em class="fa fa-bars"></em>
           </button>
 
           <div class="sidebar-brand-text mx-3">Diskominfo Kota Depok</div>
@@ -257,7 +257,7 @@
 
   <!-- Scroll to Top Button-->
   <a class="scroll-to-top rounded" href="#page-top">
-    <i class="fas fa-angle-up"></i>
+    <em class="fas fa-angle-up"></em>
   </a>
 
   <!-- Bootstrap core JavaScript-->
diff --git a/administration/templates/setting_verifikasi.html b/administration/templates/setting_verifikasi.html
index f21254ef032ac3fdae82641d1827a99e6c5403fd..aa5fbdf0f5462035f49f610aca5144a48c29d88c 100644
--- a/administration/templates/setting_verifikasi.html
+++ b/administration/templates/setting_verifikasi.html
@@ -112,7 +112,7 @@
 
           <!-- Sidebar Toggle (Topbar) -->
           <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
-            <i class="fa fa-bars"></i>
+            <em class="fa fa-bars"></em>
           </button>
 
           <div class="sidebar-brand-text mx-3">Diskominfo Kota Depok</div>
@@ -172,7 +172,7 @@
                 <label>Deskripsi &nbsp;:</label>{{ form.description }}<br>
                 <br>
                 <div class="row">
-                  <button class="primary_btn save" style="border-radius:20px;" type="submit"><i class="far fa-save"></i>
+                  <button class="primary_btn save" style="border-radius:20px;" type="submit"><em class="far fa-save"></em>
                     Simpan</button>
                 </div>
               </form>
@@ -182,24 +182,24 @@
           <!-- DataTales Example -->
           <div class="card shadow mb-4">
             <div class="card-header py-3">
-              <h6 class="m-0 font-weight-bold text-primary">Point Verifikasi Materi</h6>
+              <h6 id="table-description" class="m-0 font-weight-bold text-primary">Point Verifikasi Materi</h6>
             </div>
             <div class="card-body">
               <div class="table-responsive">
-                <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+                <table aria-describedby="table-description" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
                   <thead>
                     <tr>
-                      <th>Judul Verifikasi</th>
-                      <th>Deskripsi</th>
-                      <th></th>
+                      <th scope="col">Judul Verifikasi</th>
+                      <th scope="col">Deskripsi</th>
+                      <th scope="col"></th>
                     </tr>
                   </thead>
                   <tbody>
                     {% for item in verification_settings %}
                     <tr>
-                      <th>{{item.title}}</th>
-                      <th>{{item.description}}</th>
-                      <th> </th>
+                      <th scope="col">{{item.title}}</th>
+                      <th scope="col">{{item.description}}</th>
+                      <th scope="col"> </th>
                     </tr>
                     {% endfor %}
                   </tbody>
@@ -232,7 +232,7 @@
 
   <!-- Scroll to Top Button-->
   <a class="scroll-to-top rounded" href="#page-top">
-    <i class="fas fa-angle-up"></i>
+    <em class="fas fa-angle-up"></em>
   </a>
 
   <!-- <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
diff --git a/administration/templates/settings.html b/administration/templates/settings.html
index 6381a4e088abdaa87f41c4d531d01183de455015..3f43715589699c8aa4ee113e4989679e8f5b29cd 100644
--- a/administration/templates/settings.html
+++ b/administration/templates/settings.html
@@ -281,9 +281,9 @@
 										<tbody>
 											{% for item in items %}
 											<tr>
-												<th>{{item.title}} {{item.name}}</th>
-												<th>{{item.description}}</th>
-												<th class="verif-buttons">
+												<th scope="col">{{item.title}} {{item.name}}</th>
+												<th scope="col">{{item.description}}</th>
+												<th scope="col" class="verif-buttons">
 													{% if item.name is None %}
 													<a
 														href="/administration/setting/verification/{{ item.id }}/edit"
@@ -377,9 +377,9 @@
 										<tbody>
 											{% for item in items_archived %}
 											<tr>
-												<th>{{item.title}} {{item.name}}</th>
-												<th>{{item.description}}</th>
-												<th>{{item.archived_by.name}}</th>
+												<th scope="col">{{item.title}} {{item.name}}</th>
+												<th scope="col">{{item.description}}</th>
+												<th scope="col">{{item.archived_by.name}}</th>
 											</tr>
 											{% endfor %}
 										</tbody>
diff --git a/app/forms.py b/app/forms.py
index 84facd1f2e25e18920754b7cd44a6396e7e1f4fe..54358428322ab389abb06d3b51ba3b265c38faf6 100644
--- a/app/forms.py
+++ b/app/forms.py
@@ -2,16 +2,20 @@ from django import forms
 
 from app.models import Materi, Category, RatingContributor
 from authentication.models import User
+import datetime
 
+def year_choices():
+    return[(r,r) for r in range(2000, datetime.date.today().year+1)]
 
 class UploadMateriForm(forms.ModelForm):
     
     categories = forms.ModelMultipleChoiceField(queryset=Category.objects.all(),widget=forms.CheckboxSelectMultiple(attrs={'style' : 'column-count:2'}),required=True)
     #categories.widget.attrs["style"] = "column-count:2"
+    release_year = forms.TypedChoiceField(coerce=int, choices=year_choices, initial=datetime.date.today().year)
 
     class Meta:
         model = Materi
-        fields = ["title", "author", "publisher",
+        fields = ["title", "author", "publisher", "release_year",
                   "categories", "descriptions", "cover", "content"]
 
     def __init__(self, *args, **kwargs):
diff --git a/app/migrations/0021_dislikecomment_likecomment.py b/app/migrations/0021_dislikecomment_likecomment.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ce9111268da53f8e2a88fdf877c4250fb354695
--- /dev/null
+++ b/app/migrations/0021_dislikecomment_likecomment.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.1 on 2020-10-09 16:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0020_merge_20201009_2039'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='LikeComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DislikeComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
+            ],
+        ),
+    ]
diff --git a/app/migrations/0021_materi_release_year.py b/app/migrations/0021_materi_release_year.py
new file mode 100644
index 0000000000000000000000000000000000000000..8294e44629dc026ddff721b329e610946de3a7d5
--- /dev/null
+++ b/app/migrations/0021_materi_release_year.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.1 on 2020-10-09 16:13
+
+import app.models
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0020_merge_20201009_2039'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='materi',
+            name='release_year',
+            field=models.IntegerField(default=app.models.current_year),
+        ),
+    ]
diff --git a/app/migrations/0022_merge_20201011_1122.py b/app/migrations/0022_merge_20201011_1122.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b0d5ebf263822713c41cced80792494eb749a21
--- /dev/null
+++ b/app/migrations/0022_merge_20201011_1122.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.1 on 2020-10-11 04:22
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0021_dislikecomment_likecomment'),
+        ('app', '0021_materi_release_year'),
+    ]
+
+    operations = [
+    ]
diff --git a/app/models.py b/app/models.py
index aa54208425b22632be3f49ce1214851651e4359d..54c7f628b2c86b816f4063aaa38fe8a773588ba8 100644
--- a/app/models.py
+++ b/app/models.py
@@ -1,4 +1,5 @@
 import random
+import datetime
 
 from django.contrib.postgres import search
 from django.core.exceptions import ValidationError
@@ -24,6 +25,8 @@ def getRandomColor():
     color = "%06x" % random.randint(0, 0xFFFFFF)
     return color
 
+def current_year():
+    return datetime.date.today().year
 
 class Category(models.Model):
     name = models.CharField(max_length=20)
@@ -56,6 +59,7 @@ class Materi(models.Model):
     author = models.CharField(max_length=30, default="Penyusun")
     uploader = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
     publisher = models.CharField(max_length=30, default="Penerbit")
+    release_year = models.IntegerField(default=current_year)
     pages = models.IntegerField(default=0)
     descriptions = models.TextField(default="Deskripsi")
     status = models.CharField(max_length=30, choices=VERIFICATION_STATUS, default=VERIFICATION_STATUS[0][0])
@@ -108,6 +112,28 @@ class Comment(models.Model):
     def __str__(self):
         return self.username
 
+    @property
+    def like_count(self):
+        count = LikeComment.objects.filter(comment=self).count()
+        return count
+    
+    @property
+    def dislike_count(self):
+        count = DislikeComment.objects.filter(comment=self).count()
+        return count
+
+
+class LikeComment(models.Model):
+    comment = models.ForeignKey(Comment, models.SET_NULL, null=True)
+    timestamp = models.DateTimeField(default=timezone.now)
+    session_id = models.CharField(max_length=32, blank=False)
+
+
+class DislikeComment(models.Model):
+    comment = models.ForeignKey(Comment, models.SET_NULL, null=True)
+    timestamp = models.DateTimeField(default=timezone.now)
+    session_id = models.CharField(max_length=32, blank=False)
+
 
 class Like(models.Model):
     materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
diff --git a/app/templates/app/base_admin.html b/app/templates/app/base_admin.html
index d8b93163621bbb7494764c66f0216ff1217ef447..73465184f28526320515eb1de9dda37b81dc887b 100644
--- a/app/templates/app/base_admin.html
+++ b/app/templates/app/base_admin.html
@@ -4,7 +4,7 @@
 <html lang="en">
 
 <head>
-
+  <title>Digipus</title>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -75,7 +75,7 @@
         <nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
             <!-- Sidebar Toggle (Topbar) -->
             <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
-              <i class="fa fa-bars"></i>
+              <em class="fa fa-bars"></em>
             </button>
             <div class="sidebar-brand-text mx-3">Diskominfo Kota Depok</div>
             <!-- Topbar Navbar -->
@@ -139,7 +139,7 @@
 
   <!-- Scroll to Top Button-->
   <a class="scroll-to-top rounded" href="#page-top">
-    <i class="fas fa-angle-up"></i>
+    <em class="fas fa-angle-up"></em>
   </a>
 
   <!-- Bootstrap core JavaScript-->
diff --git a/app/templates/app/base_dashboard.html b/app/templates/app/base_dashboard.html
index 0d5b034e451e352d040882b9d41e5dce595088c6..b8281931e7b8fae10fe76dd0477fee586206fcb2 100644
--- a/app/templates/app/base_dashboard.html
+++ b/app/templates/app/base_dashboard.html
@@ -4,6 +4,7 @@
 <html lang="en">
 
 <head>
+    <title>Digipus</title>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -64,7 +65,7 @@
 
     <!-- Scroll to Top Button-->
     <a class="scroll-to-top rounded" href="#page-top">
-        <i class="fas fa-angle-up"></i>
+        <em class="fas fa-angle-up"></em>
     </a>
 
 </body>
diff --git a/app/templates/app/base_profile.html b/app/templates/app/base_profile.html
index e21cb34dcfe7c051f68daedbb888b84c7ea22b49..6dc27646954f8d5b97e55725285ec708edfe6233 100644
--- a/app/templates/app/base_profile.html
+++ b/app/templates/app/base_profile.html
@@ -4,7 +4,7 @@
 <html lang="en">
 
 <head>
-
+    <title>Digipus</title>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -66,7 +66,7 @@
 
     <!-- Scroll to Top Button-->
     <a class="scroll-to-top rounded" href="#page-top">
-        <i class="fas fa-angle-up"></i>
+        <em class="fas fa-angle-up"></em>
     </a>
 
 </body>
diff --git a/app/templates/app/detail_materi.html b/app/templates/app/detail_materi.html
index e15f5f9fa4cda0a0c915adec2200c784710696fb..577e65bfb1f077a306962335a64b9202c1b90254 100644
--- a/app/templates/app/detail_materi.html
+++ b/app/templates/app/detail_materi.html
@@ -127,6 +127,14 @@
                         <p class="info-content">{{materi_data.publisher}}</p>
                     </dd>
                 </div>
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Tahun Terbit</dt>
+                    </dl>
+                    <dd>
+                        <p class="info-content">{{materi_data.release_year}}</p>
+                    </dd>
+                </div>
                 <div class="info" id="1">
                     <dl class="col col-4">
                         <dt class="info-name">Kontributor</dt>
@@ -260,6 +268,7 @@
         </div>
         {% if materi_data.status == "APPROVE" %}
         <div id="komentar" class="container-fluid comments-wrapper p-0">
+            {% if is_authenticated %}
             <div class="add-comments col col-8 bg-white shadow-sm rounded p-3 mb-3">
                 <form method="POST">
                     {% csrf_token %}
@@ -272,6 +281,9 @@
                     </div>
                 </form>
             </div>
+            {% else %}
+                <h3>Login terlebih dahulu untuk berkomentar</h3>
+            {% endif %}
             {% for comment in comment_data %}
             <div class="col col-8 comment shadow-sm p-3 mb-1 bg-white rounded">
                 <div class="d-flex bd-highlight mb-3 align-items-center user">
@@ -284,11 +296,35 @@
                     {% else %}
                     <span style="background-color: #{{comment.profile}}" class="profile p-1 bd-highligh"></span>
                     {% endif %}
-                    <p class="p-1 bd-highligh m-0"><b>{{comment.user.name}}</b></p>
-                    <p class="p-1 bd-highligh m-0">•</p>
-                    <p class="timestamp p-1 bd-highligh m-0 text-muted">
-                        {{ comment.timestamp|naturaltime }}
-                    </p>
+                    <div class="d-flex flex-row justify-content-end">
+                        <p class="p-1 bd-highligh m-0"><strong>{{comment.user.name}}</strong></p>
+                        <p class="p-1 bd-highligh m-0">•</p>
+                        <p class="timestamp p-1 bd-highligh m-0 text-muted">
+                            {{ comment.timestamp|naturaltime }}
+                        </p>
+                        <div>
+                            <button id="thumb-like-comment-{{ comment.id }}" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded" onClick="postLikeComment({{ comment.id }})">
+                                <div class="d-flex flex-row">
+                                    {% if has_liked.comment.id %}
+                                    <i id="thumb-like-comment-icon-{{ comment.id }}" aria-hidden="true" class="fas fa-thumbs-up"></i>
+                                    {% else %}
+                                    <i id="thumb-like-comment-icon-{{ comment.id }}" aria-hidden="true" class="far fa-thumbs-up"></i>
+                                    {% endif %}
+                                    <div id="like-comment-{{ comment.id }}">{{ comment.like_count }}</div>
+                                </div
+                            </button>
+                            <button id="thumb-dislike-comment-{{ comment.id }}" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded" onClick="postDislikeComment({{ comment.id }})">
+                                <div class="d-flex flex-row">
+                                    {% if has_disliked.comment.id %}
+                                    <i id="thumb-dislike-comment-icon-{{ comment.id }}" aria-hidden="true" class="fas fa-thumbs-down"></i>
+                                    {% else %}
+                                    <i id="thumb-dislike-comment-icon-{{ comment.id }}" aria-hidden="true" class="far fa-thumbs-down"></i>
+                                    {% endif %}
+                                    <div id="dislike-comment-{{ comment.id }}">{{ comment.dislike_count }}</div>
+                                </div>
+                            </button>
+                        </div>
+                    </div>
                     {% if user.is_admin %}
                     <a class="ml-auto p-1 bd-highlight close"
                         href="{% url 'delete-comment' materi_data.id comment.id %}">
@@ -422,6 +458,46 @@
         });
     });
 
+    function postLikeComment(comment_id) {
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                }
+            }
+        });
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'comment-like-toggle' %}",
+            data: {
+                'comment_id': comment_id,
+                'session_id': "{{ session_id }}"
+            },
+            success: likeComment,
+            dataType: 'html'
+        });
+    }
+
+    function postDislikeComment(comment_id) {
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                }
+            }
+        });
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'comment-dislike-toggle' %}",
+            data: {
+                'comment_id': comment_id,
+                'session_id': "{{ session_id }}"
+            },
+            success: dislikeComment,
+            dataType: 'html'
+        });
+    }
+
     function postAddRating(rating_score) {
         $.ajaxSetup({
             beforeSend: function (xhr, settings) {
@@ -459,6 +535,32 @@
             document.getElementById("thumb").firstChild.data = " Disukai"
         }
     }
+
+    function likeComment(data, jqXHR) {
+        var data = $.parseJSON(data)
+        likeIcon = $('#thumb-like-comment-icon-' + data['comment_id'])
+        likeValue = $('#like-comment-' + data['comment_id'])
+        if (data['liked']) {
+            likeIcon.removeClass("fas fa-thumbs-up").addClass('far fa-thumbs-up')
+            likeValue.text(parseInt(likeValue.text())-1)
+        } else {
+            likeIcon.removeClass("far fa-thumbs-up").addClass('fas fa-thumbs-up')
+            likeValue.text(parseInt(likeValue.text())+1)
+        }
+    }
+
+    function dislikeComment(data, jqXHR) {
+        var data = $.parseJSON(data)
+        dislikeIcon = $('#thumb-dislike-comment-icon-' + data['comment_id'])
+        dislikeValue = $('#dislike-comment-' + data['comment_id'])
+        if (data['disliked']) {
+            dislikeIcon.removeClass("fas fa-thumbs-down").addClass('far fa-thumbs-down')
+            dislikeValue.text(parseInt(dislikeValue.text())-1)
+        } else {
+            dislikeIcon.removeClass("far fa-thumbs-down").addClass('fas fa-thumbs-down')
+            dislikeValue.text(parseInt(dislikeValue.text())+1)
+        }
+    }
         
     function getCitation(text){
         var $temp = $("<input>");
diff --git a/app/templates/app/includes/navbar_katalog_materi.html b/app/templates/app/includes/navbar_katalog_materi.html
index 14db671ecb64fadb5319600b914023baa852bdad..c747e1cfb734c93be052b0c037b007144fc5112e 100644
--- a/app/templates/app/includes/navbar_katalog_materi.html
+++ b/app/templates/app/includes/navbar_katalog_materi.html
@@ -1,4 +1,48 @@
   
-   <nav class="navbar navbar-dark  static-top shadow katalog-navbar"> <!-- Sidebar Toggle (Topbar) -->
-        <div class="sidebar-brand-text">Digipus</div>
+    <nav class="navbar navbar-expand-lg navbar-light  static-top shadow katalog-navbar"> <!-- Sidebar Toggle (Topbar) -->
+        <div class="sidebar-brand-text navbar-brand">Digipus</div>
+        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
+          <span class="navbar-toggler-icon"></span>
+        </button>
+
+        <div class="collapse navbar-collapse" id="navbarContent">
+          <ul class="navbar-nav ml-auto">
+            <li class="nav-item active">
+              <a class="nav-link" href="/">Home<span class="sr-only">(current)</span></a>
+            </li>
+            <li class="nav-item">
+              <a class="nav-link" href="/forum">Forum</a>
+            </li>
+            {% if not request.user.is_authenticated %}
+              <li class="nav-item">
+                <a class="nav-link" href="/registrasi">Registrasi</a>
+              </li>
+              <li class="nav-item">
+                <a class="nav-link" href="/login">Login Kontributor</a>
+              </li>
+              <li class="nav-item">
+                <a class="nav-link" href="/login_admin">Login Admin</a>
+              </li>
+            {% else %}
+              <li class="nav-item">
+                <a class="nav-link" href="/dashboard">Dasbor</a>
+              </li>
+              {% if request.user.is_admin %}
+                <li class="nav-item">
+                  <a class="nav-link" href="/administration">Administrasi</a>
+                </li>
+                <li class="nav-item">
+                  <a class="nav-link" href="/profil-admin">Profil</a>
+                </li>
+              {% else %}
+                <li class="nav-item">
+                  <a class="nav-link" href="/profil">Profil</a>
+                </li>
+              {% endif %}
+              <li class="nav-item">
+                <a class="nav-link" href="/logout">Logout</a>
+              </li>
+            {% endif %}
+          </ul>
+        </div>
     </nav>
\ No newline at end of file
diff --git a/app/templates/app/includes/navigation.html b/app/templates/app/includes/navigation.html
index 73360a92eab1c0560b32aa5e508c9a10ed194575..37988b70b41417630adc6d1376da77a8ee73f9d6 100644
--- a/app/templates/app/includes/navigation.html
+++ b/app/templates/app/includes/navigation.html
@@ -2,7 +2,7 @@
 
     <!-- Sidebar Toggle (Topbar) -->
     <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
-        <i class="fa fa-bars"></i>
+        <em class="fa fa-bars"></em>
     </button>
 
     <div class="sidebar-brand-text mx-3">Diskominfo Kota Depok</div>
diff --git a/app/templates/app/katalog_materi.html b/app/templates/app/katalog_materi.html
index 1b11195bd2b77ee6bb9d20cd2dacdb9f63c85146..bd49b2d93fc8ba0d0416e2088640618e17fc414e 100644
--- a/app/templates/app/katalog_materi.html
+++ b/app/templates/app/katalog_materi.html
@@ -49,10 +49,10 @@
 
 <body style="background-color: #f8f8f8;">
     
-    {% include 'app/includes/navbar_katalog_materi.html' %}
 
 <!-- Page Content -->
     <div class="container">
+      {% include 'app/includes/navbar_katalog_materi.html' %}
         <header class="jumbotron my-4">
             <div class="container">
                 <div class="row header">
@@ -96,11 +96,13 @@
                             <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>
-                                    {% endfor %}
+                                    <ul>
+                                        {% for itemKategori in kategori_list %}
+                                        <li>
+                                            <a href="?kategori={{itemKategori.pk}}">{{itemKategori.name}}</a>
+                                        </li>
+                                        {% endfor %}
+                                    </ul>
                                 </div>
                             </div>
                         </div>
@@ -117,24 +119,29 @@
                             <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>
+                                    <ul>
+                                        <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>
+                                        <li>
+                                            <a href="?sort=jumlah_unduh">jumlah unduh</a>
+                                        </li>
+                                    </ul>
                                 </div>
                             </div>
                         </div>
diff --git a/app/templates/comments.html b/app/templates/comments.html
index 96f4a9c097f101ebab3d74c877ad6d92c2566ba8..6a1180d2a5f6b61643dd13a45577ddf206b00af3 100644
--- a/app/templates/comments.html
+++ b/app/templates/comments.html
@@ -48,7 +48,7 @@
 				<span style="background-color: #{{comment.profile}}" class="profile p-1 bd-highlight"></span>
 				{% endif %}
 				<p class="p-1 bd-highligh m-0">
-					<b>{{comment.user.name}}</b>
+					<strong>{{comment.user.name}}</strong>
 				</p>
 				<p class="p-1 bd-highligh m-0">•</p>
 				<p class="timestamp p-1 bd-highligh m-0">
diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html
index 95ec1ee58a534f65270862f791cd32f71eaecce2..076ad12d1cc465adb079a69e16e3e5e5feff1d11 100644
--- a/app/templates/dashboard.html
+++ b/app/templates/dashboard.html
@@ -3,7 +3,7 @@
 
 {% block title %}
 <title>Riwayat Unggah | Digipus</title>
-{% endblock %} 
+{% endblock %}
 
 {% block content %}
 <!-- Page Heading -->
@@ -13,25 +13,25 @@
 <!-- DataTales Example -->
 <div class="card shadow mb-4">
   <div class="card-header py-3">
-    <h6 class="m-0 font-weight-bold text-primary">Tabel Riwayat Unggah</h6>
+    <h6 id="table-description" class="m-0 font-weight-bold text-primary">Tabel Riwayat Unggah</h6>
   </div>
   <div class="card-body">
     <div class="table-responsive">
-      <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
+      <table aria-describedby="table-description" class="table table-bordered" id="dataTable">
         <thead>
           <tr>
-            <th>Judul Materi</th>
-            <th>Pembuat Materi</th>
-            <th>Status</th>
-            <th>Detail</th>
+            <th scope="col">Judul Materi</th>
+            <th scope="col">Pembuat Materi</th>
+            <th scope="col">Status</th>
+            <th scope="col">Detail</th>
           </tr>
         </thead>
         <tfoot>
           <tr>
-            <th>Judul Materi</th>
-            <th>Pembuat Materi</th>
-            <th>Status</th>
-            <th>Detail</th>
+            <th scope="col">Judul Materi</th>
+            <th scope="col">Pembuat Materi</th>
+            <th scope="col">Status</th>
+            <th scope="col">Detail</th>
           </tr>
         </tfoot>
         <tbody>
@@ -43,10 +43,12 @@
             <td class="verif-buttons">
               <span>
                 <a href="/materi/{{materi.id}}/" class="accept-button button-decoration"
-                style="background-color:#4e73df">Detail</a>
+                  style="background-color:#4e73df">Detail</a>
                 {% if materi.get_status_display == "Ditolak" or materi.get_status_display == "Perbaikan" %}
                 <a href="/revisi/materi/{{materi.id}}/" class="reject-button button-decoration">Revisi</a>
                 {% endif %}
+                <a type="button" href="/materi/{{materi.id}}/delete" class="reject-button button-decoration"
+                  style="background-color:red">Hapus</a>
               </span>
             </td>
           </tr>
diff --git a/app/templates/profil-admin.html b/app/templates/profil-admin.html
index 58ad43884d115f874cf1abba499d67572341b5fa..a2a566f12071edfd2907fe1ff2b3c5380f051ef6 100644
--- a/app/templates/profil-admin.html
+++ b/app/templates/profil-admin.html
@@ -18,31 +18,46 @@
       <h4>{{ user.email }}</h4>
       <h4>{{ user.biography }}</h4>
       <div class="profile-margin"></div>
-      <table>
-          <tr>
-              <td class="profile-data">Instansi</td>
-              <td>{{ user.instansi }}</td>
-          </tr>
-         
-              <td class="profile-data">LinkedIn</td>
-              <td><a href="https://linkedin.com">{{ user.linkedin }}</a></td>
-          </tr>
-          
-          <tr>
-              <td class="profile-data">Facebook</td>
-              <td><a href="https://facebook.com">{{ user.facebook }}</a></td>
-          </tr>
-         
-          <tr>
-              <td class="profile-data">Twitter</td>
-              <td><a href="https://twitter.com">{{ user.twitter }}</a></td>
-          </tr>
-
-          <tr>
-              <td class="profile-data">Instagram</td>
-              <td><a href="https://instagram.com">{{ user.instagram }}</a></td>
-          </tr>
-      </table>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            instansi
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            {{user.instansi}}
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            LinkedIn
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://linkedin.com">{{ user.linkedin }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Facebook
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">  
+            <a href="https://facebook.com">{{ user.facebook }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Twitter
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://twitter.com">{{ user.twitter }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Instagram
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://instagram.com">{{ user.instagram }}</a>
+        </div>
+      </div>
   </div>
 </div>
 {% endblock %}
\ No newline at end of file
diff --git a/app/templates/profil.html b/app/templates/profil.html
index 13ad5403e9873c1a8efaf626397e322c35bdea06..3145ab41746a6ff532e8fe8ef2fc7dcee333370d 100644
--- a/app/templates/profil.html
+++ b/app/templates/profil.html
@@ -22,31 +22,46 @@
       <h4>{{ user.email }}</h4>
       <h4>{{ user.biography }}</h4>
       <div class="profile-margin"></div>
-      <table>
-          <tr>
-              <td class="profile-data">Instansi</td>
-              <td>{{ user.instansi }}</td>
-          </tr>
-         
-              <td class="profile-data">LinkedIn</td>
-              <td><a href="https://linkedin.com">{{ user.linkedin }}</a></td>
-          </tr>
-          
-          <tr>
-              <td class="profile-data">Facebook</td>
-              <td><a href="https://facebook.com">{{ user.facebook }}</a></td>
-          </tr>
-         
-          <tr>
-              <td class="profile-data">Twitter</td>
-              <td><a href="https://twitter.com">{{ user.twitter }}</a></td>
-          </tr>
-
-          <tr>
-              <td class="profile-data">Instagram</td>
-              <td><a href="https://instagram.com">{{ user.instagram }}</a></td>
-          </tr>
-      </table>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            instansi
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            {{user.instansi}}
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            LinkedIn
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://linkedin.com">{{ user.linkedin }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Facebook
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">  
+            <a href="https://facebook.com">{{ user.facebook }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Twitter
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://twitter.com">{{ user.twitter }}</a>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 my-auto" style="font-size: 2rem;">
+            Instagram
+        </div>
+        <div class="col-md-6 my-auto" style="font-size: 1.2rem;">
+            <a href="https://instagram.com">{{ user.instagram }}</a>
+        </div>
+      </div>
   </div>
 </div>
 {% endblock %}
\ No newline at end of file
diff --git a/app/templates/sukses_admin.html b/app/templates/sukses_admin.html
index 313d93ce805937c12d3728278727de36237fe194..40692fdb1abd598551b99f2a644c22352e9f63d1 100644
--- a/app/templates/sukses_admin.html
+++ b/app/templates/sukses_admin.html
@@ -73,7 +73,7 @@
 
   <!-- Scroll to Top Button-->
   <a class="scroll-to-top rounded" href="#page-top">
-    <i class="fas fa-angle-up"></i>
+    <em class="fas fa-angle-up"></em>
   </a>
 
   <!-- Bootstrap core JavaScript-->
diff --git a/app/templates/sukses_kontri.html b/app/templates/sukses_kontri.html
index af4ee3bf90ee2b19102a8717da5b519c5bcab9ba..0aaf4a33bbe8bbe7cb3560ae4ed7ffca5a7178fe 100644
--- a/app/templates/sukses_kontri.html
+++ b/app/templates/sukses_kontri.html
@@ -79,7 +79,7 @@
 
   <!-- Scroll to Top Button-->
   <a class="scroll-to-top rounded" href="#page-top">
-    <i class="fas fa-angle-up"></i>
+    <em class="fas fa-angle-up"></em>
   </a>
 
   <!-- Bootstrap core JavaScript-->
diff --git a/app/tests.py b/app/tests.py
index 717f6315315d66f30767c932a274be1fe1d04c39..303ed2861d9c2761193e344b0a297854bdb9d2f2 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -1,6 +1,7 @@
 import json, tempfile, os, mock
 import pandas as pd
 from io import StringIO
+import time
 
 from bs4 import BeautifulSoup
 from datetime import datetime
@@ -12,11 +13,13 @@ from django.core.files import File
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.management import call_command
-from django.urls import resolve, reverse
+from django.test import Client, TestCase, TransactionTestCase
+from django.urls import resolve
 from django.db.utils import IntegrityError
 from django.test import Client, RequestFactory, TestCase, TransactionTestCase
 from pytz import timezone
 from time import sleep
+import datetime as dt
 
 from administration.models import VerificationSetting, VerificationReport
 from administration.utils import id_generator
@@ -26,9 +29,11 @@ from digipus.settings import TIME_ZONE
 from .models import (
     Category,
     Comment,
+    DislikeComment,
     DownloadStatistics,
     Materi,
     Like,
+    LikeComment,
     Rating,
     ReqMaterial,
     RatingContributor,
@@ -52,7 +57,7 @@ from .views import (
     UploadMateriView,
     UploadMateriExcelView,
 )
-from app.forms import SuntingProfilForm
+from app.forms import SuntingProfilForm, year_choices
 from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
 
 ERROR_403_MESSAGE = "Kamu harus login untuk mengakses halaman ini"
@@ -136,6 +141,45 @@ class DaftarKatalogTest(TestCase):
 
         self.assertSequenceEqual(search_result, expected_search_result)
 
+class DaftarKatalogSortingByJumlahUnduhTest(TestCase):
+    def setUp(self):
+        self.client = Client()
+
+        self.contributor_credential = {
+        "email": "kontributor@gov.id",
+        "password": "passwordtest"
+            }
+
+        self.contributor_credential_2 = {
+                "email": "kontributor2@gov.id",
+                "password": "passwordtest"
+            }
+
+        self.contributor = get_user_model().objects.create_user(
+            **self.contributor_credential, name="Kontributor 1", is_contributor=True)
+        self.contributor2 = get_user_model().objects.create_user(
+            **self.contributor_credential_2, name="Kontributor 2", is_contributor=True)
+
+        self.cover = SimpleUploadedFile(
+            "Cherprang_Areekul40_nJM9dGt.jpg", b"Test file")
+        self.content = SimpleUploadedFile("Bahan_PA_RKK.pdf", b"Test file")
+
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+        time.sleep(1)
+        Materi(title="Materi 2", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 2",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+
+        self.last_uploaded_material = Materi.objects.last()
+
+        material_unduh_url = f"/materi/{self.last_uploaded_material.id}/unduh"
+        self.client.get(material_unduh_url)
+
+    def test_sorting_by_jumlah_unduh(self):
+        response = self.client.get("/?sort=jumlah_unduh")
+        self.assertRegex(str(response.content), rf'.*Materi 2.*Materi 1.*')
 
 class DaftarKatalogPerKontributorTest(TestCase):
     def setUp(self):
@@ -322,11 +366,108 @@ class DetailMateriTest(TestCase):
         response = self.client.get(url)
         self.assertContains(response, "Anonymous")
 
+    def test_comment_disliked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous").id
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/dislike/", payload)
+        num_of_comment_dislikes = DislikeComment.objects.filter(comment=comment).count()
+        self.assertEqual(num_of_comment_dislikes, 0)
+
+    def test_comment_liked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous").id
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/like/", payload)
+        num_of_comment_likes = LikeComment.objects.filter(comment=comment).count()
+        self.assertEqual(num_of_comment_likes, 0)
+
+    def test_comment_undisliked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/dislike/", payload)
+        
+        ajax_response = self.client.post("/comment/dislike/", payload)
+        num_of_comment_dislikes = DislikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(num_of_comment_dislikes, 0)
+
+    def test_comment_unliked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/like/", payload)
+        
+        ajax_response = self.client.post("/comment/like/", payload)
+        num_of_comment_likes = LikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(num_of_comment_likes, 0)
+
+    def test_comment_new_does_not_have_dislike(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        comment_dislike_counter = DislikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(comment_dislike_counter, 0)
+
+    def test_comment_new_does_not_have_like(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        comment_like_counter = LikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(comment_like_counter, 0)
+
     def test_detail_materi_contains_form_comment(self):
+        self.client.login(**self.contributor_credential)
         response = self.client.get(self.url)
         self.assertContains(response, "Beri komentar...")
 
+    def test_detail_materi_not_contains_form_comment(self):
+        response = self.client.get(self.url)
+        self.assertNotContains(response, "Beri komentar...")
+        self.assertContains(response, "Login terlebih dahulu untuk berkomentar")
+
     def test_delete_comments_by_admin(self):
+        self.client.login(**self.contributor_credential)
         url = self.url
         self.client.post(url, {"comment": "This is new comment by Anonymous"})
         deleteURL = (
@@ -432,7 +573,6 @@ class DetailMateriTest(TestCase):
         self.assertEqual(last_url, "/materi/%d/" % self.materi1.id)
         self.assertEqual(status_code, 302)
 
-
 class PostsViewTest(TestCase):
     @classmethod
     def generate_posts_data(cls, user):
@@ -877,6 +1017,25 @@ class DashboardKontributorViewTest(TestCase):
         self.assertIn(ERROR_403_MESSAGE, html)
 
 
+class DeleteMateriTest(TestCase):
+    def setUp(self):
+        self.client = Client()
+        self.content = SimpleUploadedFile(
+            "content.txt", b"Test")
+        self.cover = SimpleUploadedFile(
+            "flower.jpg", b"Test file")
+        self.contributor = User.objects.create_contributor(email="kontributor@gov.id",
+                                                           password="kontributor")
+        Materi(title="Materi 1", author="Agas", uploader=self.contributor,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 1",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+        self.materi1 = Materi.objects.first()
+        self.url = "/materi/" + str(self.materi1.id) + "/delete"
+
+    def test_url_delete_materi_is_success(self):
+        response = self.client.get(self.url)
+        self.assertEqual(response.status_code, 302) 
+        
 class ProfilAdminTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -2158,3 +2317,16 @@ class RandomizedMateriTest(TestCase):
         response = Client().get("/?random=1")
         self.assertIn("Materi 1", response.content.decode())
         self.assertIn("Materi 2", response.content.decode())
+    
+class YearChoicesTest(TestCase):
+    def test_release_year_contains_the_right_current_year(self):
+        now = dt.date.today().year
+
+        choices = year_choices()
+
+        self.assertEqual((now, now), choices[-1])
+
+    def test_min_release_year_is_2000(self):
+        choices = year_choices()
+
+        self.assertEqual((2000, 2000), choices[0])
\ No newline at end of file
diff --git a/app/urls.py b/app/urls.py
index 7ccc70c3a5dc1c82dbcb59772f950b9a04e9c2da..baa6bccb6630849347f02cd47363536d7cc68d83 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -13,6 +13,9 @@ urlpatterns = [
     path("materi/like/", views.toggle_like, name="PostLikeToggle"),
     path("delete/<int:pk_materi>/<int:pk_comment>",
          views.delete_comment, name="delete-comment"),
+    path("comment/like/", views.toggle_like_comment, name="comment-like-toggle"),
+    path("comment/dislike/", views.toggle_dislike_comment, name="comment-dislike-toggle"),
+    path("materi/<int:pk>/delete", views.delete_materi, name="detele-materi"),
     path("materi/<int:pk>/unduh", views.download_materi, name="download-materi"),
     path("materi/<int:pk>/view", views.view_materi, name="view-materi"),
     path("dashboard/", DashboardKontributorView.as_view(), name="dashboard"),
diff --git a/app/views.py b/app/views.py
index deeb1b9acba6eb5348b11b1b4221bfe0fbdff6d3..66b96f602abe4c0033ad5efdb02ec8cd51f90950 100644
--- a/app/views.py
+++ b/app/views.py
@@ -7,7 +7,8 @@ from django.contrib import messages
 from django.contrib.auth.models import AnonymousUser
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.db.models import Q, Count
-from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
+from django.http import (Http404, HttpResponse, HttpResponseRedirect,
+                         JsonResponse)
 from django.urls import reverse
 from django.shortcuts import get_object_or_404, redirect
 from django.template import loader
@@ -19,8 +20,10 @@ from app.forms import SuntingProfilForm, UploadMateriForm, RatingContributorForm
 from app.models import (
     Category,
     Comment,
+    DislikeComment,
     Materi,
     Like,
+    LikeComment,
     ViewStatistics,
     DownloadStatistics,
     ReqMaterial,
@@ -32,12 +35,12 @@ from authentication.models import User
 import django
 import pandas as pd
 from io import BytesIO
+from django.contrib import messages
 from pydrive.auth import GoogleAuth
 from pydrive.drive import GoogleDrive
 from pydrive.auth import AuthenticationRejected
 import random
 
-
 def permission_denied(request, exception, template_name="error_403.html"):
     return defaults.permission_denied(request, exception, template_name)
 
@@ -79,19 +82,21 @@ class DaftarKatalog(TemplateView):
         getSort = request.GET.get("sort")
         if getSort:
             url = url + "&sort={0}".format(getSort)
-            if getSort == "judul":
-                lstMateri = lstMateri.order_by("title")
-            elif getSort == "penulis":
-                lstMateri = lstMateri.order_by("author")
-            elif getSort == "pengunggah":
-                lstMateri = lstMateri.order_by("uploader")
-            elif getSort == "terbaru":
-                lstMateri = lstMateri.order_by("-date_created")
-            elif getSort == "terlama":
-                lstMateri = lstMateri.order_by("date_created")
-            elif getSort == "terpopuler":
-                lstMateri = lstMateri.annotate(count=Count("like__id")).order_by("-count")
-                
+            if(getSort == "judul"):
+                lstMateri = lstMateri.order_by('title')
+            elif(getSort == "penulis"):
+                lstMateri = lstMateri.order_by('author')
+            elif(getSort == "pengunggah"):
+                lstMateri = lstMateri.order_by('uploader')
+            elif(getSort == "terbaru"):
+                lstMateri = lstMateri.order_by('-date_created')
+            elif(getSort == "terlama"):
+                lstMateri = lstMateri.order_by('date_created')
+            elif(getSort == "terpopuler"):
+                lstMateri = lstMateri.annotate(count=Count('like__id')).order_by('-count')
+            elif(getSort == "jumlah_unduh"):
+                lstMateri = lstMateri.annotate(count=Count('unduh__id')).order_by('-count')
+
         should_random = bool(request.GET.get("random"))
         if should_random:
             lstMateri = random.sample(list(lstMateri), len(lstMateri))
@@ -162,14 +167,23 @@ class DetailMateri(TemplateView):
         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
+                context['materi_rating_score'] = materi_rating.score
+        
+        context['is_authenticated'] = self.request.user.is_authenticated
 
         return context
 
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
         query_set_for_comment = Comment.objects.filter(materi=context["materi_data"])
+        has_liked = {}
+        has_disliked = {}
+        for comment in query_set_for_comment:
+            has_liked[comment.id] = LikeComment.objects.filter(comment=comment, session_id=self.request.session.session_key).exists()
+            has_disliked[comment.id] = DislikeComment.objects.filter(comment=comment, session_id=self.request.session.session_key).exists()
         context["comment_data"] = query_set_for_comment
+        context["has_liked"] = has_liked
+        context["has_disliked"] = has_disliked
         return self.render_to_response(context=context)
 
     def get_user_name(self, request):
@@ -194,10 +208,11 @@ class DetailMateri(TemplateView):
 
         materi = get_object_or_404(Materi, pk=kwargs["pk"])
         user_obj = request.user if request.user.is_authenticated else None
-        comment = Comment.objects.create(
-            comment=commentText, username=self.get_user_name(request), materi=materi, user=user_obj
-        )
-        comment.save()
+        if user_obj:
+            comment = Comment.objects.create(
+                comment=commentText, username=self.get_user_name(request), materi=materi, user=user_obj
+            )
+            comment.save()
         return HttpResponseRedirect(request.path)
 
 
@@ -226,6 +241,41 @@ def delete_comment(request, pk_materi, pk_comment):
     comment.delete()
     return HttpResponseRedirect(url)
 
+def toggle_like_comment(request):
+    if request.method == "POST":
+        comment_id = request.POST.get("comment_id", None)
+        session_id = request.POST.get("session_id", None)
+        if comment_id is None or session_id is None:
+            return JsonResponse({"success": False, "msg": "Missing parameter", "comment_id": comment_id})
+        comment = get_object_or_404(Comment, pk=comment_id)
+        has_liked = LikeComment.objects.filter(comment=comment, session_id=session_id).exists()
+        if has_liked:
+            like = get_object_or_404(LikeComment, comment=comment, session_id=session_id)
+            like.delete()
+            return JsonResponse({"success": True, "liked": True, "comment_id": comment_id})
+        else:
+            LikeComment(comment=comment, session_id=session_id).save()
+            return JsonResponse({"success": True, "liked": False, "comment_id": comment_id})
+    else:
+        return JsonResponse({"success": False, "msg": "Unsuported method", "comment_id": comment_id})
+
+def toggle_dislike_comment(request):
+    if request.method == "POST":
+        comment_id = request.POST.get("comment_id", None)
+        session_id = request.POST.get("session_id", None)
+        if comment_id is None or session_id is None:
+            return JsonResponse({"success": False, "msg": "Missing parameter", "comment_id": comment_id})
+        comment = get_object_or_404(Comment, pk=comment_id)
+        has_disliked = DislikeComment.objects.filter(comment=comment, session_id=session_id).exists()
+        if has_disliked:
+            dislike = get_object_or_404(DislikeComment, comment=comment, session_id=session_id)
+            dislike.delete()
+            return JsonResponse({"success": True, "disliked": True, "comment_id": comment_id})
+        else:
+            DislikeComment(comment=comment, session_id=session_id).save()
+            return JsonResponse({"success": True, "disliked": False, "comment_id": comment_id})
+    else:
+        return JsonResponse({"success": False, "msg": "Unsuported method", "comment_id": comment_id})
 
 def get_citation_ieee(request, materi):
     current_date = datetime.datetime.now()
@@ -351,6 +401,11 @@ def view_materi(request, pk):
         raise Http404("File tidak dapat ditemukan.")
 
 
+def delete_materi(request, pk):
+    materi = get_object_or_404(Materi, pk=pk)
+    materi.delete()
+    return HttpResponseRedirect("/dashboard/")
+
 class UploadMateriView(TemplateView):
     template_name = "unggah.html"
     context = {}
@@ -853,10 +908,8 @@ def save_to_gdrive(request, pk):
     path = materi.content.path
     file_path = os.path.join(settings.MEDIA_ROOT, path)
     if os.path.exists(file_path):
-        with open(file_path, "rb") as fh:
-            upload_to_gdrive(file_path, materi.title)
+        upload_to_gdrive(file_path, materi.title)
     else:
         raise Http404("File tidak dapat ditemukan.")
 
-    return HttpResponseRedirect(reverse("detail-materi", kwargs={"pk": pk}))
-
+    return HttpResponseRedirect(reverse('detail-materi', kwargs={'pk': pk}))
\ No newline at end of file
diff --git a/authentication/templates/login.html b/authentication/templates/login.html
index 0dfae589ed5969411d1930b21e24b16e1fc0267b..68859550dc8a8c72519d2ab9f4a7a6a3ec0e80c1 100644
--- a/authentication/templates/login.html
+++ b/authentication/templates/login.html
@@ -69,7 +69,7 @@
 
                     </div>
 
-
+                    <div class="g-recaptcha" data-sitekey={{captcha_site_key}}></div>
                     <div class="container-login100-form-btn">
                         <button class="login100-form-btn" type="submit">
                             Login
@@ -100,6 +100,8 @@
     <script src="../static/../static/vendor/countdowntime/countdowntime.js"></script>
     <!--===============================================================================================-->
     <script src="../static/js/login.js"></script>
+    <!--===============================================================================================-->
+    <script src='https://www.google.com/recaptcha/api.js'></script>
 
 </body>
 
diff --git a/authentication/templates/login_admin.html b/authentication/templates/login_admin.html
index ca2cad7d4f40dea55792e3d7eac14320972b4b61..fca44f4cf9d0bd8344d9cbca7dae0e8189a75cd1 100644
--- a/authentication/templates/login_admin.html
+++ b/authentication/templates/login_admin.html
@@ -54,7 +54,7 @@
                     <div class="work_info">
                         <form class="login100-form validate-form" method="POST">
                             {% csrf_token %}
-
+                            <input type="hidden" name="source" value="admin" />
                             <div class="login100-form-title p-b-43">
                                 Halo, Admin
                             </div>
@@ -84,7 +84,7 @@
                                 </div>
         
                             </div>
-
+                            <div class="g-recaptcha" data-sitekey="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"></div>
                             <div class="container-login100-form-btn">
                                 <button class="login100-form-btn" type="submit">
                                     Login
@@ -116,6 +116,9 @@
     <script src="../static/../static/vendor/countdowntime/countdowntime.js"></script>
     <!--===============================================================================================-->
     <script src="../static/js/login.js"></script>
+    <!--===============================================================================================-->
+    <script src='https://www.google.com/recaptcha/api.js'></script>
+
 
 </body>
 
diff --git a/authentication/tests.py b/authentication/tests.py
index 85d09f60d9b428681a141669ae2267cf842a18f5..ae7a2f85b99d7f095ed7b0ceaae84984113965fc 100644
--- a/authentication/tests.py
+++ b/authentication/tests.py
@@ -1,5 +1,6 @@
 from django.test import Client, TestCase
 from django.urls import resolve
+from rest_framework_simplejwt import views as jwt_views
 
 from authentication.models import User
 from authentication.views import Login
@@ -75,12 +76,12 @@ class LoginPageContributorTest(TestCase):
     def setUp(self):
         self.client = Client()
         self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
-                                                     password="kontributor")
+                                                           password="kontributor")
         self.url = "/login/"
         self.view = Login
         self.template_name = "login.html"
         self.login_credential = {
-            "email": "kontributor@gov.id", "pass": "kontributor"}
+            "email": "kontributor@gov.id", "pass": "kontributor", "g-recaptcha-response" : "testcaptcha"}
         self.error_message = {
             "empty_email_or_password": "Email atau Password anda kosong.",
             "wrong_email_or_password": "Email atau Password anda salah.",
@@ -114,11 +115,11 @@ class LoginPageContributorTest(TestCase):
         self.assertContains(response, "Kata Sandi")
 
     def test_kontributor_login_missing_email_or_password(self):
-        response = self.client.post(self.url, {"email": "kontributor@gov.id"})
+        response = self.client.post(self.url, {"email": "kontributor@gov.id", "g-recaptcha-response" : "testcaptcha"})
         self.assertIn("error_message", response.context_data)
         self.assertIn(self.error_message["empty_email_or_password"],
                       response.context_data["error_message"])
-        response = self.client.post(self.url, {"pass": "kontributor"})
+        response = self.client.post(self.url, {"pass": "kontributor", "g-recaptcha-response" : "testcaptcha"})
         self.assertIn("error_message", response.context_data)
         self.assertIn(self.error_message["empty_email_or_password"],
                       response.context_data["error_message"])
@@ -126,19 +127,19 @@ class LoginPageContributorTest(TestCase):
     def test_kontributor_login_wrong_email_or_password(self):
         # Wrong password
         response = self.client.post(
-            self.url, {"email": "kontributor@gov.id", "pass": "kontributor1"})
+            self.url, {"email": "kontributor@gov.id", "pass": "kontributor1", "g-recaptcha-response" : "testcaptcha"})
         self.assertIn("error_message", response.context_data)
         self.assertIn(self.error_message["wrong_email_or_password"],
                       response.context_data["error_message"])
         # Wrong email
         response = self.client.post(
-            self.url, {"email": "kontributor1@gov.id", "pass": "kontributor"})
+            self.url, {"email": "kontributor1@gov.id", "pass": "kontributor", "g-recaptcha-response" : "testcaptcha"})
         self.assertIn("error_message", response.context_data)
         self.assertIn(self.error_message["wrong_email_or_password"],
                       response.context_data["error_message"])
         # Wrong email and password
         response = self.client.post(
-            self.url, {"email": "kontributor1@gov.id", "pass": "kontributor1"})
+            self.url, {"email": "kontributor1@gov.id", "pass": "kontributor1", "g-recaptcha-response" : "testcaptcha"})
         self.assertIn("error_message", response.context_data)
         self.assertIn(self.error_message["wrong_email_or_password"],
                       response.context_data["error_message"])
@@ -250,7 +251,6 @@ class LoginPageAdminTest(TestCase):
         self.assertEqual(302, response.status_code)
         self.assertEqual(response.url, expected_redirect_url)
 
-
     def test_admin_visit_login_after_auth(self):
         # 302 meaning successful login and redirected
         expected_redirect_url = "/sukses-admin/"
@@ -263,3 +263,67 @@ class LoginPageAdminTest(TestCase):
         )
         self.assertEqual(302, response.status_code)
         self.assertEqual(response.url, expected_redirect_url)
+
+
+class TokenLoginTest(TestCase):
+    def setUp(self):
+        self.client = Client()
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
+        self.url = "/api/token/"
+        self.view = jwt_views.TokenObtainPairView
+        self.login_credential = {"email": "admin@gov.id", "password": "admin"}
+
+    def test_token_login_view(self):
+        found = resolve(self.url)
+        self.assertEqual(found.func.cls, self.view)
+
+    def test_token_login_get_method(self):
+        # Test
+        response = self.client.get(self.url)
+        self.assertEqual(response.status_code, 405)
+
+    def test_token_login_valid_data(self):
+        # Test
+        response = self.client.post(self.url, data=self.login_credential)
+        self.assertEqual(response.status_code, 200)
+
+    def test_token_login_invalid_data(self):
+        # Test
+        response = self.client.post(
+            self.url, data={"email": "admin@gov.id", "password": "admin1"})
+        self.assertEqual(response.status_code, 401)
+
+
+class TokenRefreshTest(TestCase):
+    def setUp(self):
+        self.client = Client()
+        self.admin = User.objects.create_admin(email="admin@gov.id",
+                                               password="admin")
+        self.token_url = "/api/token/"
+        self.refresh_url = "/api/token/refresh/"
+        self.view = jwt_views.TokenRefreshView
+        self.login_credential = {"email": "admin@gov.id", "password": "admin"}
+
+    def test_token_refresh_view(self):
+        found = resolve(self.refresh_url)
+        self.assertEqual(found.func.cls, self.view)
+
+    def test_token_refresh_get_method(self):
+        # Test
+        response = self.client.get(self.refresh_url)
+        self.assertEqual(response.status_code, 405)
+
+    def test_token_refresh_valid_data(self):
+        # Test
+        response = self.client.post(self.token_url, data=self.login_credential)
+        refresh_token = response.json()["refresh"]
+        response = self.client.post(
+            self.refresh_url, data={"refresh": refresh_token})
+        self.assertEqual(response.status_code, 200)
+
+    def test_token_refresh_invalid_data(self):
+        # Test
+        response = self.client.post(
+            self.refresh_url, data={"refresh": "aaaAAAA.BBbbbb.cCcCcCc"})
+        self.assertEqual(response.status_code, 401)
diff --git a/authentication/urls.py b/authentication/urls.py
index c4d31dc4ad8d010fd1e02105561c9c67edb78432..590a95ee49d2980fd96c5f3d7357f1123f78f090 100644
--- a/authentication/urls.py
+++ b/authentication/urls.py
@@ -1,5 +1,6 @@
 from django.contrib.auth.views import LogoutView
 from django.urls import path
+from rest_framework_simplejwt import views as jwt_views
 
 from authentication.views import Login
 
@@ -7,4 +8,6 @@ urlpatterns = [
     path("login/", Login.as_view(), name="login"),
     path("login_admin/", Login.as_view(), name="login_admin"),
     path("logout/", LogoutView.as_view()),
+    path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),
+    path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
 ]
diff --git a/authentication/views.py b/authentication/views.py
index 8e50ca975d598bb7b506a76150576191abbc6e71..03408cbaf9d5204e6bf89900b327cc6f1e564691 100644
--- a/authentication/views.py
+++ b/authentication/views.py
@@ -1,7 +1,10 @@
 from django.contrib.auth import authenticate, login
 from django.http import HttpResponseRedirect, QueryDict
 from django.views.generic import TemplateView
-
+from django.conf import settings
+from django.contrib import messages
+import urllib
+import json
 
 class Login(TemplateView):
     
@@ -27,30 +30,46 @@ class Login(TemplateView):
 
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
+        context['captcha_site_key'] = settings.GOOGLE_RECAPTCHA_SITE_KEY
         return self.render_to_response(context=context)
 
     def post(self, request, *args, **kwargs):
         email = request.POST.get("email", None)
         password = request.POST.get("pass", None)
+        context = self.get_context_data(*args, **kwargs)
 
         if email is None or password is None:
             context = self.get_context_data(*args, **kwargs)
             context["error_message"] = "Email atau Password anda kosong."
             return self.render_to_response(context=context)
-        else:
-            user = authenticate(email=email, password=password)
-            if user is not None:
-                login(request, user)
-                redirect_to = "/"
-                querystring = QueryDict(request.META['QUERY_STRING'])
-                if request.user.is_admin:
-                    redirect_to = "/sukses-admin/"
-                elif request.user.is_contributor:
-                    redirect_to = "/sukses-kontributor/"
-                if 'next' in querystring:
-                    redirect_to = querystring['next']
-                return HttpResponseRedirect(redirect_to)
+        else: 
+            recaptcha_response = request.POST.get('g-recaptcha-response')
+            url = 'https://www.google.com/recaptcha/api/siteverify'
+            values = {
+                'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
+                'response': recaptcha_response
+            }
+            data = urllib.parse.urlencode(values).encode()
+            req =  urllib.request.Request(url, data=data)
+            response = urllib.request.urlopen(req)
+            result = json.loads(response.read().decode())
+ 
+            if result['success']:
+                user = authenticate(email=email, password=password)
+                if user is not None:
+                    login(request, user)
+                    redirect_to = "/"
+                    querystring = QueryDict(request.META['QUERY_STRING'])
+                    if request.user.is_admin:
+                        redirect_to = "/sukses-admin/"
+                    elif request.user.is_contributor:
+                        redirect_to = "/sukses-kontributor/"
+                    if 'next' in querystring:
+                        redirect_to = querystring['next']
+                    return HttpResponseRedirect(redirect_to)
+                else:
+                    context["error_message"] = "Email atau Password anda salah."
+                    return self.render_to_response(context=context)
             else:
-                context = self.get_context_data(*args, **kwargs)
-                context["error_message"] = "Email atau Password anda salah."
-                return self.render_to_response(context=context)
+                messages.error(request, 'Invalid reCAPTCHA. Please try again.')
+            return self.render_to_response(context=context)
diff --git a/digipus/__pycache__/settings.cpython-36.pyc b/digipus/__pycache__/settings.cpython-36.pyc
index a2dfadd532b5c69b61e9c0a2768ccc1ee6376edf..bbd092036ce26932962c8c236860e7899ca31f6f 100644
Binary files a/digipus/__pycache__/settings.cpython-36.pyc and b/digipus/__pycache__/settings.cpython-36.pyc differ
diff --git a/digipus/settings.py b/digipus/settings.py
index a1c930660339988f60d34dad5d86c7f561ce7473..6f3add23133069c7552d793518bc22cee7e4e116 100644
--- a/digipus/settings.py
+++ b/digipus/settings.py
@@ -14,6 +14,7 @@ import os
 
 import dj_database_url
 from decouple import config
+from django.contrib.messages import constants as messages
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -49,6 +50,7 @@ INSTALLED_APPS = [
     "news.apps.NewsConfig",
     "traffic_statistics",
     "forum",
+    "rest_framework",
 ]
 
 MIDDLEWARE = [
@@ -145,6 +147,16 @@ USE_L10N = True
 
 USE_TZ = True
 
+MESSAGE_TAGS = {
+    messages.DEBUG: 'alert-info',
+    messages.INFO: 'alert-info',
+    messages.SUCCESS: 'alert-success',
+    messages.WARNING: 'alert-warning',
+    messages.ERROR: 'alert-danger',
+}
+
+GOOGLE_RECAPTCHA_SECRET_KEY = config('GOOGLE_RECHAPTCHA', default= "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe")
+GOOGLE_RECAPTCHA_SITE_KEY = config('CLIENT_RECHAPTCHA', default= "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI")
 
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/3.0/howto/static-files/
@@ -164,3 +176,9 @@ MEDIA_URL = "/media/"
 LOGOUT_REDIRECT_URL = "/"
 
 CRISPY_TEMPLATE_PACK = 'bootstrap4'
+
+REST_FRAMEWORK = {
+    'DEFAULT_AUTHENTICATION_CLASSES': [
+        'rest_framework_simplejwt.authentication.JWTAuthentication',
+    ],
+}
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index f3b2e12e332740426925ee03b625d0e64be99f14..30fd8032d8b3be87ddcb85a8443175668af3dc5f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -25,6 +25,9 @@ Django==3.1
 django-crispy-forms==1.9.1
 django-heroku==0.3.1
 django-storages==1.9.1
+djangorestframework==3.12.1
+djangorestframework-jwt==1.11.0
+djangorestframework-simplejwt==4.4.0
 entrypoints==0.3
 filelock==3.0.12
 flake8==3.7.9
@@ -67,6 +70,7 @@ pycodestyle==2.5.0
 pycparser==2.20
 PyDrive==1.3.1
 pyflakes==2.1.1
+PyJWT==1.7.1
 pylint==2.4.4
 pylint-django==2.0.15
 pylint-plugin-utils==0.6