Merge pull request #984 from frappe/develop
chore: merge 'develop' into 'main'
This commit is contained in:
1
.github/workflows/ui-tests.yml
vendored
1
.github/workflows/ui-tests.yml
vendored
@@ -99,6 +99,7 @@ jobs:
|
||||
cd ~/frappe-bench/
|
||||
bench --site lms.test execute frappe.utils.install.complete_setup_wizard
|
||||
bench --site lms.test execute frappe.tests.ui_test_helpers.create_test_user
|
||||
bench --site lms.test set-password frappe@example.com admin
|
||||
|
||||
- name: cypress pre-requisites
|
||||
run: |
|
||||
|
||||
183
frontend/src/components/Members.vue
Normal file
183
frontend/src/components/Members.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div class="text-base p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="font-semibold mb-1">
|
||||
{{ __(label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ __(description) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex item-center space-x-2">
|
||||
<FormControl
|
||||
v-model="search"
|
||||
:placeholder="__('Search')"
|
||||
type="text"
|
||||
:debounce="300"
|
||||
/>
|
||||
<Button @click="() => (showForm = true)">
|
||||
<template #icon>
|
||||
<Plus class="h-3 w-3 stroke-1.5" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<!-- Form to add new member -->
|
||||
<div v-if="showForm" class="flex items-center space-x-2 mb-4">
|
||||
<FormControl
|
||||
v-model="member.email"
|
||||
:placeholder="__('Email')"
|
||||
type="email"
|
||||
class="w-full"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="member.first_name"
|
||||
:placeholder="__('First Name')"
|
||||
type="test"
|
||||
class="w-full"
|
||||
/>
|
||||
<Button @click="addMember()" variant="subtle">
|
||||
{{ __('Add') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Member list -->
|
||||
<div
|
||||
v-for="member in memberList"
|
||||
class="grid grid-cols-5 grid-flow-row py-2 cursor-pointer"
|
||||
>
|
||||
<div
|
||||
@click="openProfile(member.username)"
|
||||
class="flex items-center space-x-2 col-span-2"
|
||||
>
|
||||
<Avatar
|
||||
:image="member.user_image"
|
||||
:label="member.full_name"
|
||||
size="sm"
|
||||
/>
|
||||
<div>
|
||||
{{ member.full_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-700 col-span-2">
|
||||
{{ member.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-700 justify-self-end">
|
||||
{{ getRole(member.role) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasNextPage" class="flex justify-center">
|
||||
<Button variant="solid" @click="members.reload()">
|
||||
<template #prefix>
|
||||
<RefreshCw class="h-3 w-3 stroke-1.5" />
|
||||
</template>
|
||||
{{ __('Load More') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { createResource, Avatar, Button, FormControl } from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { RefreshCw, Plus } from 'lucide-vue-next'
|
||||
|
||||
const router = useRouter()
|
||||
const show = defineModel('show')
|
||||
const search = ref('')
|
||||
const start = ref(0)
|
||||
const memberList = ref([])
|
||||
const hasNextPage = ref(false)
|
||||
const showForm = ref(false)
|
||||
|
||||
const member = reactive({
|
||||
email: '',
|
||||
first_name: '',
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
},
|
||||
})
|
||||
|
||||
const members = createResource({
|
||||
url: 'lms.lms.api.get_members',
|
||||
makeParams: () => {
|
||||
return {
|
||||
search: search.value,
|
||||
start: start.value,
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
memberList.value = memberList.value.concat(data)
|
||||
start.value = start.value + 20
|
||||
hasNextPage.value = data.length === 20
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const openProfile = (username) => {
|
||||
show.value = false
|
||||
router.push({
|
||||
name: 'Profile',
|
||||
params: {
|
||||
username: username,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const newMember = createResource({
|
||||
url: 'frappe.client.insert',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'User',
|
||||
first_name: member.first_name,
|
||||
email: member.email,
|
||||
},
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
show.value = false
|
||||
router.push({
|
||||
name: 'Profile',
|
||||
params: {
|
||||
username: data.username,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const addMember = () => {
|
||||
newMember.reload()
|
||||
}
|
||||
|
||||
watch(search, () => {
|
||||
memberList.value = []
|
||||
start.value = 0
|
||||
members.reload()
|
||||
})
|
||||
|
||||
const getRole = (role) => {
|
||||
const map = {
|
||||
'LMS Student': 'Student',
|
||||
'Course Creator': 'Instructor',
|
||||
Moderator: 'Moderator',
|
||||
'Batch Evaluator': 'Evaluator',
|
||||
}
|
||||
return map[role]
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: '6xl' }">
|
||||
<Dialog v-model="show" :options="{ size: '3xl' }">
|
||||
<template #body>
|
||||
<div class="flex h-[calc(100vh_-_8rem)]">
|
||||
<div class="flex w-52 shrink-0 flex-col bg-gray-50 p-2">
|
||||
@@ -28,10 +28,21 @@
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col overflow-y-auto">
|
||||
<div
|
||||
v-if="activeTab && data.doc"
|
||||
class="flex flex-1 flex-col overflow-y-auto"
|
||||
>
|
||||
<Members
|
||||
v-if="activeTab.label === 'Members'"
|
||||
:label="activeTab.label"
|
||||
:description="activeTab.description"
|
||||
v-model:show="show"
|
||||
/>
|
||||
<SettingDetails
|
||||
v-if="activeTab && data.doc"
|
||||
v-else
|
||||
:fields="activeTab.fields"
|
||||
:label="activeTab.label"
|
||||
:description="activeTab.description"
|
||||
:data="data"
|
||||
/>
|
||||
</div>
|
||||
@@ -44,6 +55,7 @@ import { Dialog, createDocumentResource } from 'frappe-ui'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import SettingDetails from '../SettingDetails.vue'
|
||||
import SidebarLink from '@/components/SidebarLink.vue'
|
||||
import Members from '@/components/Members.vue'
|
||||
|
||||
const show = defineModel()
|
||||
const doctype = ref('LMS Settings')
|
||||
@@ -63,9 +75,16 @@ const tabs = computed(() => {
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
description: 'Manage the members of your learning system',
|
||||
icon: 'UserRoundPlus',
|
||||
},
|
||||
{
|
||||
label: 'Payment Gateway',
|
||||
icon: 'DollarSign',
|
||||
description:
|
||||
'Configure the payment gateway and other payment related settings',
|
||||
fields: [
|
||||
{
|
||||
label: 'Razorpay Key',
|
||||
@@ -97,7 +116,7 @@ const tabs = computed(() => {
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Apply rounding on equivalent amount',
|
||||
label: 'Apply rounding on equivalent',
|
||||
name: 'apply_rounding',
|
||||
type: 'checkbox',
|
||||
},
|
||||
@@ -105,62 +124,6 @@ const tabs = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Signup',
|
||||
icon: 'LogIn',
|
||||
fields: [
|
||||
{
|
||||
label: 'Show terms of use on signup page',
|
||||
name: 'terms_of_use',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Terms of Use Page',
|
||||
name: 'terms_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
label: 'Ask user category during signup',
|
||||
name: 'user_category',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show privacy policy on signup page',
|
||||
name: 'privacy_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Privacy Policy Page',
|
||||
name: 'privacy_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show cookie policy on signup page',
|
||||
name: 'cookie_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Cookie Policy Page',
|
||||
name: 'cookie_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
@@ -168,6 +131,7 @@ const tabs = computed(() => {
|
||||
{
|
||||
label: 'Sidebar',
|
||||
icon: 'PanelLeftIcon',
|
||||
description: 'Customize the sidebar as per your needs',
|
||||
fields: [
|
||||
{
|
||||
label: 'Courses',
|
||||
@@ -213,6 +177,7 @@ const tabs = computed(() => {
|
||||
{
|
||||
label: 'Email Templates',
|
||||
icon: 'MailPlus',
|
||||
description: 'Create email templates with the content you want',
|
||||
fields: [
|
||||
{
|
||||
label: 'Batch Confirmation Template',
|
||||
@@ -236,17 +201,61 @@ const tabs = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
/* {
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
icon: "UserRoundPlus",
|
||||
component: markRaw(MemberSettings),
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Signup',
|
||||
icon: 'LogIn',
|
||||
description:
|
||||
'Customize the signup page to inform users about your terms and policies',
|
||||
fields: [
|
||||
{
|
||||
label: 'Show terms of use on signup',
|
||||
name: 'terms_of_use',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Terms of Use Page',
|
||||
name: 'terms_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
label: 'Show privacy policy on signup',
|
||||
name: 'privacy_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Privacy Policy Page',
|
||||
name: 'privacy_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show cookie policy on signup',
|
||||
name: 'cookie_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Cookie Policy Page',
|
||||
name: 'cookie_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
label: 'Ask user category during signup',
|
||||
name: 'user_category',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}, */
|
||||
]
|
||||
|
||||
return _tabs.map((tab) => {
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-between h-full p-8">
|
||||
<div class="flex space-x-10">
|
||||
<div class="flex flex-col justify-between h-full p-4">
|
||||
<div>
|
||||
<div class="font-semibold mb-1">
|
||||
{{ __(label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ __(description) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-8 my-5">
|
||||
<div v-for="(column, index) in columns" :key="index">
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div v-for="field in column" :class="width">
|
||||
<div class="flex flex-col space-y-4 w-60">
|
||||
<div v-for="field in column">
|
||||
<Link
|
||||
v-if="field.type == 'Link'"
|
||||
v-model="field.value"
|
||||
@@ -31,11 +39,9 @@
|
||||
|
||||
<script setup>
|
||||
import { FormControl, Button } from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
let width = ref('w-full')
|
||||
|
||||
const props = defineProps({
|
||||
fields: {
|
||||
type: Array,
|
||||
@@ -45,6 +51,13 @@ const props = defineProps({
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
})
|
||||
|
||||
const columns = computed(() => {
|
||||
@@ -71,12 +84,6 @@ const columns = computed(() => {
|
||||
cols.push(currentColumn)
|
||||
}
|
||||
|
||||
if (cols.length == 3) {
|
||||
width.value = 'w-64'
|
||||
} else {
|
||||
width.value = 'w-96'
|
||||
}
|
||||
|
||||
return cols
|
||||
})
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.1.0"
|
||||
__version__ = "2.2.0"
|
||||
|
||||
@@ -562,3 +562,38 @@ def get_categories(doctype, filters):
|
||||
categoryOptions.append({"label": category, "value": category})
|
||||
|
||||
return categoryOptions
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_members(start=0, search=""):
|
||||
"""Get members for the given search term and start index.
|
||||
Args: start (int): Start index for the query.
|
||||
search (str): Search term to filter the results.
|
||||
Returns: List of members.
|
||||
"""
|
||||
|
||||
filters = {"enabled": 1, "name": ["not in", ["Administrator", "Guest"]]}
|
||||
|
||||
if search:
|
||||
filters["full_name"] = ["like", f"%{search}%"]
|
||||
|
||||
members = frappe.get_all(
|
||||
"User",
|
||||
filters=filters,
|
||||
fields=["name", "full_name", "user_image", "username"],
|
||||
page_length=20,
|
||||
start=start,
|
||||
)
|
||||
|
||||
for member in members:
|
||||
roles = frappe.get_roles(member.name)
|
||||
if "Moderator" in roles:
|
||||
member.role = "Moderator"
|
||||
elif "Course Creator" in roles:
|
||||
member.role = "Course Creator"
|
||||
elif "Batch Evaluator" in roles:
|
||||
member.role = "Batch Evaluator"
|
||||
elif "LMS Student" in roles:
|
||||
member.role = "LMS Student"
|
||||
|
||||
return members
|
||||
|
||||
@@ -11,74 +11,4 @@ from lms.lms.doctype.invite_request.invite_request import (
|
||||
|
||||
|
||||
class TestInviteRequest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
create_invite_request("test_invite@example.com")
|
||||
|
||||
def test_create_invite_request(self):
|
||||
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
|
||||
invite = frappe.db.get_value(
|
||||
"Invite Request",
|
||||
filters={"invite_email": "test_invite@example.com"},
|
||||
fieldname=["invite_email", "status", "signup_email"],
|
||||
as_dict=True,
|
||||
)
|
||||
self.assertEqual(invite.status, "Approved")
|
||||
self.assertEqual(invite.signup_email, None)
|
||||
|
||||
def test_create_invite_request_update(self):
|
||||
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
|
||||
|
||||
data = {
|
||||
"signup_email": "test_invite@example.com",
|
||||
"username": "test_invite",
|
||||
"full_name": "Test Invite",
|
||||
"password": "Test@invite",
|
||||
"invite_code": frappe.db.get_value(
|
||||
"Invite Request", {"invite_email": "test_invite@example.com"}, "name"
|
||||
),
|
||||
}
|
||||
|
||||
update_invite(data)
|
||||
invite = frappe.db.get_value(
|
||||
"Invite Request",
|
||||
filters={"invite_email": "test_invite@example.com"},
|
||||
fieldname=[
|
||||
"invite_email",
|
||||
"status",
|
||||
"signup_email",
|
||||
"full_name",
|
||||
"username",
|
||||
"invite_code",
|
||||
"name",
|
||||
],
|
||||
as_dict=True,
|
||||
)
|
||||
self.assertEqual(invite.signup_email, "test_invite@example.com")
|
||||
self.assertEqual(invite.full_name, "Test Invite")
|
||||
self.assertEqual(invite.username, "test_invite")
|
||||
self.assertEqual(invite.invite_code, invite.name)
|
||||
self.assertEqual(invite.status, "Registered")
|
||||
|
||||
user = frappe.db.get_value(
|
||||
"User",
|
||||
"test_invite@example.com",
|
||||
fieldname=["first_name", "username", "send_welcome_email", "user_type"],
|
||||
as_dict=True,
|
||||
)
|
||||
self.assertTrue(user)
|
||||
self.assertEqual(user.first_name, invite.full_name.split(" ")[0])
|
||||
self.assertEqual(user.username, invite.username)
|
||||
self.assertEqual(user.send_welcome_email, 0)
|
||||
self.assertEqual(user.user_type, "Website User")
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
if frappe.db.exists("User", "test_invite@example.com"):
|
||||
frappe.delete_doc("User", "test_invite@example.com")
|
||||
|
||||
invite_request = frappe.db.exists(
|
||||
"Invite Request", {"invite_email": "test_invite@example.com"}
|
||||
)
|
||||
if invite_request:
|
||||
frappe.delete_doc("Invite Request", invite_request)
|
||||
pass
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Frappe LMS VERSION\n"
|
||||
"Report-Msgid-Bugs-To: school@frappe.io\n"
|
||||
"POT-Creation-Date: 2024-08-09 16:04+0000\n"
|
||||
"PO-Revision-Date: 2024-08-09 16:04+0000\n"
|
||||
"POT-Creation-Date: 2024-08-16 16:04+0000\n"
|
||||
"PO-Revision-Date: 2024-08-16 16:04+0000\n"
|
||||
"Last-Translator: school@frappe.io\n"
|
||||
"Language-Team: school@frappe.io\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -48,7 +48,7 @@ msgstr ""
|
||||
msgid "Add a Lesson"
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_question/lms_question.py:59
|
||||
#: lms/doctype/lms_question/lms_question.py:60
|
||||
msgid "Add at least one possible answer for this question: {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -98,11 +98,6 @@ msgstr ""
|
||||
msgid "Allow accessing future dates"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the allow_student_progress (Check) field in DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Allow students to see each others progress in class"
|
||||
msgstr ""
|
||||
|
||||
#: overrides/user.py:195
|
||||
msgid "Already Registered"
|
||||
msgstr ""
|
||||
@@ -136,12 +131,6 @@ msgstr ""
|
||||
msgid "Answer"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Course Creation Access Through Website To' (Select) field in
|
||||
#. DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Anyone"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the apply_gst (Check) field in DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Apply GST for India"
|
||||
@@ -233,7 +222,7 @@ msgstr ""
|
||||
msgid "Assignment will appear at the bottom of the lesson."
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_question/lms_question.py:41
|
||||
#: lms/doctype/lms_question/lms_question.py:42
|
||||
msgid "At least one option must be correct for this question."
|
||||
msgstr ""
|
||||
|
||||
@@ -850,11 +839,6 @@ msgstr ""
|
||||
msgid "Course Content"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the portal_course_creation (Select) field in DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Course Creation Access Through Website To"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a role
|
||||
#: lms/doctype/lms_course/lms_course.json
|
||||
#: lms/doctype/lms_question/lms_question.json
|
||||
@@ -862,12 +846,6 @@ msgstr ""
|
||||
msgid "Course Creator"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Course Creation Access Through Website To' (Select) field in
|
||||
#. DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Course Creator Role"
|
||||
msgstr ""
|
||||
|
||||
#. Label of a Card Break in the LMS Workspace
|
||||
#: lms/workspace/lms/lms.json
|
||||
msgid "Course Data"
|
||||
@@ -892,11 +870,6 @@ msgstr ""
|
||||
msgid "Course List"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the search_placeholder (Data) field in DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Course List Search Bar Placeholder"
|
||||
msgstr ""
|
||||
|
||||
#: lms/report/course_progress_summary/course_progress_summary.py:58
|
||||
msgid "Course Name"
|
||||
msgstr ""
|
||||
@@ -912,10 +885,7 @@ msgid "Course Progress Summary"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_7 (Section Break) field in DocType 'LMS Course'
|
||||
#. Label of the course_settings_section (Section Break) field in DocType 'LMS
|
||||
#. Settings'
|
||||
#: lms/doctype/lms_course/lms_course.json
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Course Settings"
|
||||
msgstr ""
|
||||
|
||||
@@ -1116,7 +1086,7 @@ msgstr ""
|
||||
msgid "Dream Companies"
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_question/lms_question.py:31
|
||||
#: lms/doctype/lms_question/lms_question.py:32
|
||||
msgid "Duplicate options found for this question."
|
||||
msgstr ""
|
||||
|
||||
@@ -1254,7 +1224,7 @@ msgstr ""
|
||||
msgid "Enter the correct answer"
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:1088
|
||||
#: lms/utils.py:1081
|
||||
msgid "Error during payment: {0} Please contact the Administrator. Amount {1} Currency {2} Formatted {3}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1539,6 +1509,7 @@ msgid "Here are a few courses we recommend for you to get started with {0}"
|
||||
msgstr ""
|
||||
|
||||
#: lms/notification/certificate_request_creation/certificate_request_creation.html:6
|
||||
#: templates/emails/certificate_request_notification.html:1
|
||||
msgid "Hey {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -1738,7 +1709,7 @@ msgstr ""
|
||||
msgid "Invalid Start or End Time."
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:932
|
||||
#: lms/utils.py:925
|
||||
msgid "Invalid document provided."
|
||||
msgstr ""
|
||||
|
||||
@@ -2328,7 +2299,6 @@ msgstr ""
|
||||
|
||||
#. Label of the mentor_request_section (Section Break) field in DocType 'LMS
|
||||
#. Settings'
|
||||
#. Label of the mentor_request_tab (Tab Break) field in DocType 'LMS Settings'
|
||||
#: lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Mentor Request"
|
||||
msgstr ""
|
||||
@@ -2385,11 +2355,11 @@ msgstr ""
|
||||
msgid "Modified By"
|
||||
msgstr ""
|
||||
|
||||
#: lms/api.py:188
|
||||
#: lms/api.py:189
|
||||
msgid "Module Name is incorrect or does not exist."
|
||||
msgstr ""
|
||||
|
||||
#: lms/api.py:184
|
||||
#: lms/api.py:185
|
||||
msgid "Module is incorrect."
|
||||
msgstr ""
|
||||
|
||||
@@ -2430,11 +2400,11 @@ msgstr ""
|
||||
msgid "New Sign Up"
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:619
|
||||
#: lms/utils.py:612
|
||||
msgid "New comment in batch {0}"
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:612
|
||||
#: lms/utils.py:605
|
||||
msgid "New reply on the topic {0} in course {1}"
|
||||
msgstr ""
|
||||
|
||||
@@ -2471,11 +2441,6 @@ msgstr ""
|
||||
msgid "No courses under review"
|
||||
msgstr ""
|
||||
|
||||
#: templates/search_course/search_course.html:61
|
||||
#: templates/search_course/search_course.js:47
|
||||
msgid "No result found"
|
||||
msgstr ""
|
||||
|
||||
#: templates/course_list.html:13
|
||||
msgid "No {0}"
|
||||
msgstr ""
|
||||
@@ -2719,7 +2684,7 @@ msgstr ""
|
||||
msgid "Payment for Document Type"
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:949
|
||||
#: lms/utils.py:942
|
||||
msgid "Payment for {0} course"
|
||||
msgstr ""
|
||||
|
||||
@@ -2778,12 +2743,13 @@ msgstr ""
|
||||
msgid "Please enter your answer"
|
||||
msgstr ""
|
||||
|
||||
#: lms/api.py:180
|
||||
#: lms/api.py:181
|
||||
msgid "Please login to continue with payment."
|
||||
msgstr ""
|
||||
|
||||
#: lms/notification/certificate_request_creation/certificate_request_creation.html:9
|
||||
#: lms/notification/certificate_request_reminder/certificate_request_reminder.html:8
|
||||
#: templates/emails/certificate_request_notification.html:4
|
||||
msgid "Please prepare well and be on time for the evaluations."
|
||||
msgstr ""
|
||||
|
||||
@@ -2952,6 +2918,11 @@ msgstr ""
|
||||
msgid "Question "
|
||||
msgstr ""
|
||||
|
||||
#. Label of the question_detail (Text) field in DocType 'LMS Quiz Question'
|
||||
#: lms/doctype/lms_quiz_question/lms_quiz_question.json
|
||||
msgid "Question Detail"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the question_name (Link) field in DocType 'LMS Quiz Result'
|
||||
#: lms/doctype/lms_quiz_result/lms_quiz_result.json
|
||||
msgid "Question Name"
|
||||
@@ -3214,11 +3185,6 @@ msgstr ""
|
||||
msgid "Set your Password"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_hsiv (Section Break) field in DocType 'LMS Quiz'
|
||||
#: lms/doctype/lms_quiz/lms_quiz.json
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the short_introduction (Small Text) field in DocType 'LMS Course'
|
||||
#: lms/doctype/lms_course/lms_course.json
|
||||
msgid "Short Introduction"
|
||||
@@ -3670,7 +3636,7 @@ msgstr ""
|
||||
msgid "The course {0} is now available on {1}."
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:44
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:47
|
||||
msgid "The evaluator of this course is unavailable from {0} to {1}. Please select a date after {1}"
|
||||
msgstr ""
|
||||
|
||||
@@ -3678,7 +3644,7 @@ msgstr ""
|
||||
msgid "The quiz has a time limit. For each question you will be given {0} seconds."
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:62
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:65
|
||||
msgid "The slot is already booked by another participant."
|
||||
msgstr ""
|
||||
|
||||
@@ -3694,7 +3660,7 @@ msgstr ""
|
||||
msgid "There are no {0} on this site."
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:1070
|
||||
#: lms/utils.py:1063
|
||||
msgid "There is a problem with the payment gateway. Please contact the Administrator to proceed."
|
||||
msgstr ""
|
||||
|
||||
@@ -3709,7 +3675,7 @@ msgstr ""
|
||||
msgid "This certificate does no expire"
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:1028 lms/utils.py:1769
|
||||
#: lms/utils.py:1021 lms/utils.py:1762
|
||||
msgid "This course is free."
|
||||
msgstr ""
|
||||
|
||||
@@ -3823,7 +3789,7 @@ msgstr ""
|
||||
msgid "To Date is mandatory in Work Experience."
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:1037 lms/utils.py:1780
|
||||
#: lms/utils.py:1030 lms/utils.py:1773
|
||||
msgid "To join this batch, please contact the Administrator."
|
||||
msgstr ""
|
||||
|
||||
@@ -4077,15 +4043,15 @@ msgstr ""
|
||||
msgid "Write a review"
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:86
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:89
|
||||
msgid "You already have an evaluation on {0} at {1} for the course {2}."
|
||||
msgstr ""
|
||||
|
||||
#: lms/api.py:204
|
||||
#: lms/api.py:205
|
||||
msgid "You are already enrolled for this batch."
|
||||
msgstr ""
|
||||
|
||||
#: lms/api.py:196
|
||||
#: lms/api.py:197
|
||||
msgid "You are already enrolled for this course."
|
||||
msgstr ""
|
||||
|
||||
@@ -4110,11 +4076,11 @@ msgstr ""
|
||||
msgid "You can find their resume attached to this email."
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:106
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:109
|
||||
msgid "You cannot schedule evaluations after {0}."
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:95
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:98
|
||||
msgid "You cannot schedule evaluations for past slots."
|
||||
msgstr ""
|
||||
|
||||
@@ -4122,7 +4088,7 @@ msgstr ""
|
||||
msgid "You don't have any notifications."
|
||||
msgstr ""
|
||||
|
||||
#: templates/quiz/quiz.js:136
|
||||
#: templates/quiz/quiz.js:137
|
||||
msgid "You got"
|
||||
msgstr ""
|
||||
|
||||
@@ -4176,11 +4142,17 @@ msgstr ""
|
||||
|
||||
#: lms/notification/certificate_request_creation/certificate_request_creation.html:7
|
||||
#: lms/notification/certificate_request_reminder/certificate_request_reminder.html:6
|
||||
#: templates/emails/certificate_request_notification.html:2
|
||||
msgid "Your evaluation for the course {0} has been scheduled on {1} at {2} {3}."
|
||||
msgstr ""
|
||||
|
||||
#: lms/doctype/lms_certificate_request/lms_certificate_request.py:119
|
||||
msgid "Your evaluation slot has been booked"
|
||||
msgstr ""
|
||||
|
||||
#: lms/notification/certificate_request_creation/certificate_request_creation.html:8
|
||||
#: lms/notification/certificate_request_reminder/certificate_request_reminder.html:7
|
||||
#: templates/emails/certificate_request_notification.html:3
|
||||
msgid "Your evaluator is {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -4188,7 +4160,7 @@ msgstr ""
|
||||
msgid "Your request to join us as a mentor for the course"
|
||||
msgstr ""
|
||||
|
||||
#: templates/quiz/quiz.js:136
|
||||
#: templates/quiz/quiz.js:140
|
||||
msgid "Your score is"
|
||||
msgstr ""
|
||||
|
||||
@@ -4201,7 +4173,7 @@ msgstr ""
|
||||
msgid "cancel your application"
|
||||
msgstr ""
|
||||
|
||||
#: templates/quiz/quiz.js:136
|
||||
#: templates/quiz/quiz.js:137
|
||||
msgid "correct answers"
|
||||
msgstr ""
|
||||
|
||||
@@ -4217,7 +4189,7 @@ msgstr ""
|
||||
msgid "of"
|
||||
msgstr ""
|
||||
|
||||
#: templates/quiz/quiz.js:136
|
||||
#: templates/quiz/quiz.js:141
|
||||
msgid "out of"
|
||||
msgstr ""
|
||||
|
||||
@@ -4261,7 +4233,7 @@ msgstr ""
|
||||
msgid "{0} is already certified for the course {1}"
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:696
|
||||
#: lms/utils.py:689
|
||||
msgid "{0} mentioned you in a comment"
|
||||
msgstr ""
|
||||
|
||||
@@ -4269,7 +4241,7 @@ msgstr ""
|
||||
msgid "{0} mentioned you in a comment in your batch."
|
||||
msgstr ""
|
||||
|
||||
#: lms/utils.py:649 lms/utils.py:655
|
||||
#: lms/utils.py:642 lms/utils.py:648
|
||||
msgid "{0} mentioned you in a comment in {1}"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ class CustomUser(User):
|
||||
super().validate()
|
||||
self.validate_username_duplicates()
|
||||
|
||||
def after_insert(self):
|
||||
self.add_roles("LMS Student")
|
||||
|
||||
def validate_username_duplicates(self):
|
||||
while not self.username or self.username_exists():
|
||||
self.username = append_number_if_name_exists(
|
||||
|
||||
Reference in New Issue
Block a user