diff --git a/administration/migrations/0001_initial.py b/administration/migrations/0001_initial.py
index de7061bb5b822b1bdb73537ed6f3ed02053028ff..e9366599d40d7e904ee324478b2256d478b5cfbb 100644
--- a/administration/migrations/0001_initial.py
+++ b/administration/migrations/0001_initial.py
@@ -1,7 +1,9 @@
-# Generated by Django 3.0.3 on 2020-05-08 14:42
+# Generated by Django 3.1 on 2020-10-30 15:29
 
-import django.contrib.postgres.fields.jsonb
+from django.conf import settings
 from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
 
 
 class Migration(migrations.Migration):
@@ -9,28 +11,40 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
+        ('app', '0001_initial'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
+        migrations.CreateModel(
+            name='VerificationSetting',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=250)),
+                ('description', models.TextField(default='')),
+                ('archived', models.BooleanField(default=False)),
+                ('archived_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
         migrations.CreateModel(
             name='VerificationReport',
             fields=[
-                ('id', models.AutoField(auto_created=True,
-                                        primary_key=True, serialize=False, verbose_name='ID')),
-                ('report', django.contrib.postgres.fields.jsonb.JSONField()),
-                ('timestamp', models.DateTimeField(auto_now_add=True)),
-                ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), (
-                    'DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan')], default='PENDING', max_length=30)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('report', models.JSONField()),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30)),
+                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
+                ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
             ],
         ),
         migrations.CreateModel(
-            name='VerificationSetting',
+            name='DeletionHistory',
             fields=[
-                ('id', models.AutoField(auto_created=True,
-                                        primary_key=True, serialize=False, verbose_name='ID')),
-                ('title', models.CharField(max_length=250)),
-                ('description', models.TextField(default='')),
-                ('archived', models.BooleanField(default=False)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('deleted_user_name', models.CharField(max_length=150)),
+                ('deleted_user_role', models.CharField(max_length=150)),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('deletor_admin', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
             ],
         ),
     ]
diff --git a/administration/migrations/0002_verificationreport_materi.py b/administration/migrations/0002_verificationreport_materi.py
deleted file mode 100644
index a1b8db8f58940d8965b879efd43c80f06b4bca72..0000000000000000000000000000000000000000
--- a/administration/migrations/0002_verificationreport_materi.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-08 14:42
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('app', '0001_initial'),
-        ('administration', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='verificationreport',
-            name='materi',
-            field=models.ForeignKey(
-                null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.Materi'),
-        ),
-    ]
diff --git a/administration/migrations/0003_verificationreport_user.py b/administration/migrations/0003_verificationreport_user.py
deleted file mode 100644
index 236da3cd510b6203fbca8812fbcf4a33dc0da15c..0000000000000000000000000000000000000000
--- a/administration/migrations/0003_verificationreport_user.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-08 14:42
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('administration', '0002_verificationreport_materi'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='verificationreport',
-            name='user',
-            field=models.ForeignKey(
-                null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
-        ),
-    ]
diff --git a/administration/migrations/0004_auto_20200517_1713.py b/administration/migrations/0004_auto_20200517_1713.py
deleted file mode 100644
index 8189cd965de7bc2ddf6453176ec3ec75c07fc7f9..0000000000000000000000000000000000000000
--- a/administration/migrations/0004_auto_20200517_1713.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-17 10:13
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('administration', '0003_verificationreport_user'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='verificationreport',
-            name='timestamp',
-            field=models.DateTimeField(default=django.utils.timezone.now),
-        ),
-    ]
diff --git a/administration/migrations/0005_deletionhistory.py b/administration/migrations/0005_deletionhistory.py
deleted file mode 100644
index a23c37e1428e4b164cfbfa9b53a58b092cb63790..0000000000000000000000000000000000000000
--- a/administration/migrations/0005_deletionhistory.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 3.0.3 on 2020-06-03 12:57
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('administration', '0004_auto_20200517_1713'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='DeletionHistory',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('deleted_user_name', models.CharField(max_length=150)),
-                ('deleted_user_role', models.CharField(max_length=150)),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-                ('deletor_admin', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
-            ],
-        ),
-    ]
diff --git a/administration/migrations/0005_verificationsetting_archived_by.py b/administration/migrations/0005_verificationsetting_archived_by.py
deleted file mode 100644
index dadfd72ea51694f051a7730b60c37bf32210d354..0000000000000000000000000000000000000000
--- a/administration/migrations/0005_verificationsetting_archived_by.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 3.0.3 on 2020-06-04 00:09
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('administration', '0004_auto_20200517_1713'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='verificationsetting',
-            name='archived_by',
-            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
-        ),
-    ]
diff --git a/administration/migrations/0006_merge_20200604_0718.py b/administration/migrations/0006_merge_20200604_0718.py
deleted file mode 100644
index 92c7b037550593a95bfd8bdd25312fc5568f1bd3..0000000000000000000000000000000000000000
--- a/administration/migrations/0006_merge_20200604_0718.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 3.0.3 on 2020-06-04 00:18
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('administration', '0005_deletionhistory'),
-        ('administration', '0005_verificationsetting_archived_by'),
-    ]
-
-    operations = [
-    ]
diff --git a/administration/migrations/0007_auto_20200929_1218.py b/administration/migrations/0007_auto_20200929_1218.py
deleted file mode 100644
index 735e71572df51f52e11ffe98a319fdceb3f46ba7..0000000000000000000000000000000000000000
--- a/administration/migrations/0007_auto_20200929_1218.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-09-29 05:18
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('administration', '0006_merge_20200604_0718'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='verificationreport',
-            name='report',
-            field=models.JSONField(),
-        ),
-    ]
diff --git a/administration/migrations/0008_auto_20201009_1829.py b/administration/migrations/0008_auto_20201009_1829.py
deleted file mode 100644
index 0302d90c1ccf01e6a7a1b313ed4ce7ee95c79573..0000000000000000000000000000000000000000
--- a/administration/migrations/0008_auto_20201009_1829.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 11:29
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('administration', '0007_auto_20200929_1218'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='verificationreport',
-            name='status',
-            field=models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30),
-        ),
-    ]
diff --git a/app/migrations/0001_initial.py b/app/migrations/0001_initial.py
index 51a62f576bc233af8f04a305c6be41ee5073e1fe..2e76e78466a5b35f01834a00102bee3798f0b177 100644
--- a/app/migrations/0001_initial.py
+++ b/app/migrations/0001_initial.py
@@ -1,6 +1,12 @@
-# Generated by Django 3.0.3 on 2020-05-08 14:42
+# Generated by Django 3.1 on 2020-10-30 15:28
 
+import app.models
+from django.conf import settings
+import django.contrib.postgres.search
+import django.core.validators
 from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
 
 
 class Migration(migrations.Migration):
@@ -8,44 +14,215 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
         migrations.CreateModel(
             name='Category',
             fields=[
-                ('id', models.AutoField(auto_created=True,
-                                        primary_key=True, serialize=False, verbose_name='ID')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('name', models.CharField(max_length=20)),
-                ('description', models.CharField(max_length=20)),
+                ('description', models.TextField(default='')),
+                ('archived', models.BooleanField(default=False)),
+                ('archived_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
             ],
         ),
         migrations.CreateModel(
             name='Comment',
             fields=[
-                ('id', models.AutoField(auto_created=True,
-                                        primary_key=True, serialize=False, verbose_name='ID')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('username', models.CharField(max_length=100)),
-                ('profile', models.CharField(default='56acdf', max_length=100)),
-                ('comment', models.CharField(default='comments', max_length=150)),
+                ('profile', models.CharField(default=app.models.getRandomColor, max_length=100)),
+                ('comment', models.CharField(default='comments', max_length=240)),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DownloadStatistics',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('downloader', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='riwayat_unduh', to=settings.AUTH_USER_MODEL)),
             ],
         ),
         migrations.CreateModel(
             name='Materi',
             fields=[
-                ('id', models.AutoField(auto_created=True,
-                                        primary_key=True, serialize=False, verbose_name='ID')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('deleted_at', models.DateTimeField(blank=True, null=True)),
                 ('cover', models.ImageField(upload_to='')),
                 ('content', models.FileField(upload_to='')),
-                ('title', models.CharField(default='title', max_length=50)),
-                ('author', models.CharField(default='author', max_length=30)),
-                ('publisher', models.CharField(default='publiser', max_length=30)),
+                ('title', models.CharField(default='Judul', max_length=50)),
+                ('author', models.CharField(default='Penyusun', max_length=30)),
+                ('publisher', models.CharField(default='Penerbit', max_length=30)),
+                ('release_year', models.IntegerField(default=app.models.current_year)),
                 ('pages', models.IntegerField(default=0)),
-                ('descriptions', models.TextField(default='descriptions')),
-                ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), (
-                    'DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan')], default='PENDING', max_length=30)),
-                ('date_added', models.DateTimeField(auto_now_add=True)),
+                ('descriptions', models.TextField(default='Deskripsi')),
+                ('status', models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30)),
+                ('date_created', models.DateTimeField(default=django.utils.timezone.now)),
+                ('date_modified', models.DateTimeField(auto_now=True)),
+                ('yt_video_id', models.CharField(blank=True, max_length=100, null=True)),
+                ('_search_vector', django.contrib.postgres.search.SearchVectorField(editable=False, null=True)),
                 ('categories', models.ManyToManyField(to='app.Category')),
+                ('uploader', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='ReqMaterial',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=100)),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='SubmitVisitor',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('user_id', models.CharField(max_length=50)),
+                ('email', models.CharField(max_length=50)),
+                ('msg', models.CharField(max_length=100)),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='SubscribeModel',
+            fields=[
+                ('sys_id', models.AutoField(primary_key=True, serialize=False)),
+                ('email', models.EmailField(blank=True, max_length=200, unique=True)),
+                ('status', models.CharField(blank=True, max_length=64)),
+                ('created_date', models.DateTimeField(blank=True)),
+                ('updated_date', models.DateTimeField(blank=True)),
+            ],
+            options={
+                'db_table': 'app_subscribe',
+            },
+        ),
+        migrations.CreateModel(
+            name='ViewStatistics',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='baca', to='app.materi')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Review',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('username', models.CharField(max_length=100)),
+                ('profile', models.CharField(default=app.models.getRandomColor, max_length=100)),
+                ('review', models.TextField(default='review')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
+                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='RatingContributor',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(auto_now=True)),
+                ('score', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='LikeComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Like',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='LaporanMateri',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('laporan', models.TextField(default='', validators=[django.core.validators.MinValueValidator(30), django.core.validators.MaxValueValidator(120)])),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('is_rejected', models.BooleanField(default=False)),
+                ('materi', models.ForeignKey(max_length=120, on_delete=django.db.models.deletion.CASCADE, to='app.materi')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DummyViewStatistics',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.viewstatistics')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DummyLike',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.like')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DummyDownloadStatistics',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.downloadstatistics')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DummyComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.comment')),
+            ],
+        ),
+        migrations.AddField(
+            model_name='downloadstatistics',
+            name='materi',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unduh', to='app.materi'),
+        ),
+        migrations.CreateModel(
+            name='DislikeComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
+            ],
+        ),
+        migrations.AddField(
+            model_name='comment',
+            name='materi',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi'),
+        ),
+        migrations.AddField(
+            model_name='comment',
+            name='user',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.CreateModel(
+            name='Rating',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('score', models.IntegerField()),
+                ('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.materi')),
+                ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
+            options={
+                'unique_together': {('materi', 'user')},
+            },
         ),
     ]
diff --git a/app/migrations/0002_auto_20200508_2142.py b/app/migrations/0002_auto_20200508_2142.py
deleted file mode 100644
index dfeeb8495f7ea4ab0e77001b5dec910341522e0e..0000000000000000000000000000000000000000
--- a/app/migrations/0002_auto_20200508_2142.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-08 14:42
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='materi',
-            name='uploader',
-            field=models.ForeignKey(
-                null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
-        ),
-        migrations.AddField(
-            model_name='comment',
-            name='materi',
-            field=models.ForeignKey(
-                null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.Materi'),
-        ),
-        migrations.AddField(
-            model_name='comment',
-            name='user',
-            field=models.ForeignKey(
-                blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
-        ),
-    ]
diff --git a/app/migrations/0003_auto_20200509_2108.py b/app/migrations/0003_auto_20200509_2108.py
deleted file mode 100644
index 844d9d85ad27f5f5b3102597c4e8e8ef6173f8fd..0000000000000000000000000000000000000000
--- a/app/migrations/0003_auto_20200509_2108.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-09 14:08
-
-import app.models
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0002_auto_20200508_2142'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='comment',
-            name='profile',
-            field=models.CharField(default=app.models.getRandomColor, max_length=100),
-        ),
-    ]
diff --git a/app/migrations/0004_like.py b/app/migrations/0004_like.py
deleted file mode 100644
index 7240ea7ac31c62b66b4ef2cdcd9790b2f6d2e143..0000000000000000000000000000000000000000
--- a/app/migrations/0004_like.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-12 08:34
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0003_auto_20200509_2108'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Like',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(auto_now_add=True)),
-                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.Materi')),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0005_like_session_id.py b/app/migrations/0005_like_session_id.py
deleted file mode 100644
index 8520a4d3769e9bf68d9ed2fad52454391df55777..0000000000000000000000000000000000000000
--- a/app/migrations/0005_like_session_id.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-12 09:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0004_like'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='like',
-            name='session_id',
-            field=models.CharField(default='', max_length=32),
-            preserve_default=False,
-        ),
-    ]
diff --git a/app/migrations/0006_downloadstatistics_viewstatistics.py b/app/migrations/0006_downloadstatistics_viewstatistics.py
deleted file mode 100644
index 2b29fb125a53831e97d5888d6d878894dcd580d3..0000000000000000000000000000000000000000
--- a/app/migrations/0006_downloadstatistics_viewstatistics.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-13 10:34
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0005_like_session_id'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='ViewStatistics',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(auto_now_add=True)),
-                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='baca', to='app.Materi')),
-            ],
-        ),
-        migrations.CreateModel(
-            name='DownloadStatistics',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(auto_now_add=True)),
-                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unduh', to='app.Materi')),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0007_auto_20200516_1743.py b/app/migrations/0007_auto_20200516_1743.py
deleted file mode 100644
index 5a0973432393117a5aa0435217f0244c49a9cfe3..0000000000000000000000000000000000000000
--- a/app/migrations/0007_auto_20200516_1743.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-16 10:43
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0006_downloadstatistics_viewstatistics'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='materi',
-            name='date_added',
-        ),
-        migrations.AddField(
-            model_name='materi',
-            name='date_created',
-            field=models.DateTimeField(default=django.utils.timezone.now),
-        ),
-        migrations.AddField(
-            model_name='materi',
-            name='date_modified',
-            field=models.DateTimeField(auto_now=True),
-        ),
-        migrations.AlterField(
-            model_name='downloadstatistics',
-            name='timestamp',
-            field=models.DateTimeField(default=django.utils.timezone.now),
-        ),
-        migrations.AlterField(
-            model_name='like',
-            name='timestamp',
-            field=models.DateTimeField(default=django.utils.timezone.now),
-        ),
-        migrations.AlterField(
-            model_name='viewstatistics',
-            name='timestamp',
-            field=models.DateTimeField(default=django.utils.timezone.now),
-        ),
-    ]
diff --git a/app/migrations/0008_auto_20200518_1919.py b/app/migrations/0008_auto_20200518_1919.py
deleted file mode 100644
index df20b16663a25a3d064ff3f30b01012f1ecf150c..0000000000000000000000000000000000000000
--- a/app/migrations/0008_auto_20200518_1919.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-18 12:19
-
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0007_auto_20200516_1743'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='comment',
-            name='timestamp',
-            field=models.DateTimeField(default=django.utils.timezone.now),
-        ),
-        migrations.CreateModel(
-            name='DummyViewStatistics',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.ViewStatistics')),
-            ],
-        ),
-        migrations.CreateModel(
-            name='DummyLike',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Like')),
-            ],
-        ),
-        migrations.CreateModel(
-            name='DummyDownloadStatistics',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.DownloadStatistics')),
-            ],
-        ),
-        migrations.CreateModel(
-            name='DummyComment',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Comment')),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0009_auto_20200518_2245.py b/app/migrations/0009_auto_20200518_2245.py
deleted file mode 100644
index 68785da21a33971eced0e5cf01c37c9e350a56d9..0000000000000000000000000000000000000000
--- a/app/migrations/0009_auto_20200518_2245.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-18 15:45
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0008_auto_20200518_1919'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='comment',
-            name='comment',
-            field=models.CharField(default='comments', max_length=240),
-        ),
-    ]
diff --git a/app/migrations/0010_category_archived.py b/app/migrations/0010_category_archived.py
deleted file mode 100644
index a864e59965a382bdc242de2970ab9cc7b76de822..0000000000000000000000000000000000000000
--- a/app/migrations/0010_category_archived.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.3 on 2020-06-03 06:48
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0009_auto_20200518_2245'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='category',
-            name='archived',
-            field=models.BooleanField(default=False),
-        ),
-    ]
diff --git a/app/migrations/0011_auto_20200603_1350.py b/app/migrations/0011_auto_20200603_1350.py
deleted file mode 100644
index 14c08847378c0a4a310c77303a176bd5182ba90b..0000000000000000000000000000000000000000
--- a/app/migrations/0011_auto_20200603_1350.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.3 on 2020-06-03 06:50
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0010_category_archived'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='category',
-            name='description',
-            field=models.TextField(default=''),
-        ),
-    ]
diff --git a/app/migrations/0012_category_archived_by.py b/app/migrations/0012_category_archived_by.py
deleted file mode 100644
index 9919bf0538e111bef9794eeb7ab32517e3cb65e7..0000000000000000000000000000000000000000
--- a/app/migrations/0012_category_archived_by.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 3.0.3 on 2020-06-04 00:09
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0011_auto_20200603_1350'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='category',
-            name='archived_by',
-            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
-        ),
-    ]
diff --git a/app/migrations/0013_auto_20200919_2055.py b/app/migrations/0013_auto_20200919_2055.py
deleted file mode 100644
index 7b7993c96f79f86abbf68a24f2c520e98ad3882a..0000000000000000000000000000000000000000
--- a/app/migrations/0013_auto_20200919_2055.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Generated by Django 3.0.3 on 2020-09-19 13:55
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0012_category_archived_by'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='materi',
-            name='author',
-            field=models.CharField(default='Penyusun', max_length=30),
-        ),
-        migrations.AlterField(
-            model_name='materi',
-            name='descriptions',
-            field=models.TextField(default='Deskripsi'),
-        ),
-        migrations.AlterField(
-            model_name='materi',
-            name='publisher',
-            field=models.CharField(default='Penerbit', max_length=30),
-        ),
-        migrations.AlterField(
-            model_name='materi',
-            name='title',
-            field=models.CharField(default='Judul', max_length=50),
-        ),
-    ]
diff --git a/app/migrations/0014_rating.py b/app/migrations/0014_rating.py
deleted file mode 100644
index 69ae6aa1e7dcd3345443a22672118f591ef8e680..0000000000000000000000000000000000000000
--- a/app/migrations/0014_rating.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 3.0.3 on 2020-09-29 11:30
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0013_auto_20200919_2055'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Rating',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-                ('score', models.IntegerField()),
-                ('materi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.Materi')),
-                ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
-            ],
-            options={
-                'unique_together': {('materi', 'user')},
-            },
-        ),
-    ]
diff --git a/app/migrations/0015_downloadstatistics_downloader.py b/app/migrations/0015_downloadstatistics_downloader.py
deleted file mode 100644
index 16780d0c070064f852acf2e180dfcb1ba5179afc..0000000000000000000000000000000000000000
--- a/app/migrations/0015_downloadstatistics_downloader.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 3.0.4 on 2020-09-30 04:50
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0014_rating'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='downloadstatistics',
-            name='downloader',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='riwayat_unduh', to=settings.AUTH_USER_MODEL),
-        ),
-    ]
diff --git a/app/migrations/0015_reqmaterial.py b/app/migrations/0015_reqmaterial.py
deleted file mode 100644
index 197e5a18d1dbb8cc0daf3436660d9667ee421939..0000000000000000000000000000000000000000
--- a/app/migrations/0015_reqmaterial.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 3.0.3 on 2020-10-01 04:23
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0014_rating'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='ReqMaterial',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('title', models.CharField(max_length=100)),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0016_ratingcontributor.py b/app/migrations/0016_ratingcontributor.py
deleted file mode 100644
index 86d787f0b6a357456c259f9842dfd7e3b7e49415..0000000000000000000000000000000000000000
--- a/app/migrations/0016_ratingcontributor.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 3.1.1 on 2020-10-05 14:41
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0015_reqmaterial'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='RatingContributor',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(auto_now=True)),
-                ('score', models.IntegerField()),
-                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0017_auto_20201005_2145.py b/app/migrations/0017_auto_20201005_2145.py
deleted file mode 100644
index 732caef72a1b44acef70e15c4d29b91a5df106b2..0000000000000000000000000000000000000000
--- a/app/migrations/0017_auto_20201005_2145.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 3.1.1 on 2020-10-05 14:45
-
-import django.core.validators
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0016_ratingcontributor'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='ratingcontributor',
-            name='score',
-            field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)]),
-        ),
-    ]
diff --git a/app/migrations/0018_merge_20201009_0700.py b/app/migrations/0018_merge_20201009_0700.py
deleted file mode 100644
index 2e5d56480fa1f304956819f4012e5e52c2100d5e..0000000000000000000000000000000000000000
--- a/app/migrations/0018_merge_20201009_0700.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 00:00
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0017_auto_20201005_2145'),
-        ('app', '0015_downloadstatistics_downloader'),
-    ]
-
-    operations = [
-    ]
diff --git a/app/migrations/0019_auto_20201009_1829.py b/app/migrations/0019_auto_20201009_1829.py
deleted file mode 100644
index 15bec11ed71a0b3062e68aeceda2e7434ec804e8..0000000000000000000000000000000000000000
--- a/app/migrations/0019_auto_20201009_1829.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 11:29
-
-from django.conf import settings
-import django.core.validators
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0018_merge_20201009_0700'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='materi',
-            name='status',
-            field=models.CharField(choices=[('PENDING', 'Diproses'), ('APPROVE', 'Diterima'), ('DISAPPROVE', 'Ditolak'), ('REVISION', 'Perbaikan'), ('BLOCKED', 'Diblokir')], default='PENDING', max_length=30),
-        ),
-        migrations.CreateModel(
-            name='LaporanMateri',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('laporan', models.TextField(default='', validators=[django.core.validators.MinValueValidator(30), django.core.validators.MaxValueValidator(120)])),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-                ('is_rejected', models.BooleanField(default=False)),
-                ('materi', models.ForeignKey(max_length=120, on_delete=django.db.models.deletion.CASCADE, to='app.materi')),
-                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0019_materi__search_vector.py b/app/migrations/0019_materi__search_vector.py
deleted file mode 100644
index bd148040bd07b733963274efadcb1ad9995396c7..0000000000000000000000000000000000000000
--- a/app/migrations/0019_materi__search_vector.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 11:19
-
-import django.contrib.postgres.search
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0018_merge_20201009_0700'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='materi',
-            name='_search_vector',
-            field=django.contrib.postgres.search.SearchVectorField(editable=False, null=True),
-        ),
-    ]
diff --git a/app/migrations/0019_materi_yt_video_id.py b/app/migrations/0019_materi_yt_video_id.py
deleted file mode 100644
index df8dc1afeba6a410f437c1ce8c29de0e0c947346..0000000000000000000000000000000000000000
--- a/app/migrations/0019_materi_yt_video_id.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 13:48
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0018_merge_20201009_0700'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='materi',
-            name='yt_video_id',
-            field=models.CharField(blank=True, max_length=100, null=True),
-        ),
-    ]
diff --git a/app/migrations/0020_merge_20201009_2039.py b/app/migrations/0020_merge_20201009_2039.py
deleted file mode 100644
index 80867b028075f48fb2cd1b5838561fe9b20aa4cc..0000000000000000000000000000000000000000
--- a/app/migrations/0020_merge_20201009_2039.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 13:39
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0019_materi__search_vector'),
-        ('app', '0019_auto_20201009_1829'),
-    ]
-
-    operations = [
-    ]
diff --git a/app/migrations/0021_dislikecomment_likecomment.py b/app/migrations/0021_dislikecomment_likecomment.py
deleted file mode 100644
index 1ce9111268da53f8e2a88fdf877c4250fb354695..0000000000000000000000000000000000000000
--- a/app/migrations/0021_dislikecomment_likecomment.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 16:19
-
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0020_merge_20201009_2039'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='LikeComment',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-                ('session_id', models.CharField(max_length=32)),
-                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
-            ],
-        ),
-        migrations.CreateModel(
-            name='DislikeComment',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-                ('session_id', models.CharField(max_length=32)),
-                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0021_materi_release_year.py b/app/migrations/0021_materi_release_year.py
deleted file mode 100644
index 8294e44629dc026ddff721b329e610946de3a7d5..0000000000000000000000000000000000000000
--- a/app/migrations/0021_materi_release_year.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 16:13
-
-import app.models
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0020_merge_20201009_2039'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='materi',
-            name='release_year',
-            field=models.IntegerField(default=app.models.current_year),
-        ),
-    ]
diff --git a/app/migrations/0022_merge_20201011_1122.py b/app/migrations/0022_merge_20201011_1122.py
deleted file mode 100644
index 4b0d5ebf263822713c41cced80792494eb749a21..0000000000000000000000000000000000000000
--- a/app/migrations/0022_merge_20201011_1122.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 3.1 on 2020-10-11 04:22
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0021_dislikecomment_likecomment'),
-        ('app', '0021_materi_release_year'),
-    ]
-
-    operations = [
-    ]
diff --git a/app/migrations/0023_materi_deleted_at.py b/app/migrations/0023_materi_deleted_at.py
deleted file mode 100644
index 2a695fcfe8cfa105ee5b2b04ba7b163460267b26..0000000000000000000000000000000000000000
--- a/app/migrations/0023_materi_deleted_at.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-23 03:23
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0022_merge_20201011_1122'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='materi',
-            name='deleted_at',
-            field=models.DateTimeField(blank=True, null=True),
-        ),
-    ]
diff --git a/app/migrations/0023_merge_20201016_1713.py b/app/migrations/0023_merge_20201016_1713.py
deleted file mode 100644
index ecb8c7929320e2599b38912d405bce12532e23a5..0000000000000000000000000000000000000000
--- a/app/migrations/0023_merge_20201016_1713.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 3.1 on 2020-10-16 10:13
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0022_merge_20201011_1122'),
-        ('app', '0019_materi_yt_video_id'),
-    ]
-
-    operations = [
-    ]
diff --git a/app/migrations/0024_merge_20201026_0812.py b/app/migrations/0024_merge_20201026_0812.py
deleted file mode 100644
index adc2f2f6bb36bb959b15608d753516fed53bf5df..0000000000000000000000000000000000000000
--- a/app/migrations/0024_merge_20201026_0812.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 3.1 on 2020-10-26 01:12
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0023_merge_20201016_1713'),
-        ('app', '0023_materi_deleted_at'),
-    ]
-
-    operations = [
-    ]
diff --git a/app/migrations/0025_review.py b/app/migrations/0025_review.py
deleted file mode 100644
index 79b7b89a4f655cf6851c7265539222047eef56e1..0000000000000000000000000000000000000000
--- a/app/migrations/0025_review.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 3.1 on 2020-10-29 11:22
-
-import app.models
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('app', '0024_merge_20201026_0812'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Review',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('username', models.CharField(max_length=100)),
-                ('profile', models.CharField(default=app.models.getRandomColor, max_length=100)),
-                ('review', models.TextField(default='review')),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-                ('materi', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.materi')),
-                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
-            ],
-        ),
-    ]
diff --git a/app/migrations/0026_submitvisitor.py b/app/migrations/0026_submitvisitor.py
deleted file mode 100644
index b8497c75b257983dd84ea6b3cbc8b5228f6cd29d..0000000000000000000000000000000000000000
--- a/app/migrations/0026_submitvisitor.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 3.1 on 2020-10-29 13:45
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0025_review'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='SubmitVisitor',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('user_id', models.CharField(max_length=50)),
-                ('email', models.CharField(max_length=50)),
-                ('msg', models.CharField(max_length=100)),
-                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
-            ],
-        ),
-    ]
diff --git a/app/models.py b/app/models.py
index e531ad121c188ab50d73c93dfbedd3eafb464389..661cde6be8d78d8b5d785ab31b34c8764c7ecd18 100644
--- a/app/models.py
+++ b/app/models.py
@@ -47,7 +47,7 @@ class MateriManager(models.Manager):
         if self.alive_only:
             return SoftDeletionQuerySet(self.model).filter(deleted_at=None)
         return SoftDeletionQuerySet(self.model)
-        
+
     def search(self, search_text):
         search_vector = search.SearchVector("title", weight="A")
         search_query = search.SearchQuery(search_text)
@@ -68,10 +68,10 @@ class SoftDeleteModel(models.Model):
     deleted_at = models.DateTimeField(blank=True, null=True)
 
     all_objects = MateriManager(alive_only=False)
-    
+
     class Meta:
         abstract = True
-    
+
     def soft_delete(self):
         self.deleted_at = timezone.now()
         self.save()
@@ -85,7 +85,7 @@ class Materi(SoftDeleteModel):
     publisher = models.CharField(max_length=30, default="Penerbit")
     release_year = models.IntegerField(default=current_year)
     pages = models.IntegerField(default=0)
-    descriptions = models.TextField(default="Deskripsi")    
+    descriptions = models.TextField(default="Deskripsi")
     status = models.CharField(max_length=30, choices=VERIFICATION_STATUS, default=VERIFICATION_STATUS[0][0])
     categories = models.ManyToManyField(Category)
     date_created = models.DateTimeField(default=timezone.now)
@@ -124,7 +124,7 @@ class Materi(SoftDeleteModel):
     def like_count(self):
         count = Like.objects.filter(materi=self).count()
         return count
-    
+
     @property
     def comment_count(self):
         count = Comment.objects.filter(materi=self).count()
@@ -157,7 +157,7 @@ class Comment(models.Model):
     def like_count(self):
         count = LikeComment.objects.filter(comment=self).count()
         return count
-    
+
     @property
     def dislike_count(self):
         count = DislikeComment.objects.filter(comment=self).count()
@@ -265,6 +265,19 @@ class RatingContributor(models.Model):
     class Meta:
         unique_together = ["contributor", "user"]
 
+class SubscribeModel(models.Model):
+    sys_id = models.AutoField(primary_key=True, null=False, blank=True)
+    email = models.EmailField(null=False, blank=True, max_length=200, unique=True)
+    status = models.CharField(max_length=64, null=False, blank=True)
+    created_date = models.DateTimeField(null=False, blank=True)
+    updated_date = models.DateTimeField(null=False, blank=True)
+
+    class Meta:
+        app_label = "app"
+        db_table = "app_subscribe"
+
+    def __str__(self):
+        return self.email
 
 class LaporanMateri(models.Model):
     materi = models.ForeignKey(Materi, on_delete=models.CASCADE, max_length=120)
diff --git a/app/static/images/sub-btn.png b/app/static/images/sub-btn.png
new file mode 100644
index 0000000000000000000000000000000000000000..81b0489c7c988c9769980e0bca8698a24cc3851d
Binary files /dev/null and b/app/static/images/sub-btn.png differ
diff --git a/app/templates/app/emails/subscription.html b/app/templates/app/emails/subscription.html
new file mode 100644
index 0000000000000000000000000000000000000000..6a83c2d0a33ee439882c3b3008ce091db597ee62
--- /dev/null
+++ b/app/templates/app/emails/subscription.html
@@ -0,0 +1,20 @@
+<div style="padding: 20px; background: #fafafa;font-size:15px;">
+    Hi<br>
+    Thanks for subscribing to {{ project_name }} newsletter.<br>
+    We will be sending you latest published articles on <a href="{{ site_url }}">{{ site_url }}</a>. Mail frequency won't be more than twice a month.<br>
+    We hate spamming as much as you do.<br>
+    <br>
+    To confirm your subscription, please click on the link given below. If clicking doesn't work, copy paste the URL in browser.<br>
+    If you think this is a mistake, just ignore this email and we won't bother you again.
+    <br>
+    <br>
+    <a href="{{ confirmation_url }}">{{ confirmation_url }}</a>
+
+    <br>
+    <br>
+    <p>
+        Note:<br>
+        This is notification only email. Please do not reply on this email.<br>
+        You can <a href="{{ contact_us_url }}">contact us here</a>.
+    </p>
+</div>
\ No newline at end of file
diff --git a/app/templates/app/katalog_materi.html b/app/templates/app/katalog_materi.html
index 224232c1a609e82a1004dd0183bd6ac83059cd48..93c719b560ad93d582913d20205b0d125f799565 100644
--- a/app/templates/app/katalog_materi.html
+++ b/app/templates/app/katalog_materi.html
@@ -48,7 +48,7 @@
 </head>
 
 <body style="background-color: #f8f8f8;">
-    
+
 
 <!-- Page Content -->
     <div class="container">
@@ -76,14 +76,14 @@
                 </div>
             </div>
         </header>
-        
+
         <a href="/download-history/" class="btn-history">Riwayat Unduh</a><br><br>
 
         <div id="container-materi-ordering">
             <a href="?random=1" class="btn-materi-ordering">Acak Materi</a><br><br>
             <a href="?recommendation=1" class="btn-materi-ordering">Rekomendasi</a><br><br>
         </div>
-		
+
         <div class="container">
             <div class="row content">
                 <div class="col-3 sidebar">
@@ -209,4 +209,4 @@
 
 </html>
 
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/app/templates/subscribe.html b/app/templates/subscribe.html
new file mode 100644
index 0000000000000000000000000000000000000000..566776b36cbc0b63a6e9c57cd980f7a8220b6cec
--- /dev/null
+++ b/app/templates/subscribe.html
@@ -0,0 +1,12 @@
+<div style="border: 1px solid darkgreen; border-radius: 2px; padding:10px; text-align: center;">
+    <div><strong>SUBSCRIBE</strong></div>
+    <div style="margin-bottom: 10px;">Please subscribe to get the latest articles in your mailbox.</div>
+    <div>
+        <form action="{% url 'subscribe' %}" method="post" class="form-horizontal">
+            {% csrf_token %}
+            <input type="email" name="email" class="form-control" placeholder="Your Email ID Please" required>
+            <br>
+            <input type="submit" value="Subscribe" class="btn btn-primary btn-sm">
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/app/tests.py b/app/tests.py
index 9f10c73f27f528bd814fb834b1084391f60514d9..2a0a6e069a9d9297915b0907ee1b1b932c5b6e6a 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -67,6 +67,9 @@ from .views import (
 )
 from app.forms import SuntingProfilForm, year_choices
 from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
+import pandas as pd
+from django.core import mail
+from app.utils.email_utility import send_gmail
 from app.utils.PasswordValidator import PasswordPolicyValidator
 
 ERROR_403_MESSAGE = "Kamu harus login untuk mengakses halaman ini"
@@ -226,7 +229,7 @@ class DaftarKatalogSortingByJumlahTampilanTest(TestCase):
         self.client.get(self.url)
         response = self.client.get("/?sort=jumlah_tampilan")
         self.assertRegex(str(response.content), rf'.*Materi 2.*Materi 1.*')
-    
+
 class DaftarKatalogPerKontributorTest(TestCase):
     def setUp(self):
         self.client = Client()
@@ -322,13 +325,13 @@ class DetailMateriTest(TestCase):
             "ExampleCover921.jpg", b"Test file")
         self.content = SimpleUploadedFile("ExampleFile921.pdf", b"Test file")
 
-        self.materi1 = Materi.objects.create(title="Materi 1", author="Agas", 
-                        uploader=self.contributor, publisher="Kelas SC", 
-                        descriptions="Deskripsi Materi 1", status="APPROVE", 
+        self.materi1 = Materi.objects.create(title="Materi 1", author="Agas",
+                        uploader=self.contributor, publisher="Kelas SC",
+                        descriptions="Deskripsi Materi 1", status="APPROVE",
                         cover=self.cover, content=self.content)
-        self.materi2 = Materi.objects.create(title="Materi 2", author="Agad", 
-                        uploader=self.contributor, publisher="Kelas SM", 
-                        descriptions="Deskripsi Materi 2", status="APPROVE", 
+        self.materi2 = Materi.objects.create(title="Materi 2", author="Agad",
+                        uploader=self.contributor, publisher="Kelas SM",
+                        descriptions="Deskripsi Materi 2", status="APPROVE",
                         cover=self.cover, content=self.content)
         self.url = "/materi/" + str(self.materi1.id) + "/"
         self.download_url1 = self.url + "unduh"
@@ -456,10 +459,10 @@ class DetailMateriTest(TestCase):
         comment = Comment.objects.get(comment="This is new comment by Anonymous")
         response = self.client.get(url_materi)
         session_id = response.context["session_id"]
-        
+
         payload = {"comment": comment, "session_id": session_id}
         self.client.post("/comment/dislike/", payload)
-        
+
         self.client.post("/comment/dislike/", payload)
         num_of_comment_dislikes = DislikeComment.objects.filter(comment=comment, session_id=session_id).count()
         self.assertEqual(num_of_comment_dislikes, 0)
@@ -473,10 +476,10 @@ class DetailMateriTest(TestCase):
         comment = Comment.objects.get(comment="This is new comment by Anonymous")
         response = self.client.get(url_materi)
         session_id = response.context["session_id"]
-        
+
         payload = {"comment": comment, "session_id": session_id}
         self.client.post("/comment/like/", payload)
-        
+
         self.client.post("/comment/like/", payload)
         num_of_comment_likes = LikeComment.objects.filter(comment=comment, session_id=session_id).count()
         self.assertEqual(num_of_comment_likes, 0)
@@ -516,9 +519,9 @@ class DetailMateriTest(TestCase):
             is_subscribing_to_material_comments=True
         )
 
-        material = Materi.objects.create(title="Materi-subscribed", author="Tester", 
-                                uploader=contributor_subscribed, publisher="Kelas PMPL", 
-                                descriptions="Deskripsi Materi subscribed", status="APPROVE", 
+        material = Materi.objects.create(title="Materi-subscribed", author="Tester",
+                                uploader=contributor_subscribed, publisher="Kelas PMPL",
+                                descriptions="Deskripsi Materi subscribed", status="APPROVE",
                                 cover=self.cover, content=self.content)
         url = "/materi/" + str(material.id) + "/"
         self.client.login(**self.contributor_credential) # comment with other user
@@ -543,9 +546,9 @@ class DetailMateriTest(TestCase):
             is_subscribing_to_material_comments=False
         )
 
-        material = Materi.objects.create(title="Materi-not-subscribed", author="Tester", 
-                                uploader=contributor_not_subscribed, publisher="Kelas PMPL", 
-                                descriptions="Deskripsi Materi non-subscribed", status="APPROVE", 
+        material = Materi.objects.create(title="Materi-not-subscribed", author="Tester",
+                                uploader=contributor_not_subscribed, publisher="Kelas PMPL",
+                                descriptions="Deskripsi Materi non-subscribed", status="APPROVE",
                                 cover=self.cover, content=self.content)
         url = "/materi/" + str(material.id) + "/"
         self.client.login(**self.contributor_credential) # comment with other user
@@ -573,9 +576,9 @@ class DetailMateriTest(TestCase):
             is_subscribing_to_material_comments=True
         )
 
-        material = Materi.objects.create(title="Materi-subscribed", author="Tester", 
-                                uploader=contributor_subscribed, publisher="Kelas PMPL", 
-                                descriptions="Deskripsi Materi subscribed", status="APPROVE", 
+        material = Materi.objects.create(title="Materi-subscribed", author="Tester",
+                                uploader=contributor_subscribed, publisher="Kelas PMPL",
+                                descriptions="Deskripsi Materi subscribed", status="APPROVE",
                                 cover=self.cover, content=self.content)
         url = "/materi/" + str(material.id) + "/"
         self.client.login(**contributor_subscribed_credentials) # comment with the same user
@@ -653,7 +656,7 @@ class DetailMateriTest(TestCase):
         count = Comment.objects.all().filter(comment="This is new comment by Anonymous").count()
         self.assertEqual(count, 1)
 
-    
+
     def test_review_models_can_create_new_object(self):
         test = Review.objects.create(
             username="saul", profile="121212", review="232323")
@@ -700,7 +703,7 @@ class DetailMateriTest(TestCase):
         self.client.post(url, {"review": "This is new review by Anonymous"})
         response = self.client.get(url)
         self.assertContains(response, "Anonymous")
-    
+
     def test_detail_materi_contains_review_count(self):
         url = self.url
         self.client.login(**self.contributor_credential)
@@ -731,25 +734,25 @@ class DetailMateriTest(TestCase):
 
     def test_hasil_citasi_APA_materi_has_no_published_date(self):
         response = self.client.get(self.url)
-        expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher	
+        expected = self.materi1.author + " . (n.d) . " + self.materi1.title + " . " + self.materi1.publisher
         self.assertIn(expected, response.context["citationAPA"])
         self.assertIn(expected,
                       response.context["citationAPA"])
 
     def test_hasil_citasi_APA_materi_has_published_date(self):
         response = self.client.get(self.materi_with_published_date_url)
-        published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M")	
-        expected = (	
-            self.materi_with_published_date.author	
-            + " . ("	
-            + published_date	
-            + ") . "	
-            + self.materi_with_published_date.title	
-            + " . "	
-            + self.materi_with_published_date.publisher	
-        )	
+        published_date = self.materi_with_published_date.published_date.strftime("%Y-%m-%d %H:%M")
+        expected = (
+            self.materi_with_published_date.author
+            + " . ("
+            + published_date
+            + ") . "
+            + self.materi_with_published_date.title
+            + " . "
+            + self.materi_with_published_date.publisher
+        )
         self.assertIn(expected, response.context["citationAPA"])
-    
+
     def test_citation_IEEE_button(self):
         response = self.client.get(self.url)
         self.assertContains(response, "Citate IEEE")
@@ -761,19 +764,19 @@ class DetailMateriTest(TestCase):
         current_month = current_date.strftime("%b")
         current_year = str(current_date.year)
 
-        expected = (	
-            "Agas, "	
-            + "Materi 1. "	
-            + "Kelas SC, n.d. "	
-            + "Accessed on: "	
-            + current_month	
-            + ". "	
-            + current_day	
-            + ", "	
-            + current_year	
-            + ". [Online]. "	
-            + "Available: http://testserver"	
-            + self.url	
+        expected = (
+            "Agas, "
+            + "Materi 1. "
+            + "Kelas SC, n.d. "
+            + "Accessed on: "
+            + current_month
+            + ". "
+            + current_day
+            + ", "
+            + current_year
+            + ". [Online]. "
+            + "Available: http://testserver"
+            + self.url
         )
         self.assertIn(expected, response.context["citationIEEE"])
 
@@ -856,7 +859,7 @@ class DetailMateriTest(TestCase):
         self.assertNotEqual(context1['materi_download_count'], context2['materi_download_count'])
         self.assertEqual(context1['materi_download_count'], 3)
         self.assertEqual(context2['materi_download_count'], 2)
-    
+
     def test_download_count_displayed_on_template_when_no_download(self):
         response = self.client.get(self.url)
         html = response.content.decode("utf-8")
@@ -901,12 +904,12 @@ class DetailMateriTest(TestCase):
         self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi2, html)
         self.check_materi_info_in_html(self.dcount_info_name, dcount_materi2, html2)
         self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi1, html2)
-    
+
     def test_like_count_displayed_on_template_when_no_like(self):
         response = self.client.get(self.url)
         html = response.content.decode("utf-8")
         self.check_materi_info_in_html(self.lcount_info_name, 0, html)
-    
+
     def test_like_count_displayed_on_template_when_single_like(self):
         payload = {
             'materi_id': self.materi1.id,
@@ -916,8 +919,8 @@ class DetailMateriTest(TestCase):
 
         response = self.client.get(self.url)
         html = response.content.decode("utf-8")
-        self.check_materi_info_in_html(self.lcount_info_name, 1, html)        
-        
+        self.check_materi_info_in_html(self.lcount_info_name, 1, html)
+
     def test_like_count_displayed_on_template_when_multiple_like(self):
         payload1 = {
             'materi_id': self.materi1.id,
@@ -929,17 +932,17 @@ class DetailMateriTest(TestCase):
         }
         self.client.post(self.like_url, payload1)
         self.client.post(self.like_url, payload2)
-        
+
         response = self.client.get(self.url)
         html = response.content.decode("utf-8")
         self.check_materi_info_in_html(self.lcount_info_name, 2, html)
-    
+
     def test_like_count_displayed_on_template_when_unlike(self):
         payload = {
             'materi_id': self.materi1.id,
             'session_id': "dummysession"
         }
-        
+
         # Like materi once
         self.client.post(self.like_url, payload)
         response = self.client.get(self.url)
@@ -981,11 +984,11 @@ class PostsViewTest(TestCase):
 
             for j in range (LIKES_COUNT_PER_POST[i]):
                 Like.objects.create(
-                    timestamp=datetime.now(), 
-                    materi=post, 
+                    timestamp=datetime.now(),
+                    materi=post,
                     session_id=("dummysession-" + str(i) + '-' + str(j))
                 )
-            
+
         for i, post_id in enumerate(post_comment_group_dict):
             post = post_comment_group_dict[post_id]["data"]
 
@@ -1127,13 +1130,13 @@ class UploadPageTest(TestCase):
         response = Client().get("/fake/")
         self.assertEqual(response.status_code, 404)
 
-    def test_upload_page_url_admin_doesnt_exist(self):        
+    def test_upload_page_url_admin_doesnt_exist(self):
         self.client.login(email="admin@gov.id",
                           password="admin")
         response = self.client.get("/unggah/")
         self.assertEqual(response.status_code, 403)
 
-    def test_upload_page_url_admin_cant_upload(self):        
+    def test_upload_page_url_admin_cant_upload(self):
         self.client.login(email="admin@gov.id",
                           password="admin")
         response = self.client.post("/unggah/")
@@ -1271,7 +1274,7 @@ class UploadExcelPageTest(TestCase):
 
         with open(file_name, 'rb') as fp:
             response = self.client.post("/unggah_excel/", {'excel': fp})
-        
+
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
@@ -1289,7 +1292,7 @@ class UploadExcelPageTest(TestCase):
 
         with open(file_name, 'rb') as fp:
             response = self.client.post("/unggah_excel/", {'excel': fp})
-        
+
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
@@ -1308,7 +1311,7 @@ class UploadExcelPageTest(TestCase):
 
         with open(file_name, 'rb') as fp:
             response = self.client.post("/unggah_excel/", {'excel': fp})
-        
+
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
@@ -1327,7 +1330,7 @@ class UploadExcelPageTest(TestCase):
 
         with open(file_name, 'rb') as fp:
             response = self.client.post("/unggah_excel/", {'excel': fp})
-        
+
         messages = list(dj_messages.get_messages(response.wsgi_request))
         msg_text = messages[0].message
 
@@ -1348,12 +1351,12 @@ class UploadExcelPageTest(TestCase):
         }
 
         categories = ['Computer Science','Machine Learning','Deep Learning']
-        
+
         file_name, data_frame = self.create_dummy_excel(field_lengths=field_lengths, categories=categories)
 
         with open(file_name, 'rb') as fp:
             self.client.post("/unggah_excel/", {'excel': fp})
-        
+
         title = data_frame['Title'][0]
         materi = Materi.objects.get(title=title)
         default_path = 'book-cover-placeholder.png'
@@ -1367,11 +1370,11 @@ class UploadExcelPageTest(TestCase):
                           password="kontributor")
 
         response = self.client.get("/unggah_excel/?template=1")
-        
+
         self.assertEquals(response['Content-Type'],'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
         self.assertEquals(response['Content-Disposition'],'attachment; filename=template.xlsx')
 
-        
+
 
 
 
@@ -1478,14 +1481,14 @@ class DeleteMateriTest(TestCase):
         self.client.login(**self.contributor_credential)
         response = self.client.get(self.url)
         self.assertEqual(response.status_code, 302)
-    
+
     def test_url_soft_delete_materi_is_success_as_admin(self):
         self.client.login(**self.admin_credential)
         response = self.client.get(self.url)
-        self.assertEqual(response.status_code, 302) 
+        self.assertEqual(response.status_code, 302)
         self.materi1.refresh_from_db()
         self.assertNotEqual(self.materi1.deleted_at, None)
-    
+
     def test_url_soft_delete_materi_is_success_as_superuser(self):
         self.client.login(**self.superuser_credential)
         response = self.client.get(self.url)
@@ -1512,7 +1515,7 @@ class ProfilViewTest(TestCase):
         self.assertEqual(found.func.__name__, self.view.as_view().__name__)
 
     def _request_as_user(self, credentials):
-        self.client = Client() 
+        self.client = Client()
         self.client.login(**credentials)
         return self.client.get(self.url)
 
@@ -1554,7 +1557,7 @@ class SuntingProfilViewTest(TestCase):
         self.assertEqual(found.func.__name__, self.view.as_view().__name__)
 
     def _request_as_user(self, credentials):
-        self.client = Client() 
+        self.client = Client()
         self.client.login(**credentials)
         return self.client.get(self.url)
 
@@ -1935,7 +1938,7 @@ class RevisiMateriTest(TestCase):
         # Logout
         self.client.logout()
 
-    
+
 class GenerateDummyCommandTest(TestCase):
 
     def setUp(self):
@@ -2419,7 +2422,7 @@ class RatingContributorTest(TransactionTestCase):
         response = self.client.get(self.url)
         self.assertContains(response=response, text=f"Rating: {mean(avg)}", count=1)
         self.assertContains(response=response, text=f"oleh: {len(avg)} orang", count=1)
-    
+
     def test_not_authenticated_user_should_not_able_to_add_rating(self):
         response_post = self.client.post(self.url, data={
             "contributor":self.contributor.id,
@@ -2429,11 +2432,11 @@ class RatingContributorTest(TransactionTestCase):
         response_get = self.client.get(self.url)
         self.assertEqual(response_post.status_code, 403)
         self.assertContains(response_get, "Kamu harus login untuk memberi rating")
-    
+
     def test_not_authenticated_user_should_not_able_to_delete_rating(self):
         response_post = self.client.post(self.url, data={"delete":True})
         self.assertEqual(response_post.status_code, 403)
-    
+
     def test_authenticated_user_should_not_delete_rating_if_has_not_been_rated(self):
         self.client.login(**self.admin_credential)
         self.client.post(self.url, data={
@@ -2441,15 +2444,15 @@ class RatingContributorTest(TransactionTestCase):
             "user":self.admin.id,
             "score":3
         })
-        
+
         self.assertEqual(1, RatingContributor.objects.filter(contributor=self.contributor).count())
         self.client.logout()
 
         self.client.login(**self.anonymous_credential)
         self.client.post(self.url, data={"delete":True})
-        
+
         self.assertEqual(1, RatingContributor.objects.filter(contributor=self.contributor).count())
-    
+
     def test_authenticated_user_should_delete_rating_if_has_been_rated(self):
         self.client.login(**self.admin_credential)
         self.client.post(self.url, data={
@@ -2467,7 +2470,7 @@ class RatingContributorTest(TransactionTestCase):
         self.assertContains(response, 'Rating: 0')
         self.assertContains(response, 'oleh: 0 orang')
         self.assertEqual(0, RatingContributor.objects.filter(contributor=self.contributor).count())
-    
+
     def test_average_still_be_correct_when_rating_was_deleted(self):
         scores = [2,4]
         self.client.login(**self.admin_credential)
@@ -2492,7 +2495,7 @@ class RatingContributorTest(TransactionTestCase):
         response = self.client.get(self.url)
         self.assertContains(response, f'Rating: {scores[0]}')
         self.assertContains(response, 'oleh: 1 orang')
-    
+
     def test_authenticated_user_should_update_rating_if_has_been_rated(self):
         self.client.login(**self.anonymous_credential)
         self.client.post(self.url, data={
@@ -2508,7 +2511,7 @@ class RatingContributorTest(TransactionTestCase):
         response = self.client.get(self.url)
         self.assertContains(response, 'Rating: 3')
         self.assertContains(response, 'oleh: 1 orang')
-    
+
     def test_authenticated_user_should_not_update_rating_if_has_not_been_rated(self):
         self.client.login(**self.anonymous_credential)
         response = self.client.get(self.url)
@@ -2519,7 +2522,7 @@ class RatingContributorTest(TransactionTestCase):
         response = self.client.get(self.url)
         self.assertContains(response, 'Rating: 0')
         self.assertContains(response, 'oleh: 0 orang')
-    
+
 
 class UserDownloadHistoryTest(TestCase):
     def setUp(self):
@@ -2558,25 +2561,25 @@ class UserDownloadHistoryTest(TestCase):
         self.materi1 = Materi.objects.first()
         self.download_url = f"/materi/{self.materi1.id}/unduh"
         self.history_url = "/download-history/"
-	
+
     def test_multiple_insert_download_statistic_with_user(self):
         DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-		
+
         DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 2)
-		
+
     def test_download_statistics_bound_to_specific_user(self):
         DownloadStatistics(materi=self.materi1, downloader=self.user1_anonim).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-		
+
         DownloadStatistics(materi=self.materi1).save()
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-		
+
         DownloadStatistics(materi=self.materi1, downloader=self.user2_anonim).save()
         user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         user2_num_of_downloads = self.user2_anonim.riwayat_unduh.all().count()
@@ -2586,14 +2589,14 @@ class UserDownloadHistoryTest(TestCase):
     def test_registered_user_download(self):
         # Login
         self.client.login(**self.user1_credential)
-		
+
         self.client.get(self.download_url)
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-		
+
         # Logout
         self.client.logout()
-		
+
     def test_unregistered_user_download(self):
         self.client.get(self.download_url)
         downloaded_materi = self.client.session['downloaded_materi']
@@ -2611,10 +2614,10 @@ class UserDownloadHistoryTest(TestCase):
         self.client.get(self.download_url)
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 2)
-		
+
         # Logout
         self.client.logout()
-		
+
     def test_unregistered_user_multiple_download(self):
         self.client.get(self.download_url)
         downloaded_materi = self.client.session['downloaded_materi']
@@ -2627,27 +2630,27 @@ class UserDownloadHistoryTest(TestCase):
         num_of_downloads = DownloadStatistics.objects.filter(
                             pk__in=downloaded_materi).count()
         self.assertEqual(num_of_downloads, 2)
-		
+
     def test_registered_user_doesnt_use_session_when_download(self):
         # Login
         self.client.login(**self.user1_credential)
-		
+
         self.client.get(self.download_url)
         self.assertFalse('downloaded_materi' in self.client.session)
-		
+
         # Logout
         self.client.logout()
-	
+
     def test_download_history_bound_to_specific_user(self):
         # Login Anonym 1
         self.client.login(**self.user1_credential)
         self.client.get(self.download_url)
         num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
         self.assertEqual(num_of_downloads, 1)
-		
+
         # Logout Anonym 1
         self.client.logout()
-		
+
 		# Unregistered User download
         self.client.get(self.download_url)
         user1_num_of_downloads = self.user1_anonim.riwayat_unduh.all().count()
@@ -2656,7 +2659,7 @@ class UserDownloadHistoryTest(TestCase):
                             pk__in=downloaded_materi).count()
         self.assertEqual(user1_num_of_downloads, 1)
         self.assertEqual(guest_num_of_downloads, 1)
-		
+
 		# Login Anonym 2
         self.client.login(**self.user2_credential)
         self.client.get(self.download_url)
@@ -2665,7 +2668,7 @@ class UserDownloadHistoryTest(TestCase):
         self.assertEqual(user1_num_of_downloads, 1)
         self.assertEqual(guest_num_of_downloads, 1)
         self.assertEqual(user2_num_of_downloads, 1)
-		
+
         # Logout Anonym 2
         self.client.logout()
 
@@ -2685,42 +2688,42 @@ class DownloadHistoryViewTest(TestCase):
             **self.contributor_credential, name="Kontributor", is_contributor=True
         )
         self.client = Client()
-		
+
         content1 = b"Test file"
         content2 = b"File Test"
-		
+
         self.cover1 = SimpleUploadedFile("cover1.jpg",content1)
         self.content1 = SimpleUploadedFile("content1.txt",content1)
-		
+
         self.cover2 = SimpleUploadedFile("cover2.jpg",content2)
         self.content2 = SimpleUploadedFile("content2.txt",content2)
-		
+
         self.materi1 = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
                publisher="Kelas SC", descriptions="Deskripsi Materi 1",
                status="PENDING", cover=self.cover1, content=self.content1)
         self.materi2 = Materi.objects.create(title="Materi 2", author="Danin", uploader=self.contributor,
                publisher="Kelas DDP", descriptions="Deskripsi Materi 2",
                status="PENDING", cover=self.cover2, content=self.content2)
-			   
+
         self.download_url1 = f"/materi/{self.materi1.id}/unduh"
         self.download_url2 = f"/materi/{self.materi2.id}/unduh"
         self.history_url = "/download-history/"
-		
+
         # Login
         self.client.login(**self.user_credential)
 
     def tearDown(self):
         # Logout
         self.client.logout()
-		
+
     def test_allow_registered_user(self):
         response = self.client.get(self.history_url)
         self.assertEqual(response.status_code, 200)
-	
+
     def test_allow_unregistered_user(self):
 	    # Forced Logout
         self.client.logout()
-		
+
         response = self.client.get(self.history_url)
         self.assertEqual(response.status_code, 200)
 
@@ -2732,14 +2735,14 @@ class DownloadHistoryViewTest(TestCase):
         response = self.client.get(self.history_url)
         resp_html = response.content.decode('utf8')
         self.assertIn(self.user_anonim.name, resp_html)
-		
+
     def test_registered_user_download_history_correctly_displayed(self):
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
-		
+
         jkt_timezone = timezone(TIME_ZONE)
-		
+
         download_history = self.user_anonim.riwayat_unduh.all()
         response = self.client.get(self.history_url)
         resp_html = response.content.decode('utf8')
@@ -2747,19 +2750,19 @@ class DownloadHistoryViewTest(TestCase):
             downloaded_materi = riwayat.materi
             self.assertIn(downloaded_materi.title, resp_html)
             self.assertIn(downloaded_materi.author, resp_html)
-			
+
             jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone)
             self.assertIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html)
-			
+
     def test_unregistered_user_download_history_correctly_displayed(self):
         self.client.logout()
 
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
-		
+
         jkt_timezone = timezone(TIME_ZONE)
-		
+
         response = self.client.get(self.history_url)
         resp_html = response.content.decode('utf8')
         for riwayat_id in self.client.session['downloaded_materi']:
@@ -2767,19 +2770,19 @@ class DownloadHistoryViewTest(TestCase):
             downloaded_materi = riwayat.materi
             self.assertIn(downloaded_materi.title, resp_html)
             self.assertIn(downloaded_materi.author, resp_html)
-			
+
             jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone)
             self.assertIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html)
-			
+
     def test_download_history_not_display_if_user_changed(self):
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
 
         self.client.logout()
-		
+
         jkt_timezone = timezone(TIME_ZONE)
-		
+
         download_history = self.user_anonim.riwayat_unduh.all()
         response = self.client.get(self.history_url)
         resp_html = response.content.decode('utf8')
@@ -2787,23 +2790,23 @@ class DownloadHistoryViewTest(TestCase):
             downloaded_materi = riwayat.materi
             self.assertNotIn(downloaded_materi.title, resp_html)
             self.assertNotIn(downloaded_materi.author, resp_html)
-			
+
             jkt_timestamp = riwayat.timestamp.astimezone(jkt_timezone)
             self.assertNotIn(jkt_timestamp.strftime("%d %B %Y %H:%M:%S"), resp_html)
-			
+
     def test_unregistered_user_download_history_wont_be_saved_if_user_changes(self):
         self.client.logout()
 
         self.client.get(self.download_url1)
         self.client.get(self.download_url2)
         self.client.get(self.download_url1)
-		
+
         self.client.get(self.history_url)
 
         self.client.login(**self.user_credential)
         self.client.logout()
         self.assertFalse('downloaded_materi' in self.client.session)
-			
+
     def test_download_history_sorted_by_download_time(self):
 		# download with 1 second interval to differ download time
         self.client.get(self.download_url1)
@@ -2813,15 +2816,15 @@ class DownloadHistoryViewTest(TestCase):
         self.client.get(self.download_url1)
         sleep(1)
         self.client.get(self.download_url2)
-		
+
         response = self.client.get(self.history_url)
         resp_html = response.content.decode('utf8')
-		
+
         table_html = ("<table" + resp_html.split("<table")[1]).split("</table>")[0] + "</table>"
         soup = BeautifulSoup(table_html, 'html.parser')
         histories_html = soup.find('tbody').find_all('tr')
         prev_timestamp = None
-		
+
         for riwayat_html in histories_html:
             materi_data = riwayat_html.find_all("td")
             date_format = "%d %B %Y %H:%M:%S"
@@ -2829,7 +2832,7 @@ class DownloadHistoryViewTest(TestCase):
             if prev_timestamp:
                 self.assertTrue(prev_timestamp > materi_timestamp)
             prev_timestamp = materi_timestamp
-			
+
     def test_no_history_display_message(self):
         no_history_msg = "Anda belum mengunduh materi. Silahkan unduh materi yang anda butuhkan"
         response = self.client.get(self.history_url)
@@ -2838,9 +2841,9 @@ class DownloadHistoryViewTest(TestCase):
 
 
 class MateriModelTest(TestCase):
-    
+
     def setUp(self):
-        self.contributor = User.objects.create( 
+        self.contributor = User.objects.create(
             email="kontributor@gov.id",
             password="passwordtest",
             name="kontributor",
@@ -2927,7 +2930,7 @@ class RandomizedMateriTest(TestCase):
         response = Client().get("/?random=1")
         self.assertIn("Materi 1", response.content.decode())
         self.assertIn("Materi 2", response.content.decode())
-    
+
 class YearChoicesTest(TestCase):
     def test_release_year_contains_the_right_current_year(self):
         now = dt.date.today().year
@@ -2993,7 +2996,7 @@ class YTUrlVideoTest(TestCase):
         self.setUpImage()
         self.content = SimpleUploadedFile("ExampleFile221.pdf", b"Test file")
         self.category = Category.objects.create(id="1", name="medis", description="kategori medis")
-    
+
     @override_settings(MEDIA_ROOT=tempfile.gettempdir())
     def setUpImage(self):
         from django.core.files.uploadedfile import InMemoryUploadedFile
@@ -3026,7 +3029,7 @@ class YTUrlVideoTest(TestCase):
                                 "yt_video_id":"jNwz4L9MGVY"}
         )
         self.assertTrue(Materi.objects.get(title="Materi 1"))
-    
+
     def test_upload_materi_with_invalid_yt_video_id(self):
         self.client.login(**self.contributor_credential)
         self.client.post(
@@ -3036,7 +3039,7 @@ class YTUrlVideoTest(TestCase):
                                 "yt_video_id":"randomId"}
         )
         self.assertEqual(Materi.objects.filter(title="Materi 2").count(), 0)
-    
+
     def test_detail_materi_has_video_if_yt_video_id_not_empty(self):
         self.test_upload_materi_with_valid_yt_video_id()
         pk = Materi.objects.get(title="Materi 1").pk
@@ -3087,7 +3090,7 @@ class ChangePasswordTest(TestCase):
         # Logout
         self.client.logout()
 
-    
+
 class SeeRatedMateriByUser(TestCase):
     def setUp(self):
         self.client = Client()
@@ -3262,28 +3265,28 @@ class PasswordValidatorPolicyTest(TestCase):
         self.password_length_lower_than_8 = "P4ss!"
         self.password_enforcing_policy = "Passw0rd!"
         self.validator = PasswordPolicyValidator()
-    
+
     def test_using_password_no_lowercase(self):
         self.assertRaises(ValidationError, self.validator.validate, self.password_no_lowercase)
 
     def test_using_password_no_upprcase(self):
         self.assertRaises(ValidationError, self.validator.validate, self.password_no_uppercase)
-    
+
     def test_using_password_no_digit(self):
         self.assertRaises(ValidationError, self.validator.validate, self.password_no_digit)
 
     def test_using_password_no_special_char(self):
         self.assertRaises(ValidationError, self.validator.validate, self.password_no_special_char)
-    
+
     def test_using_password_with_length_less_than_8(self):
         self.assertRaises(ValidationError, self.validator.validate, self.password_length_lower_than_8)
-    
+
     def test_using_password_using_correct_policy(self):
         self.assertEquals(self.validator.validate(self.password_enforcing_policy), None)
-    
+
 
 class LandingPageNavbarTest(TestCase):
-    
+
     def setUp(self):
         self.client = Client()
         self.contributor_credential = {
@@ -3442,4 +3445,3 @@ class MateriRecommendationTest(TestCase):
         response = Client().get("/?recommendation=1")
         list = [int(id) for id in re.findall(r"Materi\s(\d+)[^\d]", response.content.decode())]
         self.assertEqual(list, [1, 2])
-    
diff --git a/app/urls.py b/app/urls.py
index a8c7281cbdd6821bf7eace5f939520e099f53ad5..06e7d42410e9289b3f1342eb6cf58d6daa10e202 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -41,4 +41,7 @@ urlpatterns = [
     path("password_success/", views.password_success, name="password_success"),
     path("given-rating/", views.see_given_rating, name="see_given_rating"),
     path("submit-visitor/", SubmitVisitorView.as_view(), name="submit-visitor"),
+    path(r'subscribe/', views.subscribe, name='subscribe'),
+    path(r'subscribeform/', views.subscribeform, name='subscribeform'),
+    path(r'subscription_confirmation/', views.subscription_confirmation, name='subscription_confirmation'),
 ]
diff --git a/app/utils/email_utility.py b/app/utils/email_utility.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc720758597e59a51e26a060f58838a2a17ef60d
--- /dev/null
+++ b/app/utils/email_utility.py
@@ -0,0 +1,90 @@
+import logging, traceback
+from django.urls import reverse
+import requests
+from django.template.loader import get_template
+from django.utils.html import strip_tags
+from django.conf import settings
+import smtplib
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+import email
+import email.mime.application
+from django.utils.html import strip_tags
+
+
+def send_email(data):
+    try:
+        url = "https://api.mailgun.net/v3/<domain-name>/messages"
+        status = requests.post(
+            url,
+            auth=("api", settings.MAILGUN_API_KEY),
+            data={"from": "OKI  <https://app.mailgun.com/app/sending/domains/sandbox34675edbad2a4ab08e896c3bd80524ec.mailgun.org>",
+                  "to": [data["email"]],
+                  "subject": data["subject"],
+                  "text": data["plain_text"],
+                  "html": data["html_text"]}
+        )
+        logging.getLogger("info").info("Mail sent to " + data["email"] + ". status: " + str(status))
+        return status
+    except Exception as e:
+        logging.getLogger("error").error(traceback.format_exc())
+        return False
+
+
+def send_subscription_email(email, subscription_confirmation_url):
+    data = dict()
+    data["confirmation_url"] = subscription_confirmation_url
+    data["subject"] = "Please Confirm The Subscription"
+    data["email"] = email
+    template = get_template("app/emails/subscription.html")
+    data["html_text"] = template.render(data)
+    data["plain_text"] = strip_tags(data["html_text"])
+    return send_email(data)
+    
+
+def send_gmail(email, subscription_confirmation_url):
+    # create message
+    msg = MIMEMultipart('alternative')
+    msg['Subject'] = "Please verify your mail"
+    msg['From'] = "pmplmailer@gmail.com"
+    msg['To'] = email
+
+    # create body
+    html_text = '<div style="border:1px solid black">Thanks for subscribing to Us. Please click this link to activate your subscription : ' + subscription_confirmation_url + '</div>'
+    plain_text = strip_tags(html_text)
+
+    # Create the body of the message (a plain-text and an HTML version).
+    # Record the MIME types of both parts - text/plain and text/html.
+    part1 = MIMEText(plain_text, 'plain')
+    part2 = MIMEText(html_text, 'html')
+
+    # Attach image if any
+
+    # Attach parts into message container.
+    # According to RFC 2046, the last part of a multipart message, in this case
+    # the HTML message, is best and preferred.
+    msg.attach(part1)
+    msg.attach(part2)
+
+    # Send the message via local SMTP server.
+    host = "smtp.gmail.com"
+    port = 587
+    mail = smtplib.SMTP(host, port, timeout=60)
+    mail.ehlo()
+    mail.starttls()
+
+    # Add recepiens, cc or bcc if any
+    recepient = [msg["To"]]
+
+    # username and password of gmail id which will be used to send email
+    username = "pmplmailer@gmail.com"
+    password = "asdf1234$"
+
+    # login using credentials
+    mail.login(username, password)
+
+    # send email
+    mail.sendmail(msg["From"], recepient, msg.as_string())
+    mail.quit()
+
+    return "\nSent\n"
\ No newline at end of file
diff --git a/app/utils/encryption_util.py b/app/utils/encryption_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..a448767e066acceda561a4a48cc602628db41148
--- /dev/null
+++ b/app/utils/encryption_util.py
@@ -0,0 +1,37 @@
+from cryptography.fernet import Fernet
+import base64
+import logging
+import traceback
+from django.conf import settings
+
+#this is your "password/ENCRYPT_KEY". keep it in settings.py file
+#key = Fernet.generate_key() 
+
+def encrypt(txt):
+    try:
+        # convert integer etc to string first
+        txt = str(txt)
+        # get the key from settings
+        cipher_suite = Fernet(settings.ENCRYPT_KEY) # key should be byte
+        # #input should be byte, so convert the text to byte
+        encrypted_text = cipher_suite.encrypt(txt.encode('ascii'))
+        # encode to urlsafe base64 format
+        encrypted_text = base64.urlsafe_b64encode(encrypted_text).decode("ascii") 
+        return encrypted_text
+    except Exception as e:
+        # log the error if any
+        logging.getLogger("error_logger").error(traceback.format_exc())
+        return None
+
+
+def decrypt(txt):
+    try:
+        # base64 decode
+        txt = base64.urlsafe_b64decode(txt)
+        cipher_suite = Fernet(settings.ENCRYPT_KEY)
+        decoded_text = cipher_suite.decrypt(txt).decode("ascii")     
+        return decoded_text
+    except Exception as e:
+        # log the error
+        logging.getLogger("error_logger").error(traceback.format_exc())
+        return None
\ No newline at end of file
diff --git a/app/views.py b/app/views.py
index a0062ed070c2065cc77c3e37757c749b70f470d1..306e84073b13dea436e90faa2952bafcb618f81c 100644
--- a/app/views.py
+++ b/app/views.py
@@ -1,5 +1,9 @@
 import mimetypes
 import os
+import re
+import time
+import logging
+import traceback
 from io import BytesIO
 
 import django
@@ -15,8 +19,7 @@ from django.core.paginator import Paginator
 from django.db.models import Q, Avg
 from django.http import (Http404, HttpResponse, HttpResponseRedirect,
                          JsonResponse)
-from django.shortcuts import get_object_or_404, redirect
-from django.shortcuts import render
+from django.shortcuts import get_object_or_404, redirect, render
 from django.template import loader
 from django.urls import reverse
 from django.urls import reverse_lazy
@@ -34,6 +37,10 @@ from app.models import (
     SubmitVisitor
 )
 from authentication.models import User
+from app.utils.encryption_util import encrypt, decrypt
+from app.utils.email_utility import send_subscription_email, send_gmail
+from app.models import SubscribeModel
+from datetime import datetime
 from .services import DafterKatalogService, DetailMateriService, LikeDislikeService, MateriFieldValidationHelperService, \
     DownloadViewMateriHelperService, UploadMateriService, EditProfileService, RevisiMateriService, \
     DownloadHistoryService, GoogleDriveUploadService
@@ -205,7 +212,7 @@ class DetailMateri(TemplateView):
                         from_email = getattr(settings, 'EMAIL_HOST_USER'),
                         recipient_list = [materi_uploader.email,],
                         fail_silently = False,
-                    ) 
+                    )
             elif (review_text != None):
                 review = Review.objects.create(
                     review=review_text, username=DetailMateriService.get_user_name(request), materi=materi, user=user_obj
@@ -285,6 +292,98 @@ def add_rating_materi(request):
         )
     return JsonResponse({"success": False, "msg": "Forbidden"}, status=403)
 
+def subscribeform(request):
+    return render(request, 'subscribe.html', )
+
+def subscribe(request):
+    post_data = request.POST.copy()
+    email = post_data.get("email", None)
+
+    error_msg = validate_email(email)
+    if error_msg:
+        messages.error(request, error_msg)
+        return HttpResponseRedirect(reverse('subscribe'))
+
+    token = encrypt(email + "_" + str(time.time()))
+    subscription_confirmation_url = request.build_absolute_uri( reverse('subscription_confirmation')) + "?token=" + token
+    status = send_gmail(email, subscription_confirmation_url)
+    save_status = save_email(email)
+
+    if save_status:
+        token = encrypt(email + "_" + str(time.time()))
+        subscription_confirmation_url = request.build_absolute_uri(reverse('subscription_confirmation')) + "?token=" + token
+        status = send_gmail(email, subscription_confirmation_url)
+        if not status:
+            SubscribeModel.objects.get(email=email).delete()
+            logging.getLogger("info").info(
+                "Deleted the record from Subscribe table for " + email + " as email sending failed. status: " + str(
+                    status))
+        else:
+            msg = "Mail sent to email Id '" + email + "'. Please confirm your subscription by clicking on " \
+                                                      "confirmation link provided in email. " \
+                                                      "Please check your spam folder as well."
+            messages.success(request, msg)
+            return HttpResponse(msg)
+    else:
+        msg = "Some error occurred. Please try in some time. Meanwhile we are looking into it."
+        messages.error(request, msg)
+        return HttpResponse(msg)
+    return  HttpResponseRedirect(reverse('subscribeform'))
+
+def subscription_confirmation(request):
+    if "POST" == request.method:
+        raise Http404
+
+    token = request.GET.get("token", None)
+
+    if not token:
+        logging.getLogger("warning").warning("Invalid Link ")
+        messages.error(request, "Invalid Link")
+        return HttpResponseRedirect(reverse('subscribeform'))
+
+    token = decrypt(token)
+    if token:
+        token = token.split("_")
+        email = token[0]
+        print(email)
+        initiate_time = token[1]  # time when email was sent , in epoch format. can be used for later calculations
+        try:
+            subscribe_model_instance = SubscribeModel.objects.get(email=email)
+            subscribe_model_instance.status = "CONFIRMED"
+            subscribe_model_instance.updated_date = datetime.today()
+            subscribe_model_instance.save()
+            messages.success(request, "Subscription Confirmed. Thank you.")
+        except Exception as e:
+            logging.getLogger("warning").warning(traceback.format_exc())
+            messages.error(request, "Invalid Link")
+    else:
+        logging.getLogger("warning").warning("Invalid token ")
+        messages.error(request, "Invalid Link")
+
+    return HttpResponse('<h1>Thank you for subscribing us</h1>')
+
+def validate_email(email):
+    if email is None:
+        return "Email is required."
+    elif not re.match(r"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", email):
+        return "Invalid Email Address."
+    else:
+        return None
+
+def save_email(email):
+    try:
+        subscribe_model_instance = SubscribeModel.objects.get(email=email)
+    except Exception as e:
+        subscribe_model_instance = SubscribeModel()
+
+
+    # does not matter if already subscribed or not...resend the email
+    subscribe_model_instance.email = email
+    subscribe_model_instance.status = "SUBSCRIBED"
+    subscribe_model_instance.created_date = datetime.today()
+    subscribe_model_instance.updated_date = datetime.today()
+    subscribe_model_instance.save()
+    return True
 
 
 def download_materi(request, pk):
@@ -652,6 +751,7 @@ class RevisiMateriView(TemplateView):
             return self.render_to_response(context)
 
 
+
 def pages(request):
     context = {}
     # All resource paths end in .html.
diff --git a/authentication/migrations/0001_initial.py b/authentication/migrations/0001_initial.py
index b28fe9c3d35bef40e5c1a2e04b5c1acbc47333fd..8dd6568cd24f5cf72e9aba257659da57907aa573 100644
--- a/authentication/migrations/0001_initial.py
+++ b/authentication/migrations/0001_initial.py
@@ -1,9 +1,8 @@
-# Generated by Django 3.0.3 on 2020-05-08 14:42
-
-import django.utils.timezone
-from django.db import migrations, models
+# Generated by Django 3.1 on 2020-10-30 15:29
 
 import authentication.models
+from django.db import migrations, models
+import django.utils.timezone
 
 
 class Migration(migrations.Migration):
@@ -11,53 +10,41 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        ('auth', '0011_update_proxy_permissions'),
+        ('auth', '0012_alter_user_first_name_max_length'),
     ]
 
     operations = [
         migrations.CreateModel(
             name='User',
             fields=[
-                ('id', models.AutoField(auto_created=True,
-                                        primary_key=True, serialize=False, verbose_name='ID')),
-                ('password', models.CharField(
-                    max_length=128, verbose_name='password')),
-                ('last_login', models.DateTimeField(
-                    blank=True, null=True, verbose_name='last login')),
-                ('is_superuser', models.BooleanField(default=False,
-                                                     help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
-                ('first_name', models.CharField(blank=True,
-                                                max_length=30, verbose_name='first name')),
-                ('last_name', models.CharField(blank=True,
-                                               max_length=150, verbose_name='last name')),
-                ('is_staff', models.BooleanField(default=False,
-                                                 help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
-                ('is_active', models.BooleanField(
-                    default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
-                ('date_joined', models.DateTimeField(
-                    default=django.utils.timezone.now, verbose_name='date joined')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+                ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
+                ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
+                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
                 ('email', models.EmailField(max_length=254, unique=True)),
-                ('username', models.CharField(
-                    blank=True, default='', max_length=150)),
+                ('username', models.CharField(blank=True, default='', max_length=150)),
                 ('name', models.CharField(max_length=150)),
                 ('is_admin', models.BooleanField(default=False)),
                 ('is_contributor', models.BooleanField(default=False)),
                 ('instansi', models.CharField(max_length=240)),
-                ('nik', models.CharField(max_length=240)),
+                ('nik', models.CharField(max_length=16)),
                 ('alamat', models.CharField(max_length=240)),
                 ('nomor_telpon', models.CharField(max_length=240)),
                 ('linkedin', models.URLField(blank=True, default='')),
                 ('facebook', models.URLField(blank=True, default='')),
                 ('twitter', models.URLField(blank=True, default='')),
                 ('instagram', models.URLField(blank=True, default='')),
-                ('biography', models.TextField(
-                    blank=True, default='', max_length=200)),
-                ('profile_picture', models.ImageField(
-                    default='https://ibb.co/gv9YHt4', upload_to='profile_picture/')),
-                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
-                                                  related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
-                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.',
-                                                            related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
+                ('biography', models.TextField(blank=True, default='', max_length=200)),
+                ('default_profile_picture', models.BooleanField(blank=True, default=False)),
+                ('profile_picture', models.ImageField(default='default-image.jpg', upload_to='')),
+                ('is_subscribing_to_material_comments', models.BooleanField(default=True)),
+                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
+                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
             ],
             options={
                 'verbose_name': 'user',
diff --git a/authentication/migrations/0002_auto_20200518_1752.py b/authentication/migrations/0002_auto_20200518_1752.py
deleted file mode 100644
index 8e88a1b0ce600471b466fc503131e56fd6defd54..0000000000000000000000000000000000000000
--- a/authentication/migrations/0002_auto_20200518_1752.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-18 10:52
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='user',
-            name='profile_picture',
-            field=models.ImageField(default='https://i.ibb.co/9wgPzyZ/default-image.png', upload_to='profile_picture/'),
-        ),
-    ]
diff --git a/authentication/migrations/0003_auto_20200519_1012.py b/authentication/migrations/0003_auto_20200519_1012.py
deleted file mode 100644
index b705ff097ac330024e85c1b8c97daf8890e7a54e..0000000000000000000000000000000000000000
--- a/authentication/migrations/0003_auto_20200519_1012.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-19 03:12
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0002_auto_20200518_1752'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='user',
-            name='default_profile_picture',
-            field=models.URLField(blank=True, default="https://i.ibb.co/9wgPzyZ/default-image.png'"),
-        ),
-        migrations.AlterField(
-            model_name='user',
-            name='profile_picture',
-            field=models.ImageField(upload_to='', verbose_name=models.URLField(blank=True, default="https://i.ibb.co/9wgPzyZ/default-image.png'")),
-        ),
-    ]
diff --git a/authentication/migrations/0004_auto_20200519_1020.py b/authentication/migrations/0004_auto_20200519_1020.py
deleted file mode 100644
index 255dcf75f6a117a94a61e45177b7e4f257a1afc3..0000000000000000000000000000000000000000
--- a/authentication/migrations/0004_auto_20200519_1020.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-19 03:20
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0003_auto_20200519_1012'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='user',
-            name='default_profile_picture',
-            field=models.BooleanField(blank=True, default=False),
-        ),
-        migrations.AlterField(
-            model_name='user',
-            name='profile_picture',
-            field=models.ImageField(upload_to='', verbose_name=models.BooleanField(blank=True, default=False)),
-        ),
-    ]
diff --git a/authentication/migrations/0005_auto_20200519_1021.py b/authentication/migrations/0005_auto_20200519_1021.py
deleted file mode 100644
index 05bf864c6833afd6496877d76da9a976f7add55f..0000000000000000000000000000000000000000
--- a/authentication/migrations/0005_auto_20200519_1021.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.3 on 2020-05-19 03:21
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0004_auto_20200519_1020'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='user',
-            name='profile_picture',
-            field=models.ImageField(default='default-image.jpg', upload_to=''),
-        ),
-    ]
diff --git a/authentication/migrations/0006_auto_20200929_2125.py b/authentication/migrations/0006_auto_20200929_2125.py
deleted file mode 100644
index a2c2da4f14cc46dadd6f038686ae09c7205e6c72..0000000000000000000000000000000000000000
--- a/authentication/migrations/0006_auto_20200929_2125.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-09-29 14:25
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0005_auto_20200519_1021'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='user',
-            name='first_name',
-            field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
-        ),
-    ]
diff --git a/authentication/migrations/0007_auto_20201009_1415.py b/authentication/migrations/0007_auto_20201009_1415.py
deleted file mode 100644
index 012a5c89e717087a23903de27bbadbdf8798d63e..0000000000000000000000000000000000000000
--- a/authentication/migrations/0007_auto_20201009_1415.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-09 07:15
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0006_auto_20200929_2125'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='user',
-            name='nik',
-            field=models.CharField(max_length=16),
-        ),
-    ]
diff --git a/authentication/migrations/0008_user_is_subscribing_to_material_comments.py b/authentication/migrations/0008_user_is_subscribing_to_material_comments.py
deleted file mode 100644
index 2348d49d3ce1c3cbe3b11628b81dde0ad5be4478..0000000000000000000000000000000000000000
--- a/authentication/migrations/0008_user_is_subscribing_to_material_comments.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-29 12:52
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('authentication', '0007_auto_20201009_1415'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='user',
-            name='is_subscribing_to_material_comments',
-            field=models.BooleanField(default=True),
-        ),
-    ]
diff --git a/digipus/settings.py b/digipus/settings.py
index 884c3e67cf7924e4165161d8d50a3ce04361a306..3fc4cad5a64dc14ba4badf26c09479b5dc7be212 100644
--- a/digipus/settings.py
+++ b/digipus/settings.py
@@ -189,4 +189,5 @@ EMAIL_PORT = config('EMAIL_PORT', default=587) # use Google Mail SMTP as default
 EMAIL_HOST_USER = config('EMAIL_HOST_USER', default="pmplclass2020@gmail.com")
 EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default="pmpldigipusemail")
 EMAIL_USE_TLS = True
-EMAIL_USE_SSL = False
\ No newline at end of file
+EMAIL_USE_SSL = False
+ENCRYPT_KEY = b'YVLSFH4WgxkLgCOmCBchVmahU_VIF2xPFdD7GUTsvgY='
diff --git a/forum/migrations/0001_initial.py b/forum/migrations/0001_initial.py
index 09be1531345689e50d9940908018970274ada73f..652260331742b31ca6bacb8926451651385c17b3 100644
--- a/forum/migrations/0001_initial.py
+++ b/forum/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1 on 2020-10-06 14:39
+# Generated by Django 3.1 on 2020-10-30 15:30
 
 from django.conf import settings
 from django.db import migrations, models
@@ -11,6 +11,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('app', '0001_initial'),
     ]
 
     operations = [
@@ -19,7 +20,10 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('title', models.TextField()),
-                ('description', models.TextField(default='')),
+                ('description', models.TextField(blank=True, default='')),
+                ('created_at', models.DateTimeField(auto_now_add=True)),
+                ('updated_at', models.DateTimeField(auto_now=True)),
+                ('materi', models.ManyToManyField(blank=True, to='app.Materi')),
                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
         ),
@@ -28,7 +32,10 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('description', models.TextField(default='')),
+                ('created_at', models.DateTimeField(auto_now_add=True)),
+                ('updated_at', models.DateTimeField(auto_now=True)),
                 ('discussion', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.discussion')),
+                ('materi', models.ManyToManyField(blank=True, to='app.Materi')),
                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
         ),
diff --git a/forum/migrations/0002_auto_20201007_1903.py b/forum/migrations/0002_auto_20201007_1903.py
deleted file mode 100644
index f26c6bac7c379bbb8dc5bb85ecbb59bb0e4b8e0c..0000000000000000000000000000000000000000
--- a/forum/migrations/0002_auto_20201007_1903.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 3.1 on 2020-10-07 12:03
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-    dependencies = [
-        ('forum', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='discussion',
-            name='created_at',
-            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
-            preserve_default=False,
-        ),
-        migrations.AddField(
-            model_name='discussion',
-            name='updated_at',
-            field=models.DateTimeField(auto_now=True),
-        ),
-    ]
diff --git a/forum/migrations/0003_auto_20201008_2056.py b/forum/migrations/0003_auto_20201008_2056.py
deleted file mode 100644
index dab5b854233c101e4563576f554eacb33b0365e9..0000000000000000000000000000000000000000
--- a/forum/migrations/0003_auto_20201008_2056.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 3.1 on 2020-10-08 13:56
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-    dependencies = [
-        ('forum', '0002_auto_20201007_1903'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='discussioncomment',
-            name='created_at',
-            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
-            preserve_default=False,
-        ),
-        migrations.AddField(
-            model_name='discussioncomment',
-            name='updated_at',
-            field=models.DateTimeField(auto_now=True),
-        ),
-    ]
diff --git a/forum/migrations/0004_discussion_materi.py b/forum/migrations/0004_discussion_materi.py
deleted file mode 100644
index 8cbd9bd33e85ad3e50b4021d38ecc61d6afeebed..0000000000000000000000000000000000000000
--- a/forum/migrations/0004_discussion_materi.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-08 18:37
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-    dependencies = [
-        ('app', '0015_reqmaterial'),
-        ('forum', '0003_auto_20201008_2056'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='discussion',
-            name='materi',
-            field=models.ManyToManyField(blank=True, null=True, to='app.Materi'),
-        ),
-    ]
diff --git a/forum/migrations/0005_auto_20201009_0138.py b/forum/migrations/0005_auto_20201009_0138.py
deleted file mode 100644
index c4fc40216dcc5203cd5f094eac30a507e281ea11..0000000000000000000000000000000000000000
--- a/forum/migrations/0005_auto_20201009_0138.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.1 on 2020-10-08 18:38
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-    dependencies = [
-        ('app', '0015_reqmaterial'),
-        ('forum', '0004_discussion_materi'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='discussion',
-            name='materi',
-            field=models.ManyToManyField(blank=True, to='app.Materi'),
-        ),
-    ]
diff --git a/forum/migrations/0006_auto_20201009_0625.py b/forum/migrations/0006_auto_20201009_0625.py
deleted file mode 100644
index 3e59801201877f0ae3fa0d9ca0972d8aad393c17..0000000000000000000000000000000000000000
--- a/forum/migrations/0006_auto_20201009_0625.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 3.1 on 2020-10-08 23:25
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-    dependencies = [
-        ('app', '0015_reqmaterial'),
-        ('forum', '0005_auto_20201009_0138'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='discussioncomment',
-            name='materi',
-            field=models.ManyToManyField(blank=True, to='app.Materi'),
-        ),
-        migrations.AlterField(
-            model_name='discussion',
-            name='description',
-            field=models.TextField(blank=True, default=''),
-        ),
-    ]
diff --git a/news/migrations/0001_initial.py b/news/migrations/0001_initial.py
index 7c584641a29f5191a19848e13e52ed48722d8ff4..ed7921e164b76563aa96c5b5d43cf0260e5f7b1a 100644
--- a/news/migrations/0001_initial.py
+++ b/news/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1 on 2020-10-03 07:47
+# Generated by Django 3.1 on 2020-10-30 15:30
 
 from django.db import migrations, models