SimpleLMS init

This commit is contained in:
l.gabrysiak 2024-08-21 16:45:47 +02:00
parent a61f230d0a
commit 990b303eea
152 changed files with 23829 additions and 0 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/Plugins/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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);
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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()
{
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 + '&sectionId=' + 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>

View File

@ -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>

View File

@ -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>&nbsp;
<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>

View File

@ -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>

View File

@ -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)
}

View File

@ -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>&nbsp;
<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>&nbsp;
<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>&nbsp;
<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>

View File

@ -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>

View File

@ -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)
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -0,0 +1,3 @@
@{
Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
}

View File

@ -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();
}
}
}

Binary file not shown.

View File

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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;
}
}

View File

@ -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")
});
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
namespace Nop.Plugin.Misc.SimpleLMS.Domains
{
public enum AttachmentType
{
WordDoc = 10,
Pdf = 20,
Ppt = 30,
Image = 100,
Others = 110
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,9 @@
using Nop.Core;
namespace Nop.Plugin.Misc.SimpleLMS.Domains
{
public partial class CustomTable : BaseEntity
{
}
}

View File

@ -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; }
}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,11 @@
namespace Nop.Plugin.Misc.SimpleLMS.Domains
{
public enum LessonType
{
Text = 10,
Image = 20,
Document = 30,
Video = 40,
Questionire = 50,
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@

namespace Nop.Plugin.Misc.SimpleLMS.Domains
{
public enum VideoType
{
Hosted = 10,
Youtube = 20,
VdoCipher = 30,
Vimeo = 40,
AWS = 50
}
}

View File

@ -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")
}); ;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
;
}
}
}

View File

@ -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();
;
}
}
}

View File

@ -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
}
}

View File

@ -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();
;
}
}

View File

@ -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();
;
}
}

View File

@ -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();
;
}
}

View File

@ -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>();
}
}

View File

@ -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>();
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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();
} }
}
}

View File

@ -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; }
}
}

View File

@ -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>

View File

@ -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>

Some files were not shown because too many files have changed in this diff Show More