feat: seperate inputs for adding youtube video and quiz
This commit is contained in:
@@ -15,6 +15,10 @@
|
||||
"include_in_preview",
|
||||
"index_label",
|
||||
"section_break_6",
|
||||
"youtube",
|
||||
"column_break_9",
|
||||
"quiz_id",
|
||||
"section_break_11",
|
||||
"body",
|
||||
"help_section",
|
||||
"help"
|
||||
@@ -81,11 +85,31 @@
|
||||
"label": "Course",
|
||||
"options": "LMS Course",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"description": "Quiz will appear at the bottom of the lesson.",
|
||||
"fieldname": "quiz_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Quiz ID"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"description": "YouTube Video will appear at the top of the lesson.",
|
||||
"fieldname": "youtube",
|
||||
"fieldtype": "Data",
|
||||
"label": "YouTube Video URL"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2022-05-02 17:16:12.450460",
|
||||
"modified": "2022-09-02 11:30:15.450624",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Course Lesson",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from ...md import find_macros
|
||||
from lms.lms.utils import get_course_progress, get_lesson_url
|
||||
@@ -11,6 +12,11 @@ from lms.lms.utils import get_course_progress, get_lesson_url
|
||||
class CourseLesson(Document):
|
||||
def validate(self):
|
||||
self.check_and_create_folder()
|
||||
self.validate_quiz_id()
|
||||
|
||||
def validate_quiz_id(self):
|
||||
if self.quiz_id and not frappe.db.exists("LMS Quiz", self.quiz_id):
|
||||
frappe.throw(_("Invalid Quiz ID"))
|
||||
|
||||
def on_update(self):
|
||||
dynamic_documents = ["Exercise", "Quiz"]
|
||||
|
||||
@@ -249,7 +249,7 @@ def save_chapter(course, title, chapter_description, idx, chapter):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def save_lesson(title, body, chapter, preview, idx, lesson):
|
||||
def save_lesson(title, body, chapter, preview, idx, lesson, youtube=None, quiz_id=None):
|
||||
if lesson:
|
||||
doc = frappe.get_doc("Course Lesson", lesson)
|
||||
else:
|
||||
@@ -261,7 +261,9 @@ def save_lesson(title, body, chapter, preview, idx, lesson):
|
||||
"chapter": chapter,
|
||||
"title": title,
|
||||
"body": body,
|
||||
"include_in_preview": preview
|
||||
"include_in_preview": preview,
|
||||
"youtube": youtube,
|
||||
"quiz_id": quiz_id
|
||||
})
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from frappe import _
|
||||
|
||||
RE_SLUG_NOTALLOWED = re.compile("[^a-z0-9]+")
|
||||
|
||||
|
||||
def slugify(title, used_slugs=[]):
|
||||
"""Converts title to a slug.
|
||||
|
||||
@@ -59,6 +60,7 @@ def get_membership(course, member, batch=None):
|
||||
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
||||
return membership
|
||||
|
||||
|
||||
def get_chapters(course):
|
||||
"""Returns all chapters of this course.
|
||||
"""
|
||||
@@ -85,6 +87,7 @@ def get_lessons(course, chapter=None):
|
||||
|
||||
return lessons
|
||||
|
||||
|
||||
def get_lesson_details(chapter):
|
||||
lessons = []
|
||||
lesson_list = frappe.get_all("Lesson Reference",
|
||||
@@ -94,10 +97,11 @@ def get_lesson_details(chapter):
|
||||
|
||||
for row in lesson_list:
|
||||
lesson_details = frappe.db.get_value("Course Lesson", row.lesson,
|
||||
["name", "title", "include_in_preview", "body", "creation"], as_dict=True)
|
||||
["name", "title", "include_in_preview", "body", "creation", "youtube", "quiz_id"], as_dict=True)
|
||||
lesson_details.number = flt("{}.{}".format(chapter.idx, row.idx))
|
||||
lesson_details.icon = "icon-list"
|
||||
macros = find_macros(lesson_details.body)
|
||||
|
||||
for macro in macros:
|
||||
if macro[0] == "YouTubeVideo":
|
||||
lesson_details.icon = "icon-video"
|
||||
@@ -106,10 +110,12 @@ def get_lesson_details(chapter):
|
||||
lessons.append(lesson_details)
|
||||
return lessons
|
||||
|
||||
|
||||
def get_tags(course):
|
||||
tags = frappe.db.get_value("LMS Course", course, "tags")
|
||||
return tags.split(",") if tags else []
|
||||
|
||||
|
||||
def get_instructors(course):
|
||||
instructor_details = []
|
||||
instructors = frappe.get_all("Course Instructor", {"parent": course},
|
||||
@@ -123,6 +129,7 @@ def get_instructors(course):
|
||||
as_dict=True))
|
||||
return instructor_details
|
||||
|
||||
|
||||
def get_students(course, batch=None):
|
||||
"""Returns (email, full_name, username) of all the students of this batch as a list of dict.
|
||||
"""
|
||||
@@ -137,12 +144,14 @@ def get_students(course, batch=None):
|
||||
filters,
|
||||
["member"])
|
||||
|
||||
|
||||
def get_average_rating(course):
|
||||
ratings = [review.rating for review in get_reviews(course)]
|
||||
if not len(ratings):
|
||||
return None
|
||||
return sum(ratings)/len(ratings)
|
||||
|
||||
|
||||
def get_reviews(course):
|
||||
reviews = frappe.get_all("LMS Course Review",
|
||||
{
|
||||
@@ -164,6 +173,7 @@ def get_reviews(course):
|
||||
|
||||
return reviews
|
||||
|
||||
|
||||
def get_sorted_reviews(course):
|
||||
rating_count = rating_percent = frappe._dict()
|
||||
keys = ["5.0", "4.0", "3.0", "2.0", "1.0"]
|
||||
@@ -180,6 +190,7 @@ def get_sorted_reviews(course):
|
||||
|
||||
return rating_percent
|
||||
|
||||
|
||||
def is_certified(course):
|
||||
certificate = frappe.get_all("LMS Certificate",
|
||||
{
|
||||
@@ -190,6 +201,7 @@ def is_certified(course):
|
||||
return certificate[0].name
|
||||
return
|
||||
|
||||
|
||||
def get_lesson_index(lesson_name):
|
||||
"""Returns the {chapter_index}.{lesson_index} for the lesson.
|
||||
"""
|
||||
@@ -205,6 +217,7 @@ def get_lesson_index(lesson_name):
|
||||
|
||||
return f"{chapter.idx}.{lesson.idx}"
|
||||
|
||||
|
||||
def get_lesson_url(course, lesson_number):
|
||||
if not lesson_number:
|
||||
return
|
||||
@@ -224,8 +237,16 @@ def get_progress(course, lesson):
|
||||
},
|
||||
["status"])
|
||||
|
||||
def render_html(body):
|
||||
return markdown_to_html(body)
|
||||
|
||||
def render_html(body, youtube, quiz_id):
|
||||
if "/" in youtube:
|
||||
youtube = youtube.split("/")[-1]
|
||||
|
||||
quiz_id = "{{ Quiz('" + quiz_id + "') }}" if quiz_id else ""
|
||||
youtube = "{{ YouTubeVideo('" + youtube + "') }}" if youtube else ""
|
||||
text = youtube + body + quiz_id
|
||||
return markdown_to_html(text)
|
||||
|
||||
|
||||
def is_mentor(course, email):
|
||||
"""Checks if given user is a mentor for this course.
|
||||
@@ -238,6 +259,7 @@ def is_mentor(course, email):
|
||||
"mentor": email
|
||||
})
|
||||
|
||||
|
||||
def is_cohort_staff(course, user_email):
|
||||
"""Returns True if the user is either a mentor or a staff for one or more active cohorts of this course.
|
||||
"""
|
||||
@@ -253,6 +275,7 @@ def is_cohort_staff(course, user_email):
|
||||
}
|
||||
return frappe.db.exists(staff) or frappe.db.exists(mentor)
|
||||
|
||||
|
||||
def get_mentors(course):
|
||||
"""Returns the list of all mentors for this course.
|
||||
"""
|
||||
@@ -269,6 +292,7 @@ def get_mentors(course):
|
||||
course_mentors.append(member)
|
||||
return course_mentors
|
||||
|
||||
|
||||
def is_eligible_to_review(course, membership):
|
||||
""" Checks if user is eligible to review the course """
|
||||
if not membership:
|
||||
@@ -281,6 +305,7 @@ def is_eligible_to_review(course, membership):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_course_progress(course, member=None):
|
||||
""" Returns the course progress of the session user """
|
||||
lesson_count = len(get_lessons(course))
|
||||
@@ -295,6 +320,7 @@ def get_course_progress(course, member=None):
|
||||
precision = cint(frappe.db.get_default("float_precision")) or 3
|
||||
return flt(((completed_lessons/lesson_count) * 100), precision)
|
||||
|
||||
|
||||
def get_initial_members(course):
|
||||
members = frappe.get_all("LMS Batch Membership",
|
||||
{
|
||||
@@ -310,12 +336,15 @@ def get_initial_members(course):
|
||||
|
||||
return member_details
|
||||
|
||||
|
||||
def is_instructor(course):
|
||||
return len(list(filter(lambda x: x.name == frappe.session.user, get_instructors(course)))) > 0
|
||||
|
||||
|
||||
def convert_number_to_character(number):
|
||||
return string.ascii_uppercase[number]
|
||||
|
||||
|
||||
def get_signup_optin_checks():
|
||||
|
||||
mapper = frappe._dict({
|
||||
@@ -343,6 +372,7 @@ def get_signup_optin_checks():
|
||||
|
||||
return (", ").join(links)
|
||||
|
||||
|
||||
def get_popular_courses():
|
||||
courses = frappe.get_all("LMS Course", {"published": 1, "upcoming": 0})
|
||||
course_membership = []
|
||||
@@ -356,6 +386,7 @@ def get_popular_courses():
|
||||
course_membership = sorted(course_membership, key = lambda x: x.get("members"), reverse=True)
|
||||
return course_membership[:3]
|
||||
|
||||
|
||||
def get_evaluation_details(course, member=None):
|
||||
info = frappe.db.get_value("LMS Course", course, ["grant_certificate_after", "max_attempts", "duration"], as_dict=True)
|
||||
request = frappe.db.get_value("LMS Certificate Request", {
|
||||
@@ -378,6 +409,7 @@ def get_evaluation_details(course, member=None):
|
||||
"no_of_attempts": no_of_attempts
|
||||
})
|
||||
|
||||
|
||||
def format_amount(amount, currency):
|
||||
amount_reduced = amount / 1000
|
||||
if amount_reduced < 1:
|
||||
@@ -397,6 +429,7 @@ def first_lesson_exists(course):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def redirect_to_courses_list():
|
||||
frappe.local.flags.redirect_location = "/courses"
|
||||
raise frappe.Redirect
|
||||
|
||||
@@ -145,7 +145,7 @@ def get_enrolled_courses():
|
||||
}
|
||||
|
||||
def get_course_membership(member, member_type=None):
|
||||
""" Returns all memberships of the user """
|
||||
""" Returns all memberships of the user. """
|
||||
filters = {
|
||||
"member": member
|
||||
}
|
||||
@@ -156,8 +156,7 @@ def get_course_membership(member, member_type=None):
|
||||
|
||||
|
||||
def get_authored_courses(member, only_published=True):
|
||||
"""Returns the number of courses authored by this user.
|
||||
"""
|
||||
""" Returns the number of courses authored by this user. """
|
||||
course_details = []
|
||||
|
||||
filters = {
|
||||
|
||||
@@ -1658,7 +1658,7 @@ li {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.help-article {
|
||||
.medium {
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
{% if lesson.edit_mode %}
|
||||
{{ EditLesson(lesson) }}
|
||||
{% else %}
|
||||
{{ render_html(lesson.body) }}
|
||||
{{ render_html(lesson.body, lesson.youtube, lesson.quiz_id) }}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
@@ -181,20 +181,34 @@
|
||||
|
||||
<!-- Edit Lesson -->
|
||||
{% macro EditLesson(lesson) %}
|
||||
<div id="body" {% if lesson.body %} data-body="{{ lesson.body }}" {% endif %}></div>
|
||||
|
||||
<label class="preview">
|
||||
<input {% if lesson.include_in_preview %} checked {% endif %} type="checkbox"
|
||||
id="preview"> {{ _("Show preview of this lesson to Guest users.") }}
|
||||
</label>
|
||||
<div class="medium mt-2" contenteditable="true" data-placeholder="{{ _('YouTube Video ID') }}"
|
||||
id="youtube">{% if lesson.youtube %}{{ lesson.youtube }}{% endif %}</div>
|
||||
<div id="body" {% if lesson.body %} data-body="{{ lesson.body }}" {% endif %}></div>
|
||||
<div class="medium mb-4" contenteditable="true" data-placeholder="{{ _('Quiz ID') }}"
|
||||
id="quiz-id">{% if lesson.quiz_id %}{{ lesson.quiz_id }}{% endif %}</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button class="btn btn-primary btn-sm btn-lesson pull-right ml-2"> {{ _("Save") }} </button>
|
||||
{% if lesson.name %}
|
||||
<button class="btn btn-secondary btn-sm pull-right btn-back ml-2"> {{ _("Back to Lesson") }} </button>
|
||||
<a class="btn btn-secondary btn-sm pull-right" href="/quizzes"> {{ _("Create Quiz") }} </a>
|
||||
{% endif %}
|
||||
<label class="preview">
|
||||
<input {% if lesson.include_in_preview %} checked {% endif %} type="checkbox"
|
||||
id="preview"> {{ _("Show preview of this lesson to Guest users.") }}
|
||||
</label>
|
||||
|
||||
<div class="mt-4">
|
||||
<button class="btn btn-primary btn-sm btn-lesson pull-right ml-2"> {{ _("Save") }} </button>
|
||||
{% if lesson.name %}
|
||||
<button class="btn btn-secondary btn-sm pull-right btn-back ml-2"> {{ _("Back to Lesson") }} </button>
|
||||
<a class="btn btn-secondary btn-sm pull-right" href="/quizzes"> {{ _("Create Quiz") }} </a>
|
||||
{% endif %}
|
||||
|
||||
{{ UploadAttachments() }}
|
||||
|
||||
</div>
|
||||
|
||||
{{ HelpArticle() }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro UploadAttachments() %}
|
||||
<div class="attachments-parent">
|
||||
<div class="attachment-controls">
|
||||
<div class="show-attachments" data-toggle="collapse" data-target="#collapse-attachments" aria-expanded="false">
|
||||
@@ -214,97 +228,111 @@
|
||||
</div>
|
||||
<table class="attachments common-card-style collapse hide" id="collapse-attachments"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ HelpArticle() }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- Help Article -->
|
||||
{% macro HelpArticle() %}
|
||||
<div class="help-article">
|
||||
<h3> {{ _("Embed Components") }} </h3>
|
||||
<p>
|
||||
{{ _("You can add additional content to the lesson using a special syntax. The table below mentions
|
||||
all types of dynamic content that you can add to the lessons and the syntax for the same.") }}
|
||||
</p>
|
||||
<table class="table w-100">
|
||||
<tr>
|
||||
<th style="width: 20%;"> {{ _("Content Type") }} </th>
|
||||
<th style="width: 40%;"> {{ _("Syntax") }} </th>
|
||||
<th> {{ _("Description") }} </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ _("YouTube Video") }}
|
||||
</td>
|
||||
<td>
|
||||
{% raw %} {{ YouTubeVideo('Video ID') }} {% endraw %}
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
{{ _("Copy and paste the syntax in the editor. Replace 'Video ID' with the embed source
|
||||
that YouTube provides. To get the source, follow the steps mentioned below.") }}
|
||||
</span>
|
||||
<ul class="p-4">
|
||||
<div class="medium">
|
||||
<h3> {{ _("Embed Components") }} </h3>
|
||||
<p>
|
||||
{{ _("You can add additional content to the lesson using a special syntax. The table below mentions
|
||||
all types of dynamic content that you can add to the lessons and the syntax for the same.") }}
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
<b> {{ _("YouTube Video") }} </b>
|
||||
<p> To get the YouTube Video ID, follow the steps mentioned below. </p>
|
||||
<ul class="px-4">
|
||||
<li>
|
||||
{{ _("Upload the video on youtube.") }}
|
||||
</li>
|
||||
<li>
|
||||
{{ _("When you share a youtube video, it shows an option called Embed.") }}
|
||||
{{ _("When you share a youtube video, it shows a URL") }}
|
||||
</li>
|
||||
<li>
|
||||
{{ _("On clicking it, it provides an iframe. Copy the source (src) of the iframe and
|
||||
paste it here.") }}
|
||||
{{ _("Copy the last parameter of the URL and paste it here.") }}
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ _("Quiz") }}
|
||||
</td>
|
||||
<td>
|
||||
{% raw %} {{ Quiz('Quiz ID') }} {% endraw %}
|
||||
</td>
|
||||
<td>
|
||||
{% set quiz_link = "<a href='/quizzes'> Quiz List </a>" %}
|
||||
{{ _("Copy and paste the syntax in the editor. Replace 'Quiz ID' with the Id of the Quiz.
|
||||
You can get the Id of the quiz from the {0}.").format(quiz_link) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<!-- <table class="table w-100">
|
||||
<tr>
|
||||
<th style="width: 20%;"> {{ _("Content Type") }} </th>
|
||||
<th style="width: 40%;"> {{ _("Syntax") }} </th>
|
||||
<th> {{ _("Description") }} </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ _("YouTube Video") }}
|
||||
</td>
|
||||
<td>
|
||||
{% raw %} {{ YouTubeVideo('Video ID') }} {% endraw %}
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
{{ _("Copy and paste the syntax in the editor. Replace 'Video ID' with the embed source
|
||||
that YouTube provides. To get the source, follow the steps mentioned below.") }}
|
||||
</span>
|
||||
<ul class="p-4">
|
||||
<li>
|
||||
{{ _("Upload the video on youtube.") }}
|
||||
</li>
|
||||
<li>
|
||||
{{ _("When you share a youtube video, it shows an option called Embed.") }}
|
||||
</li>
|
||||
<li>
|
||||
{{ _("On clicking it, it provides an iframe. Copy the source (src) of the iframe and
|
||||
paste it here.") }}
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ _("Quiz") }}
|
||||
</td>
|
||||
<td>
|
||||
{% raw %} {{ Quiz('Quiz ID') }} {% endraw %}
|
||||
</td>
|
||||
<td>
|
||||
{% set quiz_link = "<a href='/quizzes'> Quiz List </a>" %}
|
||||
{{ _("Copy and paste the syntax in the editor. Replace 'Quiz ID' with the Id of the Quiz.
|
||||
You can get the Id of the quiz from the {0}.").format(quiz_link) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table> -->
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- Discussions Component -->
|
||||
{% macro Discussions() %}
|
||||
{% set topics_count = frappe.db.count("Discussion Topic", {
|
||||
"reference_doctype": "Course Lesson",
|
||||
"reference_docname": lesson.name
|
||||
}) %}
|
||||
{% set is_instructor = frappe.session.user == course.instructor %}
|
||||
{% set condition = is_instructor if is_instructor else membership %}
|
||||
{% set doctype, docname = _("Course Lesson"), lesson.name %}
|
||||
{% set title = "Questions" if topics_count else "" %}
|
||||
{% set cta_title = "Ask a Question" %}
|
||||
{% set button_name = _("Start Learning") %}
|
||||
{% set redirect_to = "/courses/" + course.name %}
|
||||
{% set empty_state_title = _("Have a doubt?") %}
|
||||
{% set empty_state_subtitle = _("Post it here, our mentors will help you out.") %}
|
||||
{% include "frappe/templates/discussions/discussions_section.html" %}
|
||||
{% set topics_count = frappe.db.count("Discussion Topic", {
|
||||
"reference_doctype": "Course Lesson",
|
||||
"reference_docname": lesson.name
|
||||
}) %}
|
||||
{% set is_instructor = frappe.session.user == course.instructor %}
|
||||
{% set condition = is_instructor if is_instructor else membership %}
|
||||
{% set doctype, docname = _("Course Lesson"), lesson.name %}
|
||||
{% set title = "Questions" if topics_count else "" %}
|
||||
{% set cta_title = "Ask a Question" %}
|
||||
{% set button_name = _("Start Learning") %}
|
||||
{% set redirect_to = "/courses/" + course.name %}
|
||||
{% set empty_state_title = _("Have a doubt?") %}
|
||||
{% set empty_state_subtitle = _("Post it here, our mentors will help you out.") %}
|
||||
{% include "frappe/templates/discussions/discussions_section.html" %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- Scripts -->
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
var page_context = {{ page_context | tojson }};
|
||||
</script>
|
||||
{{ include_script('controls.bundle.js') }}
|
||||
{% for ext in page_extensions %}
|
||||
{{ ext.render_footer() }}
|
||||
{% endfor %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
var page_context = {{ page_context | tojson }};
|
||||
</script>
|
||||
{{ include_script('controls.bundle.js') }}
|
||||
{% for ext in page_extensions %}
|
||||
{{ ext.render_footer() }}
|
||||
{% endfor %}
|
||||
{%- endblock %}
|
||||
|
||||
@@ -490,7 +490,9 @@ const save_lesson = (e) => {
|
||||
method: "lms.lms.doctype.lms_course.lms_course.save_lesson",
|
||||
args: {
|
||||
"title": $("#title").text(),
|
||||
"body": this.code_field_group.fields_dict["code_md"].last_value,
|
||||
"body": this.code_field_group.fields_dict["code_md"].value,
|
||||
"youtube": $("#youtube").text(),
|
||||
"quiz_id": $("#quiz-id").text(),
|
||||
"chapter": $("#title").data("chapter"),
|
||||
"preview": $("#preview").prop("checked") ? 1 : 0,
|
||||
"idx": $("#title").data("index"),
|
||||
|
||||
Reference in New Issue
Block a user