@@ -28,9 +28,9 @@ order_by="creation") %}
|
||||
<div class="d-flex flex-column align-items-center font-weight-bold">
|
||||
Want to join the discussion?
|
||||
{% if frappe.session.user == "Guest" %}
|
||||
<div class="button is-primary" id="login-from-discussion">Log In</div>
|
||||
<div class="button is-primary mt-5" id="login-from-discussion">Log In</div>
|
||||
{% elif not condition %}
|
||||
<div class="button is-primary" id="login-from-discussion" data-redirect="{{ redirect_to }}">{{ button_name }}</div>
|
||||
<div class="button is-primary mt-5" id="login-from-discussion" data-redirect="{{ redirect_to }}">{{ button_name }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -11,22 +11,7 @@
|
||||
{% set member = frappe.get_doc("User", exhibitor_doc.user) %}
|
||||
|
||||
<div class="common-card-style member-card">
|
||||
{% set color = member.get_palette() %}
|
||||
<a class="button-links" href="/user/{{member.username}}">
|
||||
<span class="avatar avatar-large" title="{{ member.full_name }}">
|
||||
{% if member.user_image %}
|
||||
<img class="avatar-frame standard-image" style="object-fit: cover;" src="{{ member.user_image }}"
|
||||
title="{{ member.full_name }}">
|
||||
</img>
|
||||
{% else %}
|
||||
<span class="avatar-frame standard-image" title="{{ member.full_name }}"
|
||||
style="background-color: var({{color[0]}}); color: var({{color[1]}});">
|
||||
{{ frappe.utils.get_abbr(member.full_name) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
{{ widgets.Avatar(member=member, avatar_class="avatar-large")}}
|
||||
<div class="small-title member-card-title">
|
||||
{{ member.full_name }}
|
||||
</div>
|
||||
|
||||
@@ -10,21 +10,7 @@
|
||||
{% set member = frappe.get_doc("User", speaker_doc.user) %}
|
||||
|
||||
<div class="common-card-style member-card">
|
||||
{% set color = member.get_palette() %}
|
||||
<a class="button-links" href="/user/{{member.username}}">
|
||||
<span class="avatar avatar-large" title="{{ member.full_name }}">
|
||||
{% if member.user_image %}
|
||||
<img class="avatar-frame standard-image" style="object-fit: cover;" src="{{ member.user_image }}"
|
||||
title="{{ member.full_name }}">
|
||||
</img>
|
||||
{% else %}
|
||||
<span class="avatar-frame standard-image" title="{{ member.full_name }}"
|
||||
style="background-color: var({{color[0]}}); color: var({{color[1]}});">
|
||||
{{ frappe.utils.get_abbr(member.full_name) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
{{ widgets.Avatar(member=member, avatar_class="avatar-large") }}
|
||||
|
||||
<div class="small-title member-card-title">
|
||||
{{ member.full_name }}
|
||||
|
||||
@@ -20,23 +20,7 @@
|
||||
<div class="course-card-title">{{talk_doc.title}}</div>
|
||||
<div class="card-divider"></div>
|
||||
<div class="course-card-meta-2">
|
||||
|
||||
{% set color = member.get_palette() %}
|
||||
<a class="button-links" href="/user/{{member.username}}">
|
||||
<span class="avatar avatar-small" title="{{ member.full_name }}">
|
||||
{% if member.user_image %}
|
||||
<img class="avatar-frame standard-image" style="object-fit: cover;" src="{{ member.user_image }}"
|
||||
title="{{ member.full_name }}">
|
||||
</img>
|
||||
{% else %}
|
||||
<span class="avatar-frame standard-image" title="{{ member.full_name }}"
|
||||
style="background-color: var({{color[0]}}); color: var({{color[1]}});">
|
||||
{{ frappe.utils.get_abbr(member.full_name) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
{{ widgets.Avatar(member=member, avatar_class="avatar-small")}}
|
||||
|
||||
<span class="course-instructor"> {{ member.full_name }} </span>
|
||||
<span class="small-title company-name"></span>
|
||||
|
||||
@@ -85,7 +85,8 @@ web_include_css = "community.bundle.css"
|
||||
# Override standard doctype classes
|
||||
|
||||
override_doctype_class = {
|
||||
"User": "community.overrides.user.CustomUser"
|
||||
"User": "community.overrides.user.CustomUser",
|
||||
"Web Template": "community.overrides.web_template.CustomWebTemplate"
|
||||
}
|
||||
|
||||
# Document Events
|
||||
@@ -130,7 +131,7 @@ fixtures = ["Custom Field"]
|
||||
# auto_cancel_exempted_doctypes = ["Auto Repeat"]
|
||||
|
||||
# Add all simple route rules here
|
||||
primary_rules = [
|
||||
website_route_rules = [
|
||||
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
|
||||
{"from_route": "/courses/<course>", "to_route": "courses/course"},
|
||||
{"from_route": "/courses/<course>/<topic>", "to_route": "courses/topic"},
|
||||
@@ -147,46 +148,9 @@ primary_rules = [
|
||||
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
|
||||
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
|
||||
{"from_route": "/discussions/<discussion>", "to_route": "discussions/discussion"},
|
||||
{"from_route": "/user/<string(minlength=4):username>", "to_route": "profiles/profile"},
|
||||
]
|
||||
|
||||
# Any frappe default URL is blocked by profile-rules, add it here to unblock it
|
||||
whitelist = [
|
||||
"/home",
|
||||
"/login",
|
||||
"/update-password",
|
||||
"/update-profile",
|
||||
"/third-party-apps",
|
||||
"/website_script.js",
|
||||
"/courses",
|
||||
"/sketches",
|
||||
"/admin",
|
||||
"/socket.io",
|
||||
"/hackathons",
|
||||
"/dashboard",
|
||||
"/join-request",
|
||||
"/add-a-new-batch",
|
||||
"/new-sign-up",
|
||||
"/message",
|
||||
"/about",
|
||||
"/edit-profile",
|
||||
"/attendee-registration",
|
||||
"/speaker-registration",
|
||||
"/event",
|
||||
"/hello",
|
||||
"/exhibitor-registration",
|
||||
"/discussions",
|
||||
"/propose-talk",
|
||||
|
||||
]
|
||||
whitelist_rules = [{"from_route": p, "to_route": p[1:]} for p in whitelist]
|
||||
|
||||
# regex rule to match all profiles
|
||||
profile_rules = [
|
||||
{"from_route": "/<string(minlength=4):username>", "to_route": "profiles/profile"},
|
||||
]
|
||||
|
||||
website_route_rules = primary_rules + whitelist_rules + profile_rules
|
||||
|
||||
website_redirects = [
|
||||
{"source": "/update-profile", "target": "/edit-profile"},
|
||||
]
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"field_order": [
|
||||
"course",
|
||||
"title",
|
||||
"column_break_3",
|
||||
"description",
|
||||
"section_break_5",
|
||||
"lessons"
|
||||
],
|
||||
"fields": [
|
||||
@@ -20,7 +22,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Markdown Editor",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
@@ -35,6 +37,14 @@
|
||||
"fieldtype": "Table",
|
||||
"label": "Lessons",
|
||||
"options": "Lessons"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
@@ -45,7 +55,7 @@
|
||||
"link_fieldname": "chapter"
|
||||
}
|
||||
],
|
||||
"modified": "2021-07-27 16:28:08.667964",
|
||||
"modified": "2021-08-19 13:43:51.025072",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Chapter",
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from ..lesson.lesson import update_progress
|
||||
|
||||
class ExerciseSubmission(Document):
|
||||
|
||||
def after_insert(self):
|
||||
course_details = frappe.get_doc("LMS Course", self.course)
|
||||
if not (course_details.is_mentor(frappe.session.user) or frappe.flags.in_test):
|
||||
update_progress(self.lesson)
|
||||
pass
|
||||
|
||||
@@ -93,41 +93,3 @@ def save_progress(lesson, course, status):
|
||||
}).save(ignore_permissions=True)
|
||||
course_details = frappe.get_doc("LMS Course", course)
|
||||
return course_details.get_course_progress()
|
||||
|
||||
def update_progress(lesson):
|
||||
user = frappe.session.user
|
||||
if not all_dynamic_content_submitted(lesson, user):
|
||||
return
|
||||
if frappe.db.exists("LMS Course Progress", {"lesson": lesson, "owner": user}):
|
||||
course_progress = frappe.get_doc("LMS Course Progress", {"lesson": lesson, "owner": user})
|
||||
course_progress.status = "Complete"
|
||||
course_progress.save(ignore_permissions=True)
|
||||
|
||||
def all_dynamic_content_submitted(lesson, user):
|
||||
all_exercises_submitted = check_all_exercise_submission(lesson, user)
|
||||
all_quiz_submitted = check_all_quiz_submitted(lesson, user)
|
||||
return all_exercises_submitted and all_quiz_submitted
|
||||
|
||||
def check_all_exercise_submission(lesson, user):
|
||||
exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, pluck="name", ignore_permissions=True)
|
||||
if not len(exercise_names):
|
||||
return True
|
||||
query = {
|
||||
"exercise": ["in", exercise_names],
|
||||
"owner": user
|
||||
}
|
||||
if frappe.db.count("Exercise Submission", query) == len(exercise_names):
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_all_quiz_submitted(lesson, user):
|
||||
quizzes = frappe.get_list("LMS Quiz", {"lesson": lesson}, pluck="name", ignore_permissions=True)
|
||||
if not len(quizzes):
|
||||
return True
|
||||
query = {
|
||||
"quiz": ["in", quizzes],
|
||||
"owner": user
|
||||
}
|
||||
if frappe.db.count("LMS Quiz Submission", query) == len(quizzes):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -35,14 +35,6 @@ class LMSBatch(Document):
|
||||
filters['member_type'] = member_type
|
||||
return frappe.db.exists("LMS Batch Membership", filters)
|
||||
|
||||
def get_messages(self):
|
||||
messages = frappe.get_all("LMS Message", {"batch": self.name}, ["*"], order_by="creation")
|
||||
for message in messages:
|
||||
message.message = frappe.utils.md_to_html(message.message)
|
||||
if message.author == frappe.session.user:
|
||||
message.author_name = "You"
|
||||
message.is_author = True
|
||||
return messages
|
||||
|
||||
def get_membership(self, email):
|
||||
"""Returns the membership document of given user.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
frappe.ui.form.on('LMS Course', {
|
||||
|
||||
onload: function (frm) {
|
||||
|
||||
frm.set_query("chapter", "chapters", function () {
|
||||
return {
|
||||
filters: {
|
||||
@@ -11,6 +12,14 @@ frappe.ui.form.on('LMS Course', {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("instructor", function (doc) {
|
||||
return {
|
||||
filters: {
|
||||
"ignore_user_type": 1,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -18,17 +18,18 @@
|
||||
"video_link",
|
||||
"image",
|
||||
"column_break_3",
|
||||
"instructor",
|
||||
"tags",
|
||||
"section_break_7",
|
||||
"is_published",
|
||||
"column_break_9",
|
||||
"upcoming",
|
||||
"column_break_11",
|
||||
"disable_self_learning",
|
||||
"section_break_5",
|
||||
"short_introduction",
|
||||
"description",
|
||||
"chapters",
|
||||
"certification_section",
|
||||
"enable_certification",
|
||||
"expiry"
|
||||
"chapters"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -99,23 +100,25 @@
|
||||
"options": "Chapters"
|
||||
},
|
||||
{
|
||||
"fieldname": "certification_section",
|
||||
"fieldname": "instructor",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Instructor",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Certification"
|
||||
"label": "Course Settings"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_certification",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Certification"
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "enable_certification",
|
||||
"fieldname": "expiry",
|
||||
"fieldtype": "Select",
|
||||
"label": "Certification Expires After Years",
|
||||
"options": "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
@@ -137,7 +140,7 @@
|
||||
"link_fieldname": "course"
|
||||
}
|
||||
],
|
||||
"modified": "2021-08-18 18:02:12.623807",
|
||||
"modified": "2021-08-20 11:01:15.795219",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Course",
|
||||
|
||||
@@ -144,6 +144,8 @@ class LMSCourse(Document):
|
||||
return batch_name and frappe.get_doc("LMS Batch", batch_name)
|
||||
|
||||
def get_instructor(self):
|
||||
if self.instructor:
|
||||
return frappe.get_doc("User", self.instructor)
|
||||
return frappe.get_doc("User", self.owner)
|
||||
|
||||
def get_chapters(self):
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# Copyright (c) 2021, FOSS United and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from community.lms.doctype.lesson.lesson import update_progress
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
import json
|
||||
from frappe import _
|
||||
from ..lesson.lesson import update_progress
|
||||
|
||||
class LMSQuiz(Document):
|
||||
def validate(self):
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% set is_instructor = frappe.session.user == course.instructor %}
|
||||
<div class="lessons">
|
||||
|
||||
{% for lesson in course.get_lessons(chapter) %}
|
||||
|
||||
<div class="lesson-info{% if membership.current_lesson == lesson.name %} active-lesson {% endif %}">
|
||||
|
||||
{% if membership or lesson.include_in_preview %}
|
||||
<a class="lesson-links"
|
||||
href="{{ course.get_learn_url(lesson.number) }}{{course.query_parameter}}"
|
||||
{% if membership or lesson.include_in_preview or is_instructor %}
|
||||
<a class="lesson-links" href="{{ course.get_learn_url(lesson.number) }}{{course.query_parameter}}"
|
||||
data-course="{{ course.name }}">
|
||||
{{ lesson.title }}
|
||||
|
||||
@@ -56,6 +56,11 @@
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
expand_the_active_chapter();
|
||||
|
||||
$(".chapter-title").click((e) => {
|
||||
rotate_chapter_icon(e);
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
var expand_the_first_chapter = () => {
|
||||
@@ -97,4 +102,14 @@
|
||||
$(element).siblings(".chapter-title").children(".chapter-icon").css("transform", "rotate(90deg)");
|
||||
}
|
||||
|
||||
var rotate_chapter_icon = (e) => {
|
||||
e.preventDefault();
|
||||
var icon = $(e.currentTarget).children(".chapter-icon");
|
||||
if (icon.css("transform") == "none") {
|
||||
icon.css("transform", "rotate(90deg)");
|
||||
} else {
|
||||
icon.css("transform", "none");
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
15
community/overrides/web_template.py
Normal file
15
community/overrides/web_template.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import frappe
|
||||
from frappe.website.doctype.web_template.web_template import WebTemplate
|
||||
from community.widgets import Widgets
|
||||
import json
|
||||
|
||||
class CustomWebTemplate(WebTemplate):
|
||||
|
||||
def render(self, values=None):
|
||||
if not values:
|
||||
values = {}
|
||||
values = frappe.parse_json(values)
|
||||
values.update({"values": values})
|
||||
values.update({"widgets": Widgets()})
|
||||
template = self.get_template(self.standard)
|
||||
return frappe.render_template(template, values)
|
||||
@@ -8,3 +8,4 @@ community.patches.replace_member_with_user_in_lms_message
|
||||
community.patches.replace_member_with_user_in_mentor_request
|
||||
community.patches.v0_0.chapter_lesson_index_table
|
||||
execute:frappe.delete_doc("DocType", "LMS Message")
|
||||
community.patches.v0_0.course_instructor_update
|
||||
|
||||
7
community/patches/v0_0/course_instructor_update.py
Normal file
7
community/patches/v0_0/course_instructor_update.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_course")
|
||||
courses = frappe.get_all("LMS Course", fields=["name", "owner"])
|
||||
for course in courses:
|
||||
frappe.db.set_value("LMS Course", course.name, "instructor", course.owner)
|
||||
@@ -630,6 +630,10 @@ input[type=checkbox] {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.course-content-parent .chapter-description {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.chapter-icon {
|
||||
margin-right: .25rem;
|
||||
}
|
||||
@@ -1295,10 +1299,6 @@ pre {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a.talk-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.speaker-cards-parent {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
{% endif %}
|
||||
|
||||
{% set title = lesson.title + " - " + course.title %}
|
||||
{% set condition = membership or is_instructor %}
|
||||
{{ widgets.DiscussionMessage(doctype="Lesson", docname=lesson.name,
|
||||
title=title, condition=membership, button_name="Start Learning",
|
||||
title=title, condition=condition, button_name="Start Learning",
|
||||
redirect_to="/courses/" + course.name) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,7 +47,9 @@
|
||||
<span class="lesson-progress {{hide if course.get_progress(lesson.name) != 'Complete' else ''}}">COMPLETED</span>
|
||||
</div>
|
||||
|
||||
{% if membership or lesson.include_in_preview %}
|
||||
{% set is_instructor = frappe.session.user == course.instructor %}
|
||||
|
||||
{% if membership or lesson.include_in_preview or is_instructor %}
|
||||
<div class="common-card-style lesson-content-card markdown-source">{{ lesson.render_html() }}</div>
|
||||
{% else %}
|
||||
<div class="common-card-style lesson-content-card">
|
||||
|
||||
@@ -16,9 +16,9 @@ def get_context(context):
|
||||
redirect_to_course_list()
|
||||
|
||||
context.course = frappe.db.get_value("LMS Course", course_name,
|
||||
["owner", "title", "name"], as_dict=True)
|
||||
["instructor", "title", "name"], as_dict=True)
|
||||
|
||||
context.instructor = frappe.db.get_value("User", context.course.owner,
|
||||
context.instructor = frappe.db.get_value("User", context.course.instructor,
|
||||
["full_name", "username"], as_dict=True)
|
||||
|
||||
context.student = frappe.db.get_value("User", context.certificate.student,
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-buttons">
|
||||
{% if not course.disable_self_learning and not membership %}
|
||||
{% if not course.disable_self_learning and not membership and not course.upcoming %}
|
||||
<div class="button wide-button start-learning is-primary join-batch" data-course="{{ course.name | urlencode }}">
|
||||
Start Learning
|
||||
<img class="ml-2" src="/assets/community/icons/white-arrow.svg" />
|
||||
|
||||
@@ -29,10 +29,6 @@ frappe.ready(() => {
|
||||
show_review_dialog(e);
|
||||
});
|
||||
|
||||
$(".chapter-title").click((e) => {
|
||||
rotate_chapter_icon(e);
|
||||
});
|
||||
|
||||
$(".icon-rating").click((e) => {
|
||||
highlight_rating(e);
|
||||
});
|
||||
@@ -168,16 +164,6 @@ var show_review_dialog = (e) => {
|
||||
$("#review-modal").modal("show");
|
||||
}
|
||||
|
||||
var rotate_chapter_icon = (e) => {
|
||||
e.preventDefault();
|
||||
var icon = $(e.currentTarget).children(".chapter-icon");
|
||||
if (icon.css("transform") == "none") {
|
||||
icon.css("transform", "rotate(90deg)");
|
||||
} else {
|
||||
icon.css("transform", "none");
|
||||
}
|
||||
}
|
||||
|
||||
var highlight_rating = (e) => {
|
||||
var rating = $(e.currentTarget).attr("data-rating");
|
||||
$(".icon-rating").removeClass("star-click");
|
||||
|
||||
0
community/www/discussions/__init__.py
Normal file
0
community/www/discussions/__init__.py
Normal file
Reference in New Issue
Block a user