diff --git a/administration/templates/kelola_admin.html b/administration/templates/kelola_admin.html
index aa6962d2841966cd2f89a88a34bcece51d564bc1..ac52797935b91de8ceea83a1a23035a73b44bf85 100644
--- a/administration/templates/kelola_admin.html
+++ b/administration/templates/kelola_admin.html
@@ -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
diff --git a/app/static/app/css/katalog_kontri.css b/app/static/app/css/katalog_kontri.css
new file mode 100644
index 0000000000000000000000000000000000000000..c6ff672311e4bfad5a4811ff2b0816fcb5c99e9c
--- /dev/null
+++ b/app/static/app/css/katalog_kontri.css
@@ -0,0 +1,9 @@
+.img-profile {
+    width: 10vw;
+    height: 10vw;
+}
+
+.profile-data {
+    color: #615CFD;
+    padding-right: 75px;
+}
\ No newline at end of file
diff --git a/app/templates/app/detail_materi.html b/app/templates/app/detail_materi.html
index 8802166b25015f71432ee88ed0d66b96fb10c49f..54b0bb090fa529d1cc3562e06369a7dd462536cd 100644
--- a/app/templates/app/detail_materi.html
+++ b/app/templates/app/detail_materi.html
@@ -5,7 +5,8 @@
 <link href="{% static 'css/sb-admin-2.min.css' %}" rel="stylesheet">
 <link rel="stylesheet" type="text/css" href="{% static 'app/css/detail_materi.css' %}">
 <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;1,100&display=swap" rel="stylesheet">
-<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v6.0"></script>
+<script async defer crossorigin="anonymous"
+    src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v6.0"></script>
 <script src="{% static 'js/detail_materi.js'%}"></script>
 {% endblock header %}
 
@@ -14,51 +15,53 @@
 {% endblock title %}
 
 {% block content %}
-    {% block verification %} {% endblock verification %}
-    <div id="fb-root"></div>
-    <div class="container-fluid p-0 bg detail-materi-color">
-        <nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
+{% block verification %} {% endblock verification %}
+<div id="fb-root"></div>
+<div class="container-fluid p-0 bg detail-materi-color">
+    <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">
+        <!-- Sidebar Toggle (Topbar) -->
+        <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
             <i class="fa fa-bars" aria-hidden="true"></i>
-          </button>
+        </button>
 
-          <div class="sidebar-brand-text mx-3">Diskominfo Kota Depok</div>
+        <div class="sidebar-brand-text mx-3">Diskominfo Kota Depok</div>
 
-          <!-- Topbar Navbar -->
-          <ul class="navbar-nav ml-auto">
+        <!-- Topbar Navbar -->
+        <ul class="navbar-nav ml-auto">
 
             {% if request.user.is_contributor %}
-                {% if materi_data.status == "DISAPPROVE" %}
-                <li class="nav-item black-text">
-                    <a class="nav-link feedback" data-toggle="modal" data-target="#umpanBalikModal">
-                        <span class="mr-2 d-none d-lg-inline text-gray-600 small">Umpan Balik Materi</span>
-                    </a>
-                    <div class="modal fade" id="umpanBalikModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
-                        <div class="modal-dialog" role="document">
-                          <div class="modal-content">
+            {% if materi_data.status == "DISAPPROVE" %}
+            <li class="nav-item black-text">
+                <a class="nav-link feedback" data-toggle="modal" data-target="#umpanBalikModal">
+                    <span class="mr-2 d-none d-lg-inline text-gray-600 small">Umpan Balik Materi</span>
+                </a>
+                <div class="modal fade" id="umpanBalikModal" tabindex="-1" role="dialog"
+                    aria-labelledby="exampleModalLabel" aria-hidden="true">
+                    <div class="modal-dialog" role="document">
+                        <div class="modal-content">
                             <div class="modal-header">
-                              <h5 class="modal-title black-text" id="exampleModalLabel">Umpan Balik Materi</h5>
-                              <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                                <span aria-hidden="true">&times;</span>
-                              </button>
+                                <h5 class="modal-title black-text" id="exampleModalLabel">Umpan Balik Materi</h5>
+                                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                                    <span aria-hidden="true">&times;</span>
+                                </button>
                             </div>
                             <div class="modal-body">
-                              {% for item in report %}
+                                {% for item in report %}
                                 <p class="black-text">Alasan materi ditolak:</p>
                                 <p class="black-text">{{ item.report.feedback }}</p>
-                                <p class="black-text">Untuk merevisi materi, sila tekan tombol revisi di halaman riwayat unggah.</p>
-                              {% endfor %}
+                                <p class="black-text">Untuk merevisi materi, sila tekan tombol revisi di halaman riwayat
+                                    unggah.</p>
+                                {% endfor %}
                             </div>
                             <div class="modal-footer">
-                              <button type="button" class="btn btn-danger" data-dismiss="modal">Tutup</button>
+                                <button type="button" class="btn btn-danger" data-dismiss="modal">Tutup</button>
                             </div>
-                          </div>
                         </div>
-                      </div>
-                </li>    
-                {% endif %}
+                    </div>
+                </div>
+            </li>
+            {% endif %}
             {% endif %}
 
             <li class="nav-item">
@@ -80,63 +83,106 @@
             <!-- Nav Item - User Information -->
             {% if request.user.is_authenticated %}
             <li class="nav-item dropdown no-arrow">
-              <a class="nav-link dropdown-toggle" href="/dashboard/">
-                <span class="mr-2 d-none d-lg-inline text-gray-600 small">{{ user.name }}</span>
-                {% if not user.default_profile_picture %}
-                <img class="img-profile rounded-circle" src="https://i.ibb.co/9wgPzyZ/default-image.png" alt="User profile picture">
-                {% else %}
-                <img class="img-profile rounded-circle" src="{{ user.profile_picture.url }}" alt="User profile picture">
-                {% endif %}
-              </a>
+                <a class="nav-link dropdown-toggle" href="/dashboard/">
+                    <span class="mr-2 d-none d-lg-inline text-gray-600 small">{{ user.name }}</span>
+                    {% if not user.default_profile_picture %}
+                    <img class="img-profile rounded-circle" src="https://i.ibb.co/9wgPzyZ/default-image.png"
+                        alt="User profile picture">
+                    {% else %}
+                    <img class="img-profile rounded-circle" src="{{ user.profile_picture.url }}"
+                        alt="User profile picture">
+                    {% endif %}
+                </a>
             </li>
             {% endif %}
 
-          </ul>
+        </ul>
 
-        </nav>
-        <div class="row materi-data mr-4 ml-4 p-3 shadow-sm rounded">
-            <div class="col col-3 cover">
-                <img src={{materi_data.cover.url}} alt="cover">
+    </nav>
+    <div class="row materi-data mr-4 ml-4 p-3 shadow-sm rounded">
+        <div class="col col-3 cover">
+            <img src={{materi_data.cover.url}} alt="cover">
+        </div>
+        <div class="col col-6 ml-3 book">
+            <h2>{{materi_data.title}}</h2>
+            <div class="category-wrapper">
+                {% for category in materi_data.categories.all %}
+                <span class="mr-1">#{{category.name}}</span>
+                {% endfor %}
             </div>
-            <div class="col col-6 ml-3 book">
-                <h2>{{materi_data.title}}</h2>
-                <div class="category-wrapper">
-                    {% for category in materi_data.categories.all %}
-                        <span class="mr-1">#{{category.name}}</span>
-                    {% endfor %}
+            <div class="info-wrapper">
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Penulis</dt>
+                    </dl>
+                    <dd>
+                        <p class="info-content">{{materi_data.author}}</p>
+                    </dd>
                 </div>
-                <div class="info-wrapper">
-                    <div class="info" id="1">
-                        <dt class="col col-4">
-                            <p class="info-name">Penulis</p>
-                        </dt>
-                        <dd>
-                            <p class="info-content">{{materi_data.author}}</p>
-                        </dd>
-                    </div>
-                    <div class="info" id="1">
-                        <dt class="col col-4">
-                            <p class="info-name">Penerbit</p>
-                        </dt>
-                        <dd>
-                            <p class="info-content">{{materi_data.publisher}}</p>
-                        </dd>
-                    </div>
-                    <div class="info" id="1">
-                        <dt class="col col-4">
-                            <p class="info-name">Jumlah Halaman</p>
-                        </dt>
-                        <dd>
-                            <p class="info-content">{{materi_data.pages}}</p>
-                        </dd>
-                    </div>
-                    <div class="info" id="1">
-                        <dt class="col col-4">
-                            <p class="info-name">Ukuran File</p>
-                        </dt>
-                        <dd>
-                            <p class="info-content">{{materi_data.content.size|filesizeformat}}</p>
-                        </dd>
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Penerbit</dt>
+                    </dl>
+                    <dd>
+                        <p class="info-content">{{materi_data.publisher}}</p>
+                    </dd>
+                </div>
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Kontributor</dt>
+                    </dl>
+                    <dd>
+                        <a class="info-content"
+                            href="{% url 'katalog-per-kontributor' materi_data.uploader.email %}">{{materi_data.uploader.name}}</a>
+                    </dd>
+                </div>
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Jumlah Halaman</dt>
+                    </dl>
+                    <dd>
+                        <p class="info-content">{{materi_data.pages}}</p>
+                    </dd>
+                </div>
+                <div class="info" id="1">
+                    <dl class="col col-4">
+                        <dt class="info-name">Ukuran File</dt>
+                    </dl>
+                    <dd>
+                        <p class="info-content">{{materi_data.content.size|filesizeformat}}</p>
+                    </dd>
+                </div>
+            </div>
+            <div class="buttons d-flex flex-row bd-highlight mb-1">
+                <a href="{% url 'view-materi' materi_data.id %}"
+                    class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded">Baca</a>
+                <a href="{% url 'download-materi' materi_data.id %}"
+                    class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded">Unduh</a>
+                <div class="dropdown">
+                    <button class="btn dropdown-toggle btn-book shadow-sm p-2 mr-2 bg-white rounded" type="button"
+                        id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                        Bagikan
+                    </button>
+                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <div class="fb-share-button" data-href="{% url 'detail-materi' materi_data.id %}"
+                            data-layout="button" data-size="small">
+                            <a target="_blank"
+                                href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fdigipus-staging-2.herokuapp.com%2Fmateri%2F{{materi_data.id}}%2F&amp;src=sdkpreparse"
+                                class="dropdown-item fb-xfbml-parse-ignore">Facebook</a>
+                        </div>
+                        <a class="twitter-share-button dropdown-item"
+                            href="https://twitter.com/intent/tweet?text=Cek%20materi%20ini%20yuk%20https%3A%2F%2Fdigipus-staging-2.herokuapp.com%2Fmateri%2F{{materi_data.id}}%2F"
+                            target="_blank" data-size="large">
+                            Twitter
+                        </a>
+                        <a class="dropdown-item"
+                            href="whatsapp://send?text=Cek materi ini yuk! https://digipus-staging-2.herokuapp.com{{request.path}}"
+                            target="_blank">Whatsapp</a>
+                        <a class="dropdown-item"
+                            href="https://social-plugins.line.me/lineit/share?url=https%3A%2F%2Fdigipus-staging-2.herokuapp.com%2Fmateri%2F{{materi_data.id}}%2F"
+                            target="_blank">Line</a>
+                        <p id="url" style="display: none">https://digipus-staging-2.herokuapp.com{{request.path}}</p>
+                        <button class="dropdown-item btn-book" onclick="copyToClipboard('#url')">Bagikan Tautan</button>
                     </div>
                 </div>
                 <div class="buttons d-flex flex-row bd-highlight mb-1">
@@ -161,98 +207,198 @@
                             <a class="dropdown-item" href="https://social-plugins.line.me/lineit/share?url=https%3A%2F%2Fdigipus-staging-2.herokuapp.com%2Fmateri%2F{{materi_data.id}}%2F" target="_blank">Line</a>
                             <p id="url" style="display: none">https://digipus-staging-2.herokuapp.com{{request.path}}</p>
                             <button class="dropdown-item btn-book" onclick="copyToClipboard('#url')">Bagikan Tautan</button>
+                <form action="" method="POST">
+                    <input type="hidden" name="action" value="like">
+                </form>
+                {% if has_liked %}
+                <button id="thumb" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded"><i id="thumbIcon"
+                        aria-hidden="true" class="fas fa-thumbs-up"></i> Disukai</button>
+                {% else %}
+                <button id="thumb" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded"><i id="thumbIcon"
+                        aria-hidden="true" class="far fa-thumbs-up"></i> Sukai</button>
+                {% endif %}
+
+                {% if user.is_authenticated %}
+                <div class="dropdown">
+                    <button class="btn dropdown-toggle btn-book shadow-sm p-2 mr-2 bg-white rounded align-self-center"
+                        type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                        <em id="button-rating-star-icon" class="align-self-center far fa-star"></em>
+                        <span id="button-rating-text">Beri Rating</span>
+                    </button>
+                    <div id="star-dropdown" class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <div class="text-center btn-book">
+                            <em onclick="postAddRating(1)" id="star-1" class="far fa-star fa-lg"></em>
+                            <em onclick="postAddRating(2)" id="star-2" class="far fa-star fa-lg"></em>
+                            <em onclick="postAddRating(3)" id="star-3" class="far fa-star fa-lg"></em>
+                            <em onclick="postAddRating(4)" id="star-4" class="far fa-star fa-lg"></em>
+                            <em onclick="postAddRating(5)" id="star-5" class="far fa-star fa-lg"></em>
                         </div>
                     </div>
-                    <form action="" method="POST">
-                        <input type="hidden" name="action" value="like">
-                    </form>
-                    {% if has_liked %}
-                    <button id="thumb" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded"><i id="thumbIcon" aria-hidden="true" class="fas fa-thumbs-up"></i> Disukai</button>
-                    {% else %}
-                    <button id="thumb" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded"><i id="thumbIcon" aria-hidden="true" class="far fa-thumbs-up"></i> Sukai</button>
-                    {% endif %}
                 </div>
+                {% else %}
+                <button class="btn dropdown-toggle btn-book shadow-sm p-2 mr-2 bg-white rounded align-self-center"
+                    type="button" id="dropdownMenuButton" aria-haspopup="true" aria-expanded="false" data-toggle="modal"
+                    data-target="#notLoggedInModal">
+                    <em class="align-self-center far fa-star"></em> Beri Rating
+                </button>
+                {% endif %}
             </div>
         </div>
-        <div class="row menu-wrapper mr-4 ml-4 p-3">
-            <nav class="navbar navbar-expand-sm border-top border-bottom p-0 mt-3 mb-3">  
-                <ul class="navbar-nav">
-                    <li class="nav-item">
-                        <a class="nav-link" href="#deskripsi">Deskripsi</a>
-                    </li>
-                    {% if materi_data.status == "APPROVE" %}
-                    <li class="nav-item">
-                        <a class="nav-link" href="#komentar">Komentar</a>
-                    </li>
-                    {% endif %}
-                </ul>
-            </nav>
-            <div id="deskripsi" class="container-fluid description-wrapper bg-white shadow-sm rounded p-3">
-                <h1>Deskripsi</h1>
-                <div class="col col-8 description">
-                    <p>{{materi_data.descriptions}}</p>
-                </div>
+    </div>
+    <div class="row menu-wrapper mr-4 ml-4 p-3">
+        <nav class="navbar navbar-expand-sm border-top border-bottom p-0 mt-3 mb-3">
+            <ul class="navbar-nav">
+                <li class="nav-item">
+                    <a class="nav-link" href="#deskripsi">Deskripsi</a>
+                </li>
+                {% if materi_data.status == "APPROVE" %}
+                <li class="nav-item">
+                    <a class="nav-link" href="#komentar">Komentar</a>
+                </li>
+                {% endif %}
+            </ul>
+        </nav>
+        <div id="deskripsi" class="container-fluid description-wrapper bg-white shadow-sm rounded p-3">
+            <h1>Deskripsi</h1>
+            <div class="col col-8 description">
+                <p>{{materi_data.descriptions}}</p>
             </div>
-            {% if materi_data.status == "APPROVE" %}
-                <div id="komentar" class="container-fluid comments-wrapper p-0">
-                    <div class="add-comments col col-8 bg-white shadow-sm rounded p-3 mb-3">
-                        <form method="POST">
-                            {% csrf_token %}
-                            <h1>Komentar</h1>
-                            <div class="form-group">
-                                <textarea placeholder="Beri komentar..." class="form-control" 
-                                        id="exampleFormControlTextarea1" 
-                                        rows="3" name="comment" required
-                                ></textarea>
-                                <button type="submit" class="btn btn-link btn-book shadow-sm p-2 mt-2 bg-white rounded">Kirim</button>
-                            </div>
-                        </form>
+        </div>
+        {% if materi_data.status == "APPROVE" %}
+        <div id="komentar" class="container-fluid comments-wrapper p-0">
+            <div class="add-comments col col-8 bg-white shadow-sm rounded p-3 mb-3">
+                <form method="POST">
+                    {% csrf_token %}
+                    <h1>Komentar</h1>
+                    <div class="form-group">
+                        <textarea placeholder="Beri komentar..." class="form-control" id="exampleFormControlTextarea1"
+                            rows="3" name="comment" required></textarea>
+                        <button type="submit"
+                            class="btn btn-link btn-book shadow-sm p-2 mt-2 bg-white rounded">Kirim</button>
                     </div>
-                    {% 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">
-                                {% if comment.user != Null %}
-                                    {% if not user.default_profile_picture %}
-                                    <img class="profile" src="https://i.ibb.co/9wgPzyZ/default-image.png" alt="profile-picture">
-                                    {% else %}
-                                    <img class="profile" src="{{ comment.user.profile_picture.url }}" alt="profile-picture">
-                                    {% endif %}
-                                {% 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>
-                                {% if user.is_admin %}
-                                    <a class="ml-auto p-1 bd-highlight close" href="{% url 'delete-comment' materi_data.id comment.id %}">
-                                        <span aria-hidden="true">&times;</span>
-                                    </a>
-                                {% endif %}
-                            </div>
-                            <p class="text">{{comment.comment}}</p>
-                        </div>
-                    {% endfor %}
+                </form>
+            </div>
+            {% 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">
+                    {% if comment.user != Null %}
+                    {% if not user.default_profile_picture %}
+                    <img class="profile" src="https://i.ibb.co/9wgPzyZ/default-image.png" alt="profile-picture">
+                    {% else %}
+                    <img class="profile" src="{{ comment.user.profile_picture.url }}" alt="profile-picture">
+                    {% endif %}
+                    {% 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>
+                    {% if user.is_admin %}
+                    <a class="ml-auto p-1 bd-highlight close"
+                        href="{% url 'delete-comment' materi_data.id comment.id %}">
+                        <span aria-hidden="true">&times;</span>
+                    </a>
+                    {% endif %}
                 </div>
-            {% endif %}
+                <p class="text">{{comment.comment}}</p>
+            </div>
+            {% endfor %}
         </div>
+        {% endif %}
     </div>
-    <footer class="sticky-footer bg-white p-4">
-        <div class="container my-auto">
-          <div class="copyright text-center my-auto">
+</div>
+<footer class="sticky-footer bg-white p-4">
+    <div class="container my-auto">
+        <div class="copyright text-center my-auto">
             <span>Copyright &copy; Diskominfo Kota Depok 2020</span>
-          </div>
         </div>
-      </footer>
+    </div>
+</footer>
+
+<!-- Modal -->
+<div class="modal fade" id="notLoggedInModal" tabindex="-1" role="dialog" aria-labelledby="notLoggedInModalLabel"
+    aria-hidden="true">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="notLoggedInModalLabel">Belum Login</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                Login untuk memberikan rating
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
+                <button type="button" class="btn btn-primary" onclick="window.location.href = '/login';">Login</button>
+            </div>
+        </div>
+    </div>
+</div>
 {% endblock content %}
 {% block extra_scripts %}
 <script src="https://kit.fontawesome.com/bc2cedd6b2.js" crossorigin="anonymous"></script>
 <script type="text/javascript">
     // using jQuery
     var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
+
+    let currentRating = {{ materi_rating_score }};
+    fillRatingStar(currentRating)
+
+    function changeButtonIntoRated() {
+        $('#button-rating-star-icon').addClass('fas');
+        $('#button-rating-star-icon').removeClass('far');
+        $('#button-rating-text').text("Rating Anda")
+    }
+
+    if (currentRating > 0) {
+        changeButtonIntoRated()
+    }
+
+    $("#star-dropdown").click(function (e) {
+        e.stopPropagation();
+    });
+
+    function clearRatingStar() {
+        for (let i = 1; i <= 5; i++) {
+            $('#star-' + i).addClass('far');
+            $('#star-' + i).removeClass('fas');
+        }
+    }
+
+    function fillRatingStar(ratingScore) {
+        for (let i = 1; i <= ratingScore; i++) {
+            $('#star-' + i).addClass('fas');
+            $('#star-' + i).removeClass('far');
+        }
+    }
+
+    function makeHoverStar(starAmount) {
+        function hoverStar() {
+            clearRatingStar()
+            fillRatingStar(starAmount)
+        }
+
+        return hoverStar
+    }
+
+    function makeUnHoverStar(starAmount) {
+        function unHoverStar() {
+            clearRatingStar()
+            fillRatingStar(currentRating)
+        }
+
+        return unHoverStar
+    }
+
+    $('#star-1').hover(makeHoverStar(1), makeUnHoverStar(1));
+    $('#star-2').hover(makeHoverStar(2), makeUnHoverStar(2));
+    $('#star-3').hover(makeHoverStar(3), makeUnHoverStar(3));
+    $('#star-4').hover(makeHoverStar(4), makeUnHoverStar(4));
+    $('#star-5').hover(makeHoverStar(5), makeUnHoverStar(5));
 </script>
 <script>
     function csrfSafeMethod(method) {
@@ -261,37 +407,62 @@
     }
 </script>
 <script>
-        $('#thumb').click(function () {
+    $('#thumb').click(function () {
 
-            $.ajaxSetup({
-                beforeSend: function (xhr, settings) {
-                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
-                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
-                    }
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                 }
-            });
-            $.ajax({
-                type: 'POST',
-                url: "{% url 'PostLikeToggle' %}",
-                data: {
-                    'materi_id': "{{ materi_data.id }}",
-                    'session_id': "{{ session_id }}"
-                },
-                success: LikePost,
-                dataType: 'html'
-            });
+            }
         });
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'PostLikeToggle' %}",
+            data: {
+                'materi_id': "{{ materi_data.id }}",
+                'session_id': "{{ session_id }}"
+            },
+            success: LikePost,
+            dataType: 'html'
+        });
+    });
 
-
-        function LikePost(data, jqXHR) {
-            var data = $.parseJSON(data)
-            if (data['liked']) {
-                $('#thumbIcon').removeClass("fas fa-thumbs-up").addClass('far fa-thumbs-up')
-                document.getElementById("thumb").firstChild.data = " Sukai"
-            } else {
-                $('#thumbIcon').removeClass("far fa-thumbs-up").addClass('fas fa-thumbs-up')
-                document.getElementById("thumb").firstChild.data = " Disukai"
+    function postAddRating(rating_score) {
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                }
             }
+        });
+
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'rate-materi' %}",
+            data: {
+                'materi_id': "{{ materi_data.id }}",
+                'rating_score': rating_score
+            },
+            success: function (data) {
+                const response = JSON.parse(data)
+                currentRating = response.rating_score
+                makeUnHoverStar(currentRating)()
+                changeButtonIntoRated()
+
+            },
+            dataType: 'html'
+        });
+    }
+
+    function LikePost(data, jqXHR) {
+        var data = $.parseJSON(data)
+        if (data['liked']) {
+            $('#thumbIcon').removeClass("fas fa-thumbs-up").addClass('far fa-thumbs-up')
+            document.getElementById("thumb").firstChild.data = " Sukai"
+        } else {
+            $('#thumbIcon').removeClass("far fa-thumbs-up").addClass('fas fa-thumbs-up')
+            document.getElementById("thumb").firstChild.data = " Disukai"
         }
         function CitateAPA(text){
             alert('Hasil citasi :  '+text);
@@ -299,4 +470,4 @@
 
         
 </script>
-{% endblock extra_scripts %}
+{% endblock extra_scripts %}
\ No newline at end of file
diff --git a/app/templates/app/katalog_kontri.html b/app/templates/app/katalog_kontri.html
new file mode 100644
index 0000000000000000000000000000000000000000..3ddaf130261abbbb2ccc0c546f855e93b876f14d
--- /dev/null
+++ b/app/templates/app/katalog_kontri.html
@@ -0,0 +1,135 @@
+{% 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
diff --git a/app/templates/app/katalog_materi.html b/app/templates/app/katalog_materi.html
index 9782d19d98c75aafb5239947bcd5ffbfdb193cec..c4013e21b6549391bcea6a7281e3540f782a98d0 100644
--- a/app/templates/app/katalog_materi.html
+++ b/app/templates/app/katalog_materi.html
@@ -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
diff --git a/app/templates/req_materi.html b/app/templates/req_materi.html
index d0375d7169835ed89a4ac7a793783478a80bd35b..f4d071bab3f96663ae5575ecc911a502a5ad77ff 100644
--- a/app/templates/req_materi.html
+++ b/app/templates/req_materi.html
@@ -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,
         },
diff --git a/app/test_files/image_with_exif_data.gif b/app/test_files/image_with_exif_data.gif
new file mode 100644
index 0000000000000000000000000000000000000000..55609f4bf5f26b7569b7cab811489641cef6a882
Binary files /dev/null and b/app/test_files/image_with_exif_data.gif differ
diff --git a/app/tests.py b/app/tests.py
index 643dc7cea4f4a77ad586207d7a8a497475ba5dd7..adf63c619e866fda2bfc9044834a3dec7961a9d2 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -1,24 +1,27 @@
-import json
+import json, tempfile, os
 from io import StringIO
 
+from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.management import call_command
-from django.test import Client, RequestFactory, TestCase
 from django.db import IntegrityError
+from django.test import Client, TestCase
 from django.urls import resolve
 
 from administration.models import VerificationSetting, VerificationReport
 from administration.utils import id_generator
-from app.views import UploadMateriView
+from app.forms import SuntingProfilForm
+from app.views import UploadMateriView, add_rating_materi
 from authentication.models import User
-from .models import Category, Comment, Materi, Like, Rating
+from .models import Category, Comment, Materi, Like, Rating, ReqMaterial
 from .views import (DaftarKatalog, DashboardKontributorView, DetailMateri,
                     ProfilKontributorView, SuksesLoginAdminView,
                     SuksesLoginKontributorView, SuntingProfilView,
-                    ProfilAdminView, PostsView, SuntingProfilAdminView, RevisiMateriView)
+                    ProfilAdminView, PostsView, SuntingProfilAdminView, RevisiMateriView, ReqMateriView, KatalogPerKontributorView)
 from app.forms import SuntingProfilForm
+from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
 
 
 class DaftarKatalogTest(TestCase):
@@ -45,6 +48,60 @@ class DaftarKatalogTest(TestCase):
         resp = Materi.objects.get(id=materi.id)
         self.assertEqual(resp, materi)
 
+class DaftarKatalogPerKontributorTest(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()
+        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()
+        Materi(title="Materi 3", author="Agas", uploader=self.contributor2,
+               publisher="Kelas SC", descriptions="Deskripsi Materi 3",
+               status="APPROVE", cover=self.cover, content=self.content).save()
+
+        self.url = f"/profil/{self.contributor.email}/"
+
+    def test_katalog_per_kontributor_url_exist(self):
+        response = self.client.get(self.url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_katalog_per_kontributor_using_katalog_kontri_template(self):
+        response = self.client.get(self.url)
+        self.assertTemplateUsed(response, "app/katalog_kontri.html")
+
+    def test_katalog_per_kontributor_using_katalog_per_kontributor_func(self):
+        found = resolve(self.url)
+        self.assertEqual(found.func.__name__, KatalogPerKontributorView.as_view().__name__)
+
+    def test_katalog_per_kontributor_show_daftar_materi_kontributor(self):
+        response = self.client.get(self.url)
+
+        list_materi = Materi.objects.filter(uploader=self.contributor)
+        data = response.context_data['materi_list']
+        self.assertEqual(len(list_materi), len(data))
+
 
 class DetailMateriTest(TestCase):
     def setUp(self):
@@ -231,7 +288,7 @@ class PostsViewTest(TestCase):
     def test_url_resolves_to_posts_view(self):
         found = resolve(self.url)
         self.assertEqual(found.func.__name__, PostsView.as_view().__name__)
-    
+
     def test_returns_200_on_authenticated_access(self):
         response = self._request_as_user()
         self.assertEqual(response.status_code, 200)
@@ -267,14 +324,14 @@ class PostsViewTest(TestCase):
         }
 
         self.assertRegex(
-            str(response.content), 
+            str(response.content),
             rf'.*(<div id="post-{posts[2]}">)' + \
-                rf'.*(<div id="post-{posts[2]}-comment-{comments[2][0]}">)' + \
-                rf'.*(<div id="post-{posts[2]}-comment-{comments[2][1]}">)' + \
-                rf'.*(<div id="post-{posts[2]}-comment-{comments[2][2]}">)' + \
+            rf'.*(<div id="post-{posts[2]}-comment-{comments[2][0]}">)' + \
+            rf'.*(<div id="post-{posts[2]}-comment-{comments[2][1]}">)' + \
+            rf'.*(<div id="post-{posts[2]}-comment-{comments[2][2]}">)' + \
             rf'.*(<div id="post-{posts[1]}">)' + \
             rf'.*(<div id="post-{posts[0]}">)' + \
-                rf'.*(<div id="post-{posts[0]}-comment-{comments[0][0]}">)'
+            rf'.*(<div id="post-{posts[0]}-comment-{comments[0][0]}">)'
         )
 
 
@@ -358,6 +415,7 @@ class UploadPageTest(TestCase):
         # Negative tests
         self.assertNotContains(response, "anything")
 
+
 class DashboardKontributorViewTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -419,6 +477,7 @@ class DashboardKontributorViewTest(TestCase):
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 403)
 
+
 class ProfilAdminTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -457,6 +516,7 @@ class ProfilAdminTest(TestCase):
         # Logout
         self.client.logout()
 
+
 class ProfilKontributorTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -762,7 +822,7 @@ class LikeMateriTest(TestCase):
 
     def test_like_materi(self):
         # Verify that materi doesn't have any like to start with
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
 
         # Like a materi
@@ -774,12 +834,12 @@ class LikeMateriTest(TestCase):
             'session_id': session_id
         }
         ajax_response = Client().post(self.url_like, payload)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 1)
 
     def test_unlike_materi(self):
         # Verify that materi doesn't have any like to start with
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
 
         # Like a materi
@@ -791,7 +851,7 @@ class LikeMateriTest(TestCase):
             'session_id': session_id
         }
         ajax_response = Client().post(self.url_like, payload)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 1)
 
         # Unlike a materi
@@ -803,12 +863,12 @@ class LikeMateriTest(TestCase):
             'session_id': session_id
         }
         ajax_response = Client().post(self.url_like, payload)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
 
     def test_2_client_like_materi(self):
         # Verify that materi doesn't have any like to start with
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
 
         # Client 1 like a materi
@@ -820,7 +880,7 @@ class LikeMateriTest(TestCase):
             'session_id': session_id
         }
         ajax_response = Client().post(self.url_like, payload)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 1)
 
         # Client 2 like a materi
@@ -832,12 +892,12 @@ class LikeMateriTest(TestCase):
             'session_id': session_id
         }
         ajax_response = Client().post(self.url_like, payload)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 2)
 
     def test_incomplete_like_parameter(self):
         # Verify that materi doesn't have any like to start with
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
 
         # missing session id
@@ -848,7 +908,7 @@ class LikeMateriTest(TestCase):
         }
         ajax_response = Client().post(self.url_like, payload)
         ajax_response = json.loads(ajax_response.content)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
         self.assertEqual(ajax_response.get("success", None), False)
 
@@ -860,7 +920,7 @@ class LikeMateriTest(TestCase):
         }
         ajax_response = Client().post(self.url_like, payload)
         ajax_response = json.loads(ajax_response.content)
-        num_of_likes = Like.objects.filter(materi = self.materi1).count()
+        num_of_likes = Like.objects.filter(materi=self.materi1).count()
         self.assertEqual(num_of_likes, 0)
         self.assertEqual(ajax_response.get("success", None), False)
 
@@ -906,6 +966,7 @@ class ViewMateriStatissticsTest(TestCase):
         num_of_views = self.materi1.baca.all().count()
         self.assertEqual(num_of_views, 2)
 
+
 class DownloadMateriStatissticsTest(TestCase):
     def setUp(self):
         self.contributor_credential = {
@@ -947,6 +1008,7 @@ class DownloadMateriStatissticsTest(TestCase):
         num_of_downloads = self.materi1.unduh.all().count()
         self.assertEqual(num_of_downloads, 2)
 
+
 class RevisiMateriTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -1019,7 +1081,7 @@ class GenerateDummyCommandTest(TestCase):
         for num_of_materi in self.material_numbers:
             call_command("generatedummy", num_of_materi, stdout=self.stdout)
             self.assertIn(
-                f"Successfully created {num_of_materi} materi\n",
+                f"Successfully created {num_of_materi} materi",
                 self.stdout.getvalue()
             )
 
@@ -1043,7 +1105,7 @@ class RemoveDummyCommandTest(TestCase):
 
         call_command("removedummy", stdout=stdout)
 
-        self.assertEqual("Successfully remove all dummy object\n", stdout.getvalue())
+        self.assertIn("Successfully remove all dummy object", stdout.getvalue())
         self.assertEqual(User.objects.count(), 0)
         self.assertEqual(Category.objects.count(), 0)
         self.assertEqual(Materi.objects.count(), 0)
@@ -1053,7 +1115,6 @@ class RemoveDummyCommandTest(TestCase):
 
 class RatingMateriTest(TestCase):
     def setUp(self):
-        self.url = '/administration/'
         self.contributor_credential = {
             "email": "kontributor@gov.id",
             "password": id_generator()
@@ -1087,6 +1148,8 @@ class RatingMateriTest(TestCase):
                status="APPROVE", cover=self.cover, content=self.content).save()
         self.materi1 = Materi.objects.all()[0]
         self.materi2 = Materi.objects.all()[1]
+        self.url_rate = '/materi/rate/'
+        self.url_materi = '/materi/{}/'.format(self.materi1.id)
 
     def test_rating_model_can_be_created_with_proper_parameter(self):
         Rating(materi=self.materi1, user=self.user_one, score=5).save()
@@ -1143,3 +1206,201 @@ class RatingMateriTest(TestCase):
     def test_score_in_rating_should_not_be_null(self):
         with self.assertRaises(TypeError):
             Rating(materi=self.materi1, user=self.user_one).save()
+
+    def test_rating_materi_url_use_add_rating_materi_function(self):
+        found = resolve(self.url_rate)
+        self.assertEqual(found.func, add_rating_materi)
+
+    def test_rating_materi_get_method_should_return_403_forbidden(self):
+        response = self.client.get(self.url_rate)
+        response_json = json.loads(response.content)
+        self.assertEqual(response_json.get("success", None), False)
+        self.assertEqual(response_json.get("msg", None), "Forbidden")
+        self.assertEqual(response.status_code, 403)
+
+    def test_rating_materi_post_not_authenticated_should_return_403_forbidden(self):
+        response = self.client.post(self.url_rate, {'materi_id': 1, 'rating_score': 5})
+        response_json = json.loads(response.content)
+        self.assertEqual(response_json.get("success", None), False)
+        self.assertEqual(response_json.get("msg", None), "Forbidden")
+        self.assertEqual(response.status_code, 403)
+
+    def test_rating_materi_not_authenticated_post_wrong_param_should_return_403_forbidden(self):
+        for data in [{}, {'materi_id': 1}, {'rating_score': 1}, {'rating_score': 'STRING', 'materi_id': 'STRING'}]:
+            response = self.client.post(self.url_rate, data)
+            response_json = json.loads(response.content)
+            self.assertEqual(response_json.get("success", None), False)
+            self.assertEqual(response_json.get("msg", None), "Forbidden")
+            self.assertEqual(response.status_code, 403)
+
+    def test_rating_materi_authenticated_post_missing_param(self):
+        self.client.login(**self.user_one_credential)
+        for data in [{'rating_score': 1}, {'materi_id': 1}, {}]:
+            response = self.client.post(self.url_rate, data)
+            response_json = json.loads(response.content)
+            self.assertEqual(response_json.get("success", None), False)
+            self.assertEqual(response_json.get("msg", None), "Missing param")
+            self.assertEqual(response.status_code, 422)
+
+    def test_rating_materi_authenticated_materi_id_doesnt_exist_should_return_422(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.post(self.url_rate, {'materi_id': 123456, 'rating_score': 5})
+        response_json = json.loads(response.content)
+        self.assertEqual(response_json.get("success", None), False)
+        self.assertEqual(response_json.get("msg", None), "Materi does not exist")
+        self.assertEqual(response.status_code, 422)
+
+    def test_rating_materi_authenticated_param_wrong_data_type_should_return_422(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.post(self.url_rate, {'materi_id': "STRING", 'rating_score': 5})
+        response_json = json.loads(response.content)
+        self.assertEqual(response_json.get("success", None), False)
+        self.assertEqual(response_json.get("msg", None), "materi_id must be an integer")
+        self.assertEqual(response.status_code, 422)
+
+        response = self.client.post(self.url_rate, {'materi_id': 1, 'rating_score': "STRING"})
+        response_json = json.loads(response.content)
+        self.assertEqual(response_json.get("success", None), False)
+        self.assertEqual(response_json.get("msg", None), "rating_score must be an integer")
+        self.assertEqual(response.status_code, 422)
+
+    def test_rating_score_should_be_between_1_and_5(self):
+        self.client.login(**self.user_one_credential)
+        for i in range(1, 6):
+            Rating.objects.all().delete()
+            response = self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': i})
+            response_json = json.loads(response.content)
+            # self.assertEqual(response_json.get("success", None), True)
+            self.assertEqual(response_json.get("msg", None), "Rating successfully created")
+            self.assertEqual(response.status_code, 201)
+
+        for i in [-100, -7, -6, -1, 0, 6, 7, 100]:
+            Rating.objects.all().delete()
+            response = self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': i})
+            response_json = json.loads(response.content)
+            # self.assertEqual(response_json.get("success", None), False)
+            self.assertEqual(response_json.get("msg", None), "Rating must be an integer from 1 to 5")
+            self.assertEqual(response.status_code, 422)
+
+    def test_user_should_not_able_to_rate_materi_twice(self):
+        self.client.login(**self.user_one_credential)
+        Rating.objects.all().delete()
+        self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': 1})
+        response = self.client.post(self.url_rate, {'materi_id': self.materi1.id, 'rating_score': 2})
+        response_json = json.loads(response.content)
+        # self.assertEqual(response_json.get("success", None), False)
+        self.assertEqual(response_json.get("msg", None), "Rating already exist")
+        self.assertEqual(response.status_code, 409)
+
+    def test_user_authenticated_visit_unrated_should_get_0_materi_rating_score_context(self):
+        self.client.login(**self.user_one_credential)
+        response = self.client.get(self.url_materi)
+        self.assertEqual(0, response.context.get('materi_rating_score'))
+
+    def test_user_not_authenticated_visit_unrated_should_get_0_materi_rating_score_context(self):
+        response = self.client.get(self.url_materi)
+        self.assertEqual(0, response.context.get('materi_rating_score'))
+
+    def test_user_authenticated_visit_rated_should_get_correct_materi_rating_score_context(self):
+        self.client.login(**self.user_one_credential)
+        Rating(materi=self.materi1, user=self.user_one, score=1).save()
+        response = self.client.get(self.url_materi)
+        self.assertEqual(1, response.context.get('materi_rating_score'))
+
+class fileManagementUtilTest(TestCase):
+    def setUp(self):
+        self.filename = "image_with_exif_data.gif"
+        self.file_content = open(settings.BASE_DIR + "/app/test_files/" +
+                self.filename, "rb").read()
+
+    def test_get_random_filename_isCorrect(self):
+        generated_name = get_random_filename(self.filename)
+
+        self.assertTrue(generated_name != self.filename)
+        # 40 from 36 expected name length + 4 from extension
+        self.assertEqual(len(generated_name), 40)
+        self.assertTrue(generated_name[-4:] == ".gif")
+
+    def test_remove_image_exifdata_isCorrect(self):
+        with tempfile.TemporaryDirectory() as d:
+            image_with_exif_data_path = os.path.join(d, self.filename)
+            img = open(image_with_exif_data_path, "wb")
+            img.write(self.file_content)
+            img.close()
+
+            remove_image_exifdata(image_with_exif_data_path)
+            sanitized_img = open(image_with_exif_data_path, "rb").read()
+
+            self.assertTrue(len(sanitized_img) < len(self.file_content))
+            self.assertTrue(b'<exif:' not in sanitized_img)
+
+class RequestMateriTest(TestCase):
+    def setUp(self):
+        self.client = Client()
+        self.admin_credential = {
+            "email": "admin@gov.id",
+            "password": "passwordtest"
+        }
+        self.contributor_credential = {
+            "email": "kontributor@gov.id",
+            "password": "passwordtest"
+        }
+        self.anonymous_credential = {
+            "email": "anonymous@gov.id",
+            "password": "passwordtest"
+        }
+        self.admin = get_user_model().objects.create_user(
+            **self.admin_credential, name="Admin", is_admin=True)
+        self.contributor = get_user_model().objects.create_user(
+            **self.contributor_credential, name="Kontributor", is_contributor=True)
+        self.anonymous = get_user_model().objects.create_user(
+            **self.anonymous_credential, name="Anonymous"
+        )
+        self.url = "/req-materi/"
+        self.template_name = "req_materi.html"
+
+    def test_req_materi_url_resolves_to_get_req_materi_view(self):
+        found = resolve(self.url)
+        self.assertEqual(found.func.__name__, ReqMateriView.as_view().__name__)
+
+    def test_uses_req_material_template(self):
+        self.client.login(**self.contributor_credential)
+        response = self.client.get(self.url)
+        self.assertTemplateUsed(response, self.template_name)
+        self.client.logout()
+
+    def test_redirect_to_login_page_is_not_authenticated(self):
+        response = self.client.get(self.url)
+
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response['location'], '/login/')
+
+    def test_saving_and_retrieving_material_requests(self):
+        first_material_request = ReqMaterial(title="Material 1").save()
+        second_material_request = ReqMaterial(title="Material 2").save()
+        saved_material_request = ReqMaterial.objects.all()
+        self.assertEqual(saved_material_request.count(), 2)
+
+    def test_can_save_a_POST_request_and_return_correct_response_message(self):
+        self.client.login(**self.contributor_credential)
+
+        response = self.client.post(self.url,
+                         data={
+                             'title': 'Requested Material'
+                         })
+        self.assertEqual(ReqMaterial.objects.count(), 1)
+
+        new_material_request = ReqMaterial.objects.first()
+        self.assertEqual(new_material_request.title, 'Requested Material')
+
+        self.assertIn('Permintaan materi berhasil dikirimkan', response.content.decode())
+        self.client.logout()
+
+    def test_given_no_title_should_not_save_request_and_return_correct_response_message(self):
+        self.client.login(**self.contributor_credential)
+
+        response = self.client.post(self.url)
+        self.assertEqual(ReqMaterial.objects.count(), 0)
+
+        self.assertIn('Missing parameter', response.content.decode())
+        self.client.logout()
diff --git a/app/urls.py b/app/urls.py
index ad34cef8f45adea6ebd29e63c176a3a5f73371a1..a63a2aabfc996ec656e35b5a86e242018275b464 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -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"),
 ]
diff --git a/app/utils/fileManagementUtil.py b/app/utils/fileManagementUtil.py
new file mode 100644
index 0000000000000000000000000000000000000000..d693b01fea0cbd035f0fa6e7ef347cdac1d0373b
--- /dev/null
+++ b/app/utils/fileManagementUtil.py
@@ -0,0 +1,17 @@
+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   
+
+
diff --git a/app/views.py b/app/views.py
index 96d6e80af05f1263b41c9962b35f177506ed8c6a..8a5ef310d506b74c73c38bfef6000f9e5b7a1302 100644
--- a/app/views.py
+++ b/app/views.py
@@ -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)
 
         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-admin/")
         else:
             context = self.get_context_data(**kwargs)
@@ -403,9 +493,6 @@ class ReqMateriView(TemplateView):
     def dispatch(self, request, *args, **kwargs):
         if request.user.is_authenticated == False:
             return HttpResponseRedirect("/login/")
-            #raise PermissionDenied(request)
-        # else if not request.user.is_admin:
-        #     raise PermissionDenied(request)
         return super(ReqMateriView, self).dispatch(request, *args, **kwargs)
 
     def get_context_data(self, **kwargs):
@@ -415,39 +502,14 @@ class ReqMateriView(TemplateView):
 
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
-
-        current_user = self.request.user
-        context["user"] = current_user
-
-        context["form"] = SuntingProfilForm(instance=current_user)
         return self.render_to_response(context)
 
     def post(self, request, *args, **kwargs):
-        if request.user.is_authenticated == False:
-            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()
-            return HttpResponseRedirect("/profil-admin/")
-        else:
-            context = self.get_context_data(**kwargs)
-            context["form"] = form
-            return self.render_to_response(context)
-
-def post_req_materi(request):
-    if request.method == 'POST':
-        #return JsonResponse({"success": True, "msg": "Permintaan materi berhasil dikirimkan"})
         title = request.POST.get('title', None)
         if title is None:
             return JsonResponse({"success": False, "msg": "Missing parameter"})
         ReqMaterial(title=title).save()
         return JsonResponse({"success": True, "msg": "Permintaan materi berhasil dikirimkan"})
-    else:
-        return JsonResponse({"success": False, "msg": "Unsuported method"})
 
 
 class SuksesLoginKontributorView(TemplateView):
@@ -525,7 +587,7 @@ class PostsView(TemplateView):
 class RevisiMateriView(TemplateView):
     template_name = "revisi.html"
 
-    def dispatch(self, request, *args, **kwargs):        
+    def dispatch(self, request, *args, **kwargs):
         if not request.user.is_contributor:
             raise PermissionDenied(request)
         return super(RevisiMateriView, self).dispatch(request, *args, **kwargs)
diff --git a/authentication/tests.py b/authentication/tests.py
index 54d5cf9f8bcdbd05bf07fed0263ada910710ad77..c63f6f98e418fa4c3ee39eb0f1c186fd030fdda4 100644
--- a/authentication/tests.py
+++ b/authentication/tests.py
@@ -74,7 +74,7 @@ class UserModelTest(TestCase):
 class LoginPageContributorTest(TestCase):
     def setUp(self):
         self.client = Client()
-        self.admin = User.objects.create_contributor(email="kontributor@gov.id",
+        self.kontributor = User.objects.create_contributor(email="kontributor@gov.id",
                                                      password="kontributor")
         self.url = "/login/"
         self.view = Login
@@ -86,34 +86,34 @@ class LoginPageContributorTest(TestCase):
             "wrong_email_or_password": "Email atau Password anda salah.",
         }
 
-    def test_login_admin_view(self):
+    def test_login_kontributor_view(self):
         found = resolve(self.url)
         self.assertEqual(found.func.__name__, self.view.as_view().__name__)
 
-    def test_login_admin_template(self):
+    def test_login_kontributor_template(self):
         # Test
         response = self.client.get(self.url)
         self.assertTemplateUsed(response, self.template_name)
 
-    def test_login_admin_url(self):
+    def test_login_kontributor_url(self):
         # Test
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 200)
 
-    def test_login_admin_title(self):
+    def test_login_kontributor_title(self):
         response = self.client.get(self.url)
 
         # Positive tests
         self.assertContains(response, "Halo, kontributor")
 
-    def test_login_admin_form_field(self):
+    def test_login_kontributor_form_field(self):
         response = self.client.get(self.url)
 
         # Positive tests
         self.assertContains(response, "Email")
         self.assertContains(response, "Kata Sandi")
 
-    def test_admin_login_missing_email_or_password(self):
+    def test_kontributor_login_missing_email_or_password(self):
         response = self.client.post(self.url, {"email": "kontributor@gov.id"})
         self.assertIn("error_message", response.context_data)
         self.assertIn(self.error_message["empty_email_or_password"],
@@ -123,7 +123,7 @@ class LoginPageContributorTest(TestCase):
         self.assertIn(self.error_message["empty_email_or_password"],
                       response.context_data["error_message"])
 
-    def test_admin_login_wrong_email_or_password(self):
+    def test_kontributor_login_wrong_email_or_password(self):
         # Wrong password
         response = self.client.post(
             self.url, {"email": "kontributor@gov.id", "pass": "kontributor1"})
@@ -143,7 +143,7 @@ class LoginPageContributorTest(TestCase):
         self.assertIn(self.error_message["wrong_email_or_password"],
                       response.context_data["error_message"])
 
-    def test_admin_login(self):
+    def test_kontributor_login(self):
         # 302 meaning successful login and redirected
         expected_redirect_url = "/sukses-kontributor/"
         response = self.client.post(
@@ -151,6 +151,19 @@ class LoginPageContributorTest(TestCase):
         self.assertEqual(302, response.status_code)
         self.assertEqual(response.url, expected_redirect_url)
 
+    def test_kontributor_visit_login_after_auth(self):
+        # 302 meaning successful login and redirected
+        expected_redirect_url = "/sukses-kontributor/"
+        response = self.client.post(
+            self.url, self.login_credential)
+        self.assertEqual(302, response.status_code)
+        self.assertEqual(response.url, expected_redirect_url)
+        response = self.client.get(
+            self.url
+        )
+        self.assertEqual(302, response.status_code)
+        self.assertEqual(response.url, expected_redirect_url)
+
 
 class LoginPageAdminTest(TestCase):
     def setUp(self):
@@ -230,3 +243,17 @@ class LoginPageAdminTest(TestCase):
             self.url, self.login_credential)
         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/"
+        response = self.client.post(
+            self.url, self.login_credential)
+        self.assertEqual(302, response.status_code)
+        self.assertEqual(response.url, expected_redirect_url)
+        response = self.client.get(
+            self.url
+        )
+        self.assertEqual(302, response.status_code)
+        self.assertEqual(response.url, expected_redirect_url)
diff --git a/authentication/views.py b/authentication/views.py
index 6f67ccb35e12ab7371f48a33df5569fdf999da3c..f5e7d82c2f80b0a0f57d4fbc83819f5fddcf60e6 100644
--- a/authentication/views.py
+++ b/authentication/views.py
@@ -4,6 +4,16 @@ from django.views.generic import TemplateView
 
 
 class Login(TemplateView):
+    
+    def dispatch(self, request, *args, **kwargs):
+        if request.user.is_authenticated:
+            if request.user.is_admin:
+                redirect_to = "/sukses-admin/"
+            elif request.user.is_contributor:
+                redirect_to = "/sukses-kontributor/"
+            return HttpResponseRedirect(redirect_to)
+        return super(Login, self).dispatch(request, *args, **kwargs)
+
     def get_template_names(self):
         if self.request.path == "/login_admin/":
             template_name = "login_admin.html"