posts.html 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <div class="ts-container is-very-narrow">
  2. <p>Work in progress</p>
  3. </div>
  4. <script>
  5. let loggedIn = false;
  6. //Check the user has logged in
  7. //Post editing function still require session check
  8. //this just here to hide the edit buttons
  9. $.get("/api/auth/chk", function(data){
  10. if (data == false){
  11. //User cannot use admin function. Hide the buttons.
  12. $(".adminOnly").remove();
  13. loggedIn = false;
  14. }else{
  15. loggedIn = true;
  16. }
  17. loadValue("blog-posts", function(){
  18. initPosts();
  19. });
  20. });
  21. //Initialize blog info
  22. function initBlogInfo(){
  23. loadValue("blog-title", function(title){
  24. if (title.error != undefined || title == ""){
  25. title = "WebStick";
  26. }
  27. document.title = decodeURIComponent(title);
  28. $("#pageTitle").text(decodeURIComponent(title));
  29. });
  30. loadValue("blog-subtitle", function(title){
  31. if (title.error != undefined || title == ""){
  32. title = "A personal web server hosted on an ESP8266 using a micro SD card";
  33. }
  34. $("#pageDesc").text(decodeURIComponent(title));
  35. });
  36. }
  37. $(document).ready(function(){
  38. initBlogInfo();
  39. });
  40. //Edit blog title and subtitles
  41. function editBlogSubtitle(){
  42. let newtitle = prompt("New Blog Subtitle", "");
  43. if (newtitle != null) {
  44. setValue("blog-subtitle", encodeURIComponent(newtitle), function(){
  45. initBlogInfo();
  46. })
  47. }
  48. }
  49. function editBlogTitle(){
  50. let newtitle = prompt("New Blog Title", "");
  51. if (newtitle != null) {
  52. setValue("blog-title", encodeURIComponent(newtitle), function(){
  53. initBlogInfo();
  54. })
  55. }
  56. }
  57. /*
  58. New Post
  59. New post is created via creating a markdown file in the server
  60. side and open it with the markdown editor
  61. */
  62. $("#createNewPostBtn").on("click", function(){
  63. $("#newPostModal").toggle("fast");
  64. });
  65. function createNewPost(){
  66. let filename = $("#newPostTitle").val().trim();
  67. if (filename == ""){
  68. alert("Post title cannot be empty.");
  69. return;
  70. }
  71. if (filename.indexOf("/") >= 0){
  72. //Contains /. Reject
  73. alert("File name cannot contain path seperator");
  74. return;
  75. }
  76. $("#confirmNewPostBtn").addClass("loading").addClass("disabled");
  77. //Create the markdown file at the /blog/posts folder
  78. const blob = new Blob(["# Hello World\n"], { type: 'text/plain' });
  79. let storeFilename = parseInt(Date.now()/1000) + "_" + filename+'.md';
  80. const file = new File([blob], storeFilename);
  81. handleFile(file, "/blog/posts", function(){
  82. //Update the post index
  83. updatePostIndex();
  84. $("#confirmNewPostBtn").removeClass("loading").removeClass("disabled");
  85. //Open the markdown file in new tab
  86. let hash = encodeURIComponent(JSON.stringify({
  87. "filename": storeFilename,
  88. "filepath": "/blog/posts/" + storeFilename
  89. }))
  90. window.open("/admin/mde/index.html#" + hash);
  91. $("#newPostModal").hide();
  92. });
  93. }
  94. function handleFile(file, dir=currentPath, callback=undefined) {
  95. // Perform actions with the selected file
  96. var formdata = new FormData();
  97. formdata.append("file1", file);
  98. var ajax = new XMLHttpRequest();
  99. ajax.addEventListener("load", function(event){
  100. let responseText = event.target.responseText;
  101. try{
  102. responseText = JSON.parse(responseText);
  103. if (responseText.error != undefined){
  104. alert(responseText.error);
  105. }
  106. }catch(ex){
  107. }
  108. if (callback != undefined){
  109. callback();
  110. }
  111. }, false); // doesnt appear to ever get called even upon success
  112. ajax.addEventListener("error", errorHandler, false);
  113. //ajax.addEventListener("abort", abortHandler, false);
  114. ajax.open("POST", "/upload?dir=" + dir);
  115. ajax.send(formdata);
  116. }
  117. function errorHandler(event) {
  118. aelrt("New Post creation failed");
  119. $("#pasteButton").removeClass("disabled");
  120. }
  121. /*
  122. Post Edit functions
  123. */
  124. function editPost(btn){
  125. let postFilename = $(btn).attr("filename");
  126. let hash = encodeURIComponent(JSON.stringify({
  127. "filename": postFilename,
  128. "filepath": "/blog/posts/" + postFilename
  129. }))
  130. window.open("/admin/mde/index.html#" + hash);
  131. }
  132. function deletePost(btn){
  133. let postFilename = $(btn).attr("filename");
  134. let postTitle = $(btn).attr("ptitle");
  135. if (confirm("Confirm remove post titled: " + postTitle + "?")){
  136. $.ajax({
  137. url: "/api/fs/del?target=/blog/posts/" + postFilename,
  138. method: "POST",
  139. success: function(data){
  140. if (data.error != undefined){
  141. alert("Post delete failed. See console for more info.");
  142. console.log(data.error);
  143. }else{
  144. //Deleted
  145. initPosts();
  146. }
  147. }
  148. });
  149. }
  150. }
  151. /*
  152. Rendering for Posts
  153. */
  154. //Load a markdown file from URL and render it to target element
  155. function loadMarkdownToHTML(markdownURL, targetElement){
  156. fetch(markdownURL).then( r => r.text() ).then( text =>{
  157. var converter = new showdown.Converter();
  158. let targetHTML = converter.makeHtml(text);
  159. console.log(targetHTML);
  160. $(targetElement).html(targetHTML);
  161. });
  162. }
  163. function initPosts(){
  164. $("#posttable").html("<div class='ui basic segment'><p><i class='ui loading spinner icon'></i> Loading Blog Posts</p></div>");
  165. loadValue("blog-posts", function(data){
  166. $("#posttable").html("");
  167. try{
  168. let postList = JSON.parse(decodeURIComponent(atob(data)));
  169. //From latest to oldest
  170. postList.reverse();
  171. console.log("Post listed loaded: ", postList);
  172. if (postList.length == 0){
  173. $("#nopost").show();
  174. }else{
  175. $("#nopost").hide();
  176. postList.forEach(postFilename => {
  177. renderPost(postFilename);
  178. })
  179. }
  180. }catch(ex){
  181. $("#nopost").show();
  182. }
  183. })
  184. }
  185. function forceUpdatePostIndex(){
  186. updatePostIndex(function(){
  187. window.location.reload();
  188. });
  189. }
  190. //Render post
  191. function renderPost(filename){
  192. //Remove the timestamp
  193. let postTitle = filename.split("_");
  194. let timeStamp = postTitle.shift();
  195. postTitle = postTitle.join("_");
  196. //Pop the file extension
  197. postTitle = postTitle.split(".");
  198. postTitle.pop();
  199. postTitle = postTitle.join(".");
  200. var postTime = new Date(parseInt(timeStamp) * 1000).toLocaleDateString("en-US")
  201. let postEditFeature = `<div class="adminOnly" style="position: absolute; top: 3em; right: 0.4em;">
  202. <a class="ui basic mini icon button" onclick="editPost(this);" filename="${filename}" title="Edit Post"><i class="edit icon"></i></a>
  203. <button class="ui basic mini icon button" onclick="deletePost(this);" ptitle="${postTitle}" filename="${filename}" title="Remove Post"><i class="red trash icon"></i></button>
  204. </div>`;
  205. if (!loggedIn){
  206. postEditFeature = "";
  207. }
  208. //Create a wrapper element
  209. $("#posttable").append(`
  210. <div class="ui basic segment postObject" id="${timeStamp}">
  211. <div class="ui divider"></div>
  212. <h4 class="ui header">
  213. <i class="blue paperclip icon"></i>
  214. <div class="content">
  215. ${postTitle}
  216. </div>
  217. </h4>
  218. ${postEditFeature}
  219. <div class="postContent">
  220. </div>
  221. <small><i class="calendar alternate outline icon"></i> ${postTime}</small>
  222. </div>
  223. `);
  224. let targetElement = $("#" + timeStamp).find(".postContent");
  225. loadMarkdownToHTML("/blog/posts/" + filename,targetElement);
  226. }
  227. </script>