Browse Source

Insert Image model + Uploads Folders list

NGPixel 8 years ago
parent
commit
567e1307da

+ 21 - 0
agent.js

@@ -75,12 +75,15 @@ var job = new cron({
 
 		let jobs = [];
 		let repoPath = path.resolve(ROOTPATH, appconfig.datadir.repo);
+		let uploadsPath = path.join(repoPath, 'uploads');
 
 		// ----------------------------------------
 		// Compile Jobs
 		// ----------------------------------------
 
+		//*****************************************
 		//-> Resync with Git remote
+		//*****************************************
 
 		jobs.push(git.onReady.then(() => {
 			return git.resync().then(() => {
@@ -140,6 +143,24 @@ var job = new cron({
 			});
 		}));
 
+		//*****************************************
+		//-> Refresh uploads data
+		//*****************************************
+
+		jobs.push(fs.readdirAsync(uploadsPath).then((ls) => {
+
+			return Promise.map(ls, (f) => {
+				return fs.statAsync(path.join(uploadsPath, f)).then((s) => { return { filename: f, stat: s }; });
+			}).filter((s) => { return s.stat.isDirectory(); }).then((arrStats) => {
+				ws.emit('uploadsSetFolders', {
+					auth: WSInternalKey,
+					content: _.map(arrStats, 'filename')
+				});
+				return true;
+			});
+
+		}));
+
 		// ----------------------------------------
 		// Run
 		// ----------------------------------------

File diff suppressed because it is too large
+ 0 - 0
assets/css/app.css


File diff suppressed because it is too large
+ 0 - 0
assets/js/app.js


+ 42 - 11
client/js/components/editor-image.js

@@ -2,23 +2,54 @@
 let vueImage = new Vue({
 	el: '#modal-editor-image',
 	data: {
-		modeSelected: 'text'
+		isLoading: false,
+		isLoadingText: '',
+		newFolderName: '',
+		newFolderShow: false,
+		folders: [],
+		currentFolder: '',
+		images: []
 	},
 	methods: {
+		open: () => {
+			mdeModalOpenState = true;
+			$('#modal-editor-image').slideDown();
+			vueImage.refreshFolders();
+		},
 		cancel: (ev) => {
 			mdeModalOpenState = false;
 			$('#modal-editor-image').slideUp();
 		},
-		insertImage: (ev) => {
-
-			if(mde.codemirror.doc.somethingSelected()) {
-				mde.codemirror.execCommand('singleSelection');
-			}
-			let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n';
-
-			mde.codemirror.doc.replaceSelection(codeBlockText);
-			vueCodeBlock.cancel();
-
+		newFolder: (ev) => {
+			vueImage.newFolderShow = true;
+		},
+		newFolderDiscard: (ev) => {
+			vueImage.newFolderShow = false;
+		},
+		selectFolder: (fldName) => {
+			vueImage.currentFolder = fldName;
+			vueImage.loadImages();
+		},
+		refreshFolders: () => {
+			vueImage.isLoading = true;
+			vueImage.isLoadingText = 'Fetching folders list...';
+			vueImage.currentFolder = '';
+			Vue.nextTick(() => {
+				socket.emit('uploadsGetFolders', { }, (data) => {
+					vueImage.folders = data;
+					vueImage.loadImages();
+				});
+			});
+		},
+		loadImages: () => {
+			vueImage.isLoading = true;
+			vueImage.isLoadingText = 'Fetching images...';
+			Vue.nextTick(() => {
+				socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => {
+					vueImage.images = data;
+					vueImage.isLoading = false;
+				});
+			});
 		}
 	}
 });

+ 1 - 2
client/js/components/editor.js

@@ -90,8 +90,7 @@ if($('#mk-editor').length === 1) {
 				name: "image",
 				action: (editor) => {
 					if(!mdeModalOpenState) {
-						mdeModalOpenState = true;
-						$('#modal-editor-image').slideDown();
+						vueImage.open();
 					}
 				},
 				className: "fa fa-image",

+ 32 - 0
client/scss/components/_editor.scss

@@ -33,6 +33,38 @@
 
 }
 
+.editor-modal-load {
+
+	display: flex;
+	align-items: center;
+	opacity: 0;
+	transition: opacity .5s ease;
+
+	span {
+		font-size: 12px;
+		color: $blue;
+	}
+
+	i {
+		margin-left: 10px;
+		width: 32px;
+		height: 32px;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		&::before {
+			content: " ";
+			@include spinner($blue,0.5s,24px);
+		}
+	}
+
+	&.is-active {
+		opacity: 1;
+	}
+
+}
+
 .CodeMirror {
 	border-left: none;
 	border-right: none;

+ 11 - 0
client/scss/layout/_content.scss

@@ -14,6 +14,17 @@
 
 }
 
+.menu {
+	.menu-list a, .menu-nav a {
+		display: flex;
+		align-items: center;
+	}
+
+	.menu-list .icon {
+		margin-right: 8px
+	}
+}
+
 .section.is-small {
 	padding: 20px 20px;
 }

+ 1 - 1
models/db.js

@@ -6,7 +6,7 @@ var loki = require('lokijs'),
 	 Promise = require('bluebird'),
 	 _ = require('lodash');
 
-var cols = ['User','Entry'];
+var cols = ['User', 'Entry'];
 
 /**
  * Loki.js module

+ 1 - 1
models/git.js

@@ -119,7 +119,7 @@ module.exports = {
 			winston.error('Git remote error!');
 			throw err;
 		}).then(() => {
-			winston.info('[GIT] Git repository is now ready.');
+			winston.info('[GIT] Git repository is OK.');
 			return true;
 		});
 

+ 85 - 15
models/localdata.js

@@ -9,26 +9,96 @@ var fs = require('fs'),
  *
  * @param      {Object}  appconfig  The application configuration
  */
-module.exports = (appconfig) => {
+module.exports = {
 
-	// Create data directories
+	_uploadsPath: './repo/uploads',
+	_uploadsFolders: [],
 
-	try {
-		fs.mkdirSync(appconfig.datadir.db);
-	} catch (err) {
-		if(err.code !== 'EEXIST') {
-			winston.error(err);
-			process.exit(1);
+	/**
+	 * Initialize Local Data Storage model
+	 *
+	 * @param      {Object}  appconfig  The application config
+	 * @return     {Object}  Local Data Storage model instance
+	 */
+	init(appconfig, skipFolderCreation = false) {
+
+		let self = this;
+
+		self._uploadsPath = path.join(ROOTPATH, appconfig.datadir.db, 'uploads');
+
+		// Create data directories
+
+		if(!skipFolderCreation) {
+			self.createBaseDirectories(appconfig);
+		}
+
+		return self;
+
+	},
+
+	/**
+	 * Creates a base directories (Synchronous).
+	 *
+	 * @param      {Object}  appconfig  The application config
+	 * @return     {Void}  Void
+	 */
+	createBaseDirectories(appconfig) {
+
+		winston.info('[SERVER] Create data directories if they don\'t exist...');
+
+		try {
+			fs.mkdirSync(appconfig.datadir.db);
+		} catch (err) {
+			if(err.code !== 'EEXIST') {
+				winston.error(err);
+				process.exit(1);
+			}
+		}
+
+		try {
+			fs.mkdirSync(path.join(appconfig.datadir.db, 'cache'));
+		} catch (err) {
+			if(err.code !== 'EEXIST') {
+				winston.error(err);
+				process.exit(1);
+			}
 		}
-	}
 
-	try {
-		fs.mkdirSync(path.join(appconfig.datadir.db, 'cache'));
-	} catch (err) {
-		if(err.code !== 'EEXIST') {
-			winston.error(err);
-			process.exit(1);
+		try {
+			fs.mkdirSync(path.join(appconfig.datadir.db, 'thumbs'));
+		} catch (err) {
+			if(err.code !== 'EEXIST') {
+				winston.error(err);
+				process.exit(1);
+			}
 		}
+
+		winston.info('[SERVER] Data directories are OK.');
+
+		return;
+
+	},
+
+	/**
+	 * Sets the uploads folders.
+	 *
+	 * @param      {Array<String>}  arrFolders  The arr folders
+	 * @return     {Void}  Void
+	 */
+	setUploadsFolders(arrFolders) {
+
+		this._uploadsFolders = arrFolders;
+		return;
+
+	},
+
+	/**
+	 * Gets the uploads folders.
+	 *
+	 * @return     {Array<String>}  The uploads folders.
+	 */
+	getUploadsFolders() {
+		return this._uploadsFolders;
 	}
 
 };

+ 6 - 6
package.json

@@ -36,7 +36,7 @@
     "bcryptjs-then": "^1.0.1",
     "bluebird": "^3.4.6",
     "body-parser": "^1.15.2",
-    "bson": "^0.5.4",
+    "bson": "^0.5.5",
     "cheerio": "^0.22.0",
     "child-process-promise": "^2.1.3",
     "compression": "^1.6.2",
@@ -54,13 +54,13 @@
     "fs-extra": "^0.30.0",
     "git-wrapper2-promise": "^0.2.9",
     "highlight.js": "^9.6.0",
-    "i18next": "^3.4.1",
-    "i18next-express-middleware": "^1.0.1",
+    "i18next": "^3.4.2",
+    "i18next-express-middleware": "^1.0.2",
     "i18next-node-fs-backend": "^0.1.2",
     "js-yaml": "^3.6.1",
     "lodash": "^4.15.0",
     "lokijs": "^1.4.1",
-    "markdown-it": "^7.0.1",
+    "markdown-it": "^8.0.0",
     "markdown-it-abbr": "^1.0.4",
     "markdown-it-anchor": "^2.5.0",
     "markdown-it-attrs": "^0.7.0",
@@ -69,7 +69,7 @@
     "markdown-it-external-links": "0.0.5",
     "markdown-it-footnote": "^3.0.1",
     "markdown-it-task-lists": "^1.4.1",
-    "moment": "^2.14.1",
+    "moment": "^2.15.0",
     "moment-timezone": "^0.5.5",
     "passport": "^0.3.2",
     "passport-local": "^1.0.0",
@@ -82,7 +82,7 @@
     "snyk": "^1.19.1",
     "socket.io": "^1.4.8",
     "sticky-js": "^1.0.7",
-    "validator": "^5.6.0",
+    "validator": "^5.7.0",
     "validator-as-promised": "^1.0.2",
     "winston": "^2.2.0"
   },

+ 1 - 1
server.js

@@ -25,7 +25,7 @@ winston.add(winston.transports.Console, {
 winston.info('[SERVER] Requarks Wiki is initializing...');
 
 var appconfig = require('./models/config')('./config.yml');
-let lcdata = require('./models/localdata');
+let lcdata = require('./models/localdata').init(appconfig, false);
 
 global.db = require('./models/db')(appconfig);
 global.git = require('./models/git').init(appconfig, false);

+ 40 - 8
views/modals/editor-image.pug

@@ -4,18 +4,23 @@
 		
 		.columns
 			.column
-				h3 Image
+				h3 Insert Image
+
+			.column.is-narrow.editor-modal-load(v-bind:class="{ 'is-active': isLoading }")
+				span {{ isLoadingText }}
+				i
+					
 			.column.is-narrow
 				.control.is-grouped
 					p.control
-						a.button.is-dark.is-outlined(v-on:click="cancel")
+						a.button.is-dark.is-outlined(v-on:click="newFolder")
 							span.icon.is-small: i.fa.fa-folder
 							span New Folder
 					.control.has-addons
-						a.button.is-info.is-outlined(v-on:click="cancel")
+						a.button.is-info.is-outlined(v-on:click="uploadImage")
 							span.icon.is-small: i.fa.fa-upload
 							span Upload Image
-						a.button.is-info.is-outlined(v-on:click="cancel")
+						a.button.is-info.is-outlined(v-on:click="fetchFromUrl")
 							span.icon.is-small: i.fa.fa-download
 							span Fetch from URL
 			.column.is-narrow
@@ -27,9 +32,36 @@
 
 		.columns
 			.column.is-one-quarter(style={'max-width':'350px'})
-				.box
-					| Folders
+				.box(style={'max-height': '400px', overflow: 'auto', 'overflow-x': 'hidden'})
+					aside.menu
+						p.menu-label
+							| Folders
+						ul.menu-list
+							li
+								a(v-on:click="selectFolder('')", v-bind:class="{ 'is-active': currentFolder === '' }")
+									span.icon.is-small: i.fa.fa-folder-o
+									span /
+							li(v-for="fld in folders")
+								a(v-on:click="selectFolder(fld)", v-bind:class="{ 'is-active': currentFolder === fld }")
+									span.icon.is-small: i.fa.fa-folder
+									span / {{ fld }}
 			.column
-				| Files
+				figure.image.is-128x128
+					img(src='http://placehold.it/128x128')
 
-		
+	.modal(v-bind:class="{ 'is-active': newFolderShow }")
+		.modal-background
+		.modal-container
+			.modal-content
+				.card.is-fullwidth
+					header.card-header
+						p.card-header-title New Folder
+					.card-content
+						.content
+							label.label Enter the new folder name:
+							p.control
+								input.input(type='text', placeholder='folder name', v-model='newFolderName')
+								span.help.is-danger.is-hidden This folder name is invalid!
+					footer.card-footer
+						a.card-footer-item(v-on:click="newFolderDiscard") Discard
+						a.card-footer-item(v-on:click="newFolderCreate") Create

+ 51 - 32
ws-server.js

@@ -15,11 +15,11 @@ var _isDebug = process.env.NODE_ENV === 'development';
 global.winston = require('winston');
 winston.remove(winston.transports.Console)
 winston.add(winston.transports.Console, {
-  level: (_isDebug) ? 'info' : 'warn',
-  prettyPrint: true,
-  colorize: true,
-  silent: false,
-  timestamp: true
+	level: (_isDebug) ? 'info' : 'warn',
+	prettyPrint: true,
+	colorize: true,
+	silent: false,
+	timestamp: true
 });
 
 // ----------------------------------------
@@ -39,6 +39,7 @@ global.internalAuth = require('./lib/internalAuth').init(process.argv[2]);;
 winston.info('[WS] WS Server is initializing...');
 
 var appconfig = require('./models/config')('./config.yml');
+let lcdata = require('./models/localdata').init(appconfig, true);
 
 global.entries = require('./models/entries').init(appconfig);
 global.mark = require('./models/markdown');
@@ -66,7 +67,7 @@ global.app = express();
 // ----------------------------------------
 
 app.get('/', function(req, res){
-  res.send('Requarks Wiki WebSocket server');
+	res.send('Requarks Wiki WebSocket server');
 });
 
 // ----------------------------------------
@@ -80,41 +81,45 @@ var server = http.Server(app);
 var io = socketio(server);
 
 server.on('error', (error) => {
-  if (error.syscall !== 'listen') {
-    throw error;
-  }
-
-  switch (error.code) {
-    case 'EACCES':
-      console.error('Listening on port ' + appconfig.port + ' requires elevated privileges!');
-      process.exit(1);
-      break;
-    case 'EADDRINUSE':
-      console.error('Port ' + appconfig.port + ' is already in use!');
-      process.exit(1);
-      break;
-    default:
-      throw error;
-  }
+	if (error.syscall !== 'listen') {
+		throw error;
+	}
+
+	switch (error.code) {
+		case 'EACCES':
+			console.error('Listening on port ' + appconfig.port + ' requires elevated privileges!');
+			process.exit(1);
+			break;
+		case 'EADDRINUSE':
+			console.error('Port ' + appconfig.port + ' is already in use!');
+			process.exit(1);
+			break;
+		default:
+			throw error;
+	}
 });
 
 server.listen(appconfig.wsPort, () => {
-  winston.info('[WS] WebSocket server started successfully! [RUNNING]');
+	winston.info('[WS] WebSocket server started successfully! [RUNNING]');
 });
 
 io.on('connection', (socket) => {
 
+	//-----------------------------------------
+	// SEARCH
+	//-----------------------------------------
+
 	socket.on('searchAdd', (data) => {
 		if(internalAuth.validateKey(data.auth)) {
 			search.add(data.content);
 		}
 	});
 
-  socket.on('searchDel', (data, cb) => {
-    if(internalAuth.validateKey(data.auth)) {
-      search.delete(data.entryPath);
-    }
-  });
+	socket.on('searchDel', (data, cb) => {
+		if(internalAuth.validateKey(data.auth)) {
+			search.delete(data.entryPath);
+		}
+	});
 
 	socket.on('search', (data, cb) => {
 		search.find(data.terms).then((results) => {
@@ -122,11 +127,25 @@ io.on('connection', (socket) => {
 		});
 	});
 
-});
+	//-----------------------------------------
+	// UPLOADS
+	//-----------------------------------------
+
+	socket.on('uploadsSetFolders', (data, cb) => {
+		if(internalAuth.validateKey(data.auth)) {
+			lcdata.setUploadsFolders(data.content);
+		}
+	});
+
+	socket.on('uploadsGetFolders', (data, cb) => {
+		cb(lcdata.getUploadsFolders());
+	});
 
-/*setTimeout(() => {
-	search._si.searchAsync({ query: { AND: [{'*': ['unit']}] }}).then((stuff) => { console.log(stuff.hits); });
-}, 8000);*/
+	socket.on('uploadsGetImages', (data, cb) => {
+		cb([]);
+	});
+
+});
 
 // ----------------------------------------
 // Shutdown gracefully

Some files were not shown because too many files changed in this diff