|
@@ -0,0 +1,398 @@
|
|
|
+
|
|
|
+<link rel="stylesheet" href="mde/script/mde.css">
|
|
|
+<script src="mde/script/mde.js"></script>
|
|
|
+<style>
|
|
|
+ .selectable{
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .selectable:hover{
|
|
|
+ background-color: rgba(0, 0, 0, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ #mediaContent{
|
|
|
+ max-height: 50vh;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .editor-preview img {
|
|
|
+ max-width: 100%;
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .CodeMirror {
|
|
|
+ resize: vertical; /* Allows vertical resizing */
|
|
|
+ overflow: auto; /* Ensures scrollbars appear if content overflows */
|
|
|
+ min-height: 200px; /* Optional: Set a minimum height */
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-item.selected {
|
|
|
+ background-color: rgba(0, 0, 0, 0.1) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .folder-item.selected {
|
|
|
+ background-color: rgba(0, 0, 0, 0.1) !important;
|
|
|
+ }
|
|
|
+</style>
|
|
|
+<br>
|
|
|
+<div class="ui container" style="margin-top: 20px;">
|
|
|
+ <h2 class="ui header">New Post</h2>
|
|
|
+ <!-- Load draft message -->
|
|
|
+ <div id="loadDraftMessage" class="ui hidden message">
|
|
|
+ <div class="header">Saved Draft Found</div>
|
|
|
+ <p>A saved draft was found. Would you like to load it?</p>
|
|
|
+ <button class="ui green basic button" id="loadDraftButton">Load Draft</button>
|
|
|
+ <button class="ui red basic button" id="discardDraftButton">Discard</button>
|
|
|
+ </div>
|
|
|
+ <!-- Post Editor -->
|
|
|
+ <div class="ui form">
|
|
|
+ <div class="field">
|
|
|
+ <label for="title">Title</label>
|
|
|
+ <input type="text" id="title" placeholder="Enter title here">
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="field">
|
|
|
+ <label for="editor">Post Content</label>
|
|
|
+ <textarea id="editor"></textarea>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <button class="ui basic button" id="saveButton"><i class="ui green upload icon"></i>Publish</button>
|
|
|
+ <button class="ui blue basic button" id="saveDraftButton"><i class="ui save icon"></i>Save as Draft</button>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+
|
|
|
+<!-- Media Selector -->
|
|
|
+<div id="postengine_media_selector" class="ui modal">
|
|
|
+ <i class="close icon"></i>
|
|
|
+ <div class="content">
|
|
|
+ <div class="ui fluid input">
|
|
|
+ <button class="ui basic circular icon button" id="backButton">
|
|
|
+ <i class="arrow left icon"></i>
|
|
|
+ </button>
|
|
|
+ <button class="ui basic circular icon button" id="parentButton" style="margin-left: 0.4em;">
|
|
|
+ <i class="arrow up icon"></i>
|
|
|
+ </button>
|
|
|
+ <input type="text" placeholder="/" id="locationBar" style="margin-left: 0.4em;" value="/">
|
|
|
+ </div>
|
|
|
+ <div id="mediaContent" class="ui segments" style="margin-top: 1em;">
|
|
|
+ <div class="ui basic segment" style="pointer-events: none; user-select: none; opacity: 0.5;">
|
|
|
+ <i class="ui green circle check icon"></i> Loading media files...
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div id="limited_access_warning" class="ui hidden red message" style="display:none;">
|
|
|
+ This folder is not accessible by guest visitors but only logged-in users.
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="actions">
|
|
|
+ <div class="ui black deny button">
|
|
|
+ Cancel
|
|
|
+ </div>
|
|
|
+ <button class="ui basic button" id="importButton">
|
|
|
+ <i class="green download icon"></i> Import Selected
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+
|
|
|
+<script>
|
|
|
+ var currentDir = "/img"; // Initialize current path
|
|
|
+ var pathHistory = [currentDir]; // Initialize path history
|
|
|
+
|
|
|
+ $(document).ready(function() {
|
|
|
+ // Initialize SimpleMDE
|
|
|
+ var simplemde = new SimpleMDE({
|
|
|
+ element: document.getElementById("editor"),
|
|
|
+ toolbar: [
|
|
|
+ "bold",
|
|
|
+ "italic",
|
|
|
+ "heading",
|
|
|
+ "|",
|
|
|
+ "quote",
|
|
|
+ "unordered-list",
|
|
|
+ "ordered-list",
|
|
|
+ "|",
|
|
|
+ "link",
|
|
|
+ "image",
|
|
|
+ {
|
|
|
+ name: "mediaSelect",
|
|
|
+ action: function customFunction(editor) {
|
|
|
+ openMediaSelector(editor);
|
|
|
+ },
|
|
|
+ className: "fa fa-folder", // Font Awesome icon
|
|
|
+ title: "Select Media"
|
|
|
+ },
|
|
|
+ "|",
|
|
|
+ "preview",
|
|
|
+ ]
|
|
|
+ });
|
|
|
+
|
|
|
+ /*
|
|
|
+ Media Selector
|
|
|
+
|
|
|
+ This function will open the media selector modal when the media button is clicked.
|
|
|
+ */
|
|
|
+
|
|
|
+ function fetchDirectoryContents(path) {
|
|
|
+ $.ajax({
|
|
|
+ url: `/api/fs/list?dir=${encodeURIComponent(path)}`,
|
|
|
+ method: 'GET',
|
|
|
+ success: function(data) {
|
|
|
+ if (data.error == undefined){
|
|
|
+ pathHistory.push(currentDir);
|
|
|
+ if (path.endsWith("/")) {
|
|
|
+ currentDir = path.slice(0, -1); // Remove trailing slash if present
|
|
|
+ } else {
|
|
|
+ currentDir = path; // Update current directory
|
|
|
+ }
|
|
|
+ $("#locationBar").val(currentDir); // Update location bar
|
|
|
+ renderDirectoryContents(data);
|
|
|
+ } else {
|
|
|
+ alert("Failed to fetch directory contents: " + data.error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ error: function() {
|
|
|
+ alert("Error fetching directory contents.");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function renderDirectoryContents(contents) {
|
|
|
+ const container = $("#mediaContent");
|
|
|
+ container.empty();
|
|
|
+ let folderElements = ``;
|
|
|
+ let fileElements = ``;
|
|
|
+ let selectableFileCounter = 0;
|
|
|
+ contents.forEach(item => {
|
|
|
+ if (item.IsDir) {
|
|
|
+ folderElements += (`
|
|
|
+ <div class="ui segment folder-item selectable" data-path="${currentDir + "/" + item.Filename}">
|
|
|
+ <i class="yellow folder icon"></i> ${item.Filename}
|
|
|
+ </div>
|
|
|
+ `);
|
|
|
+ selectableFileCounter++;
|
|
|
+ } else if (!item.IsDir && isWebSafeFile(item.Filename)) {
|
|
|
+ let fileIcon = getFileTypeIcons(item.Filename);
|
|
|
+ fileElements += (`
|
|
|
+ <div class="ui segment file-item selectable" data-path="${currentDir + "/" + item.Filename}" data-type="${getFileType(item.Filename)}">
|
|
|
+ ${fileIcon} ${item.Filename}
|
|
|
+ </div>
|
|
|
+ `);
|
|
|
+ selectableFileCounter++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ container.append(folderElements);
|
|
|
+ container.append(fileElements);
|
|
|
+
|
|
|
+ if (selectableFileCounter == 0) {
|
|
|
+ container.append("<div class='ui basic segment' style='pointer-events: none; user-select: none; opacity: 0.5;'><i class='ui green circle check icon'></i> No usable files / folders found</div>");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add click handlers for folders and files
|
|
|
+ $(".folder-item").on('dblclick', function() {
|
|
|
+ const path = $(this).data('path');
|
|
|
+ fetchDirectoryContents(path);
|
|
|
+ });
|
|
|
+
|
|
|
+ $(".folder-item").on("click", function(event) {
|
|
|
+ const path = $(this).data('path');
|
|
|
+ if (!event.ctrlKey) {
|
|
|
+ // Highlight the selected folder and remove previous selection if ctrl is not held
|
|
|
+ $(".folder-item.selected").removeClass("selected");
|
|
|
+ }
|
|
|
+ $(this).toggleClass("selected"); // Toggle selection for the clicked item
|
|
|
+ });
|
|
|
+
|
|
|
+ $(".file-item").on('dblclick', function() {
|
|
|
+ $("#postengine_media_selector").modal("hide");
|
|
|
+ const path = $(this).data('path');
|
|
|
+ const type = $(this).data('type');
|
|
|
+ addMediaFileToEditor(path, type);
|
|
|
+ });
|
|
|
+
|
|
|
+ $(".file-item").on("click", function(event) {
|
|
|
+ const path = $(this).data('path');
|
|
|
+ const type = $(this).data('type');
|
|
|
+ if (!event.ctrlKey) {
|
|
|
+ // Highlight the selected file and remove previous selection if ctrl is not held
|
|
|
+ $(".file-item.selected").removeClass("selected");
|
|
|
+ }
|
|
|
+ $(this).toggleClass("selected"); // Toggle selection for the clicked item
|
|
|
+ });
|
|
|
+
|
|
|
+ //Show warning if the directory starts with /admin or /store
|
|
|
+ if (currentDir.startsWith("/admin") || currentDir.startsWith("/store")) {
|
|
|
+ $("#limited_access_warning").show();
|
|
|
+ } else {
|
|
|
+ $("#limited_access_warning").hide();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Bind event to the back button
|
|
|
+ $('#backButton').on('click', function() {
|
|
|
+ if (pathHistory.length > 1) {
|
|
|
+ pathHistory.pop(); // Remove the current directory
|
|
|
+ const previousPath = pathHistory.pop();
|
|
|
+ fetchDirectoryContents(previousPath);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // Bind event to the parent button
|
|
|
+ $('#parentButton').on('click', function() {
|
|
|
+ if (currentDir == "/") {
|
|
|
+ msgbox("Already at the root directory", 3000);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let parentPath = currentDir.split("/");
|
|
|
+ parentPath.pop(); // Remove the last element (current directory)
|
|
|
+ parentPath = parentPath.join("/"); // Join the remaining elements to form the parent path
|
|
|
+ if (parentPath == ""){
|
|
|
+ parentPath = "/"; // Set to root if empty
|
|
|
+ }
|
|
|
+ fetchDirectoryContents(parentPath);
|
|
|
+ });
|
|
|
+
|
|
|
+ // Bind event to the import button
|
|
|
+ $('#importButton').on('click', function() {
|
|
|
+ const selectedFolders = $(".folder-item.selected");
|
|
|
+ const selectedFiles = $(".file-item.selected");
|
|
|
+ if (selectedFiles.length === 0 && selectedFolders.length === 0) {
|
|
|
+ alert("No files selected for import.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ selectedFolders.each(function() {
|
|
|
+ const path = $(this).data('path');
|
|
|
+ const type = "link";
|
|
|
+ addMediaFileToEditor(path, type);
|
|
|
+ });
|
|
|
+
|
|
|
+ selectedFiles.each(function() {
|
|
|
+ const path = $(this).data('path');
|
|
|
+ const type = $(this).data('type');
|
|
|
+ addMediaFileToEditor(path, type);
|
|
|
+ });
|
|
|
+
|
|
|
+ $("#postengine_media_selector").modal("hide");
|
|
|
+ });
|
|
|
+
|
|
|
+ function isWebSafeFile(fileName) {
|
|
|
+ const webSafeExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'webm', 'mp3', 'ogg'];
|
|
|
+ const extension = fileName.split('.').pop().toLowerCase();
|
|
|
+ return webSafeExtensions.includes(extension);
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFileType(fileName) {
|
|
|
+ const extension = fileName.split('.').pop().toLowerCase();
|
|
|
+ if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(extension)) {
|
|
|
+ return 'image';
|
|
|
+ } else if (extension === 'webm') {
|
|
|
+ return 'video';
|
|
|
+ } else if (['mp3', 'ogg'].includes(extension)) {
|
|
|
+ return 'audio';
|
|
|
+ } else {
|
|
|
+ return 'unknown';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function openMediaSelector(editor){
|
|
|
+ // Reset history
|
|
|
+ pathHistory = []; // Reset path history
|
|
|
+ fetchDirectoryContents(currentDir);
|
|
|
+ $("#postengine_media_selector").modal("show");
|
|
|
+ }
|
|
|
+
|
|
|
+ function addMediaFileToEditor(mediaLink, mediaType){
|
|
|
+ if (mediaType === 'image') {
|
|
|
+ simplemde.codemirror.replaceSelection(``);
|
|
|
+ } else if (mediaType === 'video') {
|
|
|
+ let mimeType = mediaLink.split('.').pop().toLowerCase() === 'webm' ? 'video/webm' : 'video/mp4';
|
|
|
+ simplemde.codemirror.replaceSelection(`<video style="background:black;" width="720" height="480" controls><source src="${mediaLink}" type="${mimeType}"></video>`);
|
|
|
+ } else if (mediaType === 'audio') {
|
|
|
+ simplemde.codemirror.replaceSelection(`<audio style="min-width: 512px;" controls><source src="${mediaLink}" type="audio/mpeg">Your browser does not support the audio element.</audio>`);
|
|
|
+ } else if (mediaType === 'link') {
|
|
|
+ let folderName = mediaLink.split("/").pop(); // Extract folder name from path
|
|
|
+ simplemde.codemirror.replaceSelection(`[${folderName}](${mediaLink})`);
|
|
|
+ } else {
|
|
|
+ alert("Unsupported media type!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFileTypeIcons(fileName) {
|
|
|
+ const extension = fileName.split('.').pop().toLowerCase();
|
|
|
+ if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(extension)) {
|
|
|
+ return '<i class="blue file image icon"></i>';
|
|
|
+ } else if (extension === 'webm') {
|
|
|
+ return '<i class="violet file video icon"></i>';
|
|
|
+ } else if (['mp3', 'ogg'].includes(extension)) {
|
|
|
+ return '<i class="green file audio icon"></i>';
|
|
|
+ } else {
|
|
|
+ return '<i class="file icon"></i>';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // Initialize with root directory
|
|
|
+ fetchDirectoryContents(currentDir);
|
|
|
+
|
|
|
+ // Save button functionality
|
|
|
+ $('#saveButton').on('click', function() {
|
|
|
+ const title = $('#title').val();
|
|
|
+ const tags = $('#tags').val();
|
|
|
+ const content = simplemde.value();
|
|
|
+
|
|
|
+ console.log("Title:", title);
|
|
|
+ console.log("Tags:", tags);
|
|
|
+ console.log("Content:", content);
|
|
|
+
|
|
|
+ // Add your save logic here
|
|
|
+ alert("Content saved successfully!");
|
|
|
+ });
|
|
|
+
|
|
|
+ // Save Draft button functionality
|
|
|
+ $('#saveDraftButton').on('click', function() {
|
|
|
+ const title = $('#title').val();
|
|
|
+ const content = simplemde.value();
|
|
|
+
|
|
|
+ if (title.trim() === "" && content.trim() === "") {
|
|
|
+ alert("Cannot save an empty draft.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ localStorage.setItem('draftTitle', title);
|
|
|
+ localStorage.setItem('draftContent', content);
|
|
|
+
|
|
|
+ msgbox("Draft saved successfully!");
|
|
|
+ });
|
|
|
+
|
|
|
+ // Discard Draft button functionality
|
|
|
+ $('#discardDraftButton').on('click', function() {
|
|
|
+ if (confirm("Are you sure you want to discard the draft? This action cannot be undone.")) {
|
|
|
+ localStorage.removeItem('draftTitle');
|
|
|
+ localStorage.removeItem('draftContent');
|
|
|
+
|
|
|
+ $('#title').val('');
|
|
|
+ simplemde.value('');
|
|
|
+
|
|
|
+ msgbox("Draft discarded successfully!");
|
|
|
+
|
|
|
+ $('#loadDraftMessage').addClass('hidden');
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // Load draft from localStorage on page load
|
|
|
+ const savedTitle = localStorage.getItem('draftTitle');
|
|
|
+ const savedContent = localStorage.getItem('draftContent');
|
|
|
+
|
|
|
+ if (savedTitle || savedContent) {
|
|
|
+ $('#loadDraftMessage').removeClass('hidden');
|
|
|
+
|
|
|
+ $('#loadDraftButton').on('click', function() {
|
|
|
+ $('#title').val(savedTitle || '');
|
|
|
+ simplemde.value(savedContent || '');
|
|
|
+ $('#loadDraftMessage').addClass('hidden');
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+</script>
|