feat: Email Campaign
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.json b/erpnext/crm/doctype/email_campaign/email_campaign.json
index d7113f6..66b3546 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.json
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.json
@@ -7,20 +7,23 @@
  "field_order": [
   "campaign_section",
   "campaign_name",
-  "lead",
-  "column_break_4",
+  "email_campaign_for",
   "start_date",
+  "column_break_4",
+  "sender",
+  "recipient",
+  "end_date",
   "status",
   "email_schedule_section",
   "email_schedule",
-  "naming_series",
-  "amended_from"
+  "unsubscribed",
+  "naming_series"
  ],
  "fields": [
   {
    "fieldname": "campaign_section",
    "fieldtype": "Section Break",
-   "label": "CAMPAIGN "
+   "label": "Campaign"
   },
   {
    "fieldname": "campaign_name",
@@ -31,19 +34,10 @@
    "reqd": 1
   },
   {
-   "fieldname": "lead",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Lead",
-   "options": "Lead",
-   "reqd": 1
-  },
-  {
-   "default": "Started",
    "fieldname": "status",
    "fieldtype": "Select",
    "label": "Status",
-   "options": "\nDraft\nSubmitted\nStarted\nIn Progress\nCompleted"
+   "options": "\nScheduled\nIn Progress\nCompleted\nUnsubscribed"
   },
   {
    "fieldname": "column_break_4",
@@ -58,7 +52,7 @@
   {
    "fieldname": "email_schedule_section",
    "fieldtype": "Section Break",
-   "label": "EMAIL SCHEDULE"
+   "label": "Email Schedule"
   },
   {
    "fieldname": "email_schedule",
@@ -75,17 +69,41 @@
    "reqd": 1
   },
   {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Email Campaign",
-   "print_hide": 1,
+   "fieldname": "end_date",
+   "fieldtype": "Date",
+   "label": "End Date",
    "read_only": 1
+  },
+  {
+   "default": "Lead",
+   "fieldname": "email_campaign_for",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Email Campaign For ",
+   "options": "\nLead\nContact"
+  },
+  {
+   "fieldname": "recipient",
+   "fieldtype": "Dynamic Link",
+   "label": "Recipient",
+   "options": "email_campaign_for",
+   "reqd": 1
+  },
+  {
+   "default": "__user",
+   "fieldname": "sender",
+   "fieldtype": "Link",
+   "label": "Sender",
+   "options": "User"
+  },
+  {
+   "default": "0",
+   "fieldname": "unsubscribed",
+   "fieldtype": "Check",
+   "label": "Unsubscribed"
   }
  ],
- "is_submittable": 1,
- "modified": "2019-06-30 23:00:24.765312",
+ "modified": "2019-07-09 15:07:03.328591",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Email Campaign",
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index 82ee6a6..1132226 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -5,14 +5,18 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import getdate, add_days, nowdate
+from frappe.utils import getdate, add_days, today, nowdate, cstr
 from frappe.model.document import Document
-from frappe.email.inbox import link_communication_to_document
+from frappe.core.doctype.communication.email import make
 
 class EmailCampaign(Document):
 	def validate(self):
 		self.validate_dates()
-		self.validate_lead()
+		#checking if email is set for lead. Not checking for contact as email is a mandatory field for contact.
+		if self.email_campaign_for == "Lead":
+			self.validate_lead()
+		self.set_end_date()
+		self.update_status()
 
 	def validate_dates(self):
 		campaign = frappe.get_doc("Campaign", self.campaign_name)
@@ -30,37 +34,105 @@
 			frappe.throw(_("Email Schedule cannot extend Campaign End Date"))
 
 	def validate_lead(self):
-		lead = frappe.get_doc("Lead", self.lead)
+		lead = frappe.get_doc("Lead", self.recipient)
 		if not lead.get("email_id"):
-			frappe.throw(_("Please set email id for lead communication"))
+			frappe.throw(_("Please set an email id for lead communication"))
 
-	def send(self):
-		lead = frappe.get_doc("Lead", self.get("lead"))
-		email_schedule =  frappe.get_doc("Campaign Email Schedule", self.get("email_schedule"))
-		email_template = frappe.get_doc("Email Template", email_schedule.name)
-		frappe.sendmail(
-			recipients = lead.get("email_id"),
-			sender = lead.get("lead_owner"),
-			subject = email_template.get("subject"),
-			message = email_template.get("response"),
-			reference_doctype = self.doctype,
-			reference_name = self.name
-		)
+	def set_end_date(self):
+		#set the end date as start date + max(send after days) in email schedule
+		send_after_days = []
+		for entry in self.get("email_schedule"):
+			send_after_days.append(entry.send_after_days)
+		self.end_date = add_days(getdate(self.start_date), max(send_after_days))
 
-	def on_submit(self):
-		"""Create a new communication linked to the campaign if not created"""
-		if not frappe.db.sql("select subject from tabCommunication where reference_name = %s", self.name):
-			doc = frappe.new_doc("Communication")
-			doc.subject = "Email Campaign Communication: " + self.name
-			link_communication_to_document(doc, "Email Campaign", self.name, ignore_communication_links = False)
+	def update_status(self):
+		start_date = getdate(self.start_date)
+		end_date = getdate(self.end_date)
+		today_date = getdate(today())
+		if self.unsubscribed:
+			self.status = "Unsubscribed"
+		else:
+			if start_date > today_date:
+				self.status = "Scheduled"
+			elif end_date >= today_date:
+				self.status = "In Progress"
+			elif end_date < today_date:
+				self.status = "Completed"
 
-@frappe.whitelist()
+#called through hooks to send campaign mails to leads
 def send_email_to_leads():
-	email_campaigns = frappe.get_all("Email Campaign", filters = { 'start_date': ("<=", nowdate()) })
+	email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('not in', ['Unsubscribed', 'Completed', 'Scheduled']), 'unsubscribed': 0 })
 	for campaign in email_campaigns:
 		email_campaign = frappe.get_doc("Email Campaign", campaign.name)
 		for entry in email_campaign.get("email_schedule"):
 			scheduled_date = add_days(email_campaign.get('start_date'), entry.get('send_after_days'))
-			if(scheduled_date == nowdate()):
-				email_campaign.send()
-# send_email_to_leads()
+			if scheduled_date == getdate(today()):
+				send_mail(entry, email_campaign)
+
+def send_mail(entry, email_campaign):
+	if email_campaign.email_campaign_for == "Lead":
+		lead = frappe.get_doc("Lead", email_campaign.get("recipient"))
+		recipient_email = lead.email_id
+	elif email_campaign.email_campaign_for == "Contact":
+		recipient = frappe.get_doc("Contact", email_campaign.get("recipient"))
+		recipient_email = recipient.email_id
+	email_template = frappe.get_doc("Email Template", entry.get("email_template"))
+	sender = frappe.get_doc("User", email_campaign.get("sender"))
+	sender_email = sender.email
+	# send mail and link communication to document
+	comm = make(
+		doctype = "Email Campaign",
+		name = email_campaign.name,
+		subject = email_template.get("subject"),
+		content = email_template.get("response"),
+		sender = sender_email,
+		recipients = recipient_email,
+		communication_medium = "Email",
+		sent_or_received = "Sent",
+		send_email = False,
+		email_template = email_template.name
+	)
+	frappe.sendmail(
+		recipients = recipient_email,
+		sender = sender_email,
+		subject = email_template.get("subject"),
+		content = email_template.get("response"),
+		reference_doctype = "Email Campaign",
+		reference_name = email_campaign.name,
+		unsubscribe_method = "/api/method/erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient",
+		unsubscribe_params = {"name": email_campaign.name, "email": recipient_email},
+		unsubscribe_message = "Stop Getting Email Campaign Mails",
+		communication = comm.get("name")
+	)
+
+@frappe.whitelist(allow_guest=True)
+def unsubscribe_recipient(name, email):
+	# unsubsribe from comments and communications
+	try:
+		frappe.get_doc({
+			"doctype": "Email Unsubscribe",
+			"email": email,
+			"reference_doctype": "Email Campaign",
+			"reference_name": name
+		}).insert(ignore_permissions=True)
+
+	except frappe.DuplicateEntryError:
+		frappe.db.rollback()
+
+	else:
+		frappe.db.commit()
+	frappe.db.set_value("Email Campaign", name, "unsubscribed", 1)
+	frappe.db.set_value("Email Campaign", name, "status", "Unsubscribed")
+	frappe.db.commit()
+	return_unsubscribed_page(email, name)
+
+def return_unsubscribed_page(email, name):
+	frappe.respond_as_web_page(_("Unsubscribed"),
+		_("{0} has left the Email Campaign {1}").format(email, name),
+		indicator_color='green')
+
+#called through hooks to update email campaign status daily
+def set_email_campaign_status():
+	email_campaigns = frappe.get_all("Email Campaign")
+	for email_campaign in email_campaigns:
+		email_campaign.update_status()
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign_list.js b/erpnext/crm/doctype/email_campaign/email_campaign_list.js
new file mode 100644
index 0000000..d1bfdd3
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign_list.js
@@ -0,0 +1,11 @@
+frappe.listview_settings['Email Campaign'] = {
+	get_indicator: function(doc) {
+		var colors = {
+			"Unsubscribed": "red",
+			"Scheduled": "blue",
+      "In Progress": "orange",
+      "Completed": "green"
+		}
+		return [__(doc.status), colors[doc.status], "status,=," + doc.status];
+	}
+};
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 1466d24..1b34a59 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -266,7 +266,8 @@
 		"erpnext.projects.doctype.project.project.send_project_status_email_to_users",
 		"erpnext.quality_management.doctype.quality_review.quality_review.review",
 		"erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status",
-		"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads"
+		"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads",
+		"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status"
 	],
 	"daily_long": [
 		"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"