Merge pull request #332 from pateljannat/certificate-design
This commit is contained in:
@@ -10,16 +10,15 @@ from lms.lms.utils import is_certified
|
|||||||
|
|
||||||
class LMSCertificate(Document):
|
class LMSCertificate(Document):
|
||||||
|
|
||||||
def validate(self):
|
def before_insert(self):
|
||||||
certificates = frappe.get_all("LMS Certificate", {
|
certificates = frappe.get_all("LMS Certificate", {
|
||||||
"member": self.member,
|
"member": self.member,
|
||||||
"course": self.course,
|
"course": self.course
|
||||||
"expiry_date": [">", nowdate()]
|
})
|
||||||
})
|
|
||||||
if len(certificates):
|
if len(certificates):
|
||||||
full_name = frappe.db.get_value("User", self.member, "full_name")
|
full_name = frappe.db.get_value("User", self.member, "full_name")
|
||||||
course_name = frappe.db.get_value("LMS Course", self.course, "title")
|
course_name = frappe.db.get_value("LMS Course", self.course, "title")
|
||||||
frappe.throw(_("There is already a valid certificate for user {0} for the course {1}").format(full_name, course_name))
|
frappe.throw(_("{0} is already certified for the course {1}").format(full_name, course_name))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_certificate(course):
|
def create_certificate(course):
|
||||||
@@ -35,11 +34,11 @@ def create_certificate(course):
|
|||||||
expiry_date = add_years(nowdate(), expires_after_yrs)
|
expiry_date = add_years(nowdate(), expires_after_yrs)
|
||||||
|
|
||||||
certificate = frappe.get_doc({
|
certificate = frappe.get_doc({
|
||||||
"doctype": "LMS Certificate",
|
"doctype": "LMS Certificate",
|
||||||
"member": frappe.session.user,
|
"member": frappe.session.user,
|
||||||
"course": course,
|
"course": course,
|
||||||
"issue_date": nowdate(),
|
"issue_date": nowdate(),
|
||||||
"expiry_date": expiry_date
|
"expiry_date": expiry_date
|
||||||
})
|
})
|
||||||
certificate.save(ignore_permissions=True)
|
certificate.save(ignore_permissions=True)
|
||||||
return certificate
|
return certificate
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"force_profile_completion",
|
"force_profile_completion",
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"search_placeholder",
|
"search_placeholder",
|
||||||
|
"custom_certificate_template",
|
||||||
"livecode_url",
|
"livecode_url",
|
||||||
"signup_settings_section",
|
"signup_settings_section",
|
||||||
"terms_of_use",
|
"terms_of_use",
|
||||||
@@ -63,7 +64,7 @@
|
|||||||
"depends_on": "show_search",
|
"depends_on": "show_search",
|
||||||
"fieldname": "search_placeholder",
|
"fieldname": "search_placeholder",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Search Field Placeholder"
|
"label": "Course List Search Bar Placeholder"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -137,12 +138,18 @@
|
|||||||
"fieldname": "user_category",
|
"fieldname": "user_category",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Ask User Category during Signup"
|
"label": "Ask User Category during Signup"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "custom_certificate_template",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Custom Certificate Template",
|
||||||
|
"options": "Web Template"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-04-20 09:09:12.369728",
|
"modified": "2022-05-09 09:55:24.519269",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Settings",
|
"name": "LMS Settings",
|
||||||
|
|||||||
@@ -836,15 +836,22 @@ pre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.certificate-content {
|
.certificate-content {
|
||||||
padding: 2.5rem 3rem;
|
background-color: #FFFFFF;
|
||||||
background-color: #FFFFFF;
|
border-width: 100px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.certificate-content {
|
||||||
|
border-width: 50px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-footer {
|
.certificate-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 30%;
|
margin: 4rem auto 0;
|
||||||
margin: 5rem auto 0;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-price {
|
.certificate-price {
|
||||||
@@ -862,6 +869,7 @@ pre {
|
|||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-para {
|
.certificate-para {
|
||||||
@@ -875,24 +883,19 @@ pre {
|
|||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0 6rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-footer-item {
|
.certificate-footer-item {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-weight: 500;
|
font-weight: bold;
|
||||||
|
font-family: cursive;
|
||||||
|
font-size: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-logo {
|
.certificate-logo {
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1050px) {
|
|
||||||
.certificate-footer {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.certificate-card {
|
.certificate-card {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -903,10 +906,6 @@ pre {
|
|||||||
.certificate-content {
|
.certificate-content {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-footer {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-card {
|
.profile-card {
|
||||||
|
|||||||
BIN
lms/public/images/border.png
Normal file
BIN
lms/public/images/border.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 812 KiB |
@@ -1,50 +1,33 @@
|
|||||||
<div class="certificate-card">
|
<div class="certificate-card">
|
||||||
<div class="certificate-ribbon">
|
<div class="certificate-content" style="border-image: url(/assets/lms/images/border.png) 13% round;">
|
||||||
<div class="certificate-content">
|
<img src="{{ logo }}" class="certificate-logo">
|
||||||
<img src="{{ logo }}" class="certificate-logo">
|
<div class="mt-16">
|
||||||
<div class="mt-20">
|
{{ _("This certifies that") }}
|
||||||
{{ _("This certifies that") }}
|
</div>
|
||||||
|
<div class="certificate-heading"> {{ member.full_name }} </div>
|
||||||
|
<div class="mt-2"> {{ _("has successfully completed the course on") }}
|
||||||
|
<b> {{ course.title }} </b> on {{ frappe.utils.format_date(certificate.issue_date, "medium") }}. </div>
|
||||||
|
|
||||||
|
<div class="certificate-footer">
|
||||||
|
|
||||||
|
{% if instructors %}
|
||||||
|
<div class="">
|
||||||
|
<span class="certificate-footer-item"> {{ instructors }} </span>
|
||||||
|
<hr class="mt-2 mb-2">
|
||||||
|
<div class=""> {{ _("Course Instructor") }} </div>
|
||||||
</div>
|
</div>
|
||||||
<div class="certificate-heading"> {{ member.full_name }} </div>
|
{% endif %}
|
||||||
<div class="mt-2"> {{ _("has successfully completed the course on") }} </div>
|
|
||||||
<div class="certificate-heading"> {{ course.title }} </div>
|
|
||||||
|
|
||||||
<div class="certificate-footer">
|
|
||||||
{% if certificate.issue_date %}
|
|
||||||
<div class="">
|
|
||||||
<div class="certificate-footer-item">
|
|
||||||
{{ frappe.utils.format_date(certificate.issue_date, "medium") }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ _("Issue date") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if certificate.expiry_date %}
|
|
||||||
<div class="">
|
|
||||||
<div class="certificate-footer-item">
|
|
||||||
{{ frappe.utils.format_date(certificate.expiry_date, "medium") }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ _("Expiry date") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if instructor.full_name %}
|
|
||||||
<div class="">
|
|
||||||
<div class="certificate-footer-item">
|
|
||||||
{{ instructor.full_name }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ _("Instructor") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
{% if certificate.expiry_date %}
|
||||||
|
<div class="ml-8">
|
||||||
|
<div class="certificate-footer-item">
|
||||||
|
{{ frappe.utils.format_date(certificate.expiry_date, "medium") }}
|
||||||
</div>
|
</div>
|
||||||
|
<hr class="mt-2 mb-2">
|
||||||
|
<div class=""> {{ _("Expiry date") }} </div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/lms/js/html2canvas.js"></script>
|
|
||||||
|
|||||||
@@ -5,18 +5,23 @@
|
|||||||
<div class="common-page-style">
|
<div class="common-page-style">
|
||||||
<div class="container certificate-page">
|
<div class="container certificate-page">
|
||||||
|
|
||||||
{% if certificate.member == frappe.session.user %}
|
<!-- {% if certificate.member == frappe.session.user %}
|
||||||
<div class="button is-secondary pull-right mt-4" id="export-as-pdf" data-certificate="{{ certificate.name }}"
|
<div class="button is-secondary pull-right mt-4" id="export-as-pdf" data-certificate="{{ certificate.name }}"
|
||||||
data-certificate-name="{{ member.full_name }} - {{ course.title }}">{{ _("Export") }}</div>
|
data-certificate-name="{{ member.full_name }} - {{ course.title }}">{{ _("Export") }}</div>
|
||||||
{% endif %}
|
{% endif %} -->
|
||||||
|
|
||||||
<div class="breadcrumb">
|
<div class="breadcrumb">
|
||||||
<a class="dark-links" href="/courses">{{ _("All Courses") }}</a>
|
<a class="dark-links" href="/courses">{{ _("All Courses") }}</a>
|
||||||
<img class="ml-1 mr-1" src="/assets/lms/icons/chevron-right.svg">
|
<img class="ml-1 mr-1" src="/assets/lms/icons/chevron-right.svg">
|
||||||
<a class="dark-links" href="/courses/{{ course.name }}">{{ course.title }}</a>
|
<a class="dark-links" href="/courses/{{ course.name }}">{{ course.title }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
{% if custom_template %}
|
||||||
|
{{ custom_template }}
|
||||||
|
{% else %}
|
||||||
{% include "lms/templates/certificate.html" %}
|
{% include "lms/templates/certificate.html" %}
|
||||||
|
{% endif %}
|
||||||
|
<script src="/assets/lms/js/html2canvas.js"></script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
frappe.ready(() => {
|
frappe.ready(() => {
|
||||||
|
|
||||||
$("#export-as-pdf").click((e) => {
|
$("#export-as-pdf").click((e) => {
|
||||||
export_as_pdf(e);
|
export_as_pdf(e);
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|
||||||
var export_as_pdf = (e) => {
|
const export_as_pdf = (e) => {
|
||||||
var button = $(e.currentTarget);
|
let button = $(e.currentTarget);
|
||||||
button.text(__("Exporting..."));
|
button.text(__("Exporting..."));
|
||||||
|
|
||||||
html2canvas(document.querySelector('.common-card-style'), {
|
html2canvas(document.querySelector('.certificate-card'), {
|
||||||
scrollY: -window.scrollY,
|
scrollY: -window.scrollY,
|
||||||
scrollX: 0
|
scrollX: 0
|
||||||
}).then(function(canvas) {
|
}).then(function(canvas) {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils.jinja import render_template
|
||||||
|
from lms.lms.utils import get_instructors
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.no_cache = 1
|
context.no_cache = 1
|
||||||
@@ -15,16 +17,15 @@ def get_context(context):
|
|||||||
if context.certificate.course != course_name:
|
if context.certificate.course != course_name:
|
||||||
redirect_to_course_list()
|
redirect_to_course_list()
|
||||||
|
|
||||||
context.course = frappe.db.get_value("LMS Course", course_name,
|
context.course = frappe.db.get_value("LMS Course", course_name, ["title", "name"], as_dict=True)
|
||||||
["instructor", "title", "name"], as_dict=True)
|
context.instructors = (", ").join([x.full_name for x in get_instructors(course_name)])
|
||||||
|
|
||||||
context.instructor = frappe.db.get_value("User", context.course.instructor,
|
|
||||||
["full_name", "username"], as_dict=True)
|
|
||||||
|
|
||||||
context.member = frappe.db.get_value("User", context.certificate.member,
|
context.member = frappe.db.get_value("User", context.certificate.member,
|
||||||
["full_name"], as_dict=True)
|
["full_name"], as_dict=True)
|
||||||
|
|
||||||
context.logo = frappe.db.get_single_value("Website Settings", "banner_image")
|
context.logo = frappe.db.get_single_value("Website Settings", "banner_image")
|
||||||
|
template_name = frappe.db.get_single_value("LMS Settings", "custom_certificate_template")
|
||||||
|
context.custom_certificate_template = frappe.db.get_value("Web Template", template_name, "template")
|
||||||
|
context.custom_template = render_template(context.custom_certificate_template, context)
|
||||||
|
|
||||||
def redirect_to_course_list():
|
def redirect_to_course_list():
|
||||||
frappe.local.flags.redirect_location = "/courses"
|
frappe.local.flags.redirect_location = "/courses"
|
||||||
|
|||||||
Reference in New Issue
Block a user