diff --git a/community/lms/doctype/lms_message/__init__.py b/community/community/doctype/discussion_message/__init__.py similarity index 100% rename from community/lms/doctype/lms_message/__init__.py rename to community/community/doctype/discussion_message/__init__.py diff --git a/community/community/doctype/discussion_message/discussion_message.js b/community/community/doctype/discussion_message/discussion_message.js new file mode 100644 index 00000000..6044a179 --- /dev/null +++ b/community/community/doctype/discussion_message/discussion_message.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, FOSS United and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Discussion Message', { + // refresh: function(frm) { + + // } +}); diff --git a/community/lms/doctype/lms_message/lms_message.json b/community/community/doctype/discussion_message/discussion_message.json similarity index 50% rename from community/lms/doctype/lms_message/lms_message.json rename to community/community/doctype/discussion_message/discussion_message.json index f4acfc11..4a7d653d 100644 --- a/community/lms/doctype/lms_message/lms_message.json +++ b/community/community/doctype/discussion_message/discussion_message.json @@ -1,67 +1,53 @@ { "actions": [], - "creation": "2021-03-19 12:19:32.355307", + "creation": "2021-08-11 10:59:38.597046", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "author", - "batch", - "column_break_3", - "author_name", - "pin", - "section_break_6", + "thread", + "column_break_2", + "parent_message", + "section_break_4", "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", + "fieldtype": "Long Text", "in_list_view": 1, "label": "Message" }, { - "default": "0", - "fieldname": "pin", - "fieldtype": "Check", - "label": "Pin" + "fieldname": "thread", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Thread", + "options": "Discussion Thread" }, { - "fetch_from": "author.full_name", - "fieldname": "author_name", - "fieldtype": "Data", - "label": "Author Name", - "read_only": 1 - }, - { - "fieldname": "column_break_3", + "fieldname": "column_break_2", "fieldtype": "Column Break" }, { - "fieldname": "section_break_6", + "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-05-21 11:49:34.911479", + "modified": "2021-08-12 15:59:04.811286", "modified_by": "Administrator", - "module": "LMS", - "name": "LMS Message", + "module": "Community", + "name": "Discussion Message", "owner": "Administrator", "permissions": [ { @@ -77,9 +63,7 @@ "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "author", "track_changes": 1 -} +} \ No newline at end of file diff --git a/community/community/doctype/discussion_message/discussion_message.py b/community/community/doctype/discussion_message/discussion_message.py new file mode 100644 index 00000000..3a9e672d --- /dev/null +++ b/community/community/doctype/discussion_message/discussion_message.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021, FOSS United and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from community.widgets import Widget, Widgets + +class DiscussionMessage(Document): + 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/lms/doctype/lms_message/test_lms_message.py b/community/community/doctype/discussion_message/test_discussion_message.py similarity index 50% rename from community/lms/doctype/lms_message/test_lms_message.py rename to community/community/doctype/discussion_message/test_discussion_message.py index 28e61f94..7d7145a8 100644 --- a/community/lms/doctype/lms_message/test_lms_message.py +++ b/community/community/doctype/discussion_message/test_discussion_message.py @@ -1,10 +1,8 @@ -# -*- 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): +class TestDiscussionMessage(unittest.TestCase): pass diff --git a/community/community/doctype/discussion_thread/__init__.py b/community/community/doctype/discussion_thread/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/community/lms/doctype/lms_message/lms_message.js b/community/community/doctype/discussion_thread/discussion_thread.js similarity index 77% rename from community/lms/doctype/lms_message/lms_message.js rename to community/community/doctype/discussion_thread/discussion_thread.js index ad20e5d7..93fdaaa4 100644 --- a/community/lms/doctype/lms_message/lms_message.js +++ b/community/community/doctype/discussion_thread/discussion_thread.js @@ -1,7 +1,7 @@ // Copyright (c) 2021, FOSS United and contributors // For license information, please see license.txt -frappe.ui.form.on('LMS Message', { +frappe.ui.form.on('Discussion Thread', { // refresh: function(frm) { // } diff --git a/community/community/doctype/discussion_thread/discussion_thread.json b/community/community/doctype/discussion_thread/discussion_thread.json new file mode 100644 index 00000000..5442c60b --- /dev/null +++ b/community/community/doctype/discussion_thread/discussion_thread.json @@ -0,0 +1,57 @@ +{ + "actions": [], + "creation": "2021-08-11 10:55:29.341674", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "reference_doctype", + "reference_docname" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Doctype", + "options": "DocType" + }, + { + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "label": "Reference Docname", + "options": "reference_doctype" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-08-11 12:29:43.564123", + "modified_by": "Administrator", + "module": "Community", + "name": "Discussion Thread", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "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 new file mode 100644 index 00000000..cef7ef57 --- /dev/null +++ b/community/community/doctype/discussion_thread/discussion_thread.py @@ -0,0 +1,48 @@ +# Copyright (c) 2021, FOSS United and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + +class DiscussionThread(Document): + 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/doctype/discussion_thread/test_discussion_thread.py b/community/community/doctype/discussion_thread/test_discussion_thread.py new file mode 100644 index 00000000..b692f56e --- /dev/null +++ b/community/community/doctype/discussion_thread/test_discussion_thread.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, FOSS United and Contributors +# See license.txt + +# import frappe +import unittest + +class TestDiscussionThread(unittest.TestCase): + pass 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/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/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