Lee Morgan 5 лет назад
Родитель
Сommit
29e1a8126e

+ 2 - 0
.gitignore

@@ -1,3 +1,5 @@
 /node_modules
 /content/*
 *.swp
+/thumbNails/*
+/documents/*

+ 4 - 0
app.js

@@ -5,6 +5,8 @@ const compression = require("compression");
 const mongoose = require("mongoose");
 const https = require("https");
 const fs = require("fs");
+const bodyParser = require("body-parser");
+const fileUpload = require("express-fileupload");
 
 const app = express();
 htmlCreator(app);
@@ -41,6 +43,8 @@ mongoose.connect("mongodb://127.0.0.1/leemorgan", mongooseOptions);
 app.use(express.static(__dirname + "/content"));
 app.use(compression());
 app.use(express.json());
+app.use(bodyParser.urlencoded({extended: false}))
+app.use(fileUpload({limits: { fileSize: 1024 * 1024}}));
 require("./routes.js")(app);
 
 if(process.env.NODE_ENV === "production") httpsServer.listen(process.env.HTTPS_PORT);

+ 153 - 0
controllers/learn.js

@@ -0,0 +1,153 @@
+const Uploader = require("../models/uploader.js");
+const Course = require("../models/course.js");
+const Lecture = require("../models/lecture.js");
+
+module.exports = {
+    /*
+    POST: create a new course
+    req.body = {
+        uploaderId: String,
+        password: String,
+        title: String
+        thumbNail: String,
+        description: String
+    }
+    redirect to home
+    */
+    createCourse: function(req, res){
+        Uploader.findOne({_id: req.body.uploaderId})
+            .then((uploader)=>{
+                if(uploader === null) throw "uploader";
+                if(req.body.password !== uploader.password) throw "pass";
+                let imgString = `/thumbNails/${req.files.thumbNail.name}`;
+                req.files.thumbNail.mv(`${__dirname}/..${imgString}`);
+
+                let course = new Course({
+                    owner: uploader._id,
+                    title: req.body.title,
+                    thumbNail: imgString,
+                    description: req.body.description
+                });
+
+                return course.save();
+            })
+            .then((course)=>{
+                return res.redirect("/");
+            })
+            .catch((err)=>{
+                if(err === "uploader") return res.json("Uploader doesn't exist");
+                if(err === "pass") return res.json("Incorrect password");
+                return res.json("Something went wrong on the backend");
+            });
+    },
+
+    /*
+    POST: create a new lecture
+    req.body = {
+        uploader: String
+        password: String
+        course: String
+        title: String
+        video: String
+        description: String
+        exercises: [String]
+        documents: [{
+            title: String
+            link: String
+        }]
+    }
+    redirects to home
+    */
+    createLecture: function(req, res){
+        Course.findOne({_id: req.body.course})
+            .populate("owner")
+            .then((course)=>{
+                if(course === null) throw "noCourse";
+                if(req.body.uploader !== course.owner?._id.toString()) throw "badOwner";
+                if(req.body.password !== course.owner.password) throw "badPass";
+
+                let exercises = req.body.exercises.split("~");
+                exercises.splice(exercises.length - 1, 1);
+
+                let lecture = new Lecture({
+                    course: course._id,
+                    title: req.body.title,
+                    video: req.body.video,
+                    description: req.body.description,
+                    exercises: exercises,
+                    documents: []
+                });
+
+                let files = req.files.documents;
+                if(files.length === undefined){
+                    let fileString = `/documents/${files.name}`;
+                    files.mv(`${__dirname}/..${fileString}`);
+                    lecture.documents.push(fileString);
+                }else{
+                    for(let i = 0; i < files.length; i++){
+                        let fileString = `/documents/${files[i].name}`;
+                        files[i].mv(`${__dirname}/..${fileString}`);
+                        lecture.documents.push(fileString);
+                    }
+                }
+
+                return lecture.save();
+            })
+            .then((lecture)=>{
+                return res.redirect("/");
+            })
+            .catch((err)=>{
+                if(err === "noCourse") return res.json("Course does not exist");
+                if(err === "badOwner") return res.json("You do not own this course");
+                if(err === "badPass") return res.json("Incorrect password");
+                return res.redirect("/");
+            });
+    },
+
+    getCourses: function(req, res){
+        Course.find()
+            .then((courses)=>{
+                return res.json(courses);
+            })
+            .catch((err)=>{
+                return res.json("Could not retrieve courses");
+            });
+    },
+
+    /*
+    GET: get a list of lectures from a course
+    req.params.id = String (course id)
+    response = {
+        course: Course,
+        lectures: [Lecture]
+    }
+    */
+    getLectures: function(req, res){
+        let course = Course.findOne({_id: req.params.id});
+        let lectures = Lecture.find({course: req.params.id});
+
+        Promise.all([course, lectures])
+            .then((response)=>{
+                return res.json({course: response[0], lectures: response[1]});
+            })
+            .catch((err)=>{
+                return res.json("Could not retrieve lectures");
+            });
+    },
+
+    /*
+    GET: get data for a single lecture
+    req.params.id = String (lecture id)
+    response = Lecture
+    */
+    getLecture: function(req, res){
+        Lecture.findOne({_id: req.params.id})
+            .populate("course")
+            .then((lecture)=>{
+                return res.json(lecture);
+            })
+            .catch((err)=>{
+                return res.json("Could not retrieve lecture");
+            });
+    }
+}

+ 23 - 0
models/course.js

@@ -0,0 +1,23 @@
+const mongoose = require("mongoose");
+
+const CourseSchema = new mongoose.Schema({
+    owner: {
+        type: mongoose.Schema.Types.ObjectId,
+        ref: "Uploader",
+        required: true
+    },
+    title: {
+        type: String,
+        required: true
+    },
+    thumbNail: {
+        type: String,
+        required: true
+    },
+    description: {
+        type: String,
+        required: true
+    }
+});
+
+module.exports = mongoose.model("Course", CourseSchema);

+ 30 - 0
models/lecture.js

@@ -0,0 +1,30 @@
+const mongoose = require("mongoose");
+
+const LectureSchema = new mongoose.Schema({
+    course: {
+        type: mongoose.Schema.Types.ObjectId,
+        ref: "Course",
+        required: true
+    },
+    title: {
+        type: String,
+        required: true
+    },
+    video: {
+        type: String,
+        required: true
+    },
+    description: {
+        type: String,
+        required: true,
+    },
+    documents: [String],
+    exercises: [String],
+    url: String,
+    date: {
+        type: Date,
+        default: new Date()
+    }
+});
+
+module.exports = mongoose.model("Lecture", LectureSchema);

+ 8 - 0
models/uploader.js

@@ -0,0 +1,8 @@
+const mongoose = require("mongoose");
+
+const UploaderSchema = new mongoose.Schema({
+    name: String,
+    password: String
+});
+
+module.exports = mongoose.model("Uploader", UploaderSchema);

+ 72 - 0
package-lock.json

@@ -8,8 +8,10 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "body-parser": "^1.19.0",
         "compression": "^1.7.4",
         "express": "^4.17.1",
+        "express-fileupload": "^1.2.1",
         "mongoose": "^5.12.3"
       }
     },
@@ -94,6 +96,17 @@
         "node": ">=0.6.19"
       }
     },
+    "node_modules/busboy": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz",
+      "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==",
+      "dependencies": {
+        "dicer": "0.3.0"
+      },
+      "engines": {
+        "node": ">=4.5.0"
+      }
+    },
     "node_modules/bytes": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -204,6 +217,17 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
     },
+    "node_modules/dicer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
+      "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
+      "dependencies": {
+        "streamsearch": "0.1.2"
+      },
+      "engines": {
+        "node": ">=4.5.0"
+      }
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -270,6 +294,17 @@
         "node": ">= 0.10.0"
       }
     },
+    "node_modules/express-fileupload": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.2.1.tgz",
+      "integrity": "sha512-fWPNAkBj+Azt9Itmcz/Reqdg3LeBfaXptDEev2JM8bCC0yDptglCnlizhf0YZauyU5X/g6v7v4Xxqhg8tmEfEA==",
+      "dependencies": {
+        "busboy": "^0.3.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/finalhandler": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -768,6 +803,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/streamsearch": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+      "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -899,6 +942,14 @@
       "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
       "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
     },
+    "busboy": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz",
+      "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==",
+      "requires": {
+        "dicer": "0.3.0"
+      }
+    },
     "bytes": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -984,6 +1035,14 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
     },
+    "dicer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
+      "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
+      "requires": {
+        "streamsearch": "0.1.2"
+      }
+    },
     "ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -1041,6 +1100,14 @@
         "vary": "~1.1.2"
       }
     },
+    "express-fileupload": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.2.1.tgz",
+      "integrity": "sha512-fWPNAkBj+Azt9Itmcz/Reqdg3LeBfaXptDEev2JM8bCC0yDptglCnlizhf0YZauyU5X/g6v7v4Xxqhg8tmEfEA==",
+      "requires": {
+        "busboy": "^0.3.1"
+      }
+    },
     "finalhandler": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -1413,6 +1480,11 @@
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
       "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
     },
+    "streamsearch": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+      "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+    },
     "string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",

+ 2 - 0
package.json

@@ -9,8 +9,10 @@
   "author": "Lee Morgan",
   "license": "ISC",
   "dependencies": {
+    "body-parser": "^1.19.0",
     "compression": "^1.7.4",
     "express": "^4.17.1",
+    "express-fileupload": "^1.2.1",
     "mongoose": "^5.12.3"
   }
 }

+ 22 - 0
routes.js

@@ -1,5 +1,6 @@
 const writing = require("./controllers/writing.js");
 const travel = require("./controllers/travel.js");
+const learn = require("./controllers/learn.js");
 
 module.exports = function(app){
     let views = `${__dirname}/views`;
@@ -21,6 +22,8 @@ module.exports = function(app){
     app.get("/images/sudoku", (req, res)=>{res.sendFile(`${views}/images/sudoku.png`)});
     app.get("/images/birthday", (req, res)=>{res.sendFile(`${views}/images/birthday.jpg`)});
     app.get("/images/market", (req, res)=>{res.sendFile(`${views}/images/market.jpeg`)});
+    app.get("/images/web", (req, res)=>{res.sendFile(`${views}/images/webIcon.jpg`)});
+    app.get("/images/html5", (req, res)=>{res.sendFile(`${views}/images/html5.png`)});
 
     //SUDOKU
     app.get("/sudoku", (req, res)=>{res.sendFile(`${views}/sudoku/index.html`)});
@@ -37,4 +40,23 @@ module.exports = function(app){
     app.get("/travel/directories", travel.listDirectories);
     app.get("/travel/images/*", travel.getImages);
     app.get("/travel/*", (req, res)=>res.sendFile(`${views}/travel/index.html`));
+
+    //LEARN
+    app.get("/learn/style", (req, res)=>{res.sendFile(`${views}/learn/index.css`)});
+    
+    app.get("/learn/courses/new", (req, res)=>{res.sendFile(`${views}/learn/newCourse.html`)});
+    app.post("/learn/courses/new", learn.createCourse);
+    app.get("/learn/courses/json", learn.getCourses);
+    app.get("/learn/courses/:id", (req, res)=>{res.sendFile(`${views}/learn/course.html`)});
+    app.get("/learn", (req, res)=>{res.sendFile(`${views}/learn/courses.html`)});
+
+    app.get("/learn/lectures/new", (req, res)=>{res.sendFile(`${views}/learn/newLecture.html`)});
+    app.post("/learn/lectures/new", learn.createLecture);
+    app.get("/learn/lectures/json/:id", learn.getLectures);
+    app.get("/learn/lectures/:id", (req, res)=>(res.sendFile(`${views}/learn/lecture.html`)));
+    app.get("/learn/lectures/json/one/:id", learn.getLecture);
+
+    //CONTENT
+    app.get("/thumbNails/*", (req, res)=>{res.sendFile(`${__dirname}${req.url}`)});
+    app.get("/documents/*", (req, res)=>{res.sendFile(`${__dirname}${req.url}`)});
 }

BIN
views/images/html5.png


BIN
views/images/webIcon.jpg


+ 49 - 0
views/learn/course.html

@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Lee Morgan -course-</title>
+        <link rel="stylesheet" href="/learn/style">
+    </head>
+    <body>
+        <h1 id="title"></h1>
+
+        <div id="lectures" class="cardContainer"></div>
+
+        <template id="template">
+            <a class="card">
+                <img>
+
+                <div>
+                    <h2></h2>
+
+                    <p></p>
+                </div>
+            </a>
+        </template>
+
+        <script>
+            let id = window.location.href.split("/");
+            id = id[id.length-1];
+
+            fetch(`/learn/lectures/json/${id}`)
+                .then(response => response.json())
+                .then((response)=>{
+                    document.getElementById("title").innerText = response.course.title;
+                    let container = document.getElementById("lectures");
+                    let template = document.getElementById("template").content.children[0];
+
+                    for(let i = 0; i < response.lectures.length; i++){
+                        let card = template.cloneNode(true);
+                        card.href = `/learn/lectures/${response.lectures[i]._id}`;
+                        card.children[0].src = response.course.thumbNail;
+                        card.children[0].alt = `${response.course.thumbNail} thumbnail`;
+                        card.children[1].children[0].innerText = response.lectures[i].title;
+                        card.children[1].children[1].innerText = response.lectures[i].description;
+                        container.appendChild(card)
+                    }
+                })
+                .catch((err)=>{});
+        </script>
+    </body>
+</html>

+ 45 - 0
views/learn/courses.html

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Lee Morgan -courses-</title>
+        <link rel="stylesheet" href="/learn/style">
+    </head>
+    <body>
+        <h1>Courses</h1>
+
+        <div id="container" class="cardContainer"></div>
+
+        <template id="template">
+            <a class="card">
+                <img>
+
+                <div>
+                    <h2></h2>
+
+                    <p></p>
+                </div>
+            </a>
+        </template>
+
+        <script>
+            fetch("/learn/courses/json")
+                .then(response => response.json())
+                .then((response)=>{
+                    let container = document.getElementById("container");
+                    let template = document.getElementById("template").content.children[0];
+
+                    for(let i = 0; i < response.length; i++){
+                        let course = template.cloneNode(true);
+                        course.href = `/learn/courses/${response[i]._id}`;
+                        course.children[0].src = response[i].thumbNail;
+                        course.children[0].alt = `${response[i].title} thumbnail`;
+                        course.children[1].children[0].innerText = response[i].title;
+                        course.children[1].children[1].innerText = response[i].description;
+                        container.appendChild(course);
+                    }
+                })
+                .catch((err)=>{});
+        </script>
+    </body>
+</html>

+ 93 - 0
views/learn/index.css

@@ -0,0 +1,93 @@
+*{margin:0;padding:0;}
+
+body{
+    background: rgb(196, 198, 192);
+    padding: 25px;
+}
+
+h1{
+    text-align: center;
+}
+
+.cardContainer{
+    width: 90%;
+    padding: 25px;
+}
+
+.card{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin: 15px auto;
+    height: 150px;
+    background: white;
+    padding-right:15px;
+    text-decoration: none;
+}
+
+    .card:hover{
+        box-shadow: 0 0 3px black;
+    }
+
+    .card div{
+        align-items: center;
+        color: black;
+        height: 100%;
+        flex-grow: 2;
+    }
+
+    .card img{
+        height: 100%;
+    }
+
+    .card h2{
+        text-align: center;
+        margin: 10px 0;
+    }
+
+    .card p{
+        text-align:center;
+        width: 75%;
+        margin: auto;
+    }
+
+#lectureBody{
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+    #video{
+        margin: 50px;
+    }
+
+    #lectureData{
+        margin-right: auto;
+        padding-left: 100px;
+        width: 100%;
+    }
+
+        #lectureData > *{
+            margin: 15px 0;
+        }
+
+        #lectureData h2{
+            margin: 0;
+        }
+
+        #description{
+            background: white;
+            padding: 25px;
+            width: 50%;
+        }
+
+        #documents{
+            display: flex;
+            flex-direction: column;
+        }
+
+        #exercises, #documents{
+            padding-left: 15px;
+            margin-top: 0;
+        }
+

+ 80 - 0
views/learn/lecture.html

@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Lee Morgan -lecture-</title>
+        <link rel="stylesheet" href="/learn/style">
+    </head>
+    <body id="lectureBody">
+        <h1 id="title"></h1>
+
+        <h3 id="course"></h3>
+
+        <h6 id="date"></h6>
+
+        <iframe id="video"></iframe>
+
+        <div id="lectureData">
+            <h2>Details</h2>
+            <p id="description"></p>
+            
+            <h2>Resources</h2>
+            <div id="documents"></div>
+            
+            <h2>Exercises</h2>
+            <ol id="exercises"></ol>
+        </div>
+            
+        <script>
+            let video = document.getElementById("video");
+
+            let sizeIframe = ()=>{
+                let width = document.getElementById("lectureBody").clientWidth;
+                let multiplier = (width * 0.9) / video.contentWindow.document.body.scrollWidth;
+                video.width = video.contentWindow.document.body.scrollWidth * multiplier;
+                video.height = video.contentWindow.document.body.scrollHeight * multiplier;
+            }
+
+            video.onload = sizeIframe();
+            let id = window.location.href.split("/");
+            id = id[id.length-1];
+
+            fetch(`/learn/lectures/json/one/${id}`)
+                .then(response => response.json())
+                .then((response)=>{
+                    let date = new Date(response.date);
+                    let dateOptions = {
+                        day: "numeric",
+                        month: "long",
+                        year: "numeric"
+                    }
+
+                    document.getElementById("title").innerText = response.title;
+                    document.getElementById("course").innerText = response.course.title;
+                    document.getElementById("date").innerText = date.toLocaleDateString("en-us", dateOptions);
+                    video.src = response.video;
+                    document.getElementById("description").innerText = response.description;
+                    
+                    let documents = document.getElementById("documents");
+                    for(let i = 0; i < response.documents.length; i++){
+                        let name = response.documents[i].split("/");
+                        name = name[name.length-1];
+
+                        let a = document.createElement("a");
+                        a.href = response.documents[i];
+                        a.innerText = name;
+                        documents.appendChild(a);
+                    }
+
+                    let exercises = document.getElementById("exercises");
+                    for(let i = 0; i < response.exercises.length; i++){
+                        let li = document.createElement("li");
+                        li.classList.add("liPad");
+                        li.innerText = response.exercises[i];
+                        exercises.appendChild(li);
+                    }
+                })
+                .catch((err)=>{});
+        </script>
+    </body>
+</html>

+ 48 - 0
views/learn/newCourse.html

@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Create Course</title>
+        <style>
+            form{
+                display: flex;
+                flex-direction: column;
+            }
+
+            form > *{
+                margin: 10px 0;
+            }
+
+            input[type=submit]{
+                max-width: 250px;
+            }
+        </style>
+    </head>
+    <body>
+        <h1>Enter course details</h1>
+
+        <form action="/learn/courses/new" method="post" encType="multipart/form-data">
+            <label>Uploader ID
+                <input name="uploaderId" type="text" required>
+            </label>
+
+            <label>Password
+                <input name="password" type="password" required>
+            </label>
+
+            <label>Course Title
+                <input name="title" type="text" required>
+            </label>
+
+            <label>ThumbNail (max 1mb)
+                <input name="thumbNail" type="file" required>
+            </label>
+            
+            <label>Description
+                <textarea name="description" minlength="25" required></textarea>
+            </label>
+
+            <input type="submit" value="Submit">
+        </form>
+    </body>
+</html>

+ 133 - 0
views/learn/newLecture.html

@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Lee Morgan -create lecture-</title>
+        <style>
+            form{
+                display: flex;
+                flex-direction: column;
+            }
+
+            form > *{
+                margin: 10px 0;
+            }
+
+            input[type=submit]{
+                max-width: 250px;
+            }
+
+            button{
+                max-width: 250px;
+            }
+
+            #exercises{
+                display: flex;
+                flex-direction: column;
+            }
+
+            #exercises > *{
+                margin: 10px 0;
+            }
+
+            #exercises input{
+                margin-left: 5px;
+            }
+        </style>
+    </head>
+    <body>
+        <h1>Create New Lecture</h1>
+
+        <form
+            id="form"
+            action="/learn/lectures/new"
+            method="post"
+            encType="multipart/form-data"
+        >
+            <label>Uploader ID
+                <input name="uploader" type="text" required>
+            </label>
+
+            <label>Uploader Password
+                <input name="password" type="password" required>
+            </label>
+
+            <label>Course
+                <select id="course" name="course" required></select>
+            </label>
+
+            <label>Title
+                <input name="title" type="text" required>
+            </label>
+
+            <label>Video (Rumble link)
+                <input name="video" type="text" required>
+            </label>
+
+            <label>Description
+                <input name="description" type="text" required>
+            </label>
+            
+            <label>Documents
+                <input name="documents" type="file" multiple>
+            </label>
+
+            <h2>Exercises</h2>
+            <button id="exerciseButton" type="button">Add Exercise</button>
+            
+            <div id="exercises">
+                <label>Exercise 1<input type="text"></label>
+            </div>
+
+            <input id="hidden" name="exercises" type="hidden">
+
+            <input type="submit" value="Submit">
+        </form>
+
+        <script>
+            //Get Available Courses
+            fetch("/learn/courses/json")
+                .then(response => response.json())
+                .then((response)=>{
+                    let select = document.getElementById("course");
+
+                    for(let i = 0; i < response.length; i++){
+                        let option = document.createElement("option");
+                        option.innerText = response[i].title;
+                        option.value = response[i]._id;
+                        select.appendChild(option);
+                    }
+                })
+                .catch((err)=>{});
+
+            //Add exercises
+            let count = 1;
+            let div = document.getElementById("exercises");
+
+            document.getElementById("exerciseButton").onclick = ()=>{
+                count++;
+                let label = document.createElement("label");
+                label.innerText = `Exercise ${count}`;
+                div.appendChild(label);
+
+                let input = document.createElement("input");
+                input.type = "text";
+                label.appendChild(input);
+            }
+
+            //Submit
+            let form = document.getElementById("form");
+            form.onsubmit = ()=>{
+                event.preventDefault();
+                let exercises = "";
+                for(let i = 0; i < div.children.length; i++){
+                    exercises += `${div.children[i].children[0].value}~`;
+                }
+
+                document.getElementById("hidden").value = exercises;
+
+                form.submit();
+            }
+        </script>
+    </body>
+</html>

+ 39 - 2
views/main/index.html

@@ -33,7 +33,7 @@
                     <circle cx="11" cy="11" r="2"></circle>
                 </svg>
 
-                <p>My Writing</p>
+                <p>Blog</p>
             </button>
 
             <button id="linksButton">
@@ -54,6 +54,15 @@
                 
                 <p>Travel</p>
             </button>
+
+            <button id="learnButton">
+                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                    <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
+                    <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
+                </svg>
+
+                <p>Learn</p>
+            </button>
         </div>
 
         <div id="main">
@@ -161,6 +170,8 @@
                     </div>
                 </template>
             </div>
+
+            <div id="learn" class="cardContainer" style="display:none"></div>
         </div>
 
         <script>
@@ -261,6 +272,8 @@
                                     if(contents[i].contents[j].name !== undefined) finalFolder = false;
                                 }
 
+				if(back !== undefined) container.appendChild(back);
+
                                 if(finalFolder === true){
                                     let card = document.createElement("a");
                                     card.classList.add("card");
@@ -315,6 +328,30 @@
                     recurse(response, headContainer);
                 })
                 .catch((err)=>{});
+
+                //Get "learn" content
+                fetch("/learn/courses/json")
+                    .then(response => response.json())
+                    .then((response)=>{
+                        let container = document.getElementById("learn");
+
+                        for(let i = 0; i < response.length; i++){
+                            let card = document.createElement("a");
+                            card.classList.add("card");
+                            card.href = `/learn/courses/${response[i]._id}`;
+                            container.appendChild(card);
+
+                            let h2 = document.createElement("h2");
+                            h2.innerText = response[i].title;
+                            card.appendChild(h2);
+
+                            let img = document.createElement("img");
+                            img.src = response[i].thumbNail;
+                            img.alt = `${response[i].title} thumbnail`;
+                            card.appendChild(img);
+                        }
+                    })
+                    .catch((err)=>{});
         </script>
     </body>
-</html>
+</html>