| @ -0,0 +1,129 @@ | |||
| /** | |||
| * File which deals with adding and removing downloads from | |||
| * the admin section of the website. | |||
| * | |||
| * @author Jeffery Russell 6-30-18 | |||
| */ | |||
| /** Whiskers template file */ | |||
| const TEMPLATE_FILE = "admin/adminDownloads.html"; | |||
| const includes = require('../includes/includes.js'); | |||
| //updates db | |||
| const sql = require('../utils/sql'); | |||
| //parses post data | |||
| const qs = require('querystring'); | |||
| /** | |||
| * Processes post requests from the addDownload form | |||
| * | |||
| * @param postData | |||
| * @returns {*|Promise} | |||
| */ | |||
| const addDownloadPostData = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const post = qs.parse(postData); | |||
| if(post.add_download) | |||
| { | |||
| sql.addDownload(post.add_download_name, post.add_download_file) | |||
| .then(function() | |||
| { | |||
| resolve(); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| } | |||
| else | |||
| { | |||
| resolve(); | |||
| } | |||
| }); | |||
| }; | |||
| /** | |||
| * Removes a download if requested by the | |||
| * post data from an admin. | |||
| */ | |||
| const removeDownloads = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const post = qs.parse(postData); | |||
| if(post.delete_download) | |||
| { | |||
| sql.removeDownload(post.delete_download).then(function() | |||
| { | |||
| resolve(); | |||
| }).catch(function(err) | |||
| { | |||
| reject(err); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| resolve(); | |||
| } | |||
| }); | |||
| }; | |||
| /** | |||
| * Fetches the download items in the database so that | |||
| * the template engine can use it to display them in | |||
| * a table. | |||
| * | |||
| * @param templateContext-- context item used by whiskers | |||
| * @returns {Promise} | |||
| */ | |||
| const displayDownloads = function(templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getAllDownloads().then(function(downloads) | |||
| { | |||
| templateContext.downloads = downloads; | |||
| resolve(); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** Fetches context information for the template and handles | |||
| * post data for the downloads. | |||
| * | |||
| * @param postData posted by user | |||
| * @param templateContext json object used as the template context | |||
| * @returns {Promise} renders the template used for this page | |||
| */ | |||
| main: function(postData, templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([includes.fetchTemplate(TEMPLATE_FILE), | |||
| addDownloadPostData(postData), | |||
| removeDownloads(postData), | |||
| displayDownloads(templateContext)]).then(function(template) | |||
| { | |||
| resolve(template[0]); | |||
| }).catch(function(error) | |||
| { | |||
| console.log("error in add downloads.js"); | |||
| reject(error); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,138 @@ | |||
| const TEMPLATE_FILE = "admin/adminHome.html"; | |||
| const includes = require('../includes/includes.js'); | |||
| const sql = require('../utils/sql'); | |||
| const qs = require('querystring'); | |||
| /** | |||
| * Checks for post data regarding adding a new category. | |||
| * If a post is made with add_category, it parses the url-- replaces spaces | |||
| * with dashes -- and calls a insert method on the database | |||
| * | |||
| * @param postData | |||
| * @return {*|Promise} | |||
| */ | |||
| const processPostAddCategory = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const post = qs.parse(postData); | |||
| if(post.add_category) | |||
| { | |||
| const url = post.add_category.split(" ").join("-").toLowerCase(); | |||
| const q = "insert into categories (name, url) values " + | |||
| "('" + post.add_category + "','" + url + "')"; | |||
| if(sql.insert(q) != 0) | |||
| { | |||
| console.log("category added"); | |||
| } | |||
| else | |||
| { | |||
| console.log("error adding category"); | |||
| } | |||
| } | |||
| resolve(""); | |||
| }); | |||
| }; | |||
| /** | |||
| * Displays all the categories in the database | |||
| * @return {*|Promise} | |||
| */ | |||
| const appendCategoriesToTemplate = function(templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getCategories().then(function(categories) | |||
| { | |||
| templateContext.categories = categories; | |||
| resolve(); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| }; | |||
| /** | |||
| * | |||
| * @param postData | |||
| * @return {*|Promise} | |||
| */ | |||
| const processPost = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var post = qs.parse(postData); | |||
| if(post.add_post_name) | |||
| { | |||
| var urls = post.add_post_name; | |||
| urls = urls.split(" ").join("-"); | |||
| urls =urls.toLowerCase(); | |||
| var q = "insert into blog (category_id, picture_url, published, name, url) values "; | |||
| q += "('" + post.add_post_category + "', '" + post.add_post_picture + | |||
| "', '" + post.add_post_date + "', '" + post.add_post_name + "', '" + urls + "')"; | |||
| sql.insert(q).then(function() | |||
| { | |||
| var map = require('../utils/generateSiteMap'); | |||
| map.main(); | |||
| resolve(""); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| } | |||
| else if(post.clear_cache) | |||
| { | |||
| require("../sites/blog.js").clearCache(); | |||
| require("../includes/includes.js").clearCache(); | |||
| } | |||
| else if(post.git_pull) | |||
| { | |||
| const execSync = require('child_process').execSync; | |||
| code = execSync('git pull') | |||
| } | |||
| else | |||
| { | |||
| resolve(""); | |||
| } | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * | |||
| * @param postData posted by user | |||
| * @param templateContext json object used as the template context | |||
| * @returns {Promise} renders the template used for this page | |||
| */ | |||
| main: function(postData, templateContext) | |||
| { | |||
| console.log("called"); | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([includes.fetchTemplate(TEMPLATE_FILE), | |||
| processPostAddCategory(postData), | |||
| appendCategoriesToTemplate(templateContext), | |||
| processPost(postData)]) | |||
| .then(function(template) | |||
| { | |||
| resolve(template[0]); | |||
| }).catch(function(error) | |||
| { | |||
| console.log("error in add downloads.js"); | |||
| reject(error); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,15 +0,0 @@ | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">Add Category</h1> | |||
| <form action="/admin" method ="post" class="p-2"> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_category" required> | |||
| <label>Category</label> | |||
| </div> | |||
| <div class="text-center"> | |||
| <input type="submit" name="submit" value="Add" | |||
| class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <br> | |||
| @ -1,98 +0,0 @@ | |||
| //file io | |||
| const utils = require('../../utils/utils.js'); | |||
| //update db | |||
| const sql = require('../../utils/sql'); | |||
| //parse post data | |||
| const qs = require('querystring'); | |||
| /** | |||
| * Displays all the categories in the database | |||
| * @return {*|Promise} | |||
| */ | |||
| const printCategories = function() | |||
| { | |||
| var html = "<div class=\"blogPost\">" + | |||
| "<h1 class=\"text-center\">Categories</h1>" + | |||
| "<div class=\"\"><table class=\"table table-striped\">" + | |||
| "<thead class=\"thead-dark\">" + | |||
| "<tr>" + | |||
| "<td>Name</td><td>URL</td><td>Edit</td>" + | |||
| "</tr></thead><tbody>"; | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getCategories().then(function(categories) | |||
| { | |||
| categories.forEach(function(c) | |||
| { | |||
| html +="<tr>" + | |||
| "<td>" + c.name + "</td>" + | |||
| "<td>" + c.url + "</td>" + | |||
| "<td>" + c.category_id + "</td>" + | |||
| "</tr>"; | |||
| }); | |||
| resolve(html + "</tbody></table></div></div>"); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| }; | |||
| /** | |||
| * Checks for post data regarding adding a new category. | |||
| * If a post is made with add_category, it parses the url-- replaces spaces | |||
| * with dashes -- and calls a insert method on the database | |||
| * | |||
| * @param postData | |||
| * @return {*|Promise} | |||
| */ | |||
| const processPost = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const post = qs.parse(postData); | |||
| if(post.add_category) | |||
| { | |||
| const url = post.add_category.split(" ").join("-").toLowerCase(); | |||
| const q = "insert into categories (name, url) values " + | |||
| "('" + post.add_category + "','" + url + "')"; | |||
| if(sql.insert(q) != 0) | |||
| { | |||
| console.log("category added"); | |||
| } | |||
| else | |||
| { | |||
| console.log("error adding category"); | |||
| } | |||
| } | |||
| resolve(""); | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| main: function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([utils.include("./admin/category/addCategory.html"), | |||
| printCategories(), | |||
| processPost(postData)]).then(function(html) | |||
| { | |||
| resolve("<div class=\"col-md-6\">" + | |||
| html.join('') + | |||
| "</div></div>"); | |||
| }).catch(function(error) | |||
| { | |||
| console.log("error in addCategory.js"); | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,19 +0,0 @@ | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">Add Download</h1> | |||
| <form action="/admin" method ="post" class="p-2"> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_download_name" required> | |||
| <label>Download Name</label> | |||
| </div> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_download_file" required> | |||
| <label>File name</label> | |||
| </div> | |||
| <div class="text-center"> | |||
| <input type="submit" name="add_download" value="Add Download" | |||
| class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <br> | |||
| @ -1,191 +0,0 @@ | |||
| /** | |||
| * File which deals with adding and removing downloads from | |||
| * the admin section of the website. | |||
| * | |||
| * @author Jeffery Russell 6-30-18 | |||
| */ | |||
| //file IO | |||
| const utils = require('../../utils/utils.js'); | |||
| //updates db | |||
| const sql = require('../../utils/sql'); | |||
| //parses post data | |||
| const qs = require('querystring'); | |||
| /** | |||
| * Processes post requests from the addDownload form | |||
| * | |||
| * @param postData | |||
| * @returns {*|Promise} | |||
| */ | |||
| const addDownloadPostData = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const post = qs.parse(postData); | |||
| if(post.add_download) | |||
| { | |||
| sql.addDownload(post.add_download_name, post.add_download_file) | |||
| .then(function() | |||
| { | |||
| resolve(""); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| } | |||
| else | |||
| { | |||
| resolve(""); | |||
| } | |||
| }); | |||
| }; | |||
| /** | |||
| * Displays the addDownload form the the user | |||
| * | |||
| * @param postData | |||
| * @returns {*|Promise} | |||
| */ | |||
| const addDownload = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([addDownloadPostData(postData), | |||
| utils.include("./admin/downloads/addDownload.html")]).then(function(html) | |||
| { | |||
| resolve("<div class=\"col-md-6\">" + html.join('') + "</div>"); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| }; | |||
| /** | |||
| * Handel form requests from the downloads table | |||
| * | |||
| * @param postData | |||
| * @returns {*|Promise} | |||
| */ | |||
| const displayDownloadsPostData = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const post = qs.parse(postData); | |||
| if(post.delete_download) | |||
| { | |||
| sql.removeDownload(post.delete_download).then(function() | |||
| { | |||
| resolve(postData); | |||
| }).catch(function(err) | |||
| { | |||
| reject(err); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| resolve(postData); | |||
| } | |||
| }); | |||
| }; | |||
| /** | |||
| * Renders a single download row in the downloads table | |||
| * | |||
| * @param download | |||
| * @returns {*|Promise} | |||
| */ | |||
| const renderDownloadRow = function(download) | |||
| { | |||
| return "<tr>" + | |||
| "<td>" + download.name + "</td>" + | |||
| "<td>" + download.file + "</td>" + | |||
| "<td>" + download.download_count + "</td>" + | |||
| "<td><form action=\"/admin\" method =\"post\" >\n" + | |||
| " <input type=\"submit\" name=\"submit\" value=\"Delete\"\n" + | |||
| " class=\"btn btn-secondary\"/>\n" + | |||
| "<input type='hidden' name='delete_download' value='" + | |||
| download.download_id + "'/>"+ | |||
| "</form></td>" + | |||
| "</tr>"; | |||
| }; | |||
| /** | |||
| * Displays all the download information in a table | |||
| * @param postData | |||
| * @returns {*|Promise} | |||
| */ | |||
| const displayDownloads = function(postData) | |||
| { | |||
| var html = "<div class=\"col-md-6\">"; | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| displayDownloadsPostData(postData).then(function() | |||
| { | |||
| html += "<div class='blogPost'>" + | |||
| "<h1 class=\"text-center\">Downloads</h1>" + | |||
| "<div class=\"\"><table class=\"table table-striped\">" + | |||
| "<thead class=\"thead-dark\"><tr>" + | |||
| "<td>Download Name</td><td>File</td>" + | |||
| "<td>Download Count</td><td>Delete</td>" + | |||
| "</tr></thead><tbody>"; | |||
| sql.getAllDownloads().then(function(downloads) | |||
| { | |||
| var downloadPromises = []; | |||
| downloads.forEach(function(download) | |||
| { | |||
| downloadPromises.push(renderDownloadRow(download)); | |||
| }); | |||
| Promise.all(downloadPromises).then(function(htmls) | |||
| { | |||
| const htmlafter = "</tbody></table></div></div><br>" + | |||
| "</div>"; | |||
| resolve(html + htmls.join('') + htmlafter); | |||
| }); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }); | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders tha download section of the admin page | |||
| * | |||
| * @param postData | |||
| * @returns {Promise} | |||
| */ | |||
| main: function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([addDownload(postData), | |||
| displayDownloads(postData)]).then(function(html) | |||
| { | |||
| resolve("<div class=\"row\">" + html.join('') + "</div>"); | |||
| }).catch(function(error) | |||
| { | |||
| console.log("error in add downloads.js"); | |||
| reject(error); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,28 +0,0 @@ | |||
| <div class="col-md-8"> | |||
| <div class="blogPost"> | |||
| <div class="text-center"> | |||
| <h2>Login</h2> | |||
| </div> | |||
| <form action="/admin" method ="post" class="p-2"> | |||
| <div class="form-group"> | |||
| <label for="username1">User Name</label> | |||
| <input class="form-control" type="test" id="username1" name="username" placeholder="Enter username" required> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label for="password1">Password</label> | |||
| <input class="form-control" type="password" name="password" id="password1" placeholder="Password" required> | |||
| </div> | |||
| <div class="text-center"> | |||
| <button class="btn btn-lg btn-secondary">Login</button> | |||
| </div> | |||
| <br> | |||
| </form> | |||
| <!-- | |||
| /\_/\ ___ | |||
| = o_o =_______ \ \ | |||
| __^ __( \.__) ) | |||
| (@)<_____>__(_____)____/ | |||
| --> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,103 @@ | |||
| /** Whiskers template file | |||
| * this has stuff for both editing blog and viewing a list of blog*/ | |||
| const TEMPLATE_FILE = "admin/adminPosts.html"; | |||
| const includes = require('../includes/includes.js'); | |||
| const sql = require('../utils/sql'); | |||
| //parses the post data | |||
| const qs = require('querystring'); | |||
| /** | |||
| * Detects if the post data came from the edit form in blog table or edit post | |||
| * in the edit post form. | |||
| * | |||
| * @param postData | |||
| * @param renderContext | |||
| * @returns {Promise} | |||
| */ | |||
| const processPostData = function(postData, renderContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var postParsed = qs.parse(postData); | |||
| if(postParsed.edit_post) | |||
| { | |||
| renderContext.editPost = true; | |||
| sql.getPostById(postParsed.edit_post).then(function(post) | |||
| { | |||
| post.published = post.published.toISOString().split('T')[0]; | |||
| renderContext.post = post; | |||
| resolve(); | |||
| }); | |||
| } | |||
| else if(postParsed.edit_post_2) | |||
| { | |||
| sql.editPost(postParsed).then(function() | |||
| { | |||
| resolve(); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| resolve(); | |||
| } | |||
| }); | |||
| }; | |||
| /** | |||
| * Grabs and appends the list of blog from the SQL database to | |||
| * the template context for the template renderer. | |||
| * | |||
| * @param templateContext | |||
| * @returns {Promise} | |||
| */ | |||
| const fetchPostsInformation = function(templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getAllPosts().then(function(posts) | |||
| { | |||
| templateContext.posts = posts; | |||
| resolve(); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Fetches context information for the admin blog page and handles post | |||
| * data sent regarding editing blog. | |||
| * | |||
| * @param postData posted by user | |||
| * @param templateContext json object used as the template context | |||
| * @returns {Promise} renders the template used for this page | |||
| */ | |||
| main: function(postData, templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([includes.fetchTemplate(TEMPLATE_FILE), | |||
| processPostData(postData, templateContext), | |||
| fetchPostsInformation(templateContext)]).then(function(template) | |||
| { | |||
| resolve(template[0]); | |||
| }).catch(function(error) | |||
| { | |||
| console.log("error in add admin blog.js"); | |||
| reject(error); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,182 +0,0 @@ | |||
| /** | |||
| * File which renders the edit form for the posts and processes | |||
| * the post data generated by edit forms. | |||
| * | |||
| * @type {Promise|*} | |||
| */ | |||
| //parses the post data | |||
| const qs = require('querystring'); | |||
| //updates db | |||
| const sql = require('../../utils/sql'); | |||
| /** | |||
| * Displays a single row in the posts view | |||
| * | |||
| * @param post | |||
| */ | |||
| const renderPostRow = function(post) | |||
| { | |||
| return "<tr>" + | |||
| "<td>" + post.category_id + "</td>" + | |||
| "<td>" + post.name + "</td>" + | |||
| "<td>" + post.picture_url + "</td>" + | |||
| "<td>" + post.published + "</td>" + | |||
| "<td><form action=\"/admin\" method =\"post\" >\n" + | |||
| "<input type=\"submit\" name=\"submit\" value=\"Edit\"\n" + | |||
| " class=\"btn btn-secondary\"/>\n" + | |||
| "<input type='hidden' name='edit_post' value='" + post.post_id + "'/>"+ | |||
| "</form></td>" + | |||
| "</tr>"; | |||
| }; | |||
| /** | |||
| * Displays all the posts in a table | |||
| */ | |||
| const postsTable = function() | |||
| { | |||
| const html = "<div class='blogPost p-2'>" + | |||
| "<h1 class=\"text-center\">Posts</h1>" + | |||
| "<div class=\"\"><table class=\"table table-striped\">" + | |||
| "<thead class=\"thead-dark\"><tr>" + | |||
| "<td>Category #</td><td>Name</td><td>Header Picture</td><td>Date</td><td>Edit</td>" + | |||
| "</tr></thead><tbody>"; | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getAllPosts().then(function(posts) | |||
| { | |||
| var postPromises = []; | |||
| posts.forEach(function(post) | |||
| { | |||
| postPromises.push(renderPostRow(post)); | |||
| }); | |||
| Promise.all(postPromises).then(function(htmls) | |||
| { | |||
| resolve(html + htmls.join('') + "</tbody></table></div></div><br>"); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| }; | |||
| /** | |||
| * Displays the edit form for edit posts | |||
| * | |||
| * @param post_id | |||
| */ | |||
| const displayRenderForm = function(post_id) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getPostById(post_id).then(function(post) | |||
| { | |||
| const html = "<div class='blogPost'>"+ | |||
| "<h1 class=\"text-center\">Edit Post</h1>"+ | |||
| "<form action=\"/admin\" method =\"post\" >"+ | |||
| " <div class=\"form-group\">\n" + | |||
| " <input class=\"form-control\" type=\"text\" name=\"edit_cat_num\" value='" + post.category_id + "' required>\n" + | |||
| " <label class=\"w3-label w3-validate\">Category Number</label>\n" + | |||
| " </div>"+ | |||
| " <div class=\"form-group\">\n" + | |||
| " <input class=\"form-control\" type=\"text\" name=\"edit_name_new\" value='" + post.name + "' required>\n" + | |||
| " <label class=\"w3-label w3-validate\">Post Title</label>\n" + | |||
| " </div>"+ | |||
| " <div class=\"form-group\">\n" + | |||
| " <input class=\"form-control\" type=\"text\" name=\"edit_pic\" value='" + post.picture_url + "' required>\n" + | |||
| " <label class=\"w3-label w3-validate\">Picture URL</label>\n" + | |||
| " </div>"+ | |||
| " <div class=\"form-group\">\n" + | |||
| " <input class=\"form-control\" type=\"date\" name=\"edit_date\" value='" + post.published.toISOString().split('T')[0] + "' required>\n" + | |||
| " <label class=\"w3-label w3-validate\">Published Date</label>\n" + | |||
| " </div>"+ | |||
| " <div><input type=\"submit\" name=\"submit\" value=\"Edit\"\n" + | |||
| " class=\"btn btn-lg btn-secondary\"/></div>"+ | |||
| "<input type='hidden' name='edit_post_2' value='" + post_id + "'/>"+ | |||
| "</form>"+ | |||
| "</div><br>"; | |||
| resolve(html); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }); | |||
| }; | |||
| /** | |||
| * Detects if the post data came from the edit form in posts table or edit post | |||
| * in the edit post form. Based on this, this function will call one of two functions | |||
| * | |||
| * @param postData | |||
| */ | |||
| const processPost = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var postParsed = qs.parse(postData); | |||
| if(postParsed.edit_post) | |||
| { | |||
| //display edit form | |||
| displayRenderForm(postParsed.edit_post).then(function(html) | |||
| { | |||
| resolve(html); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| } | |||
| else if(postParsed.edit_post_2) | |||
| { | |||
| sql.editPost(postParsed).then(function(html) | |||
| { | |||
| resolve(html); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| resolve(""); | |||
| } | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Method which calls helper functions which processes post data for editing posts | |||
| * and calls a function which displays all the posts in a table | |||
| * | |||
| * @param postData | |||
| */ | |||
| main: function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([processPost(postData), | |||
| postsTable()]).then(function(html) | |||
| { | |||
| resolve("<br>" + html.join('')); | |||
| }).catch(function(error) | |||
| { | |||
| console.log("error in edit post.js"); | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,54 +0,0 @@ | |||
| <div class="col-md-6"> | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">Server Controls</h1> | |||
| <div class="text-center"> | |||
| <form action="/admin" method="post"> | |||
| <input type="submit" name="clearCache" value="Clear Cache" class="btn btn-lg btn-secondary" /> | |||
| <input type="hidden" name="clear_cache" value="true"> | |||
| </form> | |||
| <br> | |||
| <form action="/admin" method="post"> | |||
| <input type="submit" name="gitPull" value="Pull from Git" class="btn btn-lg btn-secondary" /> | |||
| <input type="hidden" name="git_pull" value="true"> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| <br> | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">New Post</h1> | |||
| <form action="/admin" method ="post" class="p-2"> | |||
| <!-- Post category --> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_post_category" required> | |||
| <label class="w3-label w3-validate">Category</label> | |||
| </div> | |||
| <!-- Post name --> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_post_name" required> | |||
| <label class="w3-label w3-validate">Name</label> | |||
| </div> | |||
| <!-- Post header picture --> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_post_picture" value="n/a" required> | |||
| <label class="w3-label w3-validate">Picture</label> | |||
| </div> | |||
| <!-- Post date --> | |||
| <div class="form-group"> | |||
| <input class="w3-input" type="date" name="add_post_date" required> | |||
| <label class="w3-label w3-validate">Date</label> | |||
| </div> | |||
| <div class="text-center"> | |||
| <input type="submit" name="submit" value="Add" | |||
| class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| @ -1,77 +0,0 @@ | |||
| const utils = require('../../utils/utils.js'); | |||
| const sql = require('../../utils/sql'); | |||
| const qs = require('querystring'); | |||
| const Promise = require('promise'); | |||
| /** | |||
| * | |||
| * @param postData | |||
| * @return {*|Promise} | |||
| */ | |||
| const processPost = function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var post = qs.parse(postData); | |||
| if(post.add_post_name) | |||
| { | |||
| var urls = post.add_post_name; | |||
| urls = urls.split(" ").join("-"); | |||
| urls =urls.toLowerCase(); | |||
| var q = "insert into posts (category_id, picture_url, published, name, url) values "; | |||
| q += "('" + post.add_post_category + "', '" + post.add_post_picture + | |||
| "', '" + post.add_post_date + "', '" + post.add_post_name + "', '" + urls + "')"; | |||
| sql.insert(q).then(function() | |||
| { | |||
| var map = require('../../utils/generateSiteMap'); | |||
| map.main(); | |||
| resolve(""); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| } | |||
| else if(post.clear_cache) | |||
| { | |||
| require("../../sites/blog.js").clearCache(); | |||
| require("../../includes/includes.js").clearCache(); | |||
| } | |||
| else if(post.git_pull) | |||
| { | |||
| const execSync = require('child_process').execSync; | |||
| code = execSync('git pull') | |||
| } | |||
| else | |||
| { | |||
| resolve(""); | |||
| } | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * | |||
| * @param postData | |||
| * @return {*} | |||
| */ | |||
| main: function(postData) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([utils.include("./admin/posts/newPost.html"), processPost(postData)]).then(function(html) | |||
| { | |||
| resolve(html.join('')); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,49 @@ | |||
| /** DB query */ | |||
| const sql = require('../utils/sql'); | |||
| /** Object used to render blog post previews */ | |||
| const blogBodyRenderer = require('./renderBlogPost'); | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Calls blog and sidebar modules to render blog contents in order | |||
| * | |||
| * @param requestURL | |||
| * @param request | |||
| * @returns {Promise} | |||
| */ | |||
| main: function(requestURL, request, templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var page = request.query.page; | |||
| const splitURL = requestURL.split("/"); | |||
| if(splitURL.length >= 3) | |||
| { | |||
| sql.getPostsFromCategory(splitURL[2]).then(function(posts) | |||
| { | |||
| Promise.all([blogBodyRenderer.renderBatchOfPosts(requestURL, posts, page, 5, templateContext), | |||
| require('./renderNextBar').main(requestURL, page, 5, posts.length, templateContext)]).then(function() | |||
| { | |||
| resolve(); | |||
| }); | |||
| }).catch(function() | |||
| { | |||
| delete templateContext["posts"]; | |||
| resolve(); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| //page is not found but, posts list will be empty | |||
| // so 404 will display | |||
| resolve(); | |||
| } | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,32 @@ | |||
| const sql = require('../utils/sql'); | |||
| const blogPostRenderer = require('./renderBlogPost.js'); | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders the previews of recent blog blog and the side bar | |||
| * | |||
| * @param res | |||
| * @param fileName request url | |||
| */ | |||
| main: function(requestURL, request, templateContext) | |||
| { | |||
| var page = request.query.page; | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getAllPosts().then(function(posts) | |||
| { | |||
| Promise.all([blogPostRenderer.renderBatchOfPosts(requestURL, posts, page, 5, templateContext), | |||
| require('./renderNextBar').main(requestURL, page, 5, posts.length, templateContext)]).then(function() | |||
| { | |||
| resolve(); | |||
| }); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,47 @@ | |||
| /** DB queries */ | |||
| const sql = require('../utils/sql'); | |||
| /** Object used to render blog post previews */ | |||
| const blogBodyRenderer = require('./renderBlogPost'); | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Calls blog and sidebar modules to render blog contents in order | |||
| * | |||
| * @param requestURL | |||
| * @returns {Promise|*} | |||
| */ | |||
| main: function(requestURL, request, templateContext) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const splitURL = requestURL.split("/"); | |||
| //user entered /category/name/ or /category/name | |||
| if(splitURL.length == 3 || splitURL.length == 4) | |||
| { | |||
| sql.getPost(requestURL).then(function(posts) | |||
| { | |||
| if(posts.length != 0) | |||
| { | |||
| blogBodyRenderer.renderBatchOfPosts(requestURL, posts, 1, 1, templateContext).then(function() | |||
| { | |||
| resolve(); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| resolve(); | |||
| } | |||
| }) | |||
| } | |||
| else | |||
| { | |||
| //404 will print | |||
| resolve(); | |||
| } | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,52 @@ | |||
| /** | |||
| * Determines if the requested page is out of bounds | |||
| * | |||
| * @param page current page | |||
| * @param postsPerPage - number of blog rendered on each page | |||
| * @param totalPosts - total blog in this category/total | |||
| * @returns {boolean} if this is a valid page | |||
| */ | |||
| const isValidPage = function(page, postsPerPage, totalPosts) | |||
| { | |||
| return !(page === 0 || page -1 >= totalPosts/postsPerPage); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders two buttons on the bottom of the page to | |||
| * go to the left or right | |||
| * | |||
| * Used by the home page and categories pages | |||
| * @param baseURL -- base url of page being rendered | |||
| * @param currentPage -- current page being rendered | |||
| * @param postsPerPage -- number of blog on each page | |||
| * @param totalPosts -- total amount of blog in the category | |||
| * @returns {Promise} promise which renders the buttons | |||
| */ | |||
| main: function(baseURL, currentPage, postsPerPage, totalPosts, templateContext) | |||
| { | |||
| if(typeof currentPage == "undefined") | |||
| currentPage = 1; | |||
| currentPage = Number(currentPage); | |||
| if(!isValidPage(currentPage, postsPerPage, totalPosts)) | |||
| { | |||
| reject("Invalid Page"); | |||
| } | |||
| var nextPage = currentPage + 1; | |||
| var previousPage = currentPage - 1; | |||
| if (isValidPage(previousPage, postsPerPage, totalPosts)) | |||
| { | |||
| templateContext.newPostsURL = baseURL + "?page=" + previousPage; | |||
| } | |||
| if (isValidPage(nextPage, postsPerPage, totalPosts)) | |||
| { | |||
| templateContext.oldPostsURL = baseURL + "?page=" + nextPage | |||
| } | |||
| } | |||
| }; | |||
| @ -0,0 +1,60 @@ | |||
| # MYSQL Schema | |||
|  | |||
| ```mysql | |||
| create database jrtechs_blog; | |||
| use jrtechs_blog; | |||
| create table users( | |||
| user_id mediumint unsigned not null AUTO_INCREMENT, | |||
| user_name varchar(60) not null, | |||
| password char(64) not null, | |||
| salt char(64) not null, | |||
| primary key(user_id) | |||
| ); | |||
| create table categories( | |||
| category_id mediumint unsigned not null AUTO_INCREMENT, | |||
| name varchar(60) not null, | |||
| url varchar(60) not null, | |||
| primary key(category_id) | |||
| ); | |||
| create table posts( | |||
| post_id mediumint unsigned not null AUTO_INCREMENT, | |||
| category_id mediumint unsigned not null, | |||
| picture_url varchar(100) not null, | |||
| published datetime not null, | |||
| name varchar(100) not null, | |||
| url varchar(100) not null, | |||
| primary key(post_id) | |||
| ); | |||
| create table downloads( | |||
| download_id mediumint unsigned not null AUTO_INCREMENT, | |||
| file varchar(40) not null, | |||
| name varchar(40) not null, | |||
| download_count mediumint not null, | |||
| primary key(download_id) | |||
| ); | |||
| create table popular_posts( | |||
| popular_post_id mediumint unsigned not null AUTO_INCREMENT, | |||
| post_id mediumint unsigned not null, | |||
| primary key(popular_post_id) | |||
| ); | |||
| create table traffic_log( | |||
| log_id mediumint unsigned not null AUTO_INCREMENT, | |||
| url varchar(60) not null, | |||
| ip varchar(20) not null, | |||
| date datetime not null, | |||
| primary key(log_id) | |||
| ); | |||
| grant all on jrtechs_blog.* to blog_user@localhost identified by "password"; | |||
| ``` | |||
| @ -1,59 +0,0 @@ | |||
| /** DB query */ | |||
| const sql = require('../utils/sql'); | |||
| /** Object used to render blog post previews */ | |||
| const batchPreview = require('../posts/renderBatchOfPreviewes'); | |||
| /** | |||
| * Renders all posts in a single category | |||
| * | |||
| * @param resultURL | |||
| * @returns {*} | |||
| */ | |||
| const renderPosts = function(resultURL, page) | |||
| { | |||
| const splitURL = resultURL.split("/"); | |||
| if(splitURL.length >= 3) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getPostsFromCategory(splitURL[2]).then(function(posts) | |||
| { | |||
| resolve(batchPreview.main(resultURL, posts, page, 5)); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| reject("Page Not Found"); | |||
| } | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Calls posts and sidebar modules to render blog contents in order | |||
| * | |||
| * @param requestURL | |||
| * @param request | |||
| * @returns {Promise} | |||
| */ | |||
| main: function(requestURL, request) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var page = request.query.page; | |||
| Promise.all([renderPosts(requestURL, page), | |||
| require("../sidebar/sidebar.js").main()]).then(function(content) | |||
| { | |||
| resolve(content.join('')); | |||
| }).catch(function(err) | |||
| { | |||
| reject(err); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,46 +0,0 @@ | |||
| const sql = require('../utils/sql'); | |||
| const batchPreview = require('../posts/renderBatchOfPreviewes'); | |||
| /**Renders each recent post for the homepage of the website | |||
| * | |||
| * @param result | |||
| * @returns {*|Promise} | |||
| */ | |||
| var renderRecentPosts = function(baseURL, page) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| sql.getRecentPostSQL().then(function(posts) | |||
| { | |||
| resolve(batchPreview.main(baseURL, posts, page, 5)); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders the previews of recent blog posts and the side bar | |||
| * | |||
| * @param res | |||
| * @param fileName request url | |||
| */ | |||
| main: function(requestURL, request) | |||
| { | |||
| var page = request.query.page; | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([renderRecentPosts(requestURL, page), require("../sidebar/sidebar.js").main()]).then(function(content) | |||
| { | |||
| resolve(content.join('')); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }) | |||
| } | |||
| }; | |||
| @ -1,68 +0,0 @@ | |||
| /** DB queries */ | |||
| const sql = require('../utils/sql'); | |||
| /** | |||
| * Function responsible for calling the appropriate sql requests to query | |||
| * database and serve correct blog post | |||
| * | |||
| * @param requestURL url requested from client | |||
| * @return {*|Promise} returns a resolved promise to preserve execution order | |||
| */ | |||
| const renderPost = function(requestURL) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const splitURL = requestURL.split("/"); | |||
| //user entered /category/name/ or /category/name | |||
| if(splitURL.length == 3 || splitURL.length == 4) | |||
| { | |||
| sql.getPost(requestURL).then(function(post) | |||
| { | |||
| if(post != 0) | |||
| { | |||
| return require("../posts/singlePost.js").renderPost(post); | |||
| } | |||
| else | |||
| { | |||
| reject("Page Not Found"); | |||
| } | |||
| }).then(function(html) | |||
| { | |||
| resolve("<div class='col-md-8'>" + html + "</div>"); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| } | |||
| else | |||
| { | |||
| reject("Page Not Found"); | |||
| } | |||
| }); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Calls posts and sidebar modules to render blog contents in order | |||
| * | |||
| * @param requestURL | |||
| * @returns {Promise|*} | |||
| */ | |||
| main: function(requestURL, request) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([renderPost(requestURL), | |||
| require("../sidebar/sidebar.js").main()]).then(function(content) | |||
| { | |||
| resolve(content.join('')); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,55 +0,0 @@ | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders a bunch of blog post previews to the user | |||
| * | |||
| * @param baseURL-- url of the page | |||
| * @param posts -- sql data about the posts to render | |||
| * @param currentPage -- the current page to render | |||
| * @param numOfPosts -- number of posts to render | |||
| * @returns {Promise} renders the html of the posts | |||
| */ | |||
| main: function(baseURL, posts, currentPage, numOfPosts) | |||
| { | |||
| if(typeof currentPage == "undefined") | |||
| { | |||
| currentPage = 1; | |||
| } | |||
| else | |||
| { | |||
| currentPage = Number(currentPage); | |||
| } | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| const promises = []; | |||
| for(var i = (currentPage-1) * numOfPosts; i < (currentPage-1) * numOfPosts + numOfPosts; i++) | |||
| { | |||
| if(i < posts.length) | |||
| { | |||
| promises.push(new Promise(function(res, rej) | |||
| { | |||
| require("../posts/singlePost.js") | |||
| .renderPreview(posts[i]).then(function(html) | |||
| { | |||
| res(html); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error) | |||
| }) | |||
| })); | |||
| } | |||
| } | |||
| promises.push(require('../posts/renderNextBar').main(baseURL, currentPage, numOfPosts, posts.length)); | |||
| Promise.all(promises).then(function(content) | |||
| { | |||
| resolve("<div class='col-md-8'>" + content.join('') + "</div>"); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,66 +0,0 @@ | |||
| /** | |||
| * Determines if the requested page is out of bounds | |||
| * | |||
| * @param page current page | |||
| * @param postsPerPage - number of posts rendered on each page | |||
| * @param totalPosts - total posts in this category/total | |||
| * @returns {boolean} if this is a valid page | |||
| */ | |||
| const isValidPage = function(page, postsPerPage, totalPosts) | |||
| { | |||
| return !(page === 0 || page -1 >= totalPosts/postsPerPage); | |||
| }; | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders two buttons on the bottom of the page to | |||
| * go to the left or right | |||
| * | |||
| * Used by the home page and categories pages | |||
| * @param baseURL -- base url of page being rendered | |||
| * @param currentPage -- current page being rendered | |||
| * @param postsPerPage -- number of posts on each page | |||
| * @param totalPosts -- total amount of posts in the category | |||
| * @returns {Promise} promise which renders the buttons | |||
| */ | |||
| main: function(baseURL, currentPage, postsPerPage, totalPosts) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| if(!isValidPage(currentPage, postsPerPage, totalPosts)) | |||
| { | |||
| reject("Invalid Page"); | |||
| } | |||
| var nextPage = currentPage + 1; | |||
| var previousPage = currentPage - 1; | |||
| var olderPosts = ""; | |||
| var newerPosts = ""; | |||
| if (isValidPage(previousPage, postsPerPage, totalPosts)) | |||
| { | |||
| newerPosts = "<button class=\"btn btn-secondary btn-lg " + | |||
| "w3-padding-large w3-white w3-border\" onclick=\"location.href='" + | |||
| baseURL + "?page=" + previousPage + | |||
| "'\"><b>Newer Posts »</b></button>"; | |||
| } | |||
| if (isValidPage(nextPage, postsPerPage, totalPosts)) | |||
| { | |||
| olderPosts = "<button class=\"btn btn-secondary btn-lg " + | |||
| "w3-padding-large w3-white w3-border\" onclick=\"location.href='" + | |||
| baseURL + "?page=" + nextPage + | |||
| "'\"><b>Older Posts »</b></button>"; | |||
| } | |||
| resolve(" <div class=\"row\">\n" + | |||
| " <div class=\"col-6\">" + newerPosts + "</div>\n" + | |||
| " <div class=\"col-6\"><span class=\"float-right\">" + olderPosts + "</span></div>\n" + | |||
| " <br><br></div>"); | |||
| }) | |||
| } | |||
| }; | |||
| @ -1,28 +0,0 @@ | |||
| const postGenerator = require('../utils/renderBlogPost.js'); | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Renders a preview of the post with a link to view more | |||
| * | |||
| * @param res | |||
| * @param post | |||
| */ | |||
| renderPreview: function(post) | |||
| { | |||
| return postGenerator.generateBlogPost(post, 3); | |||
| }, | |||
| /** | |||
| * renderPost() displays a single blog post in it's entirety | |||
| * | |||
| * @param res result sent to user | |||
| * @param post sql data about the blog post | |||
| * @return {*|Promise} | |||
| */ | |||
| renderPost: function(post) | |||
| { | |||
| return postGenerator.generateBlogPost(post, -1); | |||
| } | |||
| }; | |||
| @ -1,38 +0,0 @@ | |||
| const sql = require('../utils/sql'); | |||
| module.exports= | |||
| { | |||
| /** | |||
| * Responsible for querying the database and displaying all | |||
| * categories that the blog has in the sidebar | |||
| * | |||
| * @param res | |||
| * @return {*|Promise} | |||
| */ | |||
| main: function() | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var content = "<br><br><div class=\"container\">"; | |||
| content += "<div class=\"list-group\">"; | |||
| content += " <a href=\"#\" class=\"list-group-item list-group-item-action flex-column align-items-start active\">\n" + | |||
| " <h5 class=\"mb-1\">Categories</h5>\n" + | |||
| " </a>"; | |||
| sql.getCategories().then(function(categories) | |||
| { | |||
| categories.forEach(function(cat) | |||
| { | |||
| content += "<a class=\"list-group-item\" href='/category/" + cat.url + "'>" + cat.name + "<br></a>"; | |||
| }); | |||
| content += "</div></div><br>"; | |||
| resolve(content); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,35 +0,0 @@ | |||
| const sql = require('../utils/sql'); | |||
| module.exports= | |||
| { | |||
| /**Renders the popular posts sidebar. | |||
| * | |||
| * @param res | |||
| * @returns {*|Promise} | |||
| */ | |||
| main: function(res) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| res.write("<div class=\"w3-card w3-margin\">"); | |||
| res.write("<div class=\"w3-container w3-padding\">" + | |||
| "<h4>Popular Posts</h4></div>"); | |||
| res.write("<div class=\"w3-sidebar w3-bar-block\">"); | |||
| sql.getPopularPosts().then(function(posts) | |||
| { | |||
| posts.forEach(function(cat) | |||
| { | |||
| console.log(cat); | |||
| res.write("<a class=\"w3-bar-item w3-button\" href='" | |||
| + url + "'>" + p.name + "<br></a>"); | |||
| }); | |||
| res.write("</div></div>"); | |||
| resolve(); | |||
| }); | |||
| }); | |||
| } | |||
| }; | |||
| @ -1,10 +0,0 @@ | |||
| <div class="container"> | |||
| <div class="list-group"> | |||
| <a href="#" class="list-group-item list-group-item-action flex-column align-items-start active"> | |||
| <h5 class="mb-1">Project Sites</h5> | |||
| </a> | |||
| <a class="list-group-item" href='https://jrtechs.net/steam/'>Steam Graph Analysis<br></a> | |||
| <a class="list-group-item" href='https://jrtechs.me/'>Portfolio<br></a> | |||
| <a class="list-group-item" href='https://clubpanda.jrtechs.net/'>Club Panda<br></a> | |||
| </div> | |||
| </div><br> | |||
| @ -1,38 +0,0 @@ | |||
| const Promise = require('promise'); | |||
| const sql = require('../utils/sql'); | |||
| module.exports= | |||
| { | |||
| /** Renders the the recent post sidebar. | |||
| * | |||
| * @returns {*|Promise} | |||
| */ | |||
| main: function() | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| var content = "<div class=\"container\">"; | |||
| content +="<div class=\"list-group\">"; | |||
| content +=" <a href=\"#\" class=\"list-group-item list-group-item-action flex-column align-items-start active\">\n" + | |||
| " <h5 class=\"mb-1\">Recent Posts</h5>\n" + | |||
| " </a>"; | |||
| sql.getRecentPosts().then(function(posts) | |||
| { | |||
| posts.forEach(function(p) | |||
| { | |||
| var url = '/' + p.category + '/' + p.url; | |||
| content += "<a class=\"list-group-item\" href='" | |||
| + url + "'>" + p.name + "<br></a>"; | |||
| }); | |||
| content +="</div></div>"; | |||
| resolve(content); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,66 @@ | |||
| <div class="row"> | |||
| <!-- Add Download --> | |||
| <div class="col-md-6"> | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">Add Download</h1> | |||
| <form action="/admin/downloads/" method ="post" class="p-2"> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_download_name" required> | |||
| <label>Download Name</label> | |||
| </div> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_download_file" required> | |||
| <label>File name</label> | |||
| </div> | |||
| <div class="text-center"> | |||
| <input type="submit" name="add_download" value="Add Download" | |||
| class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <br> | |||
| </div> | |||
| <!-- Downloads --> | |||
| <div class="col-md-6"> | |||
| <div class='blogPost'> | |||
| <h1 class="text-center">Downloads</h1> | |||
| <div class=""> | |||
| <table class="table table-striped"> | |||
| <thead class="thead-dark"> | |||
| <tr> | |||
| <td>Download Name</td> | |||
| <td>File</td> | |||
| <td>Download Count</td> | |||
| <td>Delete</td> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| {for download in downloads} | |||
| <tr> | |||
| <td> | |||
| {download.name} | |||
| </td> | |||
| <td> | |||
| {download.file} | |||
| </td> | |||
| <td> | |||
| {download.download_count} | |||
| </td> | |||
| <td> | |||
| <form action="/admin/downloads/" method ="post" > | |||
| <input type="submit" name="submit" value="Delete" class="btn btn-secondary"/> | |||
| <input type='hidden' name='delete_download' value='{download.download_id}' /> | |||
| </form> | |||
| </td> | |||
| </tr> | |||
| {/for} | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,103 @@ | |||
| <div class="row"> | |||
| <div class="col-md-6"> | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">Server Controls</h1> | |||
| <div class="text-center"> | |||
| <form action="/admin" method="post"> | |||
| <input type="submit" name="clearCache" value="Clear Cache" class="btn btn-lg btn-secondary" /> | |||
| <input type="hidden" name="clear_cache" value="true"> | |||
| </form> | |||
| <br> | |||
| <form action="/admin" method="post"> | |||
| <input type="submit" name="gitPull" value="Pull from Git" class="btn btn-lg btn-secondary" /> | |||
| <input type="hidden" name="git_pull" value="true"> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="col-md-6"> | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">Add Category</h1> | |||
| <form action="/admin" method ="post" class="p-2"> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_category" required> | |||
| <label>Category</label> | |||
| </div> | |||
| <div class="text-center"> | |||
| <input type="submit" name="submit" value="Add" | |||
| class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <br> | |||
| </div> | |||
| </div> | |||
| <div class="row"> | |||
| <div class="col-md-6"> | |||
| <div class="blogPost"> | |||
| <h1 class="text-center">New Post</h1> | |||
| <form action="/admin" method ="post" class="p-2"> | |||
| <!-- Post category --> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_post_category" required> | |||
| <label class="w3-label w3-validate">Category</label> | |||
| </div> | |||
| <!-- Post name --> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_post_name" required> | |||
| <label class="w3-label w3-validate">Name</label> | |||
| </div> | |||
| <!-- Post header picture --> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="add_post_picture" value="n/a" required> | |||
| <label class="w3-label w3-validate">Picture</label> | |||
| </div> | |||
| <!-- Post date --> | |||
| <div class="form-group"> | |||
| <input class="w3-input" type="date" name="add_post_date" required> | |||
| <label class="w3-label w3-validate">Date</label> | |||
| </div> | |||
| <div class="text-center"> | |||
| <input type="submit" name="submit" value="Add" | |||
| class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| <div class="col-md-6"> | |||
| <h1 class="text-center">Categories</h1> | |||
| <div class="blogPost"> | |||
| <table class="table table-striped"> | |||
| <thead class="thead-dark"> | |||
| <tr> | |||
| <td>Name</td> | |||
| <td>URL</td> | |||
| <td>Edit</td> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| {for cat in categories} | |||
| <tr> | |||
| <td>{cat.name}</td> | |||
| <td>{cat.url}</td> | |||
| <td>{cat.category_id}</td> | |||
| </tr>> | |||
| {/for} | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,65 @@ | |||
| {header} | |||
| <div class="container"> | |||
| {if loggedIn} | |||
| {>adminPage} | |||
| {else} | |||
| {if goodLoginAttempt} | |||
| <meta http-equiv="refresh" content="0"> | |||
| {/if} | |||
| <div class="row"> | |||
| {if banned} | |||
| <div class="align-content-center"> | |||
| <br> | |||
| <h1>Yikes</h1> | |||
| <br> | |||
| <img src="/includes/img/404.jpg" alt="Page not found meme" width="70%" /> | |||
| <br><br><br><br> | |||
| </div> | |||
| {else} | |||
| <div class="col-md-8"> | |||
| <div class="blogPost"> | |||
| <div class="text-center"> | |||
| <h2>Login</h2> | |||
| </div> | |||
| <form action="/admin/" method ="post" class="p-2"> | |||
| <div class="form-group"> | |||
| <label for="username1">User Name</label> | |||
| <input class="form-control" type="text" id="username1" name="username" placeholder="Enter username" required> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label for="password1">Password</label> | |||
| <input class="form-control" type="password" name="password" id="password1" placeholder="Password" required> | |||
| </div> | |||
| <div class="text-center"> | |||
| <button class="btn btn-lg btn-secondary">Login</button> | |||
| </div> | |||
| <br> | |||
| </form> | |||
| {if invalid} | |||
| <p> | |||
| Invalid login attempt. | |||
| </p> | |||
| {/if} | |||
| <!-- | |||
| /\_/\ ___ | |||
| = o_o =_______ \ \ | |||
| __^ __( \.__) ) | |||
| (@)<_____>__(_____)____/ | |||
| --> | |||
| </div> | |||
| <br /><br /> | |||
| </div> | |||
| {/if} | |||
| </div> | |||
| {/if} | |||
| </div> | |||
| {footer} | |||
| @ -0,0 +1,68 @@ | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| {if editPost} | |||
| <div class='blogPost'> | |||
| <h1 class="text-center">Edit Post</h1> | |||
| <form action="/admin/posts" method ="post" > | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="edit_cat_num" value='{post.category_id}' required> | |||
| <label class="w3-label w3-validate">Category Number</label> | |||
| </div> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="edit_name_new" value='{post.name}' required> | |||
| <label class="w3-label w3-validate">Post Title</label> | |||
| </div> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="text" name="edit_pic" value='{post.picture_url}' required> | |||
| <label class="w3-label w3-validate">Picture URL</label> | |||
| </div> | |||
| <div class="form-group"> | |||
| <input class="form-control" type="date" name="edit_date" value='{post.published}' required> | |||
| <label class="w3-label w3-validate">Published Date</label> | |||
| </div> | |||
| <div> | |||
| <input type="submit" name="submit" value="Edit" class="btn btn-lg btn-secondary"/> | |||
| </div> | |||
| <input type='hidden' name='edit_post_2' value='{post.post_id}'/> | |||
| </form> | |||
| </div> | |||
| <br> | |||
| {/if} | |||
| </div> | |||
| </div> | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| <div class='blogPost p-2'> | |||
| <h1 class="text-center">Posts</h1> | |||
| <table class="table table-striped"> | |||
| <thead class="thead-dark"> | |||
| <tr> | |||
| <td>Category #</td> | |||
| <td>Name</td> | |||
| <td>Header Picture</td> | |||
| <td>Date</td> | |||
| <td>Edit</td> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| {for post in posts} | |||
| <tr> | |||
| <td>{post.category_id}</td> | |||
| <td>{post.name}</td> | |||
| <td>{post.picture_url}</td> | |||
| <td>{post.published}</td> | |||
| <td> | |||
| <form action="/admin/posts" method ="post" > | |||
| <input type="submit" name="submit" value="Edit" class="btn btn-secondary"/> | |||
| <input type='hidden' name='edit_post' value='{post.post_id}' /> | |||
| </form> | |||
| </td> | |||
| </tr> | |||
| {/for} | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| <br> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,62 @@ | |||
| {header} | |||
| <div class="container"> | |||
| <div class="row"> | |||
| <div class="col-md-8 col-12"> | |||
| {for post in posts} | |||
| <div class="blogPost"> | |||
| {if post.hasPicture} | |||
| <img src="/blogContent/headerImages/{post.picture_url}" style="width:100%;"> | |||
| {/if} | |||
| <div class="p-4"> | |||
| <h3><b>{post.name}</b></h3> | |||
| <h5> | |||
| <span class="w3-opacity">{post.published}</span> | |||
| </h5> | |||
| {post.blogBody} | |||
| </div> | |||
| </div> | |||
| <br> | |||
| <br> | |||
| {else} | |||
| <div class="row p-lg-0"> | |||
| <h1 class="align-content-center">Page Not Found</h1> | |||
| <div class="align-content-center"> | |||
| <img src="/includes/img/404.jpg" alt="Page not found" width="70%" /> | |||
| </div> | |||
| </div> | |||
| <br><br> | |||
| {/for} | |||
| <div class="row"> | |||
| <div class="col-6"> | |||
| {if newPostsURL} | |||
| <button class="btn btn-secondary btn-l" onclick="location.href='{newPostsURL}'"> | |||
| <b>Older Posts »</b> | |||
| </button> | |||
| {/if} | |||
| </div> | |||
| <div class="col-6"> | |||
| {if oldPostsURL} | |||
| <span class="float-right"> | |||
| <button class="btn btn-secondary btn-l" onclick="location.href='{oldPostsURL}'"> | |||
| <b>Older Posts »</b> | |||
| </button> | |||
| </span> | |||
| {/if} | |||
| </div> | |||
| </div> | |||
| <br> | |||
| </div> | |||
| <div class="col-md-4 col-4"> | |||
| {>sideBar} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {footer} | |||
| @ -0,0 +1,36 @@ | |||
| <div class="container"> | |||
| <div class="list-group"> | |||
| <a href="#" class="list-group-item list-group-item-action flex-column align-items-start active"> | |||
| <h5 class="mb-1">Project Sites</h5> | |||
| </a> | |||
| <a class="list-group-item" href='https://jrtechs.net/steam/'>Steam Graph Analysis<br></a> | |||
| <a class="list-group-item" href='https://jrtechs.me/'>Portfolio<br></a> | |||
| <a class="list-group-item" href='https://clubpanda.jrtechs.net/'>Club Panda<br></a> | |||
| </div> | |||
| </div> | |||
| <br> | |||
| <div class="container"> | |||
| <div class="list-group"> | |||
| <a href="#" class="list-group-item list-group-item-action flex-column align-items-start active"> | |||
| <h5 class="mb-1">Recent Posts</h5> | |||
| </a> | |||
| {for recentPost in recentPosts} | |||
| <a class="list-group-item" href='{recentPost.url}'>{recentPost.name}<br></a> | |||
| {/for} | |||
| </div> | |||
| </div> | |||
| <br> | |||
| <div class="container"> | |||
| <div class="list-group"> | |||
| <a href="#" class="list-group-item list-group-item-action flex-column align-items-start active"> | |||
| <h5 class="mb-1">Categories</h5> | |||
| </a> | |||
| {for cat in categories} | |||
| <a class="list-group-item" href='{cat.url}'>{cat.name}<br></a> | |||
| {/for} | |||
| </div> | |||
| </div> | |||
| <br> | |||