Browse Source

Finished updating the admin section of the website to contain stuff for viewing and editing posts with the Whiskers template engine.

pull/41/head
jrtechs 5 years ago
parent
commit
9c7a609430
13 changed files with 401 additions and 386 deletions
  1. +15
    -19
      admin/admin.js
  2. +16
    -18
      admin/adminDownloads.js
  3. +138
    -0
      admin/adminHome.js
  4. +0
    -15
      admin/category/addCategory.html
  5. +0
    -37
      admin/category/addCategory.js
  6. +0
    -28
      admin/login/login.html
  7. +1
    -1
      admin/login/login.js
  8. +103
    -0
      admin/posts.js
  9. +0
    -182
      admin/posts/editPost.js
  10. +0
    -56
      admin/posts/newPost.html
  11. +0
    -30
      admin/posts/newPost.js
  12. +60
    -0
      docs/sqlConfig.md
  13. +68
    -0
      templates/admin/adminPosts.html

+ 15
- 19
admin/admin.js View File

@ -1,20 +1,29 @@
/**
* Renders the admin page contents
* Determines what template and controls that will be
* displayed based on the url such as
* /
* /posts
* /downloads
*
* For each controls it calls that "pages" associated javascript file
* which fetches the template, deals with post data and gathers context
* for the template engine.
*/
//file IO
const utils = require('../utils/utils.js');
module.exports=
{
/**
* Method calls the admin widgets it correct order
*
* @param request
* @return {*|Promise}
* @param request -- used to get post data
* @param clientAddress -- used to see if user is banned for login
* @param templateContext -- used by whiskers for information to plug
* in the template
* @param filename -- specific admin page requested
* @returns {Promise} resolves once everything has been added to the template context
*/
main: function(request, clientAddress, templateContext, filename)
{
@ -48,19 +57,6 @@ module.exports=
{
console.log(error);
});
// console.log(postData);
// Promise.all([require("./posts/newPost.js").main(postData),
// require("./category/addCategory.js").main(postData),
// require("./posts/editPost.js").main(postData),
// require("./downloads/adminDownloads.js").main(postData)])
// .then(function(content)
// {
// resolve(content.join(''));
// }).catch(function(error)
// {
// reject(error);
// });
});
}
else

+ 16
- 18
admin/adminDownloads.js View File

@ -5,10 +5,9 @@
* @author Jeffery Russell 6-30-18
*/
/** Whiskers template file */
const TEMPLATE_FILE = "admin/adminDownloads.html";
//file IO
const utils = require('../utils/utils.js');
const includes = require('../includes/includes.js');
@ -50,12 +49,9 @@ const addDownloadPostData = function(postData)
/**
* Handel form requests from the downloads table
*
* @param postData
* @returns {*|Promise}
* Removes a download if requested by the
* post data from an admin.
*/
const removeDownloads = function(postData)
{
@ -81,11 +77,14 @@ const removeDownloads = function(postData)
/**
* Displays all the download information in a table
* @param postData
* @returns {*|Promise}
* 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(postData, templateContext)
const displayDownloads = function(templateContext)
{
return new Promise(function(resolve, reject)
{
@ -103,22 +102,21 @@ const displayDownloads = function(postData, templateContext)
module.exports=
{
/**
* Renders tha download section of the admin page
/** Fetches context information for the template and handles
* post data for the downloads.
*
* @param postData
* @returns {Promise}
* @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(postData);
console.log("downloads page called");
return new Promise(function(resolve, reject)
{
Promise.all([includes.fetchTemplate(TEMPLATE_FILE),
addDownloadPostData(postData),
removeDownloads(postData),
displayDownloads(postData, templateContext)]).then(function(template)
displayDownloads(templateContext)]).then(function(template)
{
resolve(template[0]);
}).catch(function(error)

+ 138
- 0
admin/adminHome.js View File

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

+ 0
- 15
admin/category/addCategory.html View File

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

+ 0
- 37
admin/category/addCategory.js View File

@ -1,37 +0,0 @@
//file io
const utils = require('../../utils/utils.js');
//update db
const sql = require('../../utils/sql');
//parse post data
const qs = require('querystring');
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);
})
});
}
};

+ 0
- 28
admin/login/login.html View File

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

+ 1
- 1
admin/login/login.js View File

@ -6,7 +6,7 @@ const sql = require('../../utils/sql');
const qs = require('querystring');
const DEBUG = true;
const DEBUG = false;
/**

+ 103
- 0
admin/posts.js View File

@ -0,0 +1,103 @@
/** Whiskers template file
* this has stuff for both editing posts and viewing a list of posts*/
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 posts 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 posts 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 posts page and handles post
* data sent regarding editing posts.
*
* @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 posts.js");
reject(error);
});
});
}
};

+ 0
- 182
admin/posts/editPost.js View File

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

+ 0
- 56
admin/posts/newPost.html View File

@ -1,56 +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>

+ 0
- 30
admin/posts/newPost.js View File

@ -1,30 +0,0 @@
const utils = require('../../utils/utils.js');
const sql = require('../../utils/sql');
const qs = require('querystring');
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);
})
});
}
};

+ 60
- 0
docs/sqlConfig.md View File

@ -0,0 +1,60 @@
# MYSQL Schema
![](docs/blogSql.svg)
```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";
```

+ 68
- 0
templates/admin/adminPosts.html View File

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

Loading…
Cancel
Save