123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- <!DOCTYPE html>
- <html>
- <head>
- <!-- HTML Meta Tags -->
- <title>Welcome to My Blog</title>
- <meta name="description" content="A personal blog hosted on WebStick!">
- <meta name="viewport" content="width=device-width, initial-scale=1" >
- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" />
- <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"></script>
- <style>
- .imgwrapper{
- max-height: 200px;
- overflow: hidden;
- }
-
- .homebtn{
- cursor: pointer;
- color: grey;
- }
- .homebtn:hover{
- opacity: 0.5;
- }
- </style>
- </head>
- <body>
- <br>
- <div class="ui text container">
- <h2 class="ui header">
- <span class="homebtn" title="Homepage" onclick="window.location.href = '../';"><i class="ui chevron left circle icon"></i></span>
- <span id="blogtitle">WebStick Blog</span> <span onclick="editBlogTitle();" class="adminOnly"><i class="ui small edit grey icon"></i></span>
- <div class="sub header"><span id="blogsubtitle">Welcome to this new personal blog of mine!</span> <span class="adminOnly" onclick="editBlogSubtitle();"><i class="ui edit icon"></i></span></div>
- </h2>
- <div class="adminOnly">
- <div class="ui divider"></div>
- <button id="createNewPostBtn" class="ui basic button"><i class="blue add icon"></i> New Post</button>
- <button onclick="forceUpdatePostIndex()" class="ui basic button"><i class="green refresh icon"></i> Force Update Post Index</button>
- <br>
- </div>
- <div id="newPostModal" class="ui basic segment" style="display:none;">
- <div class="ui divider"></div>
- <h3><i class="ui blue add icon"></i> Create a new Post</h3>
- <p>New post in WebStick Blog Engine are markdown files strored in the posts/ folder. Create a new post with filename as title (exclude the .md extension when filling in the filename).<br>
- A new window with markdown editor will be opened. After finish writing, save & close the markdown editor and refresh this page to see your post.</p>
- <div class="ui action fluid input">
- <input id="newPostTitle" type="text" maxlength="16">
- <button id="confirmNewPostBtn" onclick="createNewPost();" class="ui green button">Create & Edit</button>
- </div>
- </div>
- <div id="nopost" class="ui message" style="display:none;">
- <h4 class="ui header">
- <i class="green check icon"></i>
- <div class="content">
- Seems this blog has nothing posted. Check back later!
- <div class="sub header">You own the place? Try create a new post after login.</div>
- </div>
- </h4>
- </div>
- <div id="posttable">
- <div class="ui basic segment">
- <i class="ui loading spinner icon"></i> Loading Posts
- </div>
- </div>
- </div>
-
- <div class="ui text container center aligned">
- <div class="ui divider"></div>
- <small>Proudly powered by <a href="https://hackaday.io/project/192618-instant-webstick-esp8266-web-server-nas">WebStick</a></small>
- </div>
- </div>
- <script>
- let loggedIn = false;
- //Check the user has logged in
- $.get("/api/auth/chk", function(data){
- if (data == false){
- //User cannot use admin function. Hide the buttons.
- $(".adminOnly").hide();
- loggedIn = false;
- }else{
- loggedIn = true;
- }
- loadValue("blog-posts", function(){
- initPosts();
- })
- });
-
- //Initialize blog info
- function initBlogInfo(){
- loadValue("blog-title", function(title){
- if (title.error != undefined || title == ""){
- title = "WebStick Blog";
- }
- document.title = decodeURIComponent(title);
- $("#blogtitle").text(decodeURIComponent(title));
- });
- loadValue("blog-subtitle", function(title){
- if (title.error != undefined || title == ""){
- title = "Welcome to my personal blog!";
- }
- $("#blogsubtitle").text(decodeURIComponent(title));
- });
- }
- initBlogInfo();
- //Edit blog title and subtitles
- function editBlogSubtitle(){
- let newtitle = prompt("New Blog Subtitle", "");
- if (newtitle != null) {
- setValue("blog-subtitle", encodeURIComponent(newtitle), function(){
- initBlogInfo();
- })
- }
- }
- function editBlogTitle(){
- let newtitle = prompt("New Blog Title", "");
- if (newtitle != null) {
- setValue("blog-title", encodeURIComponent(newtitle), function(){
- initBlogInfo();
- })
- }
- }
- //Storage and loader utils
- function setValue(key, value, callback){
- $.get("/api/pref/set?key=" + key + "&value=" + value, function(data){
- callback(data);
- });
- }
- function loadValue(key, callback){
- $.get("/api/pref/get?key=" + key, function(data){
- callback(data);
- });
- }
- /*
- New Post
- New post is created via creating a markdown file in the server
- side and open it with the markdown editor
- */
- $("#createNewPostBtn").on("click", function(){
- $("#newPostModal").toggle("fast");
- });
- function createNewPost(){
- let filename = $("#newPostTitle").val().trim();
- if (filename == ""){
- alert("Post title cannot be empty.");
- return;
- }
- if (filename.indexOf("/") >= 0){
- //Contains /. Reject
- alert("File name cannot contain path seperator");
- return;
- }
- $("#confirmNewPostBtn").addClass("loading").addClass("disabled");
- //Create the markdown file at the /blog/posts folder
- const blob = new Blob(["# Hello World\n"], { type: 'text/plain' });
- let storeFilename = parseInt(Date.now()/1000) + "_" + filename+'.md';
- const file = new File([blob], storeFilename);
- handleFile(file, "/blog/posts", function(){
- //Update the post index
- updatePostIndex();
- $("#confirmNewPostBtn").removeClass("loading").removeClass("disabled");
- //Open the markdown file in new tab
- let hash = encodeURIComponent(JSON.stringify({
- "filename": storeFilename,
- "filepath": "/blog/posts/" + storeFilename
- }))
- window.open("/admin/mde/index.html#" + hash);
- $("#newPostModal").hide();
- });
-
- }
- function handleFile(file, dir=currentPath, callback=undefined) {
- // Perform actions with the selected file
- var formdata = new FormData();
- formdata.append("file1", file);
- var ajax = new XMLHttpRequest();
- ajax.addEventListener("load", function(event){
- let responseText = event.target.responseText;
- try{
- responseText = JSON.parse(responseText);
- if (responseText.error != undefined){
- alert(responseText.error);
- }
- }catch(ex){
- }
- if (callback != undefined){
- callback();
- }
- }, false); // doesnt appear to ever get called even upon success
- ajax.addEventListener("error", errorHandler, false);
- //ajax.addEventListener("abort", abortHandler, false);
- ajax.open("POST", "/upload?dir=" + dir);
- ajax.send(formdata);
- }
- function errorHandler(event) {
- aelrt("New Post creation failed");
- $("#pasteButton").removeClass("disabled");
- }
- /*
- Post Edit functions
- */
- function editPost(btn){
- let postFilename = $(btn).attr("filename");
- let hash = encodeURIComponent(JSON.stringify({
- "filename": postFilename,
- "filepath": "/blog/posts/" + postFilename
- }))
- window.open("/admin/mde/index.html#" + hash);
- }
- function deletePost(btn){
- let postFilename = $(btn).attr("filename");
- let postTitle = $(btn).attr("ptitle");
- if (confirm("Confirm remove post titled: " + postTitle + "?")){
- $.ajax({
- url: "/api/fs/del?target=/blog/posts/" + postFilename,
- method: "POST",
- success: function(data){
- if (data.error != undefined){
- alert("Post delete failed. See console for more info.");
- console.log(data.error);
- }else{
- //Deleted
- initPosts();
- }
- }
- });
- }
- }
- /*
- Rendering for Posts
- */
- //Load a markdown file from URL and render it to target element
- function loadMarkdownToHTML(markdownURL, targetElement){
- fetch(markdownURL).then( r => r.text() ).then( text =>{
- var converter = new showdown.Converter();
- let targetHTML = converter.makeHtml(text);
- console.log(targetHTML);
- $(targetElement).html(targetHTML);
- });
- }
- function initPosts(){
- $("#posttable").html("<div class='ui basic segment'><p><i class='ui loading spinner icon'></i> Loading Blog Posts</p></div>");
- loadValue("blog-posts", function(data){
- $("#posttable").html("");
- try{
- let postList = JSON.parse(decodeURIComponent(atob(data)));
- //From latest to oldest
- postList.reverse();
- console.log("Post listed loaded: ", postList);
- if (postList.length == 0){
- $("#nopost").show();
- }else{
- $("#nopost").hide();
- postList.forEach(postFilename => {
- renderPost(postFilename);
- })
- }
- }catch(ex){
- $("#nopost").show();
- }
-
- })
- }
- function forceUpdatePostIndex(){
- updatePostIndex(function(){
- window.location.reload();
- });
- }
- function updatePostIndex(callback=undefined){
- let postList = [];
- $.ajax({
- url: "/api/fs/list?dir=/blog/posts",
- success: function(data){
- data.forEach(file => {
- let filename = file.Filename;
- let ext = filename.split(".").pop();
- if (ext == "md" && file.IsDir == false){
- //Markdown file. Render it
- postList.push(filename);
- }
- });
- setValue("blog-posts", btoa(encodeURIComponent(JSON.stringify(postList))), function(data){
- console.log(data);
- if (callback != undefined){
- callback();
- }
- });
- }
- });
-
- }
- //Render post
- function renderPost(filename){
- //Remove the timestamp
- let postTitle = filename.split("_");
- let timeStamp = postTitle.shift();
- postTitle = postTitle.join("_");
- //Pop the file extension
- postTitle = postTitle.split(".");
- postTitle.pop();
- postTitle = postTitle.join(".");
- var postTime = new Date(parseInt(timeStamp) * 1000).toLocaleDateString("en-US")
- let postEditFeature = `<div class="adminOnly" style="position: absolute; top: 3em; right: 0.4em;">
- <a class="ui basic mini icon button" onclick="editPost(this);" filename="${filename}" title="Edit Post"><i class="edit icon"></i></a>
- <button class="ui basic mini icon button" onclick="deletePost(this);" ptitle="${postTitle}" filename="${filename}" title="Remove Post"><i class="red trash icon"></i></button>
- </div>`;
- if (!loggedIn){
- postEditFeature = "";
- }
- //Create a wrapper element
- $("#posttable").append(`
- <div class="ui basic segment postObject" id="${timeStamp}">
- <div class="ui divider"></div>
- <h4 class="ui header">
- <i class="blue paperclip icon"></i>
- <div class="content">
- ${postTitle}
- </div>
- </h4>
- ${postEditFeature}
- <div class="postContent">
- </div>
- <small><i class="calendar alternate outline icon"></i> ${postTime}</small>
- </div>
- `);
- let targetElement = $("#" + timeStamp).find(".postContent");
- loadMarkdownToHTML("/blog/posts/" + filename,targetElement);
- }
-
- </script>
- </body>
- </html>
|