وبلاگ فربد | Farbod Blog

توی این وبلاگ راجع به پروژه هام صحبت می کنم

ساخت پلتفرم mail با جنگو - قسمت 2

  • ۵۷۵

سلام دوستان :)

 

توی پست امروز قراره که قسمت دوم ساخت پلتفرم mail با جنگو یعنی بخش front-end رو با هم ببینیم.

توی پست قبلی model های دیتابیس، view ها و از همه مهمتر API ها رو ساختیم و دیدیم که با فرستادن یک http request از نوع POST یا GET سرور ما یک response به ما برمیگردونه. حالا می خوایم یک صفحه HTML طراحی کنیم و با javascript بتونیم از اون API هایی که تعریف کردیم استفاده کنیم.

خب اول از همه توی فولدر mail(اونجایی که فایل های views.py, models.py و... هستن) یک فولدر به نام templates درست کنین(توجه کنین که اسم فولدر فقط باید templates باشه) و داخل فولدر templates یک فولدر به اسم mail درست کنین(این یکی هم حتما باید mail باشه) که بتونیم فایل های HTML که بعدا درست میکنیم رو داخلش قرار بدیم.

 

خب حالا توی فولدر mail(اونجایی که فایل های models.py, views.py و... هستن) یک  فولدر به نام static درست کنین(توجه کنین که اسم فولدر فقط باید static باشه) و داخل فولدر static یک فولدر دیگه به نام mail درست کنین(این یکی هم حتما mail باشه) که بتونیم فایل های CSS و javascript رو داخلش قرار بدیم.

 

خب بعد از درست کردن این فولدر ها برین داخل فولدر templates/mail چندین فایل به این اسم ها درست کنین:

1- inbox.html

2- layout.html

3- login.html

4- register.html

 

خب حالا فایل inbox.html رو باز کنین و کد های زیر رو داخلش وارد کنین:

{% extends "mail/layout.html" %} 
{% load static %} 
{% block body %}
<h2>{{ request.user.email }}</h2>

<button class="btn btn-sm btn-outline-primary" id="inbox">Inbox</button>
<button class="btn btn-sm btn-outline-primary" id="compose">Compose</button>
<button class="btn btn-sm btn-outline-primary" id="sent">Sent</button>
<button class="btn btn-sm btn-outline-primary" id="archived">Archived</button>
<a class="btn btn-sm btn-outline-primary" href="{% url 'logout' %}">Log Out</a>
<hr>

<div id="emails-view">

</div>
<div id="compose-view">
  <h3>New Email</h3>
  <div id="message"></div>
  <form id="compose-form">
    <div class="form-group">
      From:
      <input disabled class="form-control" value="{{ request.user.email }}" id="from">
    </div>
    <div class="form-group">
      To:
      <input id="compose-recipients" class="form-control" name="to">
    </div>
    <div class="form-group">
      <input class="form-control" id="compose-subject" placeholder="Subject">
    </div>
    <textarea class="form-control" id="compose-body" placeholder="Body"></textarea>
    <input type="submit" class="btn btn-primary my-3" value="Send">
  </form>
</div>
{% endblock %} 
{% block script %}
<script src="{% static 'mail/inbox.js' %}"></script>
{% endblock %}

توضیح: اول با استفاده از extends، فایل layout.html رو extend کردیم که به این معنی هست که دیگه لازم نیست یک سری تگ ها مثل head, body و... رو بذاریم.

بعدش فایل های static(فایل های javascript و css مون که بعدا بهتون میگم) رو load کردیم.

حالا توی بلاک body اومدیم چندتا دکمه برای جابجایی بین inbox, compose, send و archived گذاشتیم.

حالا یک div با آیدی emails-view درست کردیم که بعدا بتونیم ایمیل ها رو با استفاده از جاوااسکریپت داخلش قرار بدیم.

بعد توی یک div دیگه فرم فرستادن ایمیل رو تعریف کردیم که البته کاربر توی صفحه اصلی اون رو نمیبینه و باید روی دکمه compose کلیک کنه تا بتونه فرمی که درست کردیم رو ببینه.

توی سه خط آخر هم script هامون رو load کردیم.

 

خب حالا فایل layout.html رو باز کنید و کد های زیر رو داخلش قرار بدین:

{% load static %}

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>{% block title %}Mail{% endblock %}</title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
        <link rel="stylesheet" href="{% static 'mail/styles.css' %}">
        {% block script %}
        {% endblock %}
    </head>
    <body>
        <div class="container">
            {% block body %}
            {% endblock %}
        </div>
    </body>
</html>

توضیح: خب اینجا کار خاصی نکردیم فقط یک سری بلاک تعریف کردیم و با استفاده از تگ link فریم ورک bootstrap رو لود کردیم.

 

حالا فایل login.html رو باز کنین و کد های زیر رو داخلش قرار بدین:

{% extends "mail/layout.html" %}

{% block body %}

    <h2>Login</h2>

    {% if message %}
        <div>{{ message }}</div>
    {% endif %}

    <form action="{% url 'login' %}" method="post">
        {% csrf_token %}
        <div class="form-group">
            <input autofocus class="form-control" type="email" name="email" placeholder="Email">
        </div>
        <div class="form-group">
            <input class="form-control" type="password" name="password" placeholder="Password">
        </div>
        <input class="btn btn-primary" type="submit" value="Login">
    </form>

    Don't have an account? <a href="{% url 'register' %}">Register here.</a>

{% endblock %}

توضیح: این جا هم یک فرم login با HTML درست کردیم.

 

حالا فایل register.html رو باز کنین و کد های زیر رو داخلش قرار بدین:

{% extends "mail/layout.html" %}

{% block body %}

    <h2>Register</h2>

    {% if message %}
        <div>{{ message }}</div>
    {% endif %}

    <form action="{% url 'register' %}" method="post">
        {% csrf_token %}
        <div class="form-group">
            <input class="form-control" type="email" name="email" placeholder="Email Address">
        </div>
        <div class="form-group">
            <input class="form-control" type="password" name="password" placeholder="Password">
        </div>
        <div class="form-group">
            <input class="form-control" type="password" name="confirmation" placeholder="Confirm Password">
        </div>
        <input class="btn btn-primary" type="submit" value="Register">
    </form>

    Already have an account? <a href="{% url 'login' %}">Log In here.</a>

{% endblock %}

توضیح: این هم باز چیز خاصی نداره و یک فرم register با HTML درست کردیم.

 

حالا فایل هایی که قرار بود توی templates/mail قرار بدیم تموم شدن.

الان میرسیم به فایل های static/mail.

 

خب حالا میرسیم به درست کردن اصلی ترین فایل یعنی inbox.js.

فایل inbox.js رو داخل فولدر static/mail درست کنین و مقادیر زیر رو داخلش قرار بدین:

document.addEventListener(
  "DOMContentLoaded",
  function () {
    const form = document.querySelector("#compose-form");
    const msg = document.querySelector("#message");
    form.addEventListener("submit", (event) => {
      event.preventDefault();
      to = document.querySelector("#compose-recipients");
      subject = document.querySelector("#compose-subject");
      body = document.querySelector("#compose-body");
      if (from.length == 0 && to.length == 0) return;

      fetch("/emails", {
        method: "POST",
        body: JSON.stringify({
          recipients: to.value,
          subject: subject.value,
          body: body.value,
        }),
      })
        .then((response) => response.json())
        .then((result) => {
          console.log(result.status);
          if (result.status == 201) {
            load_mailbox("sent");
          } else {
            msg.innerHTML = `<div class="alert alert-danger" role="alert">
            ${result.error}
          </div>`;
          }
        });
    });
  },
  false
);

document.addEventListener("DOMContentLoaded", function () {
  // Use buttons to toggle between views
  document.querySelector("#inbox").addEventListener("click", () => load_mailbox("inbox"));
  document.querySelector("#sent").addEventListener("click", () => load_mailbox("sent"));
  document.querySelector("#archived").addEventListener("click", () => load_mailbox("archive"));
  document.querySelector("#compose").addEventListener("click", compose_email);

  // By default, load the inbox
  load_mailbox("inbox");
});

function compose_email() {
  // Show compose view and hide other views
  document.querySelector("#emails-view").style.display = "none";
  document.querySelector("#compose-view").style.display = "block";

  // Clear out composition fields
  document.querySelector("#compose-recipients").value = "";
  document.querySelector("#compose-subject").value = "";
  document.querySelector("#compose-body").value = "";
}

function load_mailbox(mailbox) {
  // Show the mailbox and hide other views
  document.querySelector("#emails-view").style.display = "block";
  document.querySelector("#compose-view").style.display = "none";

  // Show the mailbox name
  document.querySelector("#emails-view").innerHTML = `<h3>${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}</h3>`;

  if (mailbox == "show_mail") {
    show_mail();
    return;
  }

  fetch(`/emails/${mailbox}`)
    .then((response) => response.json())
    .then((emails) => {
      emails.forEach((element) => {
        if (mailbox != "sent") {
          sender_recipients = element.sender;
        } else {
          sender_recipients = element.recipients;
        }
        if (mailbox == "inbox") {
          if (element.read) is_read = "read";
          else is_read = "";
        }
        var item = document.createElement("div");
        item.className = `card ${is_read} my-1 items`;

        item.innerHTML = `<div style="border: 1px solid black; height: 35px;" id="item-${element.id}">
        
        <strong style="font-size: 14pt;">&nbsp;${sender_recipients}</strong>&nbsp;&nbsp;&nbsp; ${element.subject} <div style="float: right; font-weight: 150">${element.timestamp}</div>
        
        <br>
      </div>`;
        document.querySelector("#emails-view").appendChild(item);
        item.addEventListener("click", () => {
          show_mail(element.id, mailbox);
        });
      });
    });
}

function show_mail(id, mailbox) {
  fetch(`/emails/${id}`)
    .then((response) => response.json())
    .then((email) => {
      // Print email
      // console.log(email);
      document.querySelector("#emails-view").innerHTML = "";
      var item = document.createElement("div");
      item.innerHTML = `<div style="white-space: pre-wrap;">
  <strong>From:</strong> ${email.sender}
  <strong>To:</strong> ${email.recipients}
  <strong>Subject:</strong> ${email.subject}
  <strong>Timestamp:</strong> ${email.timestamp}
  </div>
  `;
      document.querySelector("#emails-view").appendChild(item);
      if (mailbox == "sent") return;
      let archive = document.createElement("btn");
      archive.className = `btn btn-outline-primary my-2`;
      archive.addEventListener("click", () => {
        toggle_archive(id, email.archived);
        if (archive.innerText == "Archive") archive.innerText = "Unarchive";
        else archive.innerText = "Archive";
      });
      if (!email.archived) archive.textContent = "Archive";
      else archive.textContent = "Unarchive";
      document.querySelector("#emails-view").appendChild(archive);

      let reply = document.createElement("btn");
      reply.className = `btn btn-outline-primary m-2`;
      reply.textContent = "Reply";
      reply.addEventListener("click", () => {
        reply_mail(email.sender, email.subject, email.body, email.timestamp);
      });
      document.querySelector("#emails-view").appendChild(reply);
      make_read(id);
      let hr = document.createElement("HR");
      document.querySelector("#emails-view").appendChild(hr);
      let message = document.createElement("div");
      message.innerHTML = `${email.body}`;
      document.querySelector("#emails-view").appendChild(message);
    });
}

function toggle_archive(id, state) {
  fetch(`/emails/${id}`, {
    method: "PUT",
    body: JSON.stringify({
      archived: !state,
    }),
  });
}

function make_read(id) {
  fetch(`/emails/${id}`, {
    method: "PUT",
    body: JSON.stringify({
      read: true,
    }),
  });
}

function reply_mail(sender, subject, body, timestamp) {
  compose_email();
  if (!/^Re:/.test(subject)) subject = `Re: ${subject}`;
  document.querySelector("#compose-recipients").value = sender;
  document.querySelector("#compose-subject").value = subject;

  pre_fill = `On ${timestamp} ${sender} wrote:\n${body}\n`;

  document.querySelector("#compose-body").value = pre_fill;
}

توضیح: خب توابع برای فرستادن ایمیل، لود کردن ایمیل، reply دادن و... هست. حالا چجوری میتونیم از API استفاده کنیم؟ با استفاده از تابع fetch.

ما با استفاده از تابع fetch میتونیم انواع request ها رو به یک آدرس بفرستیم و respose اون رو دریافت کنیم و هر کار میخوایم باهاش بکنیم. این هم دقیقا همون چیزیه که ما میخوایم :)

 

خب حالا که بخش جاوااسکریپت رو نوشتیم می رسیم به css. توی فولدر static/mail، یک فایل به اسم styles.css درست کنین و مقادیر زیر رو داخلش بذارین:

textarea {
  min-height: 200px;
}

.items {
  cursor: pointer;
}

.read {
  background-color: #e6e6e6;
}

 

خب این هم از این.

حالا کارمون تموم شد :))

فقط مونده اجرا کردن پروژه و تست کردنش!

خب cmd یا ترمینال رو باز کنین و وارد فولدر پروژتون بشین.

حالا دستور زیر رو وارد کنین:

python manage.py makemigrations

بعد هم این دستور:

python manage.py migrate

حالا در نهایت برای اجرا کردن پروژه دستور زیر رو وارد کنین:

python manage.py runserver

الان مرورگرتون رو باز کنین و وارد آدرس زیر بشین:

http://127.0.0.1:8000

حالا میتونین صفحه رو ببینین!

اگر با هر مشکلی مواجه شدین من براتون اینجا فایل zip رو که کل محتویات پروژه داخلش هست رو میذارم که اگه خواستین بتونین از روی اون پروژه رو run کنین.

برای دانلود کلیک کنین.

امیدوارم براتون مفید بوده باشه D:


  • سلام خوبید
    من چک کردم.کد به درستی نوشته شده.جاوا اسکریپت مرورگر فعال هست.
    با ابن حال برنامه کار نمی کنه. به نظرتون چه مشکلی داره؟
    ممنون
    پاسخ:
    سلام دوست عزیز.
    من دوباره کد رو چک کردم و کار میکنه.
    مطمئنید کتابخونه ها درست نصب شدن؟ احتمالا یک جای کار توی اجرای کد ها اشتباهه. اگر باز هم مشکلی بود میتونید توسط ایمیل با من در ارتباط باشید.
    موفق باشید.
  • سلام ممنون از آموزش خوبتون. من تمام کارهایی که گفتین انجام دادم ولی هیچ کار خاصی انجام نمیده یعنی وقتی می خوام ایمیل بفرستم صندوق ارسال همچنان خالی هست.با فایل خودتون هم تست کردم اونم همین طوره.ئهلقشفث رو هم وارد کردم.
    ممنون میشم پاسخ بدین.
    پاسخ:
    سلام دوست عزیز.
    مطمئن بشید که کد های جاوااسکریپت به درستی اجرا میشن. بعد از ارسال ایمیل باید مقداری صبر کنید. برای رفع بهتر مشکل میتونید بخش network رو توی inspector مرورگرتون باز کنید و دقیقا ببینید که چه بسته هایی ارسال نمیشن و مشکل از کجاست. همچنین مطمئن بشید که جاوااسکریپت مرورگرتون روشنه در غیر این صورت برنامه درست کار نمیکنه.
    اگر مشکل باز هم رفع نشد همینجا یا داخل ایمیل با من در ارتباط باشید.
    موفق باشید.
  • ممنون از آموزش عالیتون. پسوورد فایل ضمیمه رو لطف میکنید؟
    پاسخ:
    سلام دوست عزیز
    ممنون از لطف و همراهی شما.
    فایل پسوورد نداره و میتونین به راحتی ازش استفاده کنین. (اگر فایل باز نمیشه یا ارور میده میتونید متن ارور رو همینجا یا برای ایمیل در صفحه تماس با من ارسال کنین تا من راهنماییتون کنم).
    موفق باشید.
  • خاص درست مثل خودت
  • عااااااااالی
    بینظیییییییر
    محشرررررررررره
    واقعا ممنونم از وبلاگ فوق العادتون
    پاسخ:
    سلام دوست عزیز
    ممنون از توجه و همراهی شما
    موفق باشید
ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی