SimpleLMS init
|
|
@ -0,0 +1,857 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
//using System.Web.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Nop.Core;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Factories;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Services;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Logging;
|
||||
using Nop.Services.Messages;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Web.Areas.Admin.Controllers;
|
||||
using Nop.Web.Areas.Admin.Infrastructure.Mapper.Extensions;
|
||||
using Nop.Web.Framework.Mvc.Filters;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Controllers
|
||||
{
|
||||
public partial class CourseController : BaseAdminController
|
||||
{
|
||||
private readonly IWorkContext _workContext;
|
||||
private readonly IPermissionService _permissionService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ICustomerActivityService _customerActivityService;
|
||||
private readonly AdminCourseModelFactory _courseModelFactory;
|
||||
private readonly CourseService _courseService;
|
||||
private readonly INotificationService _notificationService;
|
||||
|
||||
public CourseController(IPermissionService permissionService
|
||||
, ILocalizationService localizationService
|
||||
, ICustomerActivityService customerActivityService
|
||||
, ICustomerService customerService
|
||||
, AdminCourseModelFactory courseModelFactory
|
||||
, CourseService courseService
|
||||
, INotificationService notificationService
|
||||
, IWorkContext workContext)
|
||||
{
|
||||
_permissionService = permissionService;
|
||||
_localizationService = localizationService;
|
||||
_customerActivityService = customerActivityService;
|
||||
_courseModelFactory = courseModelFactory;
|
||||
_courseService = courseService;
|
||||
_notificationService = notificationService;
|
||||
_workContext = workContext;
|
||||
}
|
||||
|
||||
public virtual IActionResult Index()
|
||||
{
|
||||
return RedirectToAction("List");
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> List()
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseSearchModel = new CourseSearchModel();
|
||||
|
||||
//prepare model
|
||||
var model = await _courseModelFactory.PrepareCourseSearchModelAsync(courseSearchModel);
|
||||
|
||||
return View("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/List.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public virtual async Task<IActionResult> CourseList(CourseSearchModel searchModel)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
searchModel.InstructorGUID = customer.CustomerGuid.ToString();
|
||||
|
||||
//prepare model
|
||||
var model = await _courseModelFactory.PrepareCourseListModelAsync(searchModel);
|
||||
|
||||
return Json(model);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IActionResult> Create()
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var model = await _courseModelFactory.PrepareCourseModelAsync(new CourseModel(), null);
|
||||
|
||||
return View("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/Create.cshtml", model);
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> Sections(int courseId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(courseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
|
||||
|
||||
var model = await _courseModelFactory.PrepareCourseModelAsync(new CourseModel(), courseExisting);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Sections.cshtml", model.Sections);
|
||||
}
|
||||
|
||||
[HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> Create(CourseModel model, bool continueEditing)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
var course = model.ToEntity<Course>();
|
||||
|
||||
course.InstructorId = customer.Id;
|
||||
course.InstructorGuid = customer.CustomerGuid;
|
||||
course.CreatedOnUtc = DateTime.UtcNow;
|
||||
course.UpdatedOnUtc = DateTime.UtcNow;
|
||||
|
||||
await _courseService.InsertOrUpdateCourseAsync(course);
|
||||
|
||||
//foreach (var sectionModel in model.Sections)
|
||||
//{
|
||||
// var section = sectionModel.ToEntity<Section>();
|
||||
// section.CourseId = course.Id;
|
||||
// await _courseService.InsertOrUpdateSectionAsync(section);
|
||||
// foreach (var lessonModel in sectionModel.Lessons)
|
||||
// {
|
||||
// var lesson = lessonModel.ToEntity<Lesson>();
|
||||
// await _courseService.InsertOrUpdateLessonAsync(lesson);
|
||||
// await _courseService.InsertOrUpdateSectionLessonAsync(new SectionLesson
|
||||
// {
|
||||
// CourseId = course.Id,
|
||||
// DisplayOrder = lessonModel.DisplayOrder,
|
||||
// LessonId = lesson.Id,
|
||||
// IsFreeLesson = lessonModel.IsFreeLesson,
|
||||
// SectionId = section.Id
|
||||
// });
|
||||
|
||||
// if (lessonModel.LessonType == LessonType.Document)
|
||||
// {
|
||||
// foreach (var attachmentModel in lessonModel.Attachments)
|
||||
// {
|
||||
// var attachment = attachmentModel.ToEntity<Attachment>();
|
||||
// attachment.InstructorId = customer.Id;
|
||||
// attachment.InstructorGuid = customer.CustomerGuid;
|
||||
// await _courseService.InsertOrUpdateAttachmentAsync(attachment);
|
||||
// await _courseService.InsertOrUpdateLessonAttachmentAsync(new LessonAttachment
|
||||
// {
|
||||
// AttachmentId = attachment.Id,
|
||||
// LessonId = lesson.Id
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// else if (lessonModel.LessonType == LessonType.Video)
|
||||
// {
|
||||
// if (lessonModel.Video != null)
|
||||
// {
|
||||
// var video = lessonModel.Video.ToEntity<Video>();
|
||||
// await _courseService.InsertOrUpdateVideoAsync(video);
|
||||
// lesson.VideoId = video.Id;
|
||||
// await _courseService.InsertOrUpdateLessonAsync(lesson);
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
//}
|
||||
|
||||
if (!continueEditing)
|
||||
return RedirectToAction("List");
|
||||
|
||||
model = await _courseModelFactory.PrepareCourseModelAsync(new CourseModel(), null);
|
||||
|
||||
return RedirectToAction("Edit", new { id = course.Id });
|
||||
}
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IActionResult> Edit(int id)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
|
||||
var course = await _courseService.GetCourseById(id);
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (course.InstructorGuid != customer.CustomerGuid && course.Id != id)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
//prepare model
|
||||
var model = await _courseModelFactory.PrepareCourseModelAsync(new CourseModel(), course);
|
||||
|
||||
return View("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/Edit.cshtml", model);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> Edit(CourseModel model, bool continueEditing)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(model.Id);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid && courseExisting.Id != model.Id)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var courseNew = courseExisting;
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var course = model.ToEntity<Course>();
|
||||
courseNew = course;
|
||||
course.CreatedOnUtc = courseExisting.CreatedOnUtc;
|
||||
course.UpdatedOnUtc = DateTime.UtcNow;
|
||||
|
||||
course.InstructorId = customer.Id;
|
||||
course.InstructorGuid = customer.CustomerGuid;
|
||||
|
||||
await _courseService.InsertOrUpdateCourseAsync(course);
|
||||
|
||||
//var existingSections = await _courseService.GetSectionsByCourseIdAsync(course.Id);
|
||||
|
||||
|
||||
|
||||
//var lstSectionsToDelete = existingSections.Select(p => p.Id).ToList();
|
||||
|
||||
//foreach (var sectionModel in model.Sections)
|
||||
//{
|
||||
// if (sectionModel.Id > 0)
|
||||
// {
|
||||
// lstSectionsToDelete.Remove(sectionModel.Id);
|
||||
// }
|
||||
|
||||
// var section = sectionModel.ToEntity<Section>();
|
||||
// section.CourseId = course.Id;
|
||||
// await _courseService.InsertOrUpdateSectionAsync(section);
|
||||
|
||||
// foreach (var lessonModel in sectionModel.Lessons)
|
||||
// {
|
||||
// var lesson = lessonModel.ToEntity<Lesson>();
|
||||
// await _courseService.InsertOrUpdateLessonAsync(lesson);
|
||||
// await _courseService.InsertOrUpdateSectionLessonAsync(new SectionLesson
|
||||
// {
|
||||
// CourseId = course.Id,
|
||||
// DisplayOrder = lessonModel.DisplayOrder,
|
||||
// LessonId = lesson.Id,
|
||||
// IsFreeLesson = lessonModel.IsFreeLesson,
|
||||
// SectionId = section.Id
|
||||
// });
|
||||
|
||||
// if (lessonModel.LessonType == LessonType.Document)
|
||||
// {
|
||||
// foreach (var attachmentModel in lessonModel.Attachments)
|
||||
// {
|
||||
// var attachment = attachmentModel.ToEntity<Attachment>();
|
||||
// attachment.InstructorId = customer.Id;
|
||||
// attachment.InstructorGuid = customer.CustomerGuid;
|
||||
// await _courseService.InsertOrUpdateAttachmentAsync(attachment);
|
||||
// await _courseService.InsertOrUpdateLessonAttachmentAsync(new LessonAttachment
|
||||
// {
|
||||
// AttachmentId = attachment.Id,
|
||||
// LessonId = lesson.Id
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// else if (lessonModel.LessonType == LessonType.Video)
|
||||
// {
|
||||
// if (lessonModel.Video != null)
|
||||
// {
|
||||
// var video = lessonModel.Video.ToEntity<Video>();
|
||||
// await _courseService.InsertOrUpdateVideoAsync(video);
|
||||
// lesson.VideoId = video.Id;
|
||||
// await _courseService.InsertOrUpdateLessonAsync(lesson);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//await NormalizeSections(lstSectionsToDelete);
|
||||
|
||||
if (!continueEditing)
|
||||
return RedirectToAction("List");
|
||||
|
||||
return RedirectToAction("Edit", new { id = course.Id });
|
||||
}
|
||||
|
||||
//prepare model
|
||||
model = await _courseModelFactory.PrepareCourseModelAsync(model, courseNew);
|
||||
|
||||
//if we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(id);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid && courseExisting.Id != id)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
await _courseService.DeleteCourseFullByIdAsync(courseExisting.Id);
|
||||
|
||||
_notificationService.SuccessNotification(await _localizationService.GetResourceAsync("SimpleLMS.Course") + " "
|
||||
+ await _localizationService.GetResourceAsync("SimpleLMS.Deleted"));
|
||||
|
||||
return RedirectToAction("List");
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IActionResult> CreateSection(int courseId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(courseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var model = await _courseModelFactory.PrepareSectionModelAsync(new SectionModel(), courseExisting, null);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Section_CreateOrEdit.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> CreateSection(SectionModel sectionModel)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (sectionModel.Id > 0)
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(sectionModel.CourseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var section = sectionModel.ToEntity<Section>();
|
||||
await _courseService.InsertOrUpdateSectionAsync(section);
|
||||
|
||||
return Json(new { success = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
responseText = await _localizationService.GetResourceAsync("SimpleLMS.InvalidData")
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IActionResult> EditSection(int sectionId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var sectionExisting = await _courseService.GetSectionByIdAsync(sectionId);
|
||||
var courseExisting = await _courseService.GetCourseById(sectionExisting.CourseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var model = await _courseModelFactory.PrepareSectionModelAsync(new SectionModel(), courseExisting, sectionExisting);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Section_CreateOrEdit.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> EditSection(SectionModel sectionModel)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (sectionModel.Id == 0)
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(sectionModel.CourseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var section = sectionModel.ToEntity<Section>();
|
||||
await _courseService.InsertOrUpdateSectionAsync(section);
|
||||
|
||||
return Json(new { success = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
responseText = await _localizationService.GetResourceAsync("SimpleLMS.InvalidData")
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> SortLessons(int sectionId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var sectionExisting = await _courseService.GetSectionByIdAsync(sectionId);
|
||||
var courseExisting = await _courseService.GetCourseById(sectionExisting.CourseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
var sectionLessons = await _courseService.GetSectionLessonsBySectionIdAsync(sectionExisting.Id);
|
||||
var lessons = await _courseService.GetLessonsBySectionIdAsync(sectionExisting.Id);
|
||||
|
||||
var doModels = from sl in sectionLessons
|
||||
select new DisplayOrderRecord
|
||||
{
|
||||
Id = sl.Id,
|
||||
DisplayOrder = sl.DisplayOrder,
|
||||
DisplayText = lessons.Where(p => p.Id == sl.LessonId).Select(l => l.Name).FirstOrDefault()
|
||||
};
|
||||
|
||||
var model = await _courseModelFactory.PrepareSortableEntityModelAsync(new SortableEntity(), doModels, SortRecordType.Lesson, sectionExisting.Id);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Sortable.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> SortLessons(SortableEntity sortableEntity)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var sectionExisting = await _courseService.GetSectionByIdAsync(sortableEntity.ParentId);
|
||||
var courseExisting = await _courseService.GetCourseById(sectionExisting.CourseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
var sectionLessons = await _courseService.GetSectionLessonsBySectionIdAsync(sectionExisting.Id);
|
||||
|
||||
if (sectionLessons != null && sectionLessons.Count > 0)
|
||||
{
|
||||
|
||||
for (int i = 0; i < sortableEntity.NewSortOrderValues.Count; i++)
|
||||
{
|
||||
sortableEntity.NewSortOrderValues[i] = Regex.Match(sortableEntity.NewSortOrderValues[i], @"\d+").Value;
|
||||
}
|
||||
|
||||
foreach (var sl in sectionLessons)
|
||||
{
|
||||
sl.DisplayOrder = Array.FindIndex(sortableEntity.NewSortOrderValues.ToArray()
|
||||
, x => x == sl.Id.ToString()) + 1;
|
||||
}
|
||||
|
||||
await _courseService.UpdateSectionLessonsAsync(sectionLessons);
|
||||
|
||||
return Json(new { Result = true });
|
||||
}
|
||||
|
||||
return Json(new { Result = false });
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> SortSections(int courseId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(courseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
var sections = await _courseService.GetSectionsByCourseIdAsync(courseExisting.Id);
|
||||
|
||||
var inputModels = from s in sections
|
||||
select new DisplayOrderRecord
|
||||
{
|
||||
Id = s.Id,
|
||||
DisplayOrder = s.DisplayOrder,
|
||||
DisplayText = s.Title
|
||||
};
|
||||
|
||||
var model = await _courseModelFactory.PrepareSortableEntityModelAsync(new SortableEntity(), inputModels, SortRecordType.Section, courseExisting.Id);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Sortable.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> SortSections(SortableEntity sortableEntity)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(sortableEntity.ParentId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
|
||||
var sections = await _courseService.GetSectionsByCourseIdAsync(sortableEntity.ParentId);
|
||||
|
||||
if (sections != null && sections.Count > 0)
|
||||
{
|
||||
|
||||
for (int i = 0; i < sortableEntity.NewSortOrderValues.Count; i++)
|
||||
{
|
||||
sortableEntity.NewSortOrderValues[i] = Regex.Match(sortableEntity.NewSortOrderValues[i], @"\d+").Value;
|
||||
}
|
||||
|
||||
foreach (var sl in sections)
|
||||
{
|
||||
sl.DisplayOrder = Array.FindIndex(sortableEntity.NewSortOrderValues.ToArray()
|
||||
, x => x == sl.Id.ToString()) + 1;
|
||||
|
||||
await _courseService.InsertOrUpdateSectionAsync(sl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Json(new { Result = true });
|
||||
}
|
||||
|
||||
return Json(new { Result = false });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> DeleteSection(int sectionId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var section = await _courseService.GetSectionByIdAsync(sectionId);
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(section.CourseId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
await _courseService.DeleteSectionAsync(sectionId);
|
||||
|
||||
return Json(new { Result = true });
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> CreateLesson(int sectionId, int courseId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var section = await _courseService.GetSectionByIdAsync(sectionId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
var courseExisting = await _courseService.GetCourseById(section.CourseId);
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (courseExisting.Id != courseId)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var model = await _courseModelFactory.PrepareLessonModelAsync(new LessonModel(), section, courseExisting, null);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Lesson_CreateOrEdit.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> CreateLesson(LessonModel lessonModel)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
if (lessonModel.Id > 0)
|
||||
return AccessDeniedView();
|
||||
|
||||
var section = await _courseService.GetSectionByIdAsync(lessonModel.SectionId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
var courseExisting = await _courseService.GetCourseById(section.CourseId);
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var videoId = 0;
|
||||
|
||||
if (lessonModel.LessonType == LessonType.Video)
|
||||
{
|
||||
var video = new Video();
|
||||
video.Duration = lessonModel.Duration;
|
||||
video.InstructorGuid = customer.CustomerGuid;
|
||||
video.InstructorId = customer.Id;
|
||||
video.VideoTypeId = (int)lessonModel.VideoType;
|
||||
video.VideoIdFromProvider = lessonModel.VideoIdFromProvider;
|
||||
|
||||
await _courseService.InsertOrUpdateVideoAsync(video);
|
||||
videoId = video.Id;
|
||||
}
|
||||
|
||||
|
||||
var lesson = lessonModel.ToEntity<Lesson>();
|
||||
|
||||
|
||||
|
||||
if (lessonModel.LessonType == LessonType.Text)
|
||||
{
|
||||
lesson.VideoId = null;
|
||||
}
|
||||
|
||||
lesson.InstructorId = customer.Id;
|
||||
lesson.InstructorGuid = customer.CustomerGuid;
|
||||
|
||||
lesson.DocumentId = null;
|
||||
lesson.PictureId = null;
|
||||
|
||||
lesson.LessonTypeId = (int)lessonModel.LessonType;
|
||||
|
||||
if (videoId > 0)
|
||||
lesson.VideoId = videoId;
|
||||
|
||||
await _courseService.InsertOrUpdateLessonAsync(lesson);
|
||||
|
||||
var sectionLesson = new SectionLesson();
|
||||
sectionLesson.SectionId = lessonModel.SectionId;
|
||||
sectionLesson.LessonId = lesson.Id;
|
||||
sectionLesson.DisplayOrder = lessonModel.DisplayOrder;
|
||||
sectionLesson.IsFreeLesson = lessonModel.IsFreeLesson;
|
||||
sectionLesson.CourseId = lessonModel.CourseId;
|
||||
|
||||
await _courseService.InsertOrUpdateSectionLessonAsync(sectionLesson);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
return BadRequest(ModelState);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IActionResult> EditLesson(int lessonId, int sectionId, int courseId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var lesson = await _courseService.GetLessonByIdAsync(lessonId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
if (lesson.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var lessonSections = await _courseService.GetLessonSectionByLessonIdAsync(lessonId);
|
||||
|
||||
if (!lessonSections.Select(p => p.SectionId).Contains(sectionId))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var section = await _courseService.GetSectionByIdAsync(sectionId);
|
||||
|
||||
var courseExisting = await _courseService.GetCourseById(section.CourseId);
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (courseExisting.Id != courseId)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
|
||||
var model = await _courseModelFactory.PrepareLessonModelAsync(new LessonModel(), section, courseExisting, lesson);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Course/_CreateOrUpdate.Lesson_CreateOrEdit.cshtml", model);
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> EditLesson(LessonModel lessonModel)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (lessonModel.Id == 0)
|
||||
return AccessDeniedView();
|
||||
|
||||
|
||||
var lesson = await _courseService.GetLessonByIdAsync(lessonModel.Id);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
if (lesson.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
var section = await _courseService.GetSectionByIdAsync(lessonModel.SectionId);
|
||||
var courseExisting = await _courseService.GetCourseById(section.CourseId);
|
||||
|
||||
if (courseExisting.Id != lessonModel.CourseId)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return await AccessDeniedDataTablesJson();
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var videoId = 0;
|
||||
|
||||
var videoIdToDelete = 0;
|
||||
|
||||
if (lessonModel.LessonType == LessonType.Video)
|
||||
{
|
||||
var video = await _courseService.GetVideoByLessonIdAsync(lessonModel.Id);
|
||||
|
||||
if (video == null)
|
||||
video = new Video();
|
||||
|
||||
video.Duration = lessonModel.Duration;
|
||||
video.InstructorGuid = customer.CustomerGuid;
|
||||
video.InstructorId = customer.Id;
|
||||
video.VideoTypeId = (int)lessonModel.VideoType;
|
||||
video.VideoIdFromProvider = lessonModel.VideoIdFromProvider;
|
||||
|
||||
await _courseService.InsertOrUpdateVideoAsync(video);
|
||||
videoId = video.Id;
|
||||
}
|
||||
else if (lessonModel.LessonType == LessonType.Text && lesson.LessonType == LessonType.Video)
|
||||
{
|
||||
var video = await _courseService.GetVideoByLessonIdAsync(lessonModel.Id);
|
||||
videoIdToDelete = video.Id;
|
||||
}
|
||||
|
||||
lesson = lessonModel.ToEntity<Lesson>();
|
||||
|
||||
if (lessonModel.LessonType == LessonType.Text)
|
||||
{
|
||||
lesson.VideoId = null;
|
||||
}
|
||||
|
||||
lesson.InstructorId = customer.Id;
|
||||
lesson.InstructorGuid = customer.CustomerGuid;
|
||||
|
||||
lesson.DocumentId = null;
|
||||
lesson.PictureId = null;
|
||||
|
||||
lesson.LessonTypeId = (int)lessonModel.LessonType;
|
||||
|
||||
if (videoId > 0)
|
||||
lesson.VideoId = videoId;
|
||||
|
||||
await _courseService.InsertOrUpdateLessonAsync(lesson);
|
||||
|
||||
if (videoIdToDelete > 0)
|
||||
await _courseService.DeleteVideoAsync(videoIdToDelete);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
return BadRequest(ModelState);
|
||||
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> DeleteLesson(int lessonId, int sectionId, int courseId)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageProducts))
|
||||
return AccessDeniedView();
|
||||
|
||||
var lesson = await _courseService.GetLessonByIdAsync(lessonId);
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
if (lesson.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
var section = await _courseService.GetSectionByIdAsync(sectionId);
|
||||
var courseExisting = await _courseService.GetCourseById(section.CourseId);
|
||||
|
||||
if (courseExisting.Id != courseId)
|
||||
return AccessDeniedView();
|
||||
|
||||
if (courseExisting.InstructorGuid != customer.CustomerGuid)
|
||||
return AccessDeniedView();
|
||||
|
||||
await _courseService.DeleteSectionLessonAsync(sectionId, lessonId);
|
||||
|
||||
await _customerActivityService.InsertActivityAsync(customer, "DeleteLesson",
|
||||
await _localizationService.GetResourceAsync("SimpleLMS.Lesson") + ": " + lesson.Id + ". " + lesson.Name +
|
||||
await _localizationService.GetResourceAsync("SimpleLMS.Lesson") + ": " + section.Id + ". " + section.Title);
|
||||
|
||||
//_notificationService.SuccessNotification(await _localizationService.GetResourceAsync("SimpleLMS.Lesson") + ' '
|
||||
// + await _localizationService.GetResourceAsync("SimpleLMS.Deleted"));
|
||||
|
||||
return Json(new { Result = true });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task NormalizeSections(List<int> lstSectionsToDelete)
|
||||
{
|
||||
foreach (var secId in lstSectionsToDelete)
|
||||
{
|
||||
//var section = await _courseService.GetSectionByIdAsync(secId);
|
||||
//var lessons = await _courseService.GetLessonsBySectionIdAsync(secId);
|
||||
|
||||
//foreach (var lesson in lessons)
|
||||
//{
|
||||
// await _courseService.DeleteLessonAttachmentsAsync(lesson, secId);
|
||||
// await _courseService.DeleteLessonVideosAsync(lesson, secId);
|
||||
//}
|
||||
|
||||
await _courseService.DeleteSectionLessonsBySecIdAsync(secId);
|
||||
await _courseService.DeleteSectionAsync(secId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Nop.Core;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Web.Areas.Admin.Controllers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Controllers
|
||||
{
|
||||
public partial class SettingsController : BaseAdminController
|
||||
{
|
||||
private readonly ISettingService _settingService;
|
||||
private readonly IStoreContext _storeContext;
|
||||
private readonly IPermissionService _permissionService;
|
||||
public SettingsController(ISettingService settingService,IStoreContext storeContext,ILocalizationService localizationService,IPermissionService permissionService)
|
||||
{
|
||||
_settingService = settingService;
|
||||
_storeContext = storeContext;
|
||||
_permissionService = permissionService;
|
||||
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Configure()
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets))
|
||||
return AccessDeniedView();
|
||||
|
||||
//load settings for a chosen store scope
|
||||
var storeScope = await _storeContext.GetActiveStoreScopeConfigurationAsync();
|
||||
var simpleLMSSettings = await _settingService.LoadSettingAsync<SimpleLMSSettings>(storeScope);
|
||||
var model = new ConfigurationModel
|
||||
{
|
||||
//YouTubeApiKey = simpleLMSSettings.YouTubeApiKey,
|
||||
//VimeoAppId=simpleLMSSettings.VimeoAppId,
|
||||
VimeoClient=simpleLMSSettings.VimeoClient,
|
||||
VimeoAccess=simpleLMSSettings.VimeoAccess,
|
||||
VimeoSecret=simpleLMSSettings.VimeoSecret,
|
||||
//VdoCipherKey=simpleLMSSettings.VdoCipherKey
|
||||
};
|
||||
return View("~/Plugins/Misc.SimpleLMS/Areas/Admin/Views/Settings/Configure.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task<IActionResult> Configure(ConfigurationModel model)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets))
|
||||
return AccessDeniedView();
|
||||
|
||||
//load settings for a chosen store scope
|
||||
var storeScope = await _storeContext.GetActiveStoreScopeConfigurationAsync();
|
||||
var simpleLMSSettings = await _settingService.LoadSettingAsync<SimpleLMSSettings>(storeScope);
|
||||
|
||||
//get previous picture identifiers
|
||||
|
||||
|
||||
//simpleLMSSettings.YouTubeApiKey = model.YouTubeApiKey;
|
||||
|
||||
//simpleLMSSettings.VimeoAppId = model.VimeoAppId;
|
||||
|
||||
simpleLMSSettings.VimeoClient = model.VimeoClient;
|
||||
|
||||
simpleLMSSettings.VimeoAccess = model.VimeoAccess;
|
||||
|
||||
simpleLMSSettings.VimeoSecret = model.VimeoSecret;
|
||||
//simpleLMSSettings.VdoCipherKey = model.VdoCipherKey;
|
||||
/* We do not clear cache after each setting update.
|
||||
* This behavior can increase performance because cached settings will not be cleared
|
||||
* and loaded from database after each update */
|
||||
//await _settingService.SaveSettingAsync(simpleLMSSettings, x => x.YouTubeApiKey, storeScope, false);
|
||||
//await _settingService.SaveSettingAsync(simpleLMSSettings, x => x.VimeoAppId, storeScope, false);
|
||||
await _settingService.SaveSettingAsync(simpleLMSSettings, x => x.VimeoClient, storeScope, false);
|
||||
await _settingService.SaveSettingAsync(simpleLMSSettings, x => x.VimeoAccess, storeScope, false);
|
||||
await _settingService.SaveSettingAsync(simpleLMSSettings, x => x.VimeoSecret, storeScope, false);
|
||||
//await _settingService.SaveSettingAsync(simpleLMSSettings, x => x.VdoCipherKey, storeScope, false);
|
||||
//now clear settings cache
|
||||
await _settingService.ClearCacheAsync();
|
||||
|
||||
|
||||
return await Configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Nop.Core;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Areas.Admin.Infrastructure.Mapper.Extensions;
|
||||
using Nop.Web.Framework.Models.Extensions;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Services;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Factories
|
||||
{
|
||||
public partial class AdminCourseModelFactory
|
||||
{
|
||||
|
||||
private readonly CourseService _courseService;
|
||||
private readonly ICustomerService _customerService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IProductService _productService;
|
||||
private readonly IWorkContext _workContext;
|
||||
|
||||
public AdminCourseModelFactory(
|
||||
IProductService productService,
|
||||
ILocalizationService localizationService,
|
||||
IWorkContext workContext,
|
||||
ICustomerService customerService,
|
||||
CourseService courseService)
|
||||
{
|
||||
_productService = productService;
|
||||
_workContext = workContext;
|
||||
_localizationService = localizationService;
|
||||
_customerService = customerService;
|
||||
_courseService = courseService;
|
||||
}
|
||||
|
||||
public async Task<CourseListModel> PrepareCourseListModelAsync(CourseSearchModel searchModel)
|
||||
{
|
||||
if (searchModel == null)
|
||||
throw new ArgumentNullException(nameof(searchModel));
|
||||
|
||||
|
||||
//get courses
|
||||
var courses = await _courseService.SearchCoursesAsync(pageIndex: searchModel.Page - 1,
|
||||
pageSize: searchModel.PageSize,
|
||||
keyword: searchModel.SearchCourseName,
|
||||
instructorGUID: searchModel.InstructorGUID);
|
||||
|
||||
//prepare list model
|
||||
var model = await new CourseListModel().PrepareToGridAsync(searchModel, courses, () =>
|
||||
{
|
||||
return courses.SelectAwait(async course =>
|
||||
{
|
||||
//fill in model values from the entity
|
||||
var courseModel = course.ToModel<CourseModel>();
|
||||
|
||||
|
||||
//fill in additional values (not existing in the entity)
|
||||
courseModel.Category = string.Empty;
|
||||
|
||||
var product = await _productService.GetProductByIdAsync(course.ProductId);
|
||||
courseModel.ParentProductName = product.Name;
|
||||
courseModel.Price = product.Price;
|
||||
courseModel.Status = product.Published ? "Published" : "Unpublished";
|
||||
|
||||
var customer = await _customerService.GetCustomerByIdAsync(course.InstructorId);
|
||||
|
||||
courseModel.Instructor = customer.Username;
|
||||
|
||||
var courseStat = await _courseService.GetCourseStatsByCourseIdAsync(course.Id);
|
||||
|
||||
courseModel.LessonsTotal = courseStat.Lessons;
|
||||
courseModel.SectionsTotal = courseStat.Sections;
|
||||
courseModel.EnrolledStudents = courseStat.EnrolledStudents;
|
||||
|
||||
|
||||
|
||||
return courseModel;
|
||||
});
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public async Task<CourseModel> PrepareCourseModelAsync(CourseModel courseModel, Course course)
|
||||
{
|
||||
if (courseModel == null)
|
||||
throw new ArgumentNullException(nameof(courseModel));
|
||||
|
||||
if (course != null)
|
||||
{
|
||||
//fill in model values from the entity
|
||||
if (course != null)
|
||||
{
|
||||
courseModel = course.ToModel<CourseModel>();
|
||||
}
|
||||
|
||||
var parentProduct = await _productService.GetProductByIdAsync(course.ProductId);
|
||||
|
||||
if (parentProduct != null)
|
||||
{
|
||||
courseModel.ParentProductName = parentProduct.Name;
|
||||
courseModel.ProductId = parentProduct.Id;
|
||||
}
|
||||
|
||||
courseModel.Sections = (await _courseService.GetSectionsByCourseIdAsync(courseModel.Id)).OrderBy(p => p.DisplayOrder).Select(p =>
|
||||
new SectionModel
|
||||
{
|
||||
CourseName = courseModel.Name,
|
||||
DisplayOrder = p.DisplayOrder,
|
||||
Id = p.Id,
|
||||
IsFree = p.IsFree,
|
||||
Title = p.Title,
|
||||
CourseId = course.Id,
|
||||
Lessons = _courseService.GetLessonsBySectionIdAsync(p.Id)
|
||||
.GetAwaiter().GetResult().Select(l => l.ToModel<LessonModel>()).ToList()
|
||||
}).ToList();
|
||||
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i < courseModel.Sections.Count; i++)
|
||||
{
|
||||
var sectionLessons = await _courseService.GetSectionLessonsBySectionIdAsync(courseModel.Sections[i].Id);
|
||||
|
||||
for (int j = 0; j < courseModel.Sections[i].Lessons.Count; j++)
|
||||
{
|
||||
courseModel.Sections[i].Lessons[j].SectionId = courseModel.Sections[i].Id;
|
||||
courseModel.Sections[i].Lessons[j].CourseId = courseModel.Id;
|
||||
courseModel.Sections[i].Lessons[j].DisplayOrder
|
||||
= sectionLessons.Where(p => p.LessonId == courseModel.Sections[i].Lessons[j].Id).SingleOrDefault().DisplayOrder;
|
||||
}
|
||||
|
||||
courseModel.Sections[i].Lessons = courseModel.Sections[i].Lessons.OrderBy(p => p.DisplayOrder).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
courseModel.AvailableProducts = await _courseService.GetProductsByCurrentUserAsync();
|
||||
courseModel.AvailableProducts.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("SimpleLMS.Select"), Value = "" });
|
||||
|
||||
return courseModel;
|
||||
}
|
||||
|
||||
protected IList<LessonModel> GetLessonsBySectionIdForAdmin(int id)
|
||||
{
|
||||
var task = _courseService.GetLessonsBySectionIdAsync(id);
|
||||
task.RunSynchronously();
|
||||
var result = task.Result.Select(p => p.ToModel<LessonModel>());
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
|
||||
public async Task<CourseSearchModel> PrepareCourseSearchModelAsync(CourseSearchModel searchModel)
|
||||
{
|
||||
if (searchModel == null)
|
||||
throw new ArgumentNullException(nameof(searchModel));
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
searchModel.InstructorGUID = customer.CustomerGuid.ToString();
|
||||
|
||||
searchModel.SetGridPageSize();
|
||||
|
||||
return searchModel;
|
||||
}
|
||||
|
||||
public async Task<SectionModel> PrepareSectionModelAsync(SectionModel sectionModel, Course course, Section section, bool loadLessons = false)
|
||||
{
|
||||
if (sectionModel == null)
|
||||
throw new ArgumentNullException(nameof(sectionModel));
|
||||
|
||||
if (course == null)
|
||||
throw new ArgumentNullException(nameof(course));
|
||||
|
||||
if (section != null)
|
||||
{
|
||||
sectionModel = section.ToModel<SectionModel>();
|
||||
sectionModel.CourseId = section.CourseId;
|
||||
|
||||
//if (loadLessons)
|
||||
//{
|
||||
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
sectionModel.CourseId = course.Id;
|
||||
|
||||
//load display order
|
||||
var sections = await _courseService.GetSectionsByCourseIdAsync(course.Id);
|
||||
if (sections != null && sections.Count > 0)
|
||||
{
|
||||
sectionModel.DisplayOrder = sections.Max(p => p.DisplayOrder) + 1;
|
||||
}
|
||||
if (sectionModel.DisplayOrder == 0)
|
||||
{
|
||||
sectionModel.DisplayOrder = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sectionModel;
|
||||
}
|
||||
|
||||
public async Task<LessonModel> PrepareLessonModelAsync(LessonModel lessonModel, Section section, Course course, Lesson lesson)
|
||||
{
|
||||
if (lessonModel == null)
|
||||
throw new ArgumentNullException(nameof(lessonModel));
|
||||
|
||||
if (course == null)
|
||||
throw new ArgumentNullException(nameof(course));
|
||||
|
||||
if (section == null)
|
||||
throw new ArgumentNullException(nameof(section));
|
||||
|
||||
if (lesson != null)
|
||||
{
|
||||
lessonModel = lesson.ToModel<LessonModel>();
|
||||
|
||||
if (lessonModel.LessonType == LessonType.Video)
|
||||
{
|
||||
var video = await _courseService.GetVideoByLessonIdAsync(lessonModel.Id);
|
||||
lessonModel.VideoIdFromProvider = video.VideoIdFromProvider;
|
||||
lessonModel.VideoType = video.VideoType;
|
||||
lessonModel.Duration = video.Duration;
|
||||
}
|
||||
}
|
||||
|
||||
var sls = await _courseService.GetSectionLessonsBySectionIdAsync(section.Id);
|
||||
if (sls != null && sls.Count > 0)
|
||||
{
|
||||
if (lesson != null)
|
||||
{
|
||||
lessonModel.DisplayOrder = sls.Where(p => p.LessonId == lesson.Id).SingleOrDefault().DisplayOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
lessonModel.DisplayOrder = sls.Max(p => p.DisplayOrder) + 1;
|
||||
if (lessonModel.DisplayOrder == 0)
|
||||
{
|
||||
lessonModel.DisplayOrder = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lessonModel.DisplayOrder == 0)
|
||||
{
|
||||
lessonModel.DisplayOrder = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
lessonModel.SectionId = section.Id;
|
||||
lessonModel.CourseId = course.Id;
|
||||
|
||||
|
||||
lessonModel.AvailableLessonTypes.Add(new SelectListItem { Value = ((int)LessonType.Video).ToString(), Text = LessonType.Video.ToString() });
|
||||
lessonModel.AvailableLessonTypes.Add(new SelectListItem { Value = ((int)LessonType.Text).ToString(), Text = LessonType.Text.ToString() });
|
||||
//lessonModel.AvailableLessonTypes.Add(new SelectListItem { Value = ((int)LessonType.Document).ToString(), Text = LessonType.Document.ToString() });
|
||||
|
||||
|
||||
lessonModel.AvailableVideoTypes.Add(new SelectListItem { Value = ((int)VideoType.Youtube).ToString(), Text = VideoType.Youtube.ToString() });
|
||||
lessonModel.AvailableVideoTypes.Add(new SelectListItem { Value = ((int)VideoType.Vimeo).ToString(), Text = VideoType.Vimeo.ToString() });
|
||||
//lessonModel.AvailableVideoTypes.Add(new SelectListItem { Value = ((int)VideoType.VdoCipher).ToString(), Text = VideoType.VdoCipher.ToString() });
|
||||
//lessonModel.AvailableVideoTypes.Add(new SelectListItem { Value = ((int)VideoType.Hosted).ToString(), Text = VideoType.Hosted.ToString() });
|
||||
//lessonModel.AvailableVideoTypes.Add(new SelectListItem { Value = ((int)VideoType.AWS).ToString(), Text = VideoType.AWS.ToString() });
|
||||
|
||||
lessonModel.AvailableAttachmentTypes.Add(new SelectListItem { Value = ((int)AttachmentType.Pdf).ToString(), Text = AttachmentType.Pdf.ToString() });
|
||||
//lessonModel.AvailableAttachmentTypes.Add(new SelectListItem { Value = ((int)AttachmentType.Others).ToString(), Text = AttachmentType.Others.ToString() });
|
||||
|
||||
return lessonModel;
|
||||
}
|
||||
|
||||
public async Task<SortableEntity> PrepareSortableEntityModelAsync(SortableEntity sortableEntity, IEnumerable<IDisplayOrderRecord> models, SortRecordType sortRecordType, int parentId)
|
||||
{
|
||||
if (sortableEntity == null)
|
||||
throw new ArgumentNullException(nameof(sortableEntity));
|
||||
|
||||
sortableEntity.ParentId = parentId;
|
||||
sortableEntity.SortRecords = await (from p in models
|
||||
orderby p.DisplayOrder
|
||||
select new SortRecord
|
||||
{
|
||||
Id = p.Id,
|
||||
ExistingSortOrder = p.DisplayOrder,
|
||||
NewSortOrder = p.DisplayOrder,
|
||||
DisplayText = p.DisplayText
|
||||
}).ToListAsync();
|
||||
|
||||
sortableEntity.SortRecordType = sortRecordType;
|
||||
|
||||
return sortableEntity;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record AttachmentModel : BaseNopEntityModel
|
||||
{
|
||||
public AttachmentModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.AttachmentType")]
|
||||
public AttachmentType AttachmentType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VirtualPath")]
|
||||
public string VirtualPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public record ConfigurationModel : BaseNopModel
|
||||
{
|
||||
[NopResourceDisplayName("Plugins.SimpleLMS.Configuration.YouTubeApiKey.Text")]
|
||||
public string YouTubeApiKey { get; set; }
|
||||
[NopResourceDisplayName("Plugins.SimpleLMS.Configuration.VimeoAppId.Text")]
|
||||
public string VimeoAppId { get; set; }
|
||||
[NopResourceDisplayName("Plugins.SimpleLMS.Configuration.VimeoClient.Text")]
|
||||
public string VimeoClient { get; set; }
|
||||
[NopResourceDisplayName("Plugins.SimpleLMS.Configuration.VimeoSecret.Text")]
|
||||
public string VimeoSecret { get; set; }
|
||||
[NopResourceDisplayName("Plugins.SimpleLMS.Configuration.VimeoAccess.Text")]
|
||||
public string VimeoAccess { get; set; }
|
||||
[NopResourceDisplayName("Plugins.SimpleLMS.Configuration.VdoCipherKey.Text")]
|
||||
public string VdoCipherKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record CourseListModel : BasePagedListModel<CourseModel>
|
||||
{
|
||||
public CourseListModel()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
using FluentValidation.Validators;
|
||||
using FluentValidation;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record CourseModel : BaseNopEntityModel
|
||||
{
|
||||
public CourseModel()
|
||||
{
|
||||
Sections = new List<SectionModel>();
|
||||
AvailableProducts = new List<SelectListItem>();
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.ProductName")]
|
||||
public string ParentProductName { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.ProductName")]
|
||||
public int ProductId { get; set; }
|
||||
public IList<SelectListItem> AvailableProducts { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Instructor")]
|
||||
public string Instructor { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Instructor")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Sections")]
|
||||
public int SectionsTotal { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Lessons")]
|
||||
public int LessonsTotal { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.EnrolledStudents")]
|
||||
public int EnrolledStudents { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Price")]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.CreatedOn")]
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.UpdatedOn")]
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
|
||||
public IList<SectionModel> Sections { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record CourseSearchModel : BaseSearchModel
|
||||
{
|
||||
public CourseSearchModel()
|
||||
{
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.SearchCourseName")]
|
||||
public string SearchCourseName { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.InstructorGUID")]
|
||||
public string InstructorGUID { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record LessonModel : BaseNopEntityModel
|
||||
{
|
||||
public LessonModel()
|
||||
{
|
||||
//Video = new VideoModel();
|
||||
Attachments = new List<AttachmentModel>();
|
||||
AvailableLessonTypes = new List<SelectListItem>();
|
||||
AvailableVideoTypes = new List<SelectListItem>();
|
||||
AvailableAttachmentTypes = new List<SelectListItem>();
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.LessonType")]
|
||||
public LessonType LessonType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.LessonContents")]
|
||||
public string LessonContents { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.DisplayOrder")]
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Instructor")]
|
||||
public int InstructorName { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.IsFree")]
|
||||
public bool IsFreeLesson { get; set; }
|
||||
|
||||
public VideoModel Video { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.SectionName")]
|
||||
public int SectionId { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.CourseName")]
|
||||
public int CourseId { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoType")]
|
||||
public VideoType VideoType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoIdFromProvider")]
|
||||
public string VideoIdFromProvider { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Duration")]
|
||||
public int Duration { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoUrl")]
|
||||
public string VideoUrl { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoEmbedCode")]
|
||||
public string VideoEmbedCode { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.AttachmentType")]
|
||||
public AttachmentType AttachmentType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Attachments")]
|
||||
public IList<AttachmentModel> Attachments { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.LessonType")]
|
||||
public IList<SelectListItem> AvailableLessonTypes { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoType")]
|
||||
public IList<SelectListItem> AvailableVideoTypes { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.AttachmentType")]
|
||||
public IList<SelectListItem> AvailableAttachmentTypes { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record SectionModel : BaseNopEntityModel
|
||||
{
|
||||
public SectionModel()
|
||||
{
|
||||
Lessons = new List<LessonModel>();
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.DisplayOrder")]
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.CourseName")]
|
||||
public string CourseName { get; set; }
|
||||
|
||||
public int CourseId { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.IsFree")]
|
||||
public bool IsFree { get; set; }
|
||||
|
||||
public IList<LessonModel> Lessons { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record SortRecord : BaseNopEntityModel
|
||||
{
|
||||
public SortRecord()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public int ExistingSortOrder { get; set; }
|
||||
public int NewSortOrder { get; set; }
|
||||
public string DisplayText { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record SortableEntity
|
||||
{
|
||||
public int ParentId { get; set; }
|
||||
|
||||
public SortRecordType SortRecordType { get; set; }
|
||||
|
||||
public IList<SortRecord> SortRecords { get; set; }
|
||||
|
||||
public List<string> NewSortOrderValues { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
{
|
||||
public partial record VideoModel : BaseNopEntityModel
|
||||
{
|
||||
public VideoModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoType")]
|
||||
public VideoType VideoType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoIdFromProvider")]
|
||||
public string VideoIdFromProvider { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Duration")]
|
||||
public int Duration { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoUrl")]
|
||||
public string VideoUrl { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoEmbedCode")]
|
||||
public string VideoEmbedCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Nop.Data;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework.Validators;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators
|
||||
{
|
||||
public partial class AttachmentValidator : BaseNopValidator<AttachmentModel>
|
||||
{
|
||||
public AttachmentValidator(ILocalizationService localizationService, INopDataProvider dataProvider)
|
||||
{
|
||||
RuleFor(x => x.AttachmentType).NotNull();
|
||||
RuleFor(x => x.Name).NotNull();
|
||||
RuleFor(x => x.VirtualPath).NotNull();
|
||||
|
||||
SetDatabaseValidationRules<Attachment>(dataProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Nop.Data;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework.Validators;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators
|
||||
{
|
||||
public partial class CourseValidator : BaseNopValidator<CourseModel>
|
||||
{
|
||||
public CourseValidator(ILocalizationService localizationService, INopDataProvider dataProvider)
|
||||
{
|
||||
RuleFor(x => x.Name).NotNull();
|
||||
RuleFor(x => x.ProductId).NotNull().GreaterThan(0);
|
||||
SetDatabaseValidationRules<Course>(dataProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Nop.Data;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework.Validators;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators
|
||||
{
|
||||
public partial class LessonValidator : BaseNopValidator<LessonModel>
|
||||
{
|
||||
public LessonValidator(ILocalizationService localizationService, INopDataProvider dataProvider)
|
||||
{
|
||||
RuleFor(x => x.DisplayOrder).NotNull().GreaterThan(0);
|
||||
RuleFor(x => x.Name).NotEmpty();
|
||||
RuleFor(x => x.LessonType).NotEmpty();
|
||||
|
||||
RuleFor(x => x.AttachmentType).NotEmpty().When(x => x.LessonType == LessonType.Document);
|
||||
RuleFor(x => x.Attachments).Must(coll => coll.Any()).When(x => x.LessonType == LessonType.Document);
|
||||
|
||||
RuleFor(x => x.CourseId).NotNull().GreaterThan(0);
|
||||
|
||||
RuleFor(x => x.Duration).NotNull().When(x => x.LessonType == LessonType.Video)
|
||||
.GreaterThan(0).When(x => x.LessonType == LessonType.Video);
|
||||
|
||||
RuleFor(x => x.IsFreeLesson).NotNull();
|
||||
RuleFor(x => x.LessonContents).Length(1, 4000).When(x => x.LessonType == LessonType.Text);
|
||||
RuleFor(x => x.SectionId).NotNull();
|
||||
RuleFor(x => x.VideoIdFromProvider).NotEmpty().When(x => x.LessonType == LessonType.Video);
|
||||
|
||||
RuleFor(x => x.VideoType).NotEmpty().When(x => x.LessonType == LessonType.Video);
|
||||
|
||||
SetDatabaseValidationRules<Lesson>(dataProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Nop.Data;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework.Validators;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators
|
||||
{
|
||||
public partial class SectionValidator : BaseNopValidator<SectionModel>
|
||||
{
|
||||
public SectionValidator(ILocalizationService localizationService, INopDataProvider dataProvider)
|
||||
{
|
||||
RuleFor(x => x.Title).NotNull();
|
||||
RuleFor(x => x.DisplayOrder).NotNull();
|
||||
RuleFor(x => x.CourseId).NotNull();
|
||||
|
||||
SetDatabaseValidationRules<Section>(dataProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Nop.Data;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework.Validators;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators
|
||||
{
|
||||
public partial class VideoValidator : BaseNopValidator<VideoModel>
|
||||
{
|
||||
public VideoValidator(ILocalizationService localizationService, INopDataProvider dataProvider)
|
||||
{
|
||||
RuleFor(x => x.Duration).NotNull();
|
||||
RuleFor(x => x.VideoType).NotNull();
|
||||
|
||||
|
||||
RuleFor(m => m.VideoUrl).NotEmpty().When(m => string.IsNullOrEmpty(m.VideoIdFromProvider) && string.IsNullOrEmpty(m.VideoEmbedCode));
|
||||
RuleFor(m => m.VideoIdFromProvider).NotEmpty().When(m => string.IsNullOrEmpty(m.VideoUrl) && string.IsNullOrEmpty(m.VideoEmbedCode));
|
||||
RuleFor(m => m.VideoEmbedCode).NotEmpty().When(m => string.IsNullOrEmpty(m.VideoIdFromProvider) && string.IsNullOrEmpty(m.VideoUrl));
|
||||
|
||||
SetDatabaseValidationRules<Video>(dataProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
@model CourseModel
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
@inject INopHtmlHelper NopHtml
|
||||
|
||||
@{
|
||||
//page title
|
||||
ViewBag.PageTitle = T("SimpleLMS.Add").Text + " " + T("SimpleLMS.Course");
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("SimpleLMS.Courses");
|
||||
}
|
||||
|
||||
|
||||
<form asp-controller="Course" asp-action="Create" method="post" id="product-form">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
@T("SimpleLMS.Add") @T("SimpleLMS.Course")
|
||||
<small>
|
||||
<i class="fas fa-arrow-circle-left"></i>
|
||||
<a asp-action="List">@T("SimpleLMS.BackToList")</a>
|
||||
</small>
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
<button type="submit" name="save" class="btn btn-primary">
|
||||
<i class="far fa-save"></i>
|
||||
@T("Admin.Common.Save")
|
||||
</button>
|
||||
<button type="submit" name="save-continue" class="btn btn-primary">
|
||||
<i class="far fa-save"></i>
|
||||
@T("Admin.Common.SaveContinue")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@await Html.PartialAsync("_CreateOrUpdate.cshtml", Model)
|
||||
</form>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
@model CourseModel
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
@inject INopHtmlHelper NopHtml
|
||||
|
||||
@{
|
||||
//page title
|
||||
ViewBag.PageTitle = T("SimpleLMS.Edit").Text + " " + T("SimpleLMS.Course");
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("SimpleLMS.Courses");
|
||||
}
|
||||
|
||||
@*<link href="@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/css/jquery-ui-1.10.4.custom.min.css")" rel="stylesheet" type="text/css" />*@
|
||||
<link href="@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/css/simplelms.css")" type="text/css" rel="stylesheet" />
|
||||
@*<script type="text/javascript" src="@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/js/jquery-ui-1.10.4.custom.min.js")"></script>*@
|
||||
|
||||
|
||||
|
||||
<form asp-controller="Course" asp-action="Edit" method="post" id="product-form">
|
||||
<input type="hidden" id="Id" name="Id" value="@Model.Id" />
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
@T("SimpleLMS.Edit") @T("SimpleLMS.Course") - @Model.Name
|
||||
<small>
|
||||
<i class="fas fa-arrow-circle-left"></i>
|
||||
<a asp-action="List">@T("SimpleLMS.BackToList")</a>
|
||||
</small>
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
|
||||
<button type="submit" name="save" class="btn btn-primary">
|
||||
<i class="far fa-save"></i>
|
||||
@T("Admin.Common.Save")
|
||||
</button>
|
||||
<button type="submit" name="save-continue" class="btn btn-primary">
|
||||
<i class="far fa-save"></i>
|
||||
@T("Admin.Common.SaveContinue")
|
||||
</button>
|
||||
|
||||
<span id="course-delete" class="btn btn-danger">
|
||||
<i class="far fa-trash-alt"></i>
|
||||
@T("Admin.Common.Delete")
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@await Html.PartialAsync("_CreateOrUpdate.cshtml", Model)
|
||||
</form>
|
||||
<nop-delete-confirmation asp-model-id="@Model.Id" asp-button-id="course-delete" />
|
||||
<script src="@Url.Content("~/js/public.common.js")"></script>
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
@model CourseSearchModel
|
||||
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
@inject INopHtmlHelper NopHtml
|
||||
|
||||
@{
|
||||
//page title
|
||||
ViewBag.PageTitle = T("SimpleLMS.CourseList").Text;
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("SimpleLMS.Courses");
|
||||
}
|
||||
|
||||
@{
|
||||
const string hideSearchBlockAttributeName = "CourseListPage.HideSearchBlock";
|
||||
var hideSearchBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName);
|
||||
}
|
||||
|
||||
|
||||
<form asp-controller="Course" asp-action="List" method="post">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
@T("SimpleLMS.CourseList")
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="fas fa-plus-square"></i>
|
||||
@T("Admin.Common.AddNew")
|
||||
</a>
|
||||
@*<button type="button" id="delete-selected" class="btn btn-danger">
|
||||
<i class="far fa-trash-alt"></i>
|
||||
@T("Admin.Common.Delete.Selected")
|
||||
</button>*@
|
||||
@*<nop-action-confirmation asp-button-id="delete-selected" />*@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="form-horizontal">
|
||||
<div class="cards-group">
|
||||
<div class="card card-default card-search">
|
||||
<div class="card-body">
|
||||
<div class="row search-row @(!hideSearchBlock ? "opened" : "")" data-hideAttribute="@hideSearchBlockAttributeName">
|
||||
<div class="search-text">@T("Admin.Common.Search")</div>
|
||||
<div class="icon-search"><i class="fas fa-search" aria-hidden="true"></i></div>
|
||||
<div class="icon-collapse"><i class="far fa-angle-@(!hideSearchBlock ? "up" : "down")" aria-hidden="true"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="search-body @(hideSearchBlock ? "closed" : "")">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchCourseName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="SearchCourseName" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="text-center col-12">
|
||||
<button type="button" id="search-courses" class="btn btn-primary btn-search">
|
||||
<i class="fas fa-search"></i>
|
||||
@T("Admin.Common.Search")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
@await Html.PartialAsync("Table", new DataTablesModel
|
||||
{
|
||||
Name = "courses-grid",
|
||||
UrlRead = new DataUrl("CourseList", "Course", null),
|
||||
SearchButtonId = "search-courses",
|
||||
Length = Model.PageSize,
|
||||
LengthMenu = Model.AvailablePageSizes,
|
||||
Filters = new List<FilterParameter>
|
||||
{
|
||||
new FilterParameter(nameof(Model.SearchCourseName))
|
||||
},
|
||||
ColumnCollection = new List<ColumnProperty>
|
||||
{
|
||||
new ColumnProperty(nameof(CourseModel.Id))
|
||||
{
|
||||
IsMasterCheckBox = true,
|
||||
Render = new RenderCheckBox("checkbox_courses"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Width = "50"
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.Name))
|
||||
{
|
||||
Title = T("SimpleLMS.Name").Text
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.Instructor))
|
||||
{
|
||||
Title = T("SimpleLMS.Instructor").Text
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.Category))
|
||||
{
|
||||
Title = T("SimpleLMS.Category").Text
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.SectionsTotal))
|
||||
{
|
||||
Title = T("SimpleLMS.Sections").Text
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.EnrolledStudents))
|
||||
{
|
||||
Title = T("SimpleLMS.EnrolledStudents").Text
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.Status))
|
||||
{
|
||||
Title = T("SimpleLMS.Status").Text
|
||||
},
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.Price))
|
||||
{
|
||||
Title = T("SimpleLMS.Price").Text
|
||||
},
|
||||
|
||||
|
||||
new ColumnProperty(nameof(CourseModel.Id))
|
||||
{
|
||||
Title = T("Admin.Common.Edit").Text,
|
||||
Width = "80",
|
||||
ClassName = NopColumnClassDefaults.Button,
|
||||
Render = new RenderButtonEdit(new DataUrl("Edit"))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#delete-selected-action-confirmation-submit-button').bind('click', function () {
|
||||
var postData = {
|
||||
selectedIds: selectedIds
|
||||
};
|
||||
addAntiForgeryToken(postData);
|
||||
$.ajax({
|
||||
cache: false,
|
||||
type: "POST",
|
||||
url: "@(Url.Action("DeleteSelected", "Course"))",
|
||||
data: postData,
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
$('#deleteSelectedFailed-info').text(errorThrown);
|
||||
$('#deleteSelectedFailed').click();
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
updateTable('#courses-grid');
|
||||
}
|
||||
});
|
||||
$('#delete-selected-action-confirmation').modal('toggle');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<nop-alert asp-alert-id="deleteSelectedFailed" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</form>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
@model AttachmentModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header"> @Html.DisplayText(Model.Name)</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
</h5>
|
||||
<p class="card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
@model CourseModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
@if (Model.Id > 0)
|
||||
{
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="add-section">@T("SimpleLMS.Add") @T("SimpleLMS.Section")</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="sort-sections" onclick="sortSections(@Model.Id)">@T("SimpleLMS.Sort") @T("SimpleLMS.Sections")</button>
|
||||
</div>
|
||||
<div class="card-body" id="sections">
|
||||
@* @await Html.PartialAsync("_CreateOrUpdate.Sections.cshtml", Model.Sections);*@
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
refreshSections();
|
||||
$('#add-section').on('click',
|
||||
function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
@*displayPopupContentFromUrl('@Url.Action("CreateSection", "Course")?courseId=@Model.Id',
|
||||
'@T("SimpleLMS.CreateNewSection")');*@
|
||||
|
||||
$('<div id="create-or-edit-section-modal" class="form-horizontal"></div>').load('@Url.Action("CreateSection", "Course")?courseId=@Model.Id')
|
||||
.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
position: {
|
||||
of: window, my: "top+100", at: "top"
|
||||
},
|
||||
maxHeight: $(window).height() - 20,
|
||||
title: '@T("SimpleLMS.Create") @T("SimpleLMS.Section")',
|
||||
close: function (event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#delete-anything-close-dialog').click(function () {
|
||||
closeConfirmDialog();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function closeConfirmDialog() {
|
||||
$('#delete-anything').dialog("close");
|
||||
}
|
||||
|
||||
function editSection(sectionId)
|
||||
{
|
||||
|
||||
$('<div id="create-or-edit-section-modal" class="form-horizontal"></div>').load('@Url.Action("EditSection", "Course")?sectionId=' + sectionId)
|
||||
.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
position: {
|
||||
of: window, my: "top+100", at: "top"
|
||||
},
|
||||
maxHeight: $(window).height() - 20,
|
||||
title: '@T("SimpleLMS.Edit") @T("SimpleLMS.Section")',
|
||||
close: function (event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function refreshSections() {
|
||||
$("#sections").load('@Url.Action("Sections", "Course")' + '?courseId=@Model.Id');
|
||||
}
|
||||
|
||||
function addLesson(sectionId,courseId)
|
||||
{
|
||||
$('<div id="create-or-edit-lesson-modal" class="form-horizontal"></div>').load('@Url.Action("CreateLesson", "Course")?sectionId=' + sectionId + '&courseId=' + courseId)
|
||||
.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
position: {
|
||||
of: window, my: "top+100", at: "top"
|
||||
},
|
||||
maxHeight: $(window).height() - 20,
|
||||
title: '@T("SimpleLMS.Create") @T("SimpleLMS.Lesson")',
|
||||
close: function (event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
removeTinymce();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editLesson(lessonId,sectionId,courseId)
|
||||
{
|
||||
$('<div id="create-or-edit-lesson-modal" class="form-horizontal"></div>').load('@Url.Action("EditLesson", "Course")?lessonId=' + lessonId + '§ionId=' + sectionId + '&courseId=' + courseId)
|
||||
.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
position: {
|
||||
of: window, my: "top+100", at: "top"
|
||||
},
|
||||
maxHeight: $(window).height() - 20,
|
||||
title: '@T("SimpleLMS.Edit") @T("SimpleLMS.Lesson")',
|
||||
close: function (event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
removeTinymce();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sortLessons(sectionId, courseId)
|
||||
{
|
||||
$('<div id="sort-modal" class="form-horizontal"></div>').load('@Url.Action("SortLessons", "Course")?sectionId=' + sectionId)
|
||||
.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
position: {
|
||||
of: window, my: "top+100", at: "top"
|
||||
},
|
||||
maxHeight: $(window).height() - 20,
|
||||
title: '@T("SimpleLMS.Sort") @T("SimpleLMS.Lessons")',
|
||||
close: function (event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
removeTinymce();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function sortSections(courseId)
|
||||
{
|
||||
$('<div id="sort-modal" class="form-horizontal"></div>').load('@Url.Action("SortSections", "Course")?courseId=' + courseId)
|
||||
.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
position: {
|
||||
of: window, my: "top+100", at: "top"
|
||||
},
|
||||
maxHeight: $(window).height() - 20,
|
||||
title: '@T("SimpleLMS.Sort") @T("SimpleLMS.Sections")',
|
||||
close: function (event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
removeTinymce();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteLesson(lessonId,sectionId,courseId)
|
||||
{
|
||||
|
||||
openDialog();
|
||||
|
||||
$('#delete-anything-submit-button').off();
|
||||
|
||||
$('#delete-anything-submit-button').on('click',
|
||||
function (e) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action("DeleteLesson", "Course")',
|
||||
//data: $('#create-or-edit-section').serialize(),
|
||||
data: {
|
||||
lessonId: lessonId,
|
||||
sectionId: sectionId,
|
||||
courseId: courseId
|
||||
},
|
||||
headers: {
|
||||
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
closeConfirmDialog();
|
||||
$('#deleteSelectedFailed-info').text(jqXHR.responseText + ' ' + errorThrown);
|
||||
$('#deleteSelectedFailed').click();
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
closeConfirmDialog();
|
||||
refreshSections();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function deleteSection(sectionId)
|
||||
{
|
||||
|
||||
openDialog();
|
||||
|
||||
$('#delete-anything-submit-button').off();
|
||||
$('#delete-anything-submit-button').on('click',
|
||||
function(e) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action("DeleteSection", "Course")',
|
||||
//data: $('#create-or-edit-section').serialize(),
|
||||
data: {
|
||||
sectionId: sectionId
|
||||
},
|
||||
headers: {
|
||||
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
closeConfirmDialog();
|
||||
$('#deleteSelectedFailed-info').text(jqXHR.responseText + ' ' + errorThrown);
|
||||
$('#deleteSelectedFailed').click();
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
closeConfirmDialog();
|
||||
refreshSections();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteCourse(courseId)
|
||||
{
|
||||
|
||||
openDialog();
|
||||
|
||||
$('#delete-anything-submit-button').off();
|
||||
$('#delete-anything-submit-button').on('click',
|
||||
function(e) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action("Delete", "Course")',
|
||||
//data: $('#create-or-edit-section').serialize(),
|
||||
data: {
|
||||
courseId: courseId
|
||||
},
|
||||
headers: {
|
||||
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
closeConfirmDialog();
|
||||
$('#deleteSelectedFailed-info').text(jqXHR.responseText + ' ' + errorThrown);
|
||||
$('#deleteSelectedFailed').click();
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
closeConfirmDialog();
|
||||
refreshSections();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
$('#delete-anything').dialog({
|
||||
resizable: false,
|
||||
height: "auto",
|
||||
width: 400,
|
||||
modal: true,
|
||||
title: '@T("Admin.Common.AreYouSure")'
|
||||
});
|
||||
}
|
||||
|
||||
function removeTinymce() {
|
||||
while (tinymce.editors.length > 0) {
|
||||
tinymce.remove(tinymce.editors[0]);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<nop-alert asp-alert-id="deleteSelectedFailed" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
||||
@T("SimpleLMS.SaveCourseToAddContentMessage")
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
<div id="delete-anything" style="display: none;">
|
||||
<div class="modal-body">
|
||||
@T("Admin.Common.DeleteConfirmation")
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" id="delete-anything-submit-button" class="btn btn-primary float-right">
|
||||
@T("Admin.Common.Yes")
|
||||
</button> <span class="btn btn-default float-right margin-r-5" data-dismiss="modal" id="delete-anything-close-dialog">
|
||||
@T("Admin.Common.NoCancel")
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
@model CourseModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="ProductId" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-select asp-for="ProductId" asp-items="Model.AvailableProducts" asp-required="true" />
|
||||
<span asp-validation-for="ProductId"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="Name"/>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="Name" asp-required="true" />
|
||||
<span asp-validation-for="Name"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
@model LessonModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
<div class="card bg-white ml-3" >
|
||||
<div class="card-header border-info">
|
||||
|
||||
@if (Model.LessonType == Nop.Plugin.Misc.SimpleLMS.Domains.LessonType.Video)
|
||||
{
|
||||
<img src='@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/images/video-player.png")' height="16" class="mr-3" />
|
||||
}
|
||||
else if (Model.LessonType == Nop.Plugin.Misc.SimpleLMS.Domains.LessonType.Text)
|
||||
{
|
||||
<img src='@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/images/text.png")' height="16" class="mr-3" />
|
||||
}
|
||||
|
||||
|
||||
@Model.DisplayOrder. Lesson: <strong>@Model.Name</strong>
|
||||
|
||||
|
||||
|
||||
@if (Model.IsFreeLesson)
|
||||
{
|
||||
<span class='badge badge-success ml-2'>Free</span>
|
||||
}
|
||||
|
||||
|
||||
|
||||
<div class="float-right">
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="editLesson(@Model.Id,@Model.SectionId,@Model.CourseId)" id="edit-lesson-@Model.Id">@T("SimpleLMS.Edit") @T("SimpleLMS.Lesson")</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" id="delete-lesson-@Model.Id" onclick="deleteLesson(@Model.Id,@Model.SectionId,@Model.CourseId)">@T("SimpleLMS.Delete") @T("SimpleLMS.Lesson")</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
@model LessonModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
|
||||
<form asp-controller="Course" asp-action="CreateLesson" method="post" id="lesson-form">
|
||||
|
||||
<div class="card-body" id="create-or-edit-lesson">
|
||||
|
||||
<input type="hidden" id="Id" name="Id" value="@Model.Id" />
|
||||
<input type="hidden" id="CourseId" name="CourseId" value="@Model.CourseId" />
|
||||
<input type="hidden" id="SectionId" name="SectionId" value="@Model.SectionId" />
|
||||
<input type="hidden" id="DisplayOrder" name="DisplayOrder" value="@Model.DisplayOrder" />
|
||||
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="Name" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="Name" asp-required="true" />
|
||||
<span asp-validation-for="Name"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="IsFreeLesson" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="IsFreeLesson" asp-required="false" />
|
||||
<span asp-validation-for="IsFreeLesson"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="LessonType" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-select asp-for="LessonType" asp-items="Model.AvailableLessonTypes" asp-required="true" />
|
||||
<span asp-validation-for="LessonType"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="lesson-contents">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="LessonContents" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="LessonContents" asp-template="RichEditor" />
|
||||
<span asp-validation-for="LessonContents"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="video-type">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VideoType" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-select asp-for="VideoType" asp-items="Model.AvailableVideoTypes" asp-required="true" />
|
||||
<span asp-validation-for="VideoType"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="video-id-from-provider">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VideoIdFromProvider" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VideoIdFromProvider" asp-required="true" />
|
||||
<span asp-validation-for="VideoIdFromProvider"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="video-embed-code">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VideoEmbedCode" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VideoEmbedCode" asp-required="true" />
|
||||
<span asp-validation-for="VideoEmbedCode"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="video-url">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VideoUrl" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VideoUrl" asp-required="true" />
|
||||
<span asp-validation-for="VideoUrl"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="video-duration">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="Duration" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="Duration" asp-required="true" />
|
||||
<span asp-validation-for="Duration"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row" id="attachment-type">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AttachmentType" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-select asp-for="AttachmentType" asp-items="Model.AvailableAttachmentTypes" asp-required="true" />
|
||||
<span asp-validation-for="AttachmentType"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group row" id="submit-button">
|
||||
<div class="col-md-3">
|
||||
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="add-lesson-submit">
|
||||
@(Model.Id>0 ? T("SimpleLMS.Update") + " " + @T("SimpleLMS.Lesson") : T("SimpleLMS.Add") + " " + @T("SimpleLMS.Lesson"))
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#add-lesson-submit').off();
|
||||
|
||||
$('#add-lesson-submit').on('click',
|
||||
function (e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var _form = $(this).closest("form");
|
||||
_form.removeData('validator');
|
||||
_form.removeData('unobtrusiveValidation');
|
||||
$.validator.unobtrusive.parse(_form);
|
||||
|
||||
var isValid = $(_form).validate().form();
|
||||
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//alert();
|
||||
|
||||
|
||||
//alert($('#create-or-edit-section').serialize());
|
||||
$(this).prop('disabled', true);
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action((Model.Id>0? "EditLesson": "CreateLesson"), "Course")',
|
||||
//data: $('#create-or-edit-section').serialize(),
|
||||
data: {
|
||||
id: $("#create-or-edit-lesson").find("#Id").val(),
|
||||
courseId: $("#create-or-edit-lesson").find("#CourseId").val(),
|
||||
sectionId: $("#create-or-edit-lesson").find("#SectionId").val(),
|
||||
displayOrder: $("#create-or-edit-lesson").find("#DisplayOrder").val(),
|
||||
name: $("#create-or-edit-lesson").find("#Name").val(),
|
||||
isFreeLesson: $("#create-or-edit-lesson").find("#IsFreeLesson").is(":checked"),
|
||||
lessonType: $("#create-or-edit-lesson").find("#LessonType").val(),
|
||||
videoType: $("#create-or-edit-lesson").find("#VideoType").val(),
|
||||
videoIdFromProvider: $("#create-or-edit-lesson").find("#VideoIdFromProvider").val(),
|
||||
duration: $("#create-or-edit-lesson").find("#Duration").val(),
|
||||
lessonContents: tinymce.get("LessonContents").getContent({ format: "html" })
|
||||
},
|
||||
headers: {
|
||||
"RequestVerificationToken":
|
||||
$('input:hidden[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
success: function (response) {
|
||||
|
||||
$("form").removeData("unobtrusiveValidation");
|
||||
$.validator.unobtrusive.parse("form");
|
||||
$('#create-or-edit-lesson-modal').dialog('destroy').remove();
|
||||
removeTinymce();
|
||||
refreshSections();
|
||||
},
|
||||
failure: function (response) {
|
||||
$('#add-lesson-submit').prop('disabled', false);
|
||||
alert(JSON.stringify(response));
|
||||
},
|
||||
error: function (response) {
|
||||
|
||||
let obj = JSON.parse(response.responseText);
|
||||
var errorArray = {};
|
||||
|
||||
$.each(obj, function (key, value) {
|
||||
errorArray[key] = value;
|
||||
});
|
||||
|
||||
|
||||
|
||||
try {
|
||||
$('#lesson-form').data('validator').showErrors(errorArray);
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
}
|
||||
|
||||
$('#add-lesson-submit').prop('disabled', false);
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("#LessonType").change(function () {
|
||||
lessonTypeChange();
|
||||
});
|
||||
|
||||
hidelAll();
|
||||
lessonTypeChange();
|
||||
|
||||
});
|
||||
|
||||
|
||||
function lessonTypeChange() {
|
||||
|
||||
hidelAll();
|
||||
|
||||
var lessonTypeText = $('#LessonType').find(":selected").text();
|
||||
|
||||
if (lessonTypeText == "Video") {
|
||||
$("#video-type").show();
|
||||
$("#video-id-from-provider").show();
|
||||
$("#video-duration").show();
|
||||
$("#lesson-contents").show();
|
||||
}
|
||||
else if (lessonTypeText == "Text") {
|
||||
$("#lesson-contents").show();
|
||||
}
|
||||
else if (lessonTypeText == "Document") {
|
||||
$("#attachment-type").show();
|
||||
}
|
||||
$("#submit-button").show();
|
||||
}
|
||||
|
||||
function hidelAll() {
|
||||
$("#lesson-contents").hide();
|
||||
$("#video-type").hide();
|
||||
$("#video-id-from-provider").hide();
|
||||
$("#video-embed-code").hide();
|
||||
$("#video-url").hide();
|
||||
$("#video-duration").hide();
|
||||
$("#attachment-type").hide();
|
||||
$("#submit-button").hide();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</form>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
@model IList<LessonModel>
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@foreach (var lesson in Model)
|
||||
{
|
||||
@await Html.PartialAsync("_CreateOrUpdate.Lesson.cshtml", lesson)
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
@model SectionModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">
|
||||
|
||||
@Model.DisplayOrder. @T("SimpleLMS.Section"): <strong>@Model.Title</strong>
|
||||
@if (Model.IsFree)
|
||||
{
|
||||
<span class='badge badge-success ml-2'>Free</span>
|
||||
}
|
||||
<div class="float-right">
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="editSection(@Model.Id)" id="edit-section-@Model.Id">@T("SimpleLMS.Edit") @T("SimpleLMS.Section")</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="addLesson(@Model.Id,@Model.CourseId)" id="add-lesson-@Model.Id">@T("SimpleLMS.Add") @T("SimpleLMS.Lesson")</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="sort-lessons-@Model.Id" onclick="sortLessons(@Model.Id,@Model.CourseId)">@T("SimpleLMS.Sort") @T("SimpleLMS.Lessons")</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" id="delete-section-@Model.Id" onclick="deleteSection(@Model.Id)">@T("SimpleLMS.Delete") @T("SimpleLMS.Section")</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.Lessons != null && Model.Lessons.Count > 0)
|
||||
{
|
||||
|
||||
@await Html.PartialAsync("_CreateOrUpdate.Lessons.cshtml", Model.Lessons)
|
||||
}
|
||||
else
|
||||
{
|
||||
<h5 class="card-title">
|
||||
<strong> @T("SimpleLMS.NoLessonsAvailable")</strong>
|
||||
</h5>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
@model SectionModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
|
||||
<form asp-controller="Course" asp-action="CreateSection" method="post" id="section-form">
|
||||
<div class="card-body" id="create-or-edit-section">
|
||||
|
||||
<input type="hidden" id="Id" name="Id" value="@Model.Id" />
|
||||
<input type="hidden" id="CourseId" name="CourseId" value="@Model.CourseId" />
|
||||
<input type="hidden" id="DisplayOrder" name="DisplayOrder" value="@Model.DisplayOrder" />
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="Title" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="Title" asp-required="true" />
|
||||
<span asp-validation-for="Title"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="IsFree" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="IsFree" asp-required="false" />
|
||||
<span asp-validation-for="IsFree"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="add-section-submit">
|
||||
@(Model.Id>0 ? T("SimpleLMS.Update") + " " + @T("SimpleLMS.Section") : T("SimpleLMS.Add") + " " + @T("SimpleLMS.Section"))
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#add-lesson-submit').off();
|
||||
|
||||
$('#add-section-submit').on('click',
|
||||
function (e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var _form = $(this).closest("form");
|
||||
_form.removeData('validator');
|
||||
_form.removeData('unobtrusiveValidation');
|
||||
$.validator.unobtrusive.parse(_form);
|
||||
|
||||
var isValid = $(_form).validate().form();
|
||||
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//alert($('#create-or-edit-section').serialize());
|
||||
$(this).prop('disabled', true);
|
||||
|
||||
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action((Model.Id > 0 ? "EditSection" : "CreateSection"), "Course")',
|
||||
//data: $('#create-or-edit-section').serialize(),
|
||||
data: {
|
||||
id: $("#create-or-edit-section").find("#Id").val(),
|
||||
courseId: $("#create-or-edit-section").find("#CourseId").val(),
|
||||
displayOrder: $("#create-or-edit-section").find("#DisplayOrder").val(),
|
||||
title: $("#create-or-edit-section").find("#Title").val(),
|
||||
isFree: $("#create-or-edit-section").find("#IsFree").is(":checked")
|
||||
},
|
||||
headers: {
|
||||
"RequestVerificationToken":
|
||||
$('input:hidden[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
success: function (response) {
|
||||
$('#create-or-edit-section-modal').dialog('destroy').remove();
|
||||
refreshSections();
|
||||
},
|
||||
failure: function (response) {
|
||||
|
||||
alert(JSON.stringify(response));
|
||||
$(this).prop('enabled', false);
|
||||
|
||||
},
|
||||
error: function (response) {
|
||||
alert(JSON.stringify(response));
|
||||
$(this).prop('enabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
@model IList<SectionModel>
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
@foreach (var section in Model)
|
||||
{
|
||||
@await Html.PartialAsync("_CreateOrUpdate.Section.cshtml", section)
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
@model SortableEntity
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
|
||||
<form method="post" id="sort-form">
|
||||
|
||||
<div class="card-body" id="sort">
|
||||
|
||||
<input type="hidden" id="ParentId" name="ParentId" value="@Model.ParentId" />
|
||||
<input type="hidden" id="SortRecordType" name="SortRecordType" value="@Model.SortRecordType" />
|
||||
|
||||
<ul id="sort-records" class="list-group bg-white">
|
||||
@if (Model.SortRecords != null && Model.SortRecords.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < Model.SortRecords.Count; i++)
|
||||
{
|
||||
<li class="ui-state-default list-group-item mb-1 all-scroll border-0" id="@Model.SortRecords[i].Id">
|
||||
<span class="border-1 border-info">
|
||||
<strong> @Model.SortRecords[i].DisplayText</strong>
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
|
||||
|
||||
<div class="form-group row mt-3" id="submit-button">
|
||||
|
||||
<div class="col-md-12">
|
||||
@if (Model.SortRecords != null && Model.SortRecords.Count > 0)
|
||||
{
|
||||
<button type="button" class="btn btn-primary btn-sm float-right" id="sort-submit">
|
||||
@T("SimpleLMS.Update")
|
||||
</button>
|
||||
}
|
||||
else if (@Model.SortRecordType == SortRecordType.Section)
|
||||
{
|
||||
<span>
|
||||
@T("SimpleLMS.NoSectionsAvailable")
|
||||
</span>
|
||||
}
|
||||
else if (@Model.SortRecordType == SortRecordType.Lesson)
|
||||
{
|
||||
<span>
|
||||
@T("SimpleLMS.NoLessonsAvailable")
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$("#sort-records").sortable();
|
||||
|
||||
|
||||
$("#sort-records").disableSelection();
|
||||
|
||||
$('#sort-submit').off();
|
||||
|
||||
$('#sort-submit').on('click',
|
||||
function (e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
|
||||
var idsInOrder = $("#sort-records").sortable("toArray");
|
||||
|
||||
|
||||
var _form = $(this).closest("form");
|
||||
_form.removeData('validator');
|
||||
_form.removeData('unobtrusiveValidation');
|
||||
$.validator.unobtrusive.parse(_form);
|
||||
|
||||
var isValid = $(_form).validate().form();
|
||||
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this).prop('disabled', true);
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action((Model.SortRecordType == SortRecordType.Lesson ? "SortLessons" : "SortSections"), "Course")',
|
||||
//data: $('#create-or-edit-section').serialize(),
|
||||
data: {
|
||||
parentId: $("#sort").find("#ParentId").val(),
|
||||
sortRecordType: $("#sort").find("#SortRecordType").val(),
|
||||
newSortOrderValues: JSON.stringify(idsInOrder),
|
||||
},
|
||||
headers: {
|
||||
"RequestVerificationToken":
|
||||
$('input:hidden[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
success: function (response) {
|
||||
|
||||
$("form").removeData("unobtrusiveValidation");
|
||||
$.validator.unobtrusive.parse("form");
|
||||
$('#sort-modal').dialog('destroy').remove();
|
||||
|
||||
refreshSections();
|
||||
},
|
||||
failure: function (response) {
|
||||
$('#sort-submit').prop('disabled', false);
|
||||
alert(JSON.stringify(response));
|
||||
},
|
||||
error: function (response) {
|
||||
try {
|
||||
|
||||
let obj = JSON.parse(response.responseText);
|
||||
var errorArray = {};
|
||||
|
||||
$.each(obj, function (key, value) {
|
||||
errorArray[key] = value;
|
||||
});
|
||||
|
||||
$('#sort-form').data('validator').showErrors(errorArray);
|
||||
}
|
||||
catch (e) {
|
||||
alert(response.responseText);
|
||||
}
|
||||
|
||||
$('#sort-submit').prop('disabled', false);
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
<style type="text/css">
|
||||
.all-scroll {
|
||||
cursor: all-scroll;
|
||||
}
|
||||
</style>
|
||||
|
||||
</form>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
@model VideoModel
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header"> @Model.Duration</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
</h5>
|
||||
<p class="card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
@model CourseModel
|
||||
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
@inject INopHtmlHelper NopHtml
|
||||
|
||||
|
||||
@{
|
||||
|
||||
const string hideInfoBlockAttributeName = "CoursePage.HideInfoBlock";
|
||||
|
||||
var hideInfoBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideInfoBlockAttributeName);
|
||||
|
||||
const string hideCourseContentBlockAttributeName = "CoursePage.HideCourseContentBlock";
|
||||
|
||||
var hideCourseContentBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideCourseContentBlockAttributeName);
|
||||
|
||||
NopHtml.AddScriptParts(ResourceLocation.Footer, "~/lib_npm/tinymce/tinymce.min.js", excludeFromBundle: true);
|
||||
|
||||
}
|
||||
<div asp-validation-summary="All"></div>
|
||||
<input asp-for="Id" type="hidden" />
|
||||
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="form-horizontal">
|
||||
|
||||
|
||||
|
||||
<nop-cards id="course-cards">
|
||||
<nop-card asp-name="course-info" asp-icon="fas fa-info" asp-title="@T("SimpleLMS.Course") @T("SimpleLMS.Info")"
|
||||
asp-hide-block-attribute-name="@hideInfoBlockAttributeName" asp-hide="@hideInfoBlock" asp-advanced="false">
|
||||
@await Html.PartialAsync("_CreateOrUpdate.Info.cshtml", Model)
|
||||
</nop-card>
|
||||
|
||||
<nop-card asp-name="course-content" asp-icon="fas fa-info" asp-title="@T("SimpleLMS.CourseContent")"
|
||||
asp-hide-block-attribute-name="@hideCourseContentBlockAttributeName" asp-hide="@hideCourseContentBlock" asp-advanced="false">
|
||||
@await Html.PartialAsync("_CreateOrUpdate.CourseContent.cshtml", Model)
|
||||
</nop-card>
|
||||
</nop-cards>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
@model ConfigurationModel
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
@inject INopHtmlHelper NopHtml
|
||||
|
||||
@{
|
||||
const string hideYouTubeBlockAttributeName = "Configuration.HideYouTubeBlock";
|
||||
|
||||
var hideYouTubeBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideYouTubeBlockAttributeName);
|
||||
|
||||
const string hideVimeoBlockAttributeName = "Configuration.HideVimeoBlock";
|
||||
|
||||
var hideVimeoBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideVimeoBlockAttributeName);
|
||||
|
||||
const string hideVdoCipherBlockAttributeName = "Configuration.HideVdoCipherBlock";
|
||||
|
||||
var hideVdoCipherBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideVdoCipherBlockAttributeName);
|
||||
|
||||
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("SimpleLMS.configuration");
|
||||
}
|
||||
|
||||
@*<link href="@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/css/jquery-ui-1.10.4.custom.min.css")" rel="stylesheet" type="text/css" />*@
|
||||
@*<link href="@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/css/simplelms.css")" type="text/css" rel="stylesheet" />*@
|
||||
@*<script type="text/javascript" src="@Url.Content("~/Plugins/Misc.SimpleLMS/Content/Admin/js/jquery-ui-1.10.4.custom.min.js")"></script>*@
|
||||
|
||||
<form asp-controller="Settings" asp-action="Configure" method="post">
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="cards-group">
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
@T("Plugins.SimpleLMS.Configuration.PageTitle")
|
||||
</div>
|
||||
<div class="card-body form-horizontal">
|
||||
<nop-cards id="course-cards">
|
||||
@*<nop-card asp-name="YouTube" asp-icon="fas fa-info" asp-title="@T("SimpleLMS.Configuration.Youtube")" asp-hide-block-attribute-name="@hideYouTubeBlockAttributeName" asp-hide="@hideYouTubeBlock" asp-advanced="false">@await Html.PartialAsync("_CreateOrUpdate.Youtube.cshtml", Model)</nop-card>*@
|
||||
|
||||
<nop-card asp-name="Vimeo" asp-icon="fas fa-info" asp-title="@T("SimpleLMS.Configuration.Vimeo")" asp-hide-block-attribute-name="@hideVimeoBlockAttributeName" asp-hide="@hideVimeoBlock" asp-advanced="false">@await Html.PartialAsync("_CreateOrUpdate.Vimeo.cshtml", Model)</nop-card>
|
||||
@*<nop-card asp-name="VdoCipher" asp-icon="fas fa-info" asp-title="@T("SimpleLMS.Configuration.VdoCipher")" asp-hide-block-attribute-name="@hideVdoCipherBlockAttributeName" asp-hide="@hideVdoCipherBlock" asp-advanced="false">@await Html.PartialAsync("_CreateOrUpdate.VdoCipher.cshtml", Model)</nop-card>*@
|
||||
|
||||
</nop-cards>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-9 offset-md-3">
|
||||
<button type="submit" name="save" class="btn btn-primary">@T("Admin.Common.Save")</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
@model ConfigurationModel
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VdoCipherKey" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VdoCipherKey" asp-required="true" />
|
||||
<span asp-validation-for="VdoCipherKey"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
@model ConfigurationModel
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VimeoClient" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VimeoClient" asp-required="true" />
|
||||
<span asp-validation-for="VimeoClient"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VimeoSecret" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VimeoSecret" asp-required="true" />
|
||||
<span asp-validation-for="VimeoSecret"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="VimeoAccess" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="VimeoAccess" asp-required="true" />
|
||||
<span asp-validation-for="VimeoAccess"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
@model ConfigurationModel
|
||||
@using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Services
|
||||
@using Nop.Services.Stores
|
||||
|
||||
|
||||
|
||||
@{
|
||||
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="YouTubeApiKey" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="YouTubeApiKey" asp-required="true" />
|
||||
<span asp-validation-for="YouTubeApiKey"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage<TModel>
|
||||
|
||||
@inject IGenericAttributeService genericAttributeService
|
||||
@inject IWorkContext workContext
|
||||
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, Nop.Web.Framework
|
||||
|
||||
@using System.Text.Encodings.Web
|
||||
@using Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
@using Microsoft.AspNetCore.Routing
|
||||
@using Microsoft.Extensions.Primitives
|
||||
@using Nop.Core
|
||||
@using Nop.Core.Domain.Common
|
||||
@using Nop.Core.Events
|
||||
@using Nop.Core.Infrastructure
|
||||
@using Nop.Services.Common
|
||||
@using static Nop.Services.Common.NopLinksDefaults
|
||||
@using Nop.Web.Areas.Admin.Models.Affiliates
|
||||
@using Nop.Web.Areas.Admin.Models.Blogs
|
||||
@using Nop.Web.Areas.Admin.Models.Catalog
|
||||
@using Nop.Web.Areas.Admin.Models.Cms
|
||||
@using Nop.Web.Areas.Admin.Models.Common
|
||||
@using Nop.Web.Areas.Admin.Models.Customers
|
||||
@using Nop.Web.Areas.Admin.Models.Directory
|
||||
@using Nop.Web.Areas.Admin.Models.Discounts
|
||||
@using Nop.Web.Areas.Admin.Models.ExternalAuthentication
|
||||
@using Nop.Web.Areas.Admin.Models.Forums
|
||||
@using Nop.Web.Areas.Admin.Models.Home
|
||||
@using Nop.Web.Areas.Admin.Models.Localization
|
||||
@using Nop.Web.Areas.Admin.Models.Logging
|
||||
@using Nop.Web.Areas.Admin.Models.Messages
|
||||
@using Nop.Web.Areas.Admin.Models.MultiFactorAuthentication
|
||||
@using Nop.Web.Areas.Admin.Models.News
|
||||
@using Nop.Web.Areas.Admin.Models.Orders
|
||||
@using Nop.Web.Areas.Admin.Models.Payments
|
||||
@using Nop.Web.Areas.Admin.Models.Plugins
|
||||
@using Nop.Web.Areas.Admin.Models.Plugins.Marketplace
|
||||
@using Nop.Web.Areas.Admin.Models.Polls
|
||||
@using Nop.Web.Areas.Admin.Models.Reports
|
||||
@using Nop.Web.Areas.Admin.Models.Security
|
||||
@using Nop.Web.Areas.Admin.Models.Settings
|
||||
@using Nop.Web.Areas.Admin.Models.Shipping
|
||||
@using Nop.Web.Areas.Admin.Models.ShoppingCart
|
||||
@using Nop.Web.Areas.Admin.Models.Stores
|
||||
@using Nop.Web.Areas.Admin.Models.Tasks
|
||||
@using Nop.Web.Areas.Admin.Models.Tax
|
||||
@using Nop.Web.Areas.Admin.Models.Templates
|
||||
@using Nop.Web.Areas.Admin.Models.Topics
|
||||
@using Nop.Web.Areas.Admin.Models.Vendors
|
||||
@using Nop.Web.Extensions
|
||||
@using Nop.Web.Framework
|
||||
@using Nop.Web.Framework.Menu
|
||||
@using Nop.Web.Framework.Models
|
||||
@using Nop.Web.Framework.Events
|
||||
@using Nop.Web.Framework.Extensions
|
||||
@using Nop.Web.Framework.Infrastructure
|
||||
@using Nop.Web.Framework.Models.DataTables
|
||||
@using Nop.Web.Framework.Security.Captcha
|
||||
@using Nop.Web.Framework.Security.Honeypot
|
||||
@using Nop.Web.Framework.Themes
|
||||
@using Nop.Web.Framework.UI
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Nop.Web.Framework.Components;
|
||||
using System;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Components
|
||||
{
|
||||
[ViewComponent(Name = "Custom")]
|
||||
public class CustomViewComponent : NopViewComponent
|
||||
{
|
||||
public CustomViewComponent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(int productId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
|
||||
.ajax-loading-block-window {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 999;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: -16px 0 0 -16px;
|
||||
background: url('../images/loading.gif') center no-repeat;
|
||||
}
|
||||
|
||||
.please-wait {
|
||||
background: url('../images/ajax-loader-small.gif') no-repeat;
|
||||
padding-left: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ui-dialog {
|
||||
max-width: 90%;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 0 0 2px rgba(0,0,0,0.15);
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
/*override jQuery UI styles, do not delete doubled properties*/
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
font: normal 14px Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.ui-dialog:before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.ui-dialog-titlebar {
|
||||
border-bottom: 1px solid #ddd;
|
||||
overflow: hidden;
|
||||
background-color: #eee;
|
||||
padding: 10px 15px;
|
||||
/*override jQuery UI styles, do not delete doubled properties*/
|
||||
border-width: 0 0 1px;
|
||||
border-radius: 0;
|
||||
background-image: none;
|
||||
padding: 10px 15px !important;
|
||||
font-weight: normal;
|
||||
cursor: auto !important;
|
||||
}
|
||||
|
||||
.ui-dialog-titlebar > span {
|
||||
float: left;
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
/*override jQuery UI styles, do not delete doubled properties*/
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.ui-dialog-titlebar button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
background: url('../images/close.png') center no-repeat;
|
||||
font-size: 0;
|
||||
/*override jQuery UI styles, do not delete doubled properties*/
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 42px !important;
|
||||
height: 42px !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: 0;
|
||||
background: url('../images/close.png') center no-repeat !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.ui-dialog-titlebar button span {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ui-dialog-content {
|
||||
padding: 15px;
|
||||
line-height: 20px;
|
||||
/*override jQuery UI styles, do not delete doubled properties*/
|
||||
background-color: #fff !important;
|
||||
padding: 15px 15px 20px 15px !important;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.ui-dialog-content .page {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.ui-dialog-content .page-title {
|
||||
min-height: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 0px 10px 10px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ui-dialog-content .page-title h1 {
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.ui-dialog-content .back-in-stock-subscription-page {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ui-dialog-content .back-in-stock-subscription-page .tooltip {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ui-dialog-content .back-in-stock-subscription-page .button-1 {
|
||||
border: none;
|
||||
background-color: #4ab2f1;
|
||||
padding: 10px 15px;
|
||||
font-size: 15px;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ui-dialog-content .back-in-stock-subscription-page .button-1:hover,
|
||||
.ui-dialog-content .back-in-stock-subscription-page .button-1:focus {
|
||||
background-color: #248ece;
|
||||
}
|
||||
BIN
src/Plugins/Nop.Plugin.Misc.SimpleLMS/Content/Admin/images/ajax-loader-small.gif
Executable file
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 989 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 208 B |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -0,0 +1,208 @@
|
|||
.hide-overflow-x {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.cstm-nav .nav-link {
|
||||
padding-right: 1rem !important;
|
||||
padding-left: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.course-page .about-course h5 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.course-page .list-group.side-menu {
|
||||
font-size: 0.85rem;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.course-page .side-menu .list-group-item {
|
||||
padding: 0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.course-page .duration {
|
||||
line-height: 1.4;
|
||||
font-size: 12px;
|
||||
color: #6c757d
|
||||
}
|
||||
|
||||
.side-menu a, .dropdown-btn {
|
||||
text-decoration: none;
|
||||
color: #222;
|
||||
display: block;
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
padding: 8px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.side-menu a, .dropdown-btn:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.side-menu .list-group-item.side-menu-title {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.side-menu a:hover, .dropdown-btn:hover {
|
||||
color: #818181;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #818181;
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
display: none;
|
||||
padding: 8px 0;
|
||||
transition: 0.3s ease-out;
|
||||
}
|
||||
|
||||
.dropdown-container a:hover, .dropdown-container a:hover.active {
|
||||
background-color: #D1D7DC;
|
||||
}
|
||||
|
||||
.fa-caret-down {
|
||||
float: right;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
[data-toggle="collapse"] .fa:before {
|
||||
content: "\f0d7";
|
||||
}
|
||||
|
||||
[data-toggle="collapse"].collapsed .fa:before {
|
||||
content: "\f0da";
|
||||
}
|
||||
|
||||
.vertical-align {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.course-lesson {
|
||||
}
|
||||
|
||||
.course-lesson:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.course-lesson-active {
|
||||
background: #d1d7dc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@keyframes growProgressBar {
|
||||
0%, 33% {
|
||||
--pgPercentage: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
--pgPercentage: var(--value);
|
||||
}
|
||||
}
|
||||
|
||||
@property --pgPercentage {
|
||||
syntax: '<number>';
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
|
||||
div[role="progressbar"] {
|
||||
--size: 3.5rem;
|
||||
--fg: #369;
|
||||
--bg: #def;
|
||||
--pgPercentage: var(--value);
|
||||
animation: growProgressBar 3s 1 forwards;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
border-radius: 50%;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
background: radial-gradient(closest-side, white 80%, transparent 0 99.9%, white 0), conic-gradient(var(--fg) calc(var(--pgPercentage) * 1%), var(--bg) 0);
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: calc(var(--size) / 5);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
div[role="progressbar"]::before {
|
||||
counter-reset: percentage var(--value);
|
||||
content: counter(percentage) '%';
|
||||
}
|
||||
|
||||
#loader {
|
||||
position: fixed;
|
||||
width: 200px;
|
||||
padding: 10px;
|
||||
left: 45%;
|
||||
top: 0;
|
||||
display: none;
|
||||
background-color: #fff;
|
||||
z-index: 99999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.video-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.video-container::after {
|
||||
padding-top: 56.25%;
|
||||
display: block;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.video-container iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#loaddiv {
|
||||
height: 60vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 648px) {
|
||||
#loaddiv {
|
||||
height: 50vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
#loaddiv {
|
||||
height: 40vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Nop.Core;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Services;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Logging;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Web.Framework.Controllers;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Controllers
|
||||
{
|
||||
public partial class CustomerController : BaseController
|
||||
{
|
||||
private readonly CourseService _courseService;
|
||||
private readonly ISettingService _settingService;
|
||||
private readonly IWorkContext _workContext;
|
||||
private readonly IStoreContext _storeContext;
|
||||
private readonly IPermissionService _permissionService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ICustomerService _customerService;
|
||||
private readonly CourseModelFactory _courseModelFactory;
|
||||
|
||||
|
||||
public CustomerController(IAclService aclService, IProductService productService, ISettingService settingService
|
||||
, IWorkContext workContext, IStoreContext storeContext
|
||||
, IPermissionService permissionService, ILanguageService languageService
|
||||
, ILocalizationService localizationService, ICustomerActivityService customerActivityService, ICustomerService customerService
|
||||
, CourseModelFactory courseModelFactory
|
||||
, CourseService courseService) : base()
|
||||
{
|
||||
_settingService = settingService;
|
||||
_workContext = workContext;
|
||||
_storeContext = storeContext;
|
||||
_permissionService = permissionService;
|
||||
_localizationService = localizationService;
|
||||
_customerService = customerService;
|
||||
_courseModelFactory = courseModelFactory;
|
||||
_courseService = courseService;
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> Courses()
|
||||
{
|
||||
if (!await _customerService.IsRegisteredAsync(await _workContext.GetCurrentCustomerAsync()))
|
||||
return Challenge();
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
//prepare model
|
||||
var model = await _courseModelFactory.PrepareCourseSearchModelAsync(new CourseSearchModel());
|
||||
|
||||
return View("~/Plugins/Misc.SimpleLMS/Views/Customer/List.cshtml", model);
|
||||
}
|
||||
|
||||
|
||||
//public virtual async Task<I>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public virtual async Task<IActionResult> CourseOverviewList([FromBody]CourseSearchModel searchModel)
|
||||
{
|
||||
if (!await _customerService.IsRegisteredAsync(await _workContext.GetCurrentCustomerAsync()))
|
||||
return Challenge();
|
||||
|
||||
|
||||
//prepare model
|
||||
var model = await _courseModelFactory.PrepareCourseOverviewListModelAsync(searchModel);
|
||||
|
||||
return PartialView("~/Plugins/Misc.SimpleLMS/Views/Customer/_MyCourseList.cshtml", model);
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> CoursesDetails(int courseId)
|
||||
{
|
||||
if (!await _customerService.IsRegisteredAsync(await _workContext.GetCurrentCustomerAsync()))
|
||||
return Challenge();
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
if (_courseService.IsUserCourse(courseId, customer.Id))
|
||||
{
|
||||
var courseExisting = await _courseService.GetCourseByProductIdAsync(courseId);
|
||||
var lessonProgresses = await _courseService.GetCourseProgressByUserId(courseId, customer.Id);
|
||||
var model = await _courseModelFactory.PrepareCourseDetailModelAsync(new CourseDetail(), courseExisting, lessonProgresses);
|
||||
return View("~/Plugins/Misc.SimpleLMS/Views/Customer/CoursesDetails.cshtml", model);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task<IActionResult> LessonContent(int lessonId, int courseId)
|
||||
{
|
||||
if (!await _customerService.IsRegisteredAsync(await _workContext.GetCurrentCustomerAsync()))
|
||||
return Challenge();
|
||||
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
if (_courseService.IsUserCourse(courseId, customer.Id))
|
||||
{
|
||||
var lessonExisting = await _courseService.GetLessonByCourseIdAndLessonIdAsync(courseId, lessonId);
|
||||
var model = await _courseModelFactory.PrepareLessonModelAsync(new LessonDetail(), lessonExisting);
|
||||
|
||||
var storeScope = await _storeContext.GetActiveStoreScopeConfigurationAsync();
|
||||
var simpleLMSSettings = await _settingService.LoadSettingAsync<SimpleLMSSettings>(storeScope);
|
||||
ViewData["simpleLMSSettings"] = simpleLMSSettings;
|
||||
|
||||
|
||||
return View("~/Plugins/Misc.SimpleLMS/Views/Customer/_LessonContent.cshtml", model);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<IActionResult> UpdateLessonStatus(LessonStatusModel lessonStatus)
|
||||
{
|
||||
if (!await _customerService.IsRegisteredAsync(await _workContext.GetCurrentCustomerAsync()))
|
||||
return Challenge();
|
||||
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
if (_courseService.IsUserCourse(lessonStatus.CourseId, customer.Id)
|
||||
&& _courseService.ValidateCourseSectionLessonCombination(lessonStatus.LessonId, lessonStatus.SectionId, lessonStatus.CourseId))
|
||||
{
|
||||
|
||||
await _courseService.InsertOrUpdateLessonProgressByCourseLessonAndUser(lessonStatus.CourseId, customer.Id, lessonStatus.LessonId, lessonStatus.IsCompleted, lessonStatus.CurrentlyAt);
|
||||
|
||||
|
||||
|
||||
return Json(new { success = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
responseText = await _localizationService.GetResourceAsync("SimpleLMS.InvalidData")
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class Attachment : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int AtttachmentTypeId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string VirtualPath { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
public int InstructorId { get; set; }
|
||||
|
||||
public Guid InstructorGuid { get; set; }
|
||||
|
||||
public AttachmentType AttachmentType
|
||||
{
|
||||
get => (AttachmentType)AtttachmentTypeId;
|
||||
set => AtttachmentTypeId = (int)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public enum AttachmentType
|
||||
{
|
||||
WordDoc = 10,
|
||||
Pdf = 20,
|
||||
Ppt = 30,
|
||||
Image = 100,
|
||||
Others = 110
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Security;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents Course
|
||||
/// </summary>
|
||||
public partial class Course : BaseEntity, ILocalizedEntity, IStoreMappingSupported, ISoftDeletedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets associated product id
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets course name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets LimitedToStores
|
||||
/// </summary>
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets Deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Instructor Id
|
||||
/// </summary>
|
||||
public int InstructorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Instructor Guid
|
||||
/// </summary>
|
||||
public Guid InstructorGuid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of product creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of product update
|
||||
/// </summary>
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using Nop.Core;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class CourseProgress : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int CourseId { get; set; }
|
||||
|
||||
public int StudentId { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Core;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public class CourseStat : BaseEntity
|
||||
{
|
||||
public int EnrolledStudents { get; set; }
|
||||
public int Sections { get; set; }
|
||||
public int Lessons { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Nop.Core;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class CustomTable : BaseEntity
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial interface IDisplayOrderRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int DisplayOrder { get; set; }
|
||||
public string DisplayText { get; set; }
|
||||
}
|
||||
|
||||
public partial class DisplayOrderRecord : IDisplayOrderRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int DisplayOrder { get; set; }
|
||||
public string DisplayText { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class Lesson : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public int LessonTypeId { get; set; }
|
||||
|
||||
public string LessonContents { get; set; }
|
||||
|
||||
public int? VideoId { get; set; }
|
||||
|
||||
public int? DocumentId { get; set; }
|
||||
|
||||
public int? PictureId { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
public int InstructorId { get; set; }
|
||||
|
||||
public Guid InstructorGuid { get; set; }
|
||||
|
||||
//public int ExamId { get; set; }
|
||||
|
||||
public bool IsFreeLesson { get; set; }
|
||||
|
||||
public LessonType LessonType
|
||||
{
|
||||
get => (LessonType)LessonTypeId;
|
||||
set => LessonTypeId = (int)value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class LessonAttachment : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int LessonId { get; set; }
|
||||
|
||||
public int AttachmentId { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using Nop.Core;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class LessonProgress : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int CourseProgressId { get; set; }
|
||||
|
||||
public int LessonId { get; set; }
|
||||
|
||||
public bool IsCompleted { get; set; }
|
||||
|
||||
public int CurrentlyAt { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public enum LessonType
|
||||
{
|
||||
Text = 10,
|
||||
Image = 20,
|
||||
Document = 30,
|
||||
Video = 40,
|
||||
Questionire = 50,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using Nop.Core;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class Section : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int CourseId { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
public bool IsFree { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Nop.Core;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public class SectionLesson : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int CourseId { get; set; }
|
||||
|
||||
public int SectionId { get; set; }
|
||||
|
||||
public int LessonId { get; set; }
|
||||
|
||||
public bool IsFreeLesson { get; set; }
|
||||
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public enum SortRecordType
|
||||
{
|
||||
Section = 1,
|
||||
Lesson =2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public partial class Video : BaseEntity, ILocalizedEntity, IStoreMappingSupported
|
||||
{
|
||||
public int VideoTypeId { get; set; }
|
||||
|
||||
public string VideoIdFromProvider { get; set; }
|
||||
|
||||
public string VideoUrl { get; set; }
|
||||
|
||||
public int Duration { get; set; }
|
||||
|
||||
public string VideoEmbedCode { get; set; }
|
||||
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
public int InstructorId { get; set; }
|
||||
|
||||
public Guid InstructorGuid { get; set; }
|
||||
|
||||
public VideoType VideoType
|
||||
{
|
||||
get => (VideoType)VideoTypeId;
|
||||
set => VideoTypeId = (int)value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Domains
|
||||
{
|
||||
public enum VideoType
|
||||
{
|
||||
Hosted = 10,
|
||||
Youtube = 20,
|
||||
VdoCipher = 30,
|
||||
Vimeo = 40,
|
||||
AWS = 50
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Services.Events;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework.Events;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Models.Customer;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Events
|
||||
{
|
||||
public class EventConsumer : IConsumer<ModelPreparedEvent<BaseNopModel>>
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
public EventConsumer(ILocalizationService localizationService)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
public async Task HandleEventAsync(ModelPreparedEvent<BaseNopModel> eventMessage)
|
||||
{
|
||||
if (eventMessage.Model is not CustomerNavigationModel navigationModel)
|
||||
return;
|
||||
|
||||
navigationModel.CustomerNavigationItems.Insert(0, new CustomerNavigationItemModel
|
||||
{
|
||||
RouteName = SimpleLMSDefaults.CustomerMyCoursers,
|
||||
ItemClass = "my-courses",
|
||||
Tab = SimpleLMSDefaults.CustomerMyCoursesMenuTab,
|
||||
Title = await _localizationService.GetResourceAsync("SimpleLMS.MyCourses")
|
||||
|
||||
}); ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Core;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Services;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Models;
|
||||
using Nop.Web.Framework.Models.Extensions;
|
||||
using Nop.Web.Areas.Admin.Infrastructure.Mapper.Extensions;
|
||||
using Nop.Services.Media;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS
|
||||
{
|
||||
public partial class CourseModelFactory
|
||||
{
|
||||
private readonly CourseService _courseService;
|
||||
private readonly ICustomerService _customerService;
|
||||
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IProductService _productService;
|
||||
private readonly IWorkContext _workContext;
|
||||
|
||||
private readonly IPictureService _pictureService;
|
||||
|
||||
|
||||
public CourseModelFactory(
|
||||
CourseService courseService,
|
||||
ICustomerService customerService,
|
||||
ILocalizationService localizationService,
|
||||
IProductService productService,
|
||||
IWorkContext workContext,
|
||||
IPictureService pictureService)
|
||||
{
|
||||
|
||||
_courseService = courseService;
|
||||
_customerService = customerService;
|
||||
_localizationService = localizationService;
|
||||
_productService = productService;
|
||||
_workContext = workContext;
|
||||
_pictureService = pictureService;
|
||||
}
|
||||
|
||||
public async Task<CourseSearchModel> PrepareCourseSearchModelAsync(CourseSearchModel searchModel)
|
||||
{
|
||||
if (searchModel == null)
|
||||
throw new ArgumentNullException(nameof(searchModel));
|
||||
|
||||
searchModel.CourseOverviewList = await PrepareCourseOverviewListModelAsync(searchModel);
|
||||
|
||||
searchModel.SetGridPageSize();
|
||||
|
||||
return searchModel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<CourseOverviewListModel> PrepareCourseOverviewListModelAsync(CourseSearchModel searchModel)
|
||||
{
|
||||
if (searchModel == null)
|
||||
throw new ArgumentNullException(nameof(searchModel));
|
||||
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
|
||||
|
||||
var courses = await _courseService.SearchCoursesAsync(pageIndex: searchModel.PageNumber - 1,
|
||||
pageSize: searchModel.PageSize > 0 ? searchModel.PageSize : 10,
|
||||
keyword: searchModel.SearchCourseName,
|
||||
userId: customer.Id);
|
||||
|
||||
|
||||
var model = new CourseOverviewListModel();
|
||||
|
||||
|
||||
model.Courses = (await PrepareCourseOverviewModelsAsyns(courses, customer)).ToList();
|
||||
model.LoadPagedList(courses);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private async Task<IList<CourseOverviewModel>> PrepareCourseOverviewModelsAsyns(IPagedList<Course> courses, Customer customer)
|
||||
{
|
||||
if (courses == null)
|
||||
throw new ArgumentNullException(nameof(courses));
|
||||
|
||||
|
||||
var models = new List<CourseOverviewModel>();
|
||||
foreach (var course in courses)
|
||||
{
|
||||
|
||||
var model = course.ToModel<CourseOverviewModel>();
|
||||
|
||||
var product = await _productService.GetProductByIdAsync(course.ProductId);
|
||||
model.ParentProductName = product.Name;
|
||||
|
||||
model.CourseProgress = await _courseService.GetCourseProgressPercentByUserId(course.Id, customer.Id);
|
||||
var pictures = await _pictureService.GetPicturesByProductIdAsync(product.Id);
|
||||
|
||||
|
||||
if (pictures != null && pictures.Count() > 0)
|
||||
model.ProductMainImage = (await _pictureService.GetPictureUrlAsync(pictures[0])).Url;
|
||||
|
||||
|
||||
models.Add(model);
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
|
||||
public async Task<CourseDetail> PrepareCourseDetailModelAsync(CourseDetail courseDetail, Course course, IList<LessonProgress> lessonProgresses)
|
||||
{
|
||||
|
||||
if (courseDetail == null)
|
||||
throw new ArgumentNullException(nameof(courseDetail));
|
||||
|
||||
if (course != null)
|
||||
{
|
||||
//fill in model values from the entity
|
||||
if (course != null)
|
||||
{
|
||||
courseDetail = course.ToModel<CourseDetail>();
|
||||
}
|
||||
|
||||
var parentProduct = await _productService.GetProductByIdAsync(course.ProductId);
|
||||
|
||||
if (parentProduct != null)
|
||||
{
|
||||
courseDetail.ParentProductName = parentProduct.Name;
|
||||
courseDetail.ProductId = parentProduct.Id;
|
||||
}
|
||||
|
||||
courseDetail.Sections = (await _courseService.GetSectionsByCourseIdAsync(courseDetail.Id)).OrderBy(p => p.DisplayOrder).Select(p =>
|
||||
new SectionDetail
|
||||
{
|
||||
CourseName = courseDetail.Name,
|
||||
DisplayOrder = p.DisplayOrder,
|
||||
Id = p.Id,
|
||||
IsFree = p.IsFree,
|
||||
Title = p.Title,
|
||||
CourseId = course.Id,
|
||||
Lessons = _courseService.GetLessonsBySectionIdAsync(p.Id)
|
||||
.GetAwaiter().GetResult().Select(l => l.ToModel<LessonDetail>()).ToList()
|
||||
}).ToList();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i < courseDetail.Sections.Count; i++)
|
||||
{
|
||||
var sectionLessons = await _courseService.GetSectionLessonsBySectionIdAsync(courseDetail.Sections[i].Id);
|
||||
|
||||
|
||||
for (int j = 0; j < courseDetail.Sections[i].Lessons.Count; j++)
|
||||
{
|
||||
courseDetail.Sections[i].Lessons[j].SectionId = courseDetail.Sections[i].Id;
|
||||
courseDetail.Sections[i].Lessons[j].CourseId = courseDetail.Id;
|
||||
courseDetail.Sections[i].Lessons[j].DisplayOrder
|
||||
= sectionLessons.Where(p => p.LessonId == courseDetail.Sections[i].Lessons[j].Id).SingleOrDefault().DisplayOrder;
|
||||
courseDetail.Sections[i].Lessons[j].Duration = courseDetail.Sections[i].Lessons[j].LessonType == LessonType.Video ?
|
||||
_courseService.GetVideoByLessonIdAsync(courseDetail.Sections[i].Lessons[j].Id).GetAwaiter().GetResult().Duration : 0;
|
||||
|
||||
var lessonProgress = lessonProgresses.Where(p => p.LessonId == courseDetail.Sections[i].Lessons[j].Id).SingleOrDefault();
|
||||
|
||||
courseDetail.Sections[i].Lessons[j].IsCompleted = (lessonProgress != null ? lessonProgress.IsCompleted : false);
|
||||
|
||||
if (courseDetail.Sections[i].Lessons[j].Video != null && lessonProgress.CurrentlyAt > 0)
|
||||
{
|
||||
courseDetail.Sections[i].Lessons[j].Video.TimeCode =
|
||||
(courseDetail.Sections[i].Lessons[j].Video.VideoType == VideoType.Vimeo ?
|
||||
lessonProgress.CurrentlyAt / 60 + "m" + (lessonProgress.CurrentlyAt % 60) + "s" : lessonProgress.CurrentlyAt.ToString());
|
||||
}
|
||||
|
||||
if (courseDetail.CurrentLesson == 0 && !courseDetail.Sections[i].Lessons[j].IsCompleted)
|
||||
{
|
||||
courseDetail.CurrentLesson = courseDetail.Sections[i].Lessons[j].Id;
|
||||
courseDetail.CurrentSection = courseDetail.Sections[i].Id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
courseDetail.Sections[i].Lessons = courseDetail.Sections[i].Lessons.OrderBy(p => p.DisplayOrder).ToList();
|
||||
}
|
||||
|
||||
if (courseDetail.CurrentLesson == 0)
|
||||
{
|
||||
var currSection = (from p in courseDetail.Sections
|
||||
where p.Lessons.Count > 0
|
||||
select p).FirstOrDefault();
|
||||
if (currSection != null)
|
||||
{
|
||||
courseDetail.CurrentLesson = currSection.Lessons.FirstOrDefault().Id;
|
||||
courseDetail.CurrentSection = currSection.Id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return courseDetail;
|
||||
}
|
||||
|
||||
public async Task<LessonDetail> PrepareLessonModelAsync(LessonDetail lessonDetail, Lesson lesson)
|
||||
{
|
||||
if (lessonDetail == null)
|
||||
throw new ArgumentNullException(nameof(lessonDetail));
|
||||
|
||||
|
||||
if (lesson != null)
|
||||
{
|
||||
lessonDetail = lesson.ToModel<LessonDetail>();
|
||||
if (lesson.LessonType == LessonType.Video)
|
||||
{
|
||||
lessonDetail.Video = (await _courseService.GetVideoByLessonIdAsync(lessonDetail.Id)).ToModel<VideoDetail>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return lessonDetail;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Nop.Core.Infrastructure;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Factories;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Validators;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Services;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Infrastructure
|
||||
{
|
||||
public class NopStartup : INopStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.Configure<RazorViewEngineOptions>(options =>
|
||||
{
|
||||
options.ViewLocationExpanders.Add(new ViewLocationExpander());
|
||||
});
|
||||
|
||||
services.AddTransient<IValidator<CourseModel>, CourseValidator>();
|
||||
services.AddTransient<IValidator<AttachmentModel>, AttachmentValidator>();
|
||||
services.AddTransient<IValidator<LessonModel>, LessonValidator>();
|
||||
services.AddTransient<IValidator<SectionModel>, SectionValidator>();
|
||||
services.AddTransient<IValidator<VideoModel>, VideoValidator>();
|
||||
|
||||
|
||||
services.AddScoped<CourseService>();
|
||||
services.AddScoped<CourseModelFactory>();
|
||||
services.AddScoped<AdminCourseModelFactory>();
|
||||
|
||||
//services.AddScoped<CustomModelFactory, ICustomerModelFactory>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder application)
|
||||
{
|
||||
}
|
||||
|
||||
public int Order => 10000;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Nop.Web.Framework.Mvc.Routing;
|
||||
using Nop.Web.Infrastructure;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Infrastructure
|
||||
{
|
||||
public partial class RouteProvider : BaseRouteProvider, IRouteProvider
|
||||
{
|
||||
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
|
||||
{
|
||||
var lang = GetLanguageRoutePattern();
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.AdminCourseListRoute,
|
||||
pattern: $"{lang}/Admin/Course/List",
|
||||
defaults: new { controller = "CourseController", action = "Index", area = "Admin" });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.AdminCourseListJsonRoute,
|
||||
pattern: $"{lang}/Admin/Course/CourseList",
|
||||
defaults: new { controller = "CourseController", action = "CourseList", area = "Admin" });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.AdminCourseCreateSectionRoute,
|
||||
pattern: $"{lang}/Admin/Course/CreateSection",
|
||||
defaults: new { controller = "CourseController", action = "CreateSection", area = "Admin" });
|
||||
|
||||
|
||||
//customer account links
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.CustomerMyCoursers,
|
||||
pattern: $"{lang}/customer/mycourses",
|
||||
defaults: new { controller = "Customer", action = "Courses" });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.CustomerMyCoursers,
|
||||
pattern: $"{lang}/customer/searchmycourses",
|
||||
defaults: new { controller = "Customer", action = "CourseOverviewList" });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.CustomerCoursesDetails,
|
||||
pattern: $"{lang}/coursesdetail/{{productid:min(0)}}",
|
||||
defaults: new { controller = "Customer", action = "CoursesDetails" });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: SimpleLMSDefaults.CustomerLessonVideo,
|
||||
pattern: $"{lang}/videocontent/{{lessionId:min(0)}}",
|
||||
defaults: new { controller = "Customer", action = "VideoContent" });
|
||||
|
||||
|
||||
}
|
||||
|
||||
public int Priority => 999;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Nop.Core.Infrastructure.Mapper;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Infrastructure
|
||||
{
|
||||
public class SimpleLMSMapperConfiguration : Profile, IOrderedMapperProfile
|
||||
{
|
||||
public SimpleLMSMapperConfiguration()
|
||||
{
|
||||
CreateMaps();
|
||||
}
|
||||
|
||||
private void CreateMaps()
|
||||
{
|
||||
CreateMap<Course, CourseModel>();
|
||||
CreateMap<Attachment, AttachmentModel>();
|
||||
CreateMap<Lesson, LessonModel>();
|
||||
CreateMap<Section, SectionModel>();
|
||||
CreateMap<Video, VideoModel>();
|
||||
|
||||
CreateMap<CourseModel, Course>();
|
||||
CreateMap<AttachmentModel, Attachment>();
|
||||
CreateMap<LessonModel, Lesson>();
|
||||
CreateMap<SectionModel, Section>();
|
||||
CreateMap<VideoModel, Video>();
|
||||
|
||||
CreateMap<CourseOverviewModel, Course>();
|
||||
CreateMap<Course, CourseOverviewModel>();
|
||||
|
||||
CreateMap<Course, CourseDetail>();
|
||||
CreateMap<CourseDetail, Course>();
|
||||
|
||||
CreateMap<SectionDetail, Section>();
|
||||
CreateMap<Section, SectionDetail>();
|
||||
|
||||
|
||||
CreateMap<LessonDetail, Lesson>();
|
||||
CreateMap<Lesson, LessonDetail>();
|
||||
|
||||
CreateMap<VideoDetail, Video>();
|
||||
CreateMap<Video, VideoDetail>();
|
||||
|
||||
}
|
||||
|
||||
public int Order => 99;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Infrastructure
|
||||
{
|
||||
public class ViewLocationExpander : IViewLocationExpander
|
||||
{
|
||||
public void PopulateValues(ViewLocationExpanderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
|
||||
{
|
||||
if (context.AreaName == "Admin")
|
||||
{
|
||||
viewLocations = new[] { $"/Plugins/Misc.SimpleLMS/Areas/Admin/Views/{context.ControllerName}/{context.ViewName}.cshtml" }.Concat(viewLocations);
|
||||
}
|
||||
//else if (context.AreaName == null && context.ViewName == "Components/CustomerNavigation/Default")
|
||||
//{
|
||||
// viewLocations = new[] { $"/Plugins/Misc.SimpleLMS/Views/Shared/Components/CustomCustomerNavigation/Default.cshtml" }.Concat(viewLocations);
|
||||
//}
|
||||
else
|
||||
{
|
||||
viewLocations = new[] { $"/Plugins/Misc.SimpleLMS/Views/{context.ControllerName}/{context.ViewName}.cshtml"
|
||||
}.Concat(viewLocations);
|
||||
}
|
||||
|
||||
return viewLocations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Data.Extensions;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Data.Mapping.Builders
|
||||
{
|
||||
public class AtttachmentBuilder : NopEntityBuilder<Attachment>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(Attachment.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(Attachment.AtttachmentTypeId))
|
||||
.AsInt32().NotNullable()
|
||||
.WithColumn(nameof(Attachment.Name))
|
||||
.AsString(500).NotNullable()
|
||||
.WithColumn(nameof(Attachment.VirtualPath))
|
||||
.AsString().NotNullable()
|
||||
.WithColumn(nameof(Attachment.InstructorId))
|
||||
.AsInt32().NotNullable().ForeignKey<Customer>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Attachment.InstructorGuid))
|
||||
.AsGuid().NotNullable()
|
||||
.WithColumn(nameof(Attachment.LimitedToStores))
|
||||
.AsBoolean().NotNullable();
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Data.Extensions;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Data.Mapping.Builders
|
||||
{
|
||||
public class CourseBuilder : NopEntityBuilder<Course>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(Course.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(Course.ProductId))
|
||||
.AsInt32().NotNullable().ForeignKey<Product>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Course.Name))
|
||||
.AsString(500).NotNullable()
|
||||
.WithColumn(nameof(Course.LimitedToStores))
|
||||
.AsBoolean().NotNullable()
|
||||
.WithColumn(nameof(Course.Deleted))
|
||||
.AsBoolean().NotNullable()
|
||||
.WithColumn(nameof(Course.InstructorId))
|
||||
.AsInt32().NotNullable().ForeignKey<Customer>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Course.InstructorGuid))
|
||||
.AsGuid().NotNullable()
|
||||
.WithColumn(nameof(Course.CreatedOnUtc))
|
||||
.AsDateTime().NotNullable()
|
||||
.WithColumn(nameof(Course.UpdatedOnUtc))
|
||||
.AsDateTime().NotNullable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Data.Extensions;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Data.Mapping.Builders
|
||||
{
|
||||
public class CourseProgressBuilder : NopEntityBuilder<CourseProgress>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(CourseProgress.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(CourseProgress.CourseId))
|
||||
.AsInt32().NotNullable().ForeignKey<Course>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(CourseProgress.StudentId))
|
||||
.AsInt32().NotNullable().ForeignKey<Customer>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(CourseProgress.LimitedToStores))
|
||||
.AsBoolean().NotNullable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Data.Extensions;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Mapping.Builders
|
||||
{
|
||||
public class LessonAttachmentBuilder : NopEntityBuilder<LessonAttachment>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(LessonAttachment.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(LessonAttachment.LessonId))
|
||||
.AsInt32().Nullable().ForeignKey<Lesson>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(LessonAttachment.AttachmentId))
|
||||
.AsInt32().Nullable().ForeignKey<Attachment>(onDelete: System.Data.Rule.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Data.Extensions;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Media;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Data.Mapping.Builders
|
||||
{
|
||||
public class LessonBuilder : NopEntityBuilder<Lesson>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(Lesson.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(Lesson.Name))
|
||||
.AsString(500).NotNullable()
|
||||
.WithColumn(nameof(Lesson.LessonTypeId))
|
||||
.AsInt32().NotNullable()
|
||||
.WithColumn(nameof(Lesson.LessonContents))
|
||||
.AsString(4000).Nullable()
|
||||
.WithColumn(nameof(Lesson.VideoId))
|
||||
.AsInt32().Nullable().ForeignKey<Video>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Lesson.PictureId))
|
||||
.AsInt32().Nullable().ForeignKey<Picture>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Lesson.DocumentId))
|
||||
.AsInt32().Nullable().ForeignKey<Attachment>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Lesson.LimitedToStores))
|
||||
.AsBoolean().NotNullable()
|
||||
.WithColumn(nameof(Lesson.IsFreeLesson))
|
||||
.AsBoolean().NotNullable()
|
||||
.WithColumn(nameof(Lesson.InstructorId))
|
||||
.AsInt32().NotNullable().ForeignKey<Customer>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Lesson.InstructorGuid))
|
||||
.AsGuid().NotNullable();
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Data.Extensions;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Data.Mapping.Builders
|
||||
{
|
||||
public class LessonProgressBuilder : NopEntityBuilder<LessonProgress>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(LessonProgress.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(LessonProgress.CourseProgressId))
|
||||
.AsInt32().NotNullable().ForeignKey<CourseProgress>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(LessonProgress.LessonId))
|
||||
.AsInt32().NotNullable().ForeignKey<Lesson>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(LessonProgress.IsCompleted))
|
||||
.AsBoolean().Nullable()
|
||||
.WithColumn(nameof(LessonProgress.CurrentlyAt))
|
||||
.AsInt32().Nullable()
|
||||
.WithColumn(nameof(LessonProgress.LimitedToStores))
|
||||
.AsBoolean().NotNullable();
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Mapping.Builders
|
||||
{
|
||||
public class PluginBuilder : NopEntityBuilder<CustomTable>
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Nop.Data.Extensions;
|
||||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
|
||||
public class SectionBuilder : NopEntityBuilder<Section>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(Section.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(Section.CourseId))
|
||||
.AsInt32().NotNullable().ForeignKey<Course>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Section.Title))
|
||||
.AsString(500).NotNullable()
|
||||
.WithColumn(nameof(Section.DisplayOrder))
|
||||
.AsInt32().NotNullable()
|
||||
.WithColumn(nameof(Section.IsFree))
|
||||
.AsBoolean().NotNullable()
|
||||
.WithColumn(nameof(Section.LimitedToStores))
|
||||
.AsBoolean().NotNullable();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Nop.Data.Extensions;
|
||||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
|
||||
public class SectionLessonBuilder : NopEntityBuilder<SectionLesson>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(SectionLesson.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(SectionLesson.CourseId))
|
||||
.AsInt32().NotNullable().ForeignKey<Course>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(SectionLesson.SectionId))
|
||||
.AsInt32().NotNullable().ForeignKey<Section>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(SectionLesson.LessonId))
|
||||
.AsInt32().NotNullable().ForeignKey<Lesson>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(SectionLesson.DisplayOrder))
|
||||
.AsInt32().NotNullable()
|
||||
.WithColumn(nameof(SectionLesson.IsFreeLesson))
|
||||
.AsBoolean().NotNullable()
|
||||
.WithColumn(nameof(SectionLesson.LimitedToStores))
|
||||
.AsBoolean().NotNullable();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using Nop.Data.Extensions;
|
||||
using FluentMigrator.Builders.Create.Table;
|
||||
using Nop.Data.Mapping.Builders;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
public class VideoBuilder : NopEntityBuilder<Video>
|
||||
{
|
||||
public override void MapEntity(CreateTableExpressionBuilder table)
|
||||
{
|
||||
table.WithColumn(nameof(Video.Id))
|
||||
.AsInt32().PrimaryKey().Identity()
|
||||
.WithColumn(nameof(Video.VideoTypeId))
|
||||
.AsInt32().NotNullable()
|
||||
.WithColumn(nameof(Video.VideoIdFromProvider))
|
||||
.AsString(4000).NotNullable()
|
||||
.WithColumn(nameof(Video.Duration))
|
||||
.AsInt32().NotNullable()
|
||||
.WithColumn(nameof(Video.VideoUrl))
|
||||
.AsString(4000).Nullable()
|
||||
.WithColumn(nameof(Video.VideoEmbedCode))
|
||||
.AsString(4000).Nullable()
|
||||
.WithColumn(nameof(Video.InstructorGuid))
|
||||
.AsGuid().NotNullable()
|
||||
.WithColumn(nameof(Video.InstructorId))
|
||||
.AsInt32().NotNullable().ForeignKey<Customer>(onDelete: System.Data.Rule.None)
|
||||
.WithColumn(nameof(Video.LimitedToStores))
|
||||
.AsBoolean().NotNullable();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using Nop.Data.Mapping;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Mapping
|
||||
{
|
||||
public partial class NameCompatibility : INameCompatibility
|
||||
{
|
||||
public Dictionary<Type, string> TableNames => new Dictionary<Type, string>()
|
||||
{
|
||||
{typeof(Course),"SimpleLMS_Course" },
|
||||
{typeof(Video),"SimpleLMS_Video" },
|
||||
{typeof(Attachment),"SimpleLMS_Attachment" },
|
||||
{typeof(SectionLesson),"SimpleLMS_SectionLesson" },
|
||||
{typeof(Section),"SimpleLMS_Section" },
|
||||
{typeof(LessonProgress),"SimpleLMS_LessonProgress" },
|
||||
{typeof(Lesson),"SimpleLMS_Lesson" },
|
||||
{typeof(CourseProgress),"SimpleLMS_CourseProgress" }
|
||||
//{typeof(LessonAttachment),"SimpleLMS_LessonAttachment" }
|
||||
};
|
||||
|
||||
public Dictionary<(Type, string), string> ColumnName => new Dictionary<(Type, string), string>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using FluentMigrator;
|
||||
using Nop.Data.Extensions;
|
||||
using Nop.Data.Mapping;
|
||||
using Nop.Data.Migrations;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Migrations
|
||||
{
|
||||
[NopMigration("2022/06/11 16:21:00:0000000","4.5",UpdateMigrationType.Data ,MigrationProcessType.Installation)]
|
||||
public class SchemaMigration : Migration
|
||||
{
|
||||
public SchemaMigration()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collect the UP migration expressions
|
||||
/// </summary>
|
||||
public override void Up()
|
||||
{
|
||||
|
||||
if(!Schema.Table(NameCompatibilityManager.GetTableName(typeof(Course))).Exists())
|
||||
{
|
||||
Create.TableFor<Course>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(CourseProgress))).Exists())
|
||||
{
|
||||
Create.TableFor<CourseProgress>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(Attachment))).Exists())
|
||||
{
|
||||
Create.TableFor<Attachment>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(Video))).Exists())
|
||||
{
|
||||
Create.TableFor<Video>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(Lesson))).Exists())
|
||||
{
|
||||
Create.TableFor<Lesson>();
|
||||
}
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(LessonAttachment))).Exists())
|
||||
{
|
||||
Create.TableFor<LessonAttachment>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(LessonProgress))).Exists())
|
||||
{
|
||||
Create.TableFor<LessonProgress>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(Section))).Exists())
|
||||
{
|
||||
Create.TableFor<Section>();
|
||||
}
|
||||
|
||||
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(SectionLesson))).Exists())
|
||||
{
|
||||
Create.TableFor<SectionLesson>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public record CourseDetail : BaseNopEntityModel
|
||||
{
|
||||
public CourseDetail()
|
||||
{
|
||||
Sections = new List<SectionDetail>();
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.ProductName")]
|
||||
public string ParentProductName { get; set; }
|
||||
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.ProductName")]
|
||||
public int ProductId { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Sections")]
|
||||
public int SectionsTotal { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Lessons")]
|
||||
public int LessonsTotal { get; set; }
|
||||
|
||||
public IList<SectionDetail> Sections { get; set; }
|
||||
|
||||
public int TotalLessons { get { return Sections.SelectMany(p => p.Lessons).ToList().Count; } }
|
||||
|
||||
public int CompletedLessons { get { return Sections.SelectMany(p => p.Lessons.Where(q => q.IsCompleted)).Count(); } }
|
||||
|
||||
public int Progress { get { return (TotalLessons > 0 ? CompletedLessons * 100 / TotalLessons : 0); } }
|
||||
|
||||
public int CurrentSection { get; set; }
|
||||
public int CurrentLesson { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.UI.Paging;
|
||||
using Nop.Web.Models.Common;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public partial record CourseOverviewListModel : BasePageableModel
|
||||
{
|
||||
public CourseOverviewListModel()
|
||||
{
|
||||
Courses = new List<CourseOverviewModel>();
|
||||
}
|
||||
|
||||
public IList<CourseOverviewModel> Courses { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Nop.Plugin.Misc.SimpleLMS.Areas.Admin.Models;
|
||||
using Nop.Web.Areas.Admin.Models.Catalog;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public partial record CourseOverviewModel : BaseNopEntityModel
|
||||
{
|
||||
|
||||
public CourseOverviewModel()
|
||||
{
|
||||
CourseProgress = 0;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string ParentProductName { get; set; }
|
||||
public string ProductMainImage { get; set; }
|
||||
public int CourseProgress { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public partial record CourseSearchModel : BaseSearchModel
|
||||
{
|
||||
public CourseSearchModel()
|
||||
{
|
||||
CourseOverviewList = new CourseOverviewListModel();
|
||||
PageNumber = 1;
|
||||
Length = 6;
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.SearchCourseName")]
|
||||
public string SearchCourseName { get; set; }
|
||||
|
||||
public CourseOverviewListModel CourseOverviewList { get; set; }
|
||||
|
||||
public int PageNumber { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public record LessonDetail : BaseNopEntityModel
|
||||
{
|
||||
public LessonDetail()
|
||||
{
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.LessonType")]
|
||||
public LessonType LessonType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.LessonContents")]
|
||||
public string LessonContents { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.DisplayOrder")]
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Instructor")]
|
||||
public int InstructorName { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.SectionName")]
|
||||
public int SectionId { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.IsFree")]
|
||||
public bool IsFreeLesson { get; set; }
|
||||
|
||||
public VideoDetail Video { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoType")]
|
||||
public VideoType VideoType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoIdFromProvider")]
|
||||
public string VideoIdFromProvider { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Duration")]
|
||||
public int Duration { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoUrl")]
|
||||
public string VideoUrl { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoEmbedCode")]
|
||||
public string VideoEmbedCode { get; set; }
|
||||
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.CourseName")]
|
||||
public int CourseId { get; internal set; }
|
||||
|
||||
public bool IsCompleted { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public record LessonStatusModel : BaseNopEntityModel
|
||||
{
|
||||
public LessonStatusModel()
|
||||
{
|
||||
}
|
||||
|
||||
public int CourseId { get; set; }
|
||||
public int SectionId { get; set; }
|
||||
public int LessonId { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public int CurrentlyAt { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public record SectionDetail : BaseNopEntityModel
|
||||
{
|
||||
public SectionDetail()
|
||||
{
|
||||
Lessons = new List<LessonDetail>();
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Name")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.DisplayOrder")]
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.CourseName")]
|
||||
public string CourseName { get; set; }
|
||||
|
||||
public int CourseId { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.IsFree")]
|
||||
public bool IsFree { get; set; }
|
||||
|
||||
public IList<LessonDetail> Lessons { get; set; }
|
||||
|
||||
public int TotalLessons { get { return Lessons.Count; } }
|
||||
|
||||
public int CompletedLessons { get { return Lessons.Where(p => p.IsCompleted).Count(); } }
|
||||
|
||||
public int Duration { get
|
||||
{
|
||||
return
|
||||
Lessons.Select(p=>p.Duration).Sum();
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using Nop.Plugin.Misc.SimpleLMS.Domains;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.SimpleLMS.Models
|
||||
{
|
||||
public record VideoDetail : BaseNopEntityModel
|
||||
{
|
||||
public VideoDetail()
|
||||
{
|
||||
}
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoType")]
|
||||
public VideoType VideoType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoIdFromProvider")]
|
||||
public string VideoIdFromProvider { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.Duration")]
|
||||
public int Duration { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoUrl")]
|
||||
public string VideoUrl { get; set; }
|
||||
|
||||
[NopResourceDisplayName("SimpleLMS.VideoEmbedCode")]
|
||||
public string VideoEmbedCode { get; set; }
|
||||
|
||||
|
||||
public string TimeCode { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputPath>..\..\Presentation\Nop.Web\Plugins\Misc.SimpleLMS</OutputPath>
|
||||
<OutDir>$(OutputPath)</OutDir>
|
||||
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
|
||||
You need to set this parameter to true if your plugin has a nuget package
|
||||
to ensure that the dlls copied from the NuGet cache to the output of your project-->
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<AssemblyName>Nop.Plugin.Misc.SimpleLMS</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Attachment.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.CourseContent.cshtml~RF78d26d16.TMP" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Lesson.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Lessons.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Lesson_CreateOrEdit.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Section.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Sections.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Section_CreateOrEdit.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Sortable.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\Course\_CreateOrUpdate.Video.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\_ViewStart.cshtml" />
|
||||
<None Remove="Mapping\Builders\ve-F2D.tmp" />
|
||||
<None Remove="plugin.json" />
|
||||
<None Remove="Views\Customer\CoursesDetails.cshtml" />
|
||||
<None Remove="Views\Customer\_YouTubeVideo.cshtml" />
|
||||
<None Remove="FluentValidation.AspNetCore" />
|
||||
<None Remove="Areas\Admin\Infrastructure\" />
|
||||
<None Remove="Events\" />
|
||||
<None Remove="Views\Customer\_MyCourseBox.cshtml" />
|
||||
<None Remove="Views\Customer\_MyCourseList.cshtml" />
|
||||
<None Remove="Views\Customer\_CourseDetailRoot.cshtml" />
|
||||
<None Remove="logo.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Sortable.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Lessons.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Lesson_CreateOrEdit.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Sections.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Section_CreateOrEdit.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Attachment.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Video.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Lesson.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\Course\_CreateOrUpdate.Section.cshtml">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\_ViewStart.cshtml">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Customer\CoursesDetails.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Customer\_LessonContent.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Customer\_MyCourseList.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Customer\_CourseDetailRoot.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="logo.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ClearPluginAssemblies Include="$(MSBuildProjectDirectory)\..\..\Build\ClearPluginAssemblies.proj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Areas\Admin\Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Areas\Admin\Extensions\" />
|
||||
<Folder Include="Content\Admin\js\" />
|
||||
<Folder Include="Extensions\" />
|
||||
<Folder Include="Factories\" />
|
||||
<Folder Include="Areas\Admin\Infrastructure\" />
|
||||
<Folder Include="Events\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.6" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="10.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Libraries\Nop.Core\Nop.Core.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\Nop.Data\Nop.Data.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Presentation\Nop.Web\Nop.Web.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Areas\Admin\Views\Course\Create.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Course\Edit.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Course\List.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Course\_CreateOrUpdate.CourseContent.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Course\_CreateOrUpdate.Info.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Course\_CreateOrUpdate.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Settings\Configure.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Settings\_CreateOrUpdate.VdoCipher.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Settings\_CreateOrUpdate.Youtube.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\Settings\_CreateOrUpdate.Vimeo.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Areas\Admin\Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\css\simplelms.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\ajax-loader-small.gif">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\close.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\loading.gif">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\text.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\ui-bg_flat_75_ffffff_40x100.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\ui-icons_222222_256x240.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Admin\images\video-player.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Public\Productstyle.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Views\Customer\List.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Views\Customer\_CourseDetailRoot.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- This target execute after "Build" target -->
|
||||
<ItemGroup>
|
||||
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Infrastructure\ViewLocationExpander.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Target Name="NopTarget" AfterTargets="Build">
|
||||
<MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(MSBuildProjectDirectory)\$(OutDir)" Targets="NopClear" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ShowAllFiles>true</ShowAllFiles>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||