Fakultas Ilmu Komputer UI

Commit ea1bd139 authored by oki priyadi's avatar oki priyadi
Browse files

add subscribe function

parent f7557f93
Pipeline #58125 failed with stages
in 33 minutes and 47 seconds
# Generated by Django 3.1 on 2020-10-09 07:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0017_auto_20201005_2145'),
]
operations = [
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',
},
),
]
......@@ -145,4 +145,18 @@ class RatingContributor(models.Model):
if 1 <= self.score <= 5:
super().save(force_insert, force_update, using, update_fields)
else:
raise ValidationError("Rating score must be integer between 1-5")
\ No newline at end of file
raise ValidationError("Rating score must be integer between 1-5")
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
\ No newline at end of file
<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
......@@ -54,6 +54,7 @@
<div class="container">
<div class="row header">
<div class="col">
<a href= "/subscribeform"><img src="{% static 'images/sub-btn.png' %}" style="height:100px"></a>
<h2 class="pageTitle">Temukan Materi Yang Kamu Mau!</h2>
<p class="description">Cari dengan judul buku, penerbit, atau penulis</p>
<form class="searchBar" action=''>
......
<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
......@@ -25,7 +25,8 @@ from .views import (DaftarKatalog, DashboardKontributorView, DetailMateri,
from app.forms import SuntingProfilForm
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
class DaftarKatalogTest(TestCase):
def test_daftar_katalog_url_exist(self):
......@@ -1658,3 +1659,13 @@ class RatingContributorTest(TransactionTestCase):
self.assertEqual(0, RatingContributor.objects.filter(user=self.contributor.id).count())
self.client.post(url, data={"user": self.contributor.id, "score": 0})
self.assertEqual(0, RatingContributor.objects.filter(user=self.contributor.id).count())
class EmailTest(TestCase):
def test_send_email(self):
send_gmail('bass_of_ncreep@yahoo.com', 'Here is the message')
mail.send_mail('Subject here', 'Here is the message.',
'pmplmailer@gmail.com', ['bass_of_ncreep@yahoo.com'],
fail_silently=False)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, 'Subject here')
\ No newline at end of file
......@@ -32,4 +32,7 @@ urlpatterns = [
path("profil/<str:email>/", KatalogPerKontributorView.as_view(),
name="katalog-per-kontributor"),
path("materi/rate/", views.add_rating_materi, name="rate-materi"),
path(r'subscribe/', views.subscribe, name='subscribe'),
path(r'subscribeform/', views.subscribeform, name='subscribeform'),
path(r'subscription_confirmation/', views.subscription_confirmation, name='subscription_confirmation'),
]
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
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
import mimetypes
import os
import re
import time
import logging
import traceback
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import AnonymousUser
......@@ -9,7 +12,7 @@ from django.core.paginator import Paginator
from django.db.models import Q, Count
from django.http import (Http404, HttpResponse, HttpResponseRedirect,
JsonResponse)
from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader
from django.views.generic import TemplateView
......@@ -23,6 +26,11 @@ import django
import pandas as pd
from io import BytesIO
from django.contrib import messages
from django.urls import reverse
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
class DaftarKatalog(TemplateView):
......@@ -235,7 +243,99 @@ def add_rating_materi(request):
return JsonResponse({"success": True, "msg": "Rating successfully created", "rating_score": rating_score},
status=201)
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):
materi = get_object_or_404(Materi, pk=pk)
......@@ -736,6 +836,8 @@ class RevisiMateriView(TemplateView):
context["form_revisi"] = form
return self.render_to_response(context)
def pages(request):
context = {}
# All resource paths end in .html.
......
......@@ -163,3 +163,5 @@ MEDIA_URL = "/media/"
LOGOUT_REDIRECT_URL = "/"
CRISPY_TEMPLATE_PACK = 'bootstrap4'
ENCRYPT_KEY = b'YVLSFH4WgxkLgCOmCBchVmahU_VIF2xPFdD7GUTsvgY='
MAILGUN_API_KEY = 'a93a52270d7871930c8813bf0b96f21b-0d2e38f7-bb01a5b0'
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment