Merge branch 'main' of https://github.com/frappe/community into style-fixes
This commit is contained in:
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
|
||||||
// For license information, please see license.txt
|
|
||||||
|
|
||||||
frappe.ui.form.on('Community Course Member', {
|
|
||||||
// refresh: function(frm) {
|
|
||||||
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
{
|
|
||||||
"actions": [],
|
|
||||||
"autoname": "field:user_name",
|
|
||||||
"creation": "2021-03-02 11:24:49.612530",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"field_order": [
|
|
||||||
"enabled",
|
|
||||||
"full_name",
|
|
||||||
"user_name",
|
|
||||||
"email",
|
|
||||||
"short_intro",
|
|
||||||
"bio",
|
|
||||||
"photo",
|
|
||||||
"route"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"default": "1",
|
|
||||||
"fieldname": "enabled",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Enabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "full_name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Full Name",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "user_name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "User Name",
|
|
||||||
"reqd": 1,
|
|
||||||
"unique": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "email",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Email",
|
|
||||||
"options": "Email",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "short_intro",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Short Intro"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "bio",
|
|
||||||
"fieldtype": "Markdown Editor",
|
|
||||||
"label": "Bio"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "photo",
|
|
||||||
"fieldtype": "Attach Image",
|
|
||||||
"label": "Photo"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "route",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Route"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_web_view": 1,
|
|
||||||
"index_web_pages_for_search": 1,
|
|
||||||
"is_published_field": "enabled",
|
|
||||||
"links": [],
|
|
||||||
"modified": "2021-04-06 11:50:41.551665",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Community",
|
|
||||||
"name": "Community Course Member",
|
|
||||||
"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,
|
|
||||||
"route": "community-course-member",
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, Frappe and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
import re
|
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
|
||||||
from frappe import _
|
|
||||||
|
|
||||||
class CommunityCourseMember(WebsiteGenerator):
|
|
||||||
|
|
||||||
def get_context(self, context):
|
|
||||||
context.abbr = ("").join([ s[0] for s in self.full_name.split() ])
|
|
||||||
return context
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
self.validate_user_name()
|
|
||||||
if not self.route:
|
|
||||||
self.route = self.user_name
|
|
||||||
|
|
||||||
def validate_user_name(self):
|
|
||||||
if len(self.user_name) < 4:
|
|
||||||
frappe.throw(_("Username must be atleast 4 characters long."))
|
|
||||||
if not re.match("^[A-Za-z0-9_]*$", self.user_name):
|
|
||||||
frappe.throw(_("Username can only contain alphabets, numbers, and underscore."))
|
|
||||||
self.user_name = self.user_name.lower()
|
|
||||||
|
|
||||||
def after_insert(self):
|
|
||||||
if frappe.db.exists("User", self.email):
|
|
||||||
user = frappe.get_doc("User", self.email)
|
|
||||||
else:
|
|
||||||
user, update_password_link = self.create_user()
|
|
||||||
self.send_email(update_password_link)
|
|
||||||
|
|
||||||
def send_email(self, update_password_link):
|
|
||||||
|
|
||||||
args = {
|
|
||||||
'update_password_link': update_password_link,
|
|
||||||
'full_name': self.full_name,
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.sendmail(
|
|
||||||
recipients=self.email,
|
|
||||||
sender="Administrator",
|
|
||||||
subject=_("Set your Password"),
|
|
||||||
template="community_course_membership",
|
|
||||||
reference_doctype=self.doctype,
|
|
||||||
reference_name=self.name,
|
|
||||||
send_priority=0,
|
|
||||||
queue_separately=True,
|
|
||||||
args=args)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{% extends "templates/web.html" %}
|
|
||||||
{% block page_content %}
|
|
||||||
<div class="py-20 row">
|
|
||||||
{% if photo %}
|
|
||||||
<div class="col-sm-2 border border-dark">
|
|
||||||
<img src="{{ photo }}" alt="{{ full_name }}">
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<div class="standard-image" style="font-size: 30px;">{{ abbr }}</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="col">
|
|
||||||
<h1>{{ full_name }}</h1>
|
|
||||||
{% if short_intro %}
|
|
||||||
<p class="lead"> {{ short_intro }} </p>
|
|
||||||
{% endif %}
|
|
||||||
{% if bio %}
|
|
||||||
<p class="markdown-style"> {{ frappe.utils.md_to_html(bio) }} </p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<!-- this is a sample default web page template -->
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<div>
|
|
||||||
<a href="{{ doc.route }}">{{ doc.full_name }}</a>
|
|
||||||
</div>
|
|
||||||
<!-- this is a sample default list template -->
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, Frappe and Contributors
|
|
||||||
# See license.txt
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# import frappe
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
class TestCommunityCourseMember(unittest.TestCase):
|
|
||||||
pass
|
|
||||||
@@ -6,11 +6,13 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"batch",
|
"batch",
|
||||||
"role",
|
|
||||||
"column_break_3",
|
|
||||||
"member",
|
"member",
|
||||||
"member_name",
|
"member_name",
|
||||||
"member_type"
|
"member_email",
|
||||||
|
"column_break_3",
|
||||||
|
"course",
|
||||||
|
"member_type",
|
||||||
|
"role"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
"options": "User"
|
"options": "User"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "Student",
|
||||||
"fieldname": "member_type",
|
"fieldname": "member_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -37,6 +40,7 @@
|
|||||||
"options": "\nStudent\nMentor\nStaff"
|
"options": "\nStudent\nMentor\nStaff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "Member",
|
||||||
"fieldname": "role",
|
"fieldname": "role",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
@@ -54,11 +58,28 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "member.email",
|
||||||
|
"fieldname": "member_email",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Member Email",
|
||||||
|
"read_only": 1,
|
||||||
|
"read_only_depends_on": "member.email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "batch.course",
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Course",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-21 11:48:05.363738",
|
"modified": "2021-05-24 09:32:04.128620",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Batch Membership",
|
"name": "LMS Batch Membership",
|
||||||
|
|||||||
@@ -92,27 +92,17 @@ class LMSCourse(Document):
|
|||||||
Returns None if the student is not part of any batch.
|
Returns None if the student is not part of any batch.
|
||||||
"""
|
"""
|
||||||
if not email:
|
if not email:
|
||||||
return False
|
return
|
||||||
result = frappe.db.get_all(
|
|
||||||
"LMS Batch Membership",
|
|
||||||
filters={
|
|
||||||
"member": email,
|
|
||||||
"member_type": "Student",
|
|
||||||
},
|
|
||||||
fields=['batch']
|
|
||||||
)
|
|
||||||
batches = [row['batch'] for row in result]
|
|
||||||
|
|
||||||
# filter the batches that are for this course
|
batch_name = frappe.get_value(
|
||||||
result = frappe.db.get_all(
|
doctype="LMS Batch Membership",
|
||||||
"LMS Batch",
|
|
||||||
filters={
|
filters={
|
||||||
"course": self.name,
|
"course": self.name,
|
||||||
"name": ["IN", batches]
|
"member_type": "Student",
|
||||||
})
|
"member": email
|
||||||
batches = [row['name'] for row in result]
|
},
|
||||||
if batches:
|
fieldname="batch")
|
||||||
return frappe.get_doc("LMS Batch", batches[0])
|
return batch_name and frappe.get_doc("LMS Batch", batch_name)
|
||||||
|
|
||||||
def get_instructor(self):
|
def get_instructor(self):
|
||||||
return frappe.get_doc("User", self.owner)
|
return frappe.get_doc("User", self.owner)
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="course-footer">
|
<div class="course-footer">
|
||||||
|
{% set batch = course.get_student_batch(frappe.session.user) %}
|
||||||
|
{% if batch %}
|
||||||
|
<a class="btn btn-secondary pull-right" href="/courses/{{course.name}}/{{batch.name}}/learn">Resume Course</a>
|
||||||
|
{% endif %}
|
||||||
<div class="course-author">
|
<div class="course-author">
|
||||||
{% with author = course.get_instructor() %}
|
{% with author = course.get_instructor() %}
|
||||||
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="/{{author.username}}">{{ author.full_name }}</a>
|
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="/{{author.username}}">{{ author.full_name }}</a>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "templates/base.html" %}
|
{% extends "templates/base.html" %}
|
||||||
{% from "www/macros/profile.html" import Profile %}
|
|
||||||
{% block title %}{{ _("Community") }}{% endblock %}
|
{% block title %}{{ _("Community") }}{% endblock %}
|
||||||
{% block head_include %}
|
{% block head_include %}
|
||||||
<meta name="description" content="{{ 'Community' }}" />
|
<meta name="description" content="{{ 'Community' }}" />
|
||||||
@@ -15,34 +14,6 @@
|
|||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard__profile {
|
|
||||||
width: 150px;
|
|
||||||
height: 155px;
|
|
||||||
border-radius: 50%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard__profileSmall {
|
|
||||||
width: 59px;
|
|
||||||
height: 57px;
|
|
||||||
border-radius: 50%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard__abbr {
|
|
||||||
font-size: 50px;
|
|
||||||
width: 155px;
|
|
||||||
height: 155px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard__abbrSmall {
|
|
||||||
font-size: 20px;
|
|
||||||
width: 59px;
|
|
||||||
height: 57px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard__parent {
|
.dashboard__parent {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{% macro Profile(photo, full_name, abbr, icon) %}
|
|
||||||
{% if photo %}
|
|
||||||
<img class="avatar rounded-circle img-fluid mr-5{% if icon == 'large' %} dashboard__profile {% else %} dashboard__profileSmall {% endif %}"
|
|
||||||
src="{{ photo }}" alt="{{ full_name }}">
|
|
||||||
{% else %}
|
|
||||||
<div class="standard-image mr-5 {% if icon == 'large' %} dashboard__abbr {% else %} dashboard__abbrSmall {% endif %}">
|
|
||||||
{{ abbr }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endmacro %}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "templates/web.html" %}
|
{% extends "templates/web.html" %}
|
||||||
{% from "www/macros/profile.html" import Profile %}
|
|
||||||
{% block head_include %}
|
{% block head_include %}
|
||||||
<meta name="description" content="{{ 'Community' }}" />
|
<meta name="description" content="{{ 'Community' }}" />
|
||||||
<meta name="keywords" content="An app that supports Communities." />
|
<meta name="keywords" content="An app that supports Communities." />
|
||||||
|
|||||||
Reference in New Issue
Block a user