From a3672e9d91e21ef356bbf2de3a2f499756796a06 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Mon, 16 Aug 2021 13:33:08 +0530 Subject: [PATCH] feat: discussions --- .../discussion_message.json | 27 +++- .../discussion_message/discussion_message.py | 18 ++- .../discussion_thread/discussion_thread.json | 4 +- .../discussion_thread/discussion_thread.py | 44 +++++- .../community/widgets/DiscussionComment.html | 70 +++++++++ .../community/widgets/DiscussionMessage.html | 80 ++++++++++ community/hooks.py | 6 +- community/lms/doctype/lms_message/__init__.py | 0 .../lms/doctype/lms_message/lms_message.js | 8 - .../lms/doctype/lms_message/lms_message.json | 85 ----------- .../lms/doctype/lms_message/lms_message.py | 110 ------------- .../doctype/lms_message/test_lms_message.py | 10 -- community/lms/widgets/BreadCrumb.html | 12 +- community/lms/widgets/ChapterTeaser.html | 2 +- community/patches.txt | 1 + community/public/css/style.css | 144 ++++++------------ community/public/icons/message.svg | 5 + community/public/icons/small-add.svg | 4 + community/templates/message_card.html | 12 ++ community/www/batch/discuss.html | 47 ------ community/www/batch/discuss.js | 35 ----- community/www/batch/discuss.py | 8 - community/www/batch/learn.html | 8 +- community/www/courses/course.js | 1 + community/www/discussions/discussion.html | 16 ++ community/www/discussions/discussion.py | 18 +++ community/www/discussions/index.html | 65 ++++++++ community/www/discussions/index.js | 14 ++ community/www/discussions/index.py | 17 +++ 29 files changed, 451 insertions(+), 420 deletions(-) create mode 100644 community/community/widgets/DiscussionComment.html create mode 100644 community/community/widgets/DiscussionMessage.html delete mode 100644 community/lms/doctype/lms_message/__init__.py delete mode 100644 community/lms/doctype/lms_message/lms_message.js delete mode 100644 community/lms/doctype/lms_message/lms_message.json delete mode 100644 community/lms/doctype/lms_message/lms_message.py delete mode 100644 community/lms/doctype/lms_message/test_lms_message.py create mode 100644 community/public/icons/message.svg create mode 100644 community/public/icons/small-add.svg create mode 100644 community/templates/message_card.html delete mode 100644 community/www/batch/discuss.html delete mode 100644 community/www/batch/discuss.js delete mode 100644 community/www/batch/discuss.py create mode 100644 community/www/discussions/discussion.html create mode 100644 community/www/discussions/discussion.py create mode 100644 community/www/discussions/index.html create mode 100644 community/www/discussions/index.js create mode 100644 community/www/discussions/index.py diff --git a/community/community/doctype/discussion_message/discussion_message.json b/community/community/doctype/discussion_message/discussion_message.json index c335fca2..4a7d653d 100644 --- a/community/community/doctype/discussion_message/discussion_message.json +++ b/community/community/doctype/discussion_message/discussion_message.json @@ -5,25 +5,46 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "message", - "thread" + "thread", + "column_break_2", + "parent_message", + "section_break_4", + "message" ], "fields": [ { "fieldname": "message", "fieldtype": "Long Text", + "in_list_view": 1, "label": "Message" }, { "fieldname": "thread", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Thread", "options": "Discussion Thread" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "parent_message", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Message", + "options": "Discussion Message" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-08-11 10:59:38.597046", + "modified": "2021-08-12 15:59:04.811286", "modified_by": "Administrator", "module": "Community", "name": "Discussion Message", diff --git a/community/community/doctype/discussion_message/discussion_message.py b/community/community/doctype/discussion_message/discussion_message.py index 2ca8893b..3a9e672d 100644 --- a/community/community/doctype/discussion_message/discussion_message.py +++ b/community/community/doctype/discussion_message/discussion_message.py @@ -1,8 +1,22 @@ # Copyright (c) 2021, FOSS United and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document +from community.widgets import Widget, Widgets class DiscussionMessage(Document): - pass + def after_insert(self): + data = { + "message": self, + "widgets": Widgets() + } + template = frappe.render_template("community/templates/message_card.html", data) + thread_info = frappe.db.get_value("Discussion Thread", self.thread, ["reference_doctype", "reference_docname"], as_dict=True) + frappe.publish_realtime(event="publish_message", + message = { + "thread": self.thread, + "template": template, + "thread_info": thread_info + }, + after_commit=True) diff --git a/community/community/doctype/discussion_thread/discussion_thread.json b/community/community/doctype/discussion_thread/discussion_thread.json index 3ceeba8b..5442c60b 100644 --- a/community/community/doctype/discussion_thread/discussion_thread.json +++ b/community/community/doctype/discussion_thread/discussion_thread.json @@ -30,7 +30,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-08-11 10:55:29.341674", + "modified": "2021-08-11 12:29:43.564123", "modified_by": "Administrator", "module": "Community", "name": "Discussion Thread", @@ -49,7 +49,9 @@ "write": 1 } ], + "search_fields": "title", "sort_field": "modified", "sort_order": "DESC", + "title_field": "title", "track_changes": 1 } \ No newline at end of file diff --git a/community/community/doctype/discussion_thread/discussion_thread.py b/community/community/doctype/discussion_thread/discussion_thread.py index cd44880c..cef7ef57 100644 --- a/community/community/doctype/discussion_thread/discussion_thread.py +++ b/community/community/doctype/discussion_thread/discussion_thread.py @@ -1,8 +1,48 @@ # Copyright (c) 2021, FOSS United and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document class DiscussionThread(Document): - pass + pass + +@frappe.whitelist() +def submit_discussion(doctype, docname, message, title=None, thread_name=None): + thread = [] + filters = {} + if doctype and docname: + filters = { + "reference_doctype": doctype, + "reference_docname": docname + } + + elif thread_name: + filters = { + "name": thread_name + } + + if filters: + thread = frappe.get_all("Discussion Thread",filters) + if len(thread): + thread = thread[0] + save_message(message, thread) + + else: + thread = frappe.get_doc({ + "doctype": "Discussion Thread", + "title": title, + "reference_doctype": doctype, + "reference_docname": docname + }) + thread.save(ignore_permissions=True) + save_message(message, thread) + + return thread.name + +def save_message(message, thread): + frappe.get_doc({ + "doctype": "Discussion Message", + "message": message, + "thread": thread.name + }).save(ignore_permissions=True) diff --git a/community/community/widgets/DiscussionComment.html b/community/community/widgets/DiscussionComment.html new file mode 100644 index 00000000..d5c8e6b9 --- /dev/null +++ b/community/community/widgets/DiscussionComment.html @@ -0,0 +1,70 @@ +
+ +
+ + + + +
+
+
+ +
+
+
+ +
+
+ diff --git a/community/community/widgets/DiscussionMessage.html b/community/community/widgets/DiscussionMessage.html new file mode 100644 index 00000000..e1dc2c5b --- /dev/null +++ b/community/community/widgets/DiscussionMessage.html @@ -0,0 +1,80 @@ +{% if doctype and docname and not thread %} + +{% set thread_info = frappe.get_all("Discussion Thread", {"reference_doctype": doctype, "reference_docname": docname}, +["name"]) %} + +{% if thread_info | length %} +{% set thread = thread_info[0].name %} +{% endif %} + +{% endif %} + +{% if thread %} +{% set messages = frappe.get_all("Discussion Message", {"thread": thread}, ["name", "message", "owner", "creation"], +order_by="creation") %} +{% endif %} + +{% if doctype %} +
Discussions
+{% endif %} + +
+ {% for message in messages %} + {% include "community/templates/message_card.html" %} + {% endfor %} +
+ +{% if frappe.session.user == "Guest" or (condition is defined and not condition) %} +
+ Want to join the discussion? + {% if frappe.session.user == "Guest" %} +
Log In
+ {% elif not condition %} +
{{ button_name }}
+ {% endif %} +
+{% else %} +{{ widgets.DiscussionComment(doctype=doctype, docname=docname, title=title, thread=thread ) }} +{% endif %} + + diff --git a/community/hooks.py b/community/hooks.py index 2f9caf24..64b54277 100644 --- a/community/hooks.py +++ b/community/hooks.py @@ -145,7 +145,8 @@ primary_rules = [ {"from_route": "/courses//discuss", "to_route": "batch/discuss"}, {"from_route": "/courses//about", "to_route": "batch/about"}, {"from_route": "/courses//progress", "to_route": "batch/progress"}, - {"from_route": "/courses//join", "to_route": "batch/join"} + {"from_route": "/courses//join", "to_route": "batch/join"}, + {"from_route": "/discussions/", "to_route": "discussions/discussion"} ] # Any frappe default URL is blocked by profile-rules, add it here to unblock it @@ -167,7 +168,8 @@ whitelist = [ "/new-sign-up", "/message", "/about", - "/edit-profile" + "/edit-profile", + "/discussions" ] whitelist_rules = [{"from_route": p, "to_route": p[1:]} for p in whitelist] diff --git a/community/lms/doctype/lms_message/__init__.py b/community/lms/doctype/lms_message/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/community/lms/doctype/lms_message/lms_message.js b/community/lms/doctype/lms_message/lms_message.js deleted file mode 100644 index ad20e5d7..00000000 --- a/community/lms/doctype/lms_message/lms_message.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021, FOSS United and contributors -// For license information, please see license.txt - -frappe.ui.form.on('LMS Message', { - // refresh: function(frm) { - - // } -}); diff --git a/community/lms/doctype/lms_message/lms_message.json b/community/lms/doctype/lms_message/lms_message.json deleted file mode 100644 index f4acfc11..00000000 --- a/community/lms/doctype/lms_message/lms_message.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "actions": [], - "creation": "2021-03-19 12:19:32.355307", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "author", - "batch", - "column_break_3", - "author_name", - "pin", - "section_break_6", - "message" - ], - "fields": [ - { - "fieldname": "batch", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Batch", - "options": "LMS Batch" - }, - { - "fieldname": "author", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Author", - "options": "User" - }, - { - "fieldname": "message", - "fieldtype": "Markdown Editor", - "in_list_view": 1, - "label": "Message" - }, - { - "default": "0", - "fieldname": "pin", - "fieldtype": "Check", - "label": "Pin" - }, - { - "fetch_from": "author.full_name", - "fieldname": "author_name", - "fieldtype": "Data", - "label": "Author Name", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-05-21 11:49:34.911479", - "modified_by": "Administrator", - "module": "LMS", - "name": "LMS Message", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "author", - "track_changes": 1 -} diff --git a/community/lms/doctype/lms_message/lms_message.py b/community/lms/doctype/lms_message/lms_message.py deleted file mode 100644 index b190c210..00000000 --- a/community/lms/doctype/lms_message/lms_message.py +++ /dev/null @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2021, FOSS United and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document -from frappe import _ -from frappe.utils import add_days, nowdate - -class LMSMessage(Document): - def after_insert(self): - self.publish_message() - #Todo: Adding email preference field for users - #self.send_email() - - def publish_message(self): - template = self.get_message_template() - message = frappe._dict({ - "author_name": self.author_name, - "message_time": frappe.utils.format_datetime(self.creation, "dd-mm-yyyy HH:mm"), - "message": frappe.utils.md_to_html(self.message) - }) - - js = """ - $(".msger-input").val(""); - var template = `{0}`; - var message = {1}; - var session_user = ("{2}" == frappe.session.user) ? true : false; - message.author_name = session_user ? "You" : message.author_name - message.is_author = session_user; - template = frappe.render_template(template, {{ - "message": message - }}) - $(".messages").append(template); - var message_element = document.getElementsByClassName("messages")[0] - message_element.scrollTo(0, message_element.scrollHeight); - """.format(template, message, self.owner) - - frappe.publish_realtime(event="eval_js", message=js, after_commit=True) - - def get_message_template(self): - return """ -
  • -
    -
    - {{ message.author_name }} -
    - - {{ message.message_time }} - -
    -
    - {{ message.message }} -
    -
  • - """ - - def send_email(self): - membership = frappe.get_all("LMS Batch Membership", {"batch": self.batch}, ["member"]) - for entry in membership: - member = frappe.get_doc("User", entry.member) - if member.name != self.author: - #Todo: wrap sendmail in frappe.enqueue, else messages takes long to display. - frappe.sendmail( - recipients = member.email, - subject = _("New Message on ") + self.batch, - header = _("New Message on ") + self.batch, - template = "lms_message", - args = { - "author": self.author, - "message": frappe.utils.md_to_html(self.message), - "creation": frappe.utils.format_datetime(self.creation, "medium"), - "course": frappe.db.get_value("LMS Batch", self.batch, ["course"]) - } - ) - -def send_daily_digest(): - #Todo: Optimize this - emails = frappe._dict() - messages = frappe.get_all("LMS Message", {"creation": [">=", add_days(nowdate(), -1)]}, ["message", "batch", "author", "creation"]) - for message in messages: - membership = frappe.get_all("LMS Batch Membership", {"batch": message.batch}, ["member"]) - for entry in membership: - member = frappe.db.get_value("User", entry.member, ["name", "email"], as_dict=1) - if member.name != message.author: - if member.name in emails.keys(): - emails[member.name]["messages"].append(message) - else: - emails[member.name] = frappe._dict({ - "email": member.email, - "messages": [message] - }) - for email in emails: - group_by_batch = frappe._dict() - for message in emails[email]["messages"]: - if message.batch in group_by_batch.keys(): - group_by_batch[message.batch].append(message) - else: - group_by_batch[message.batch] = [message] - frappe.sendmail( - recipients = frappe.db.get_value("User", email, "email"), - subject = _("Message Digest"), - header = _("Message Digest"), - template = "lms_daily_digest", - args = { - "batches": group_by_batch - }, - delayed = False - ) diff --git a/community/lms/doctype/lms_message/test_lms_message.py b/community/lms/doctype/lms_message/test_lms_message.py deleted file mode 100644 index 28e61f94..00000000 --- a/community/lms/doctype/lms_message/test_lms_message.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2021, FOSS United and Contributors -# See license.txt -from __future__ import unicode_literals - -# import frappe -import unittest - -class TestLMSMessage(unittest.TestCase): - pass diff --git a/community/lms/widgets/BreadCrumb.html b/community/lms/widgets/BreadCrumb.html index ae68a30d..cd4bafa0 100644 --- a/community/lms/widgets/BreadCrumb.html +++ b/community/lms/widgets/BreadCrumb.html @@ -1,8 +1,9 @@ diff --git a/community/lms/widgets/ChapterTeaser.html b/community/lms/widgets/ChapterTeaser.html index c5019573..24433cd9 100644 --- a/community/lms/widgets/ChapterTeaser.html +++ b/community/lms/widgets/ChapterTeaser.html @@ -1,6 +1,6 @@
    -
    @@ -45,7 +50,8 @@
    {{ lesson.render_html() }}
    {% else %}
    - This lesson is not available for Preview. Please join the course to access this lesson. Checkout Course Details. + This lesson is not available for Preview. Please join the course to access this lesson. Checkout Course Details.
    {% endif %} diff --git a/community/www/courses/course.js b/community/www/courses/course.js index 2e7688dc..4429816b 100644 --- a/community/www/courses/course.js +++ b/community/www/courses/course.js @@ -169,6 +169,7 @@ var show_review_dialog = (e) => { } var rotate_chapter_icon = (e) => { + e.preventDefault(); var icon = $(e.currentTarget).children(".chapter-icon"); if (icon.css("transform") == "none") { icon.css("transform", "rotate(90deg)"); diff --git a/community/www/discussions/discussion.html b/community/www/discussions/discussion.html new file mode 100644 index 00000000..afa8d8b1 --- /dev/null +++ b/community/www/discussions/discussion.html @@ -0,0 +1,16 @@ +{% extends "templates/base.html" %} +{% block title %}{{ thread.title }}{% endblock %} +{% block head_include %} + +{% endblock %} + +{% block content %} +
    +
    + {{ widgets.BreadCrumb(thread=thread) }} +
    {{ thread.title }}
    + {{ widgets.DiscussionMessage(thread=thread.name) }} +
    +
    +{% endblock %} diff --git a/community/www/discussions/discussion.py b/community/www/discussions/discussion.py new file mode 100644 index 00000000..605e70c8 --- /dev/null +++ b/community/www/discussions/discussion.py @@ -0,0 +1,18 @@ +import frappe + +def get_context(context): + context.no_cache = 1 + + try: + thread_name = frappe.form_dict["discussion"] + except KeyError: + redirect_to_discussions() + + context.thread = frappe.db.get_value("Discussion Thread", thread_name, ["name", "title"], as_dict=True) + + if not len(context.thread): + redirect_to_discussions + +def redirect_to_discussions(): + frappe.local.flags.redirect_location = "/discussions" + raise frappe.Redirect diff --git a/community/www/discussions/index.html b/community/www/discussions/index.html new file mode 100644 index 00000000..1f7a6ad2 --- /dev/null +++ b/community/www/discussions/index.html @@ -0,0 +1,65 @@ +{% extends "templates/base.html" %} +{% block title %}{{ 'Discussions' }}{% endblock %} +{% block head_include %} + +{% endblock %} + +{% block content %} +
    +
    +
    + {{_('Discussions')}} +
    + + Start a Discussion
    +
    +
    + {% if threads | length %} +
    + {% for thread in threads %} +
    +
    {{ thread.title }}
    +
    +
    + + + + {{ thread.message_count }} + + + + {{ thread.member_count }} + + +
    + +
    + {% endfor %} +
    + {% else %} +
    + No discussions yet. +
    + {% endif %} +
    +
    + + + + +{% endblock %} diff --git a/community/www/discussions/index.js b/community/www/discussions/index.js new file mode 100644 index 00000000..0cac1f1a --- /dev/null +++ b/community/www/discussions/index.js @@ -0,0 +1,14 @@ +frappe.ready(() => { + $("#new-topic").click((e) => { + show_new_topic_modal(e); + }) +}) + +var show_new_topic_modal = (e) => { + e.preventDefault(); + if (frappe.session.user == "Guest") { + window.location.href = `/login?redirect-to=/discussions/`; + return; + } + $("#discussion-modal").modal("show"); +} diff --git a/community/www/discussions/index.py b/community/www/discussions/index.py new file mode 100644 index 00000000..d474b2a5 --- /dev/null +++ b/community/www/discussions/index.py @@ -0,0 +1,17 @@ +import frappe + +def get_context(context): + context.threads = get_threads() + +def get_threads(): + threads = frappe.get_all("Discussion Thread", fields=["name", "title"]) + for thread in threads: + messages = frappe.get_all("Discussion Message", + { + "thread": thread.name + }, + ["owner"], + as_list=True) + thread.message_count = len(messages) + thread.member_count = len(set(messages)) + return threads