Desktop markdown wiki app. Built with node, Electron Framework and AngularJS.

file-service.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. angular.module('codexApp')
  2. .service('FileService', [ '$rootScope', '$http', 'ThumbnailService', '$state', function($rootScope, $http, ThumbnailService, $state) {
  3. var defaultUserContentPath = "";
  4. var appDataPath = "";
  5. var appData = {};
  6. var notes_dir = "";
  7. var getAppData = function(){
  8. var remote = require('remote');
  9. var app = remote.require('app');
  10. appDataPath = app.getPath("userData");
  11. defaultUserContentPath = app.getPath("home") + "/Documents/codex";
  12. findOrGenerateUserDataFile(appDataPath, defaultUserContentPath);
  13. var raw_data = fs.readFileSync(appDataPath + '/userData.json', 'utf8');
  14. var data = JSON.parse(raw_data);
  15. appData = data;
  16. notes_dir = appData.UserDataDirectory;
  17. return data
  18. }
  19. var findOrGenerateUserDataFile = function(path, defaultUserContentPath) {
  20. var generate_settings = true;
  21. fs = require('fs')
  22. fs.readdirSync(path).forEach(function(file) {
  23. if(file == "UserData.json"){
  24. console.log("-> Loading Settings");
  25. generate_settings = false;
  26. }
  27. });
  28. if (generate_settings){
  29. // Generate file
  30. file_path = path + "/UserData.json";
  31. console.log("-> Generating user settings file: '" + file_path + "'");
  32. var content = '{ "UserDataDirectory" : "' + defaultUserContentPath +'" }';
  33. mkdirSync(defaultUserContentPath);
  34. console.log(content);
  35. saveAppData(JSON.parse(content));
  36. return true;
  37. }
  38. }
  39. var saveAppData = function(data) {
  40. console.log("-> Saving user data...");
  41. console.log(data);
  42. fs.writeFileSync(appDataPath + "/UserData.json", JSON.stringify(data), 'utf8');
  43. }
  44. var mkdirSync = function (path) {
  45. try {
  46. fs.mkdirSync(path);
  47. } catch(e) {
  48. if ( e.code != 'EEXIST' ) throw e;
  49. }
  50. }
  51. getAppData();
  52. console.log("-> Loading content from folder: " + appData.UserDataDirectory);
  53. var default_notes_dir = "/Users/james/dev/codex/codex/inbox";
  54. var default_home_note = "/Users/james/dev/codex/codex/index.md"
  55. var notes = [];
  56. var current_note = "";
  57. var note_history = [];
  58. var note_history_index = 0;
  59. var prettySize = function(bytes) {
  60. if (bytes <= 1024) {
  61. return bytes + " KB"
  62. } else {
  63. var megabytes = parseFloat(Math.round(bytes/1024 * 100) / 100).toFixed(0);
  64. return megabytes + " MB"
  65. }
  66. }
  67. var getFilePathExtension = function(path) {
  68. var filename = path.split('\\').pop().split('/').pop();
  69. var lastIndex = filename.lastIndexOf(".");
  70. if (lastIndex < 1) return "";
  71. return filename.substr(lastIndex + 1);
  72. }
  73. var getFileType = function(path) {
  74. var extension = getFilePathExtension(path);
  75. if (typeof(extension)==='undefined' || extension == "") extension = 'dir';
  76. switch (extension) {
  77. case "pdf":
  78. return "Document";
  79. case "jpg":
  80. return "Image";
  81. case "png":
  82. return "Image";
  83. case "md":
  84. return "Markdown";
  85. case 'dir':
  86. return "Folder";
  87. default:
  88. return "File";
  89. }
  90. }
  91. var directorySize = function(dir) {
  92. var filesystem = require("fs");
  93. var size = 0;
  94. var results = [];
  95. filesystem.readdirSync(dir).forEach(function(file) {
  96. file_path = dir+'/'+file;
  97. var stat = filesystem.statSync(file_path);
  98. if (stat && stat.isDirectory()) {
  99. results = results.concat(directorySize(file_path))
  100. } else {
  101. if(file != ".DS_Store") {
  102. size = size + stat["size"];
  103. //console.log("* " + stat["size"] + " KB -> " + file_path)
  104. }
  105. }
  106. });
  107. return size;
  108. }
  109. var noteCount = function(){
  110. var count = $scope.notes.length
  111. switch (count) {
  112. case 0:
  113. return '0 notes';
  114. case 1:
  115. return '1 note';
  116. default:
  117. return count + ' notes';
  118. }
  119. }
  120. var getThumbnail = function(file_path) {
  121. var type = getFileType(file_path);
  122. switch (type) {
  123. case "Markdown":
  124. return ThumbnailService.createNoteThumbnail(file_path);
  125. default:
  126. return "";
  127. }
  128. }
  129. var isValidFile = function(file) {
  130. if(file != ".DS_Store" && file != "info.json") {
  131. var parts = file.split('.');
  132. if (parts[parts.length -2] == "thumb") {
  133. return false;
  134. } else {
  135. return true
  136. }
  137. }
  138. }
  139. var SetFileInfo = function(jsonData, dir, file_path, stat) {
  140. //console.log(jsonData.title);
  141. if (typeof(jsonData)==='undefined') jsonData = {};
  142. if(jsonData.title != "" && jsonData.title != undefined){
  143. var title = jsonData.title;
  144. } else {
  145. var title = getNameFromPath(file_path);
  146. }
  147. if(jsonData.thumbnail != "" && jsonData.thumbnail != undefined){
  148. var thumbnail_path = jsonData.thumbnail;
  149. } else {
  150. var thumbnail_path = getThumbnail(file_path);
  151. }
  152. var file_obj = {
  153. title: title,
  154. path: file_path,
  155. size: prettySize(directorySize(dir)),
  156. type: getFileType(file_path),
  157. author: jsonData.author,
  158. index: jsonData.index,
  159. id: jsonData.id,
  160. created_at: dateFormat(stat["birthdate"], "mediumDate"),
  161. modified_at: dateFormat(stat["mtime"], "mediumDate"),
  162. accessed_at: dateFormat(stat["atime"], "mediumDate"),
  163. thumbnail: thumbnail_path
  164. }
  165. return file_obj;
  166. }
  167. var getNotesFromFolders = function(dir) {
  168. if (typeof(dir)==='undefined') dir = notes_dir;
  169. var filesystem = require("fs");
  170. filesystem.readdirSync(dir).forEach(function(file) {
  171. file_path = dir+'/'+file;
  172. var stat = filesystem.statSync(file_path);
  173. if (stat && stat.isDirectory()) {
  174. notes = notes.concat(getNotesFromFolders(file_path))
  175. } else {
  176. if(file == "info.json"){
  177. filesystem.readFile(file_path, function(err, data) {
  178. var jsonData = JSON.parse(data);
  179. var file_obj = SetFileInfo(jsonData, dir, file_path, stat)
  180. notes.push(file_obj);
  181. });
  182. }
  183. }
  184. });
  185. return notes;
  186. };
  187. this.getNotesFromFolders = getNotesFromFolders;
  188. var getAllFilesFromFolder = function(dir) {
  189. if (typeof(dir)==='undefined') dir = notes_dir;
  190. var filesystem = require("fs");
  191. var results = [];
  192. filesystem.readdirSync(dir).forEach(function(file) {
  193. file_path = dir+'/'+file;
  194. var stat = filesystem.statSync(file_path);
  195. if (stat && stat.isDirectory()) {
  196. results = results.concat(getAllFilesFromFolder(file_path))
  197. } else {
  198. if(isValidFile(file)) {
  199. var jsonData = {};
  200. var file_obj = SetFileInfo(jsonData, dir, file_path, stat)
  201. results.push(file_obj);
  202. }
  203. }
  204. //console.log(file_obj);
  205. });
  206. $rootScope.$broadcast('file-service:files-loaded');
  207. return results;
  208. };
  209. var getFilesFromFolder = function(dir) {
  210. if (typeof(dir)==='undefined') dir = notes_dir;
  211. var filesystem = require("fs");
  212. var results = [];
  213. filesystem.readdirSync(dir).forEach(function(file) {
  214. file_path = dir+'/'+file;
  215. var stat = filesystem.statSync(file_path);
  216. if(isValidFile(file)) {
  217. var jsonData = {};
  218. var file_obj = SetFileInfo(jsonData, dir, file_path, stat)
  219. results.push(file_obj);
  220. }
  221. });
  222. $rootScope.$broadcast('file-service:files-loaded');
  223. return results;
  224. };
  225. var findNoteInFolder = function(note_id, dir) {
  226. if (typeof(dir)==='undefined') dir = notes_dir;
  227. var filesystem = require("fs");
  228. var results = [];
  229. filesystem.readdirSync(dir).forEach(function(file) {
  230. file_path = dir+'/'+file;
  231. var stat = filesystem.statSync(file_path);
  232. if (stat && stat.isDirectory()) {
  233. results = results.concat(findNoteInFolder(note_id, file_path))
  234. } else {
  235. if(file == "info.json"){
  236. filesystem.readFile(file_path, function(err, data) {
  237. var jsonData = JSON.parse(data);
  238. console.log(jsonData.id)
  239. if(jsonData.id == note_id){
  240. var file_obj = SetFileInfo(jsonData, dir, file_path, stat)
  241. results.push(file_obj);
  242. }
  243. });
  244. }
  245. }
  246. });
  247. console.log(results);
  248. return results[0];
  249. };
  250. this.saveFile = function(file_path, content){
  251. var fs = require('fs');
  252. fs.writeFile(file_path, content, 'utf8', function(err) {
  253. if(err) {
  254. console.log("-> ERROR SAVING FILE: " + file_path);
  255. console.log(err);
  256. } else {
  257. console.log("-> FILE SAVED: " + file_path);
  258. }
  259. });
  260. }
  261. var getNote = function(file_path){
  262. var filesystem = require("fs");
  263. var stat = filesystem.statSync(file_path);
  264. var file_obj = {
  265. title: "",
  266. path: file_path,
  267. size: stat['size'],
  268. type: getFileType(file_path),
  269. author: "",
  270. index: "",
  271. id: "",
  272. created_at: dateFormat(stat["birthdate"], "mediumDate"),
  273. modified_at: dateFormat(stat["mtime"], "mediumDate"),
  274. accessed_at: dateFormat(stat["atime"], "mediumDate")
  275. }
  276. return file_obj
  277. }
  278. var getNameFromPath = function(path) {
  279. path = path.split('/');
  280. var filename = path[path.length - 1];
  281. //filename = filename.split('.');
  282. //filename.pop();
  283. //name = filename[filename.length -1];
  284. return filename
  285. }
  286. var getUrlParts = function(url) {
  287. var a = document.createElement('a');
  288. a.href = url;
  289. return {
  290. href: a.href,
  291. host: a.host,
  292. hostname: a.hostname,
  293. port: a.port,
  294. pathname: a.pathname,
  295. protocol: a.protocol,
  296. hash: a.hash,
  297. search: a.search
  298. };
  299. }
  300. var absoluteToRelativeURL = function(current_url, absolute_url) {
  301. //console.log(current_url);
  302. //console.log(absolute_url);
  303. // split urls and create arrays
  304. var current_path = current_url.split('/');
  305. var absolute_path = getUrlParts(absolute_url).pathname.split('/');
  306. var root_path = getUrlParts(notes_dir).pathname.split('/');
  307. // remove the current note's filename from the url and the image filename from the url
  308. //current_path.pop();
  309. current_path.shift();
  310. //absolute_path.shift();
  311. // count how many folders the current path has
  312. var current_path_count = 0;
  313. for (var i = 0; i < current_path.length; i++) {
  314. current_path_count = current_path_count + 1;
  315. }
  316. // count how many folders the absolute path has
  317. var absolute_path_count = 0;
  318. for (var i = 0; i < absolute_path.length; i++) {
  319. absolute_path_count = absolute_path_count + 1;
  320. }
  321. var root_path_count = 0;
  322. for (var i = 0; i < root_path.length; i++) {
  323. root_path_count = root_path_count + 1;
  324. }
  325. var count = current_path_count - root_path_count;
  326. //console.log(current_path_count + " - " + root_path_count + " = " + count);
  327. //dif = current_path_count - (absolute_path_count -1);
  328. for (var i = 0; i < count; i++) {
  329. absolute_path.shift();
  330. }
  331. // make the relative path a string again
  332. var relative_path = absolute_path.join('/');
  333. //console.log("* Converted relative URL: " + relative_path)
  334. return relative_path;
  335. }
  336. // Absolute to relative URL
  337. this.absoluteToRelativeURL = function(current_url, absolute_url) {
  338. return absoluteToRelativeURL(current_url, absolute_url);
  339. }
  340. var shortenPath = function(path) {
  341. //console.log(current_url);
  342. //console.log(absolute_url);
  343. // split urls and create arrays
  344. var current_path = path.split('/');
  345. var root_path = getUrlParts(notes_dir).pathname.split('/');
  346. // count how many folders the current path has
  347. var current_path_count = 0;
  348. for (var i = 0; i < current_path.length; i++) {
  349. current_path_count = current_path_count + 1;
  350. }
  351. var root_path_count = 0;
  352. for (var i = 0; i < root_path.length; i++) {
  353. root_path_count = root_path_count + 1;
  354. }
  355. var diff = current_path_count - root_path_count;
  356. var count = current_path_count - diff;
  357. console.log(current_path_count + " - " + root_path_count + " = " + count);
  358. //dif = current_path_count - (absolute_path_count -1);
  359. for (var i = 0; i < count; i++) {
  360. current_path.shift();
  361. }
  362. // make the relative path a string again
  363. var relative_path = current_path.join('/');
  364. console.log("* Shortened Path: " + relative_path)
  365. return relative_path;
  366. }
  367. // Absolute to relative URL
  368. this.shortenPath = function(path) {
  369. return shortenPath(path);
  370. }
  371. // Get folders
  372. var getFolders = function(root) {
  373. if (typeof(root)==='undefined') root = notes_dir;
  374. var filesystem = require("fs");
  375. var results = [];
  376. filesystem.readdirSync(root).forEach(function(file) {
  377. file_path = root+'/'+file;
  378. var stat = filesystem.statSync(file_path);
  379. if (stat && stat.isDirectory()) {
  380. results.push(SetFileInfo( undefined, root, file_path, stat));
  381. }
  382. });
  383. //console.log(results);
  384. return results;
  385. }
  386. this.getFolders = function(root){
  387. return getFolders(root);
  388. }
  389. // Sort files
  390. var date_sort_asc = function (date1, date2) {
  391. if (date1.modified_at > date2.modified_at) return 1;
  392. if (date1.modified_at < date2.modified_at) return -1;
  393. return 0;
  394. };
  395. var date_sort_desc = function (date1, date2) {
  396. if (date1.modified_at > date2.modified_at) return -1;
  397. if (date1.modified_at < date2.modified_at) return 1;
  398. return 0;
  399. };
  400. var filterNotes = function(files) {
  401. var filtered = [];
  402. for (var i = 0; i < files.length; i++) {
  403. if(files[i].type == "Markdown") {
  404. filtered.push(files[i]);
  405. }
  406. }
  407. return filtered;
  408. }
  409. var deleteFile = function(file){
  410. var fs = require('fs');
  411. fs.unlinkSync(file);
  412. console.log('-> Successfully Deleted ' + file);
  413. return true;
  414. }
  415. // RESPONSE
  416. this.getAllFiles = function(dir) {
  417. getAppData();
  418. if (typeof(dir)==='undefined') dir = notes_dir;
  419. notes = getAllFilesFromFolder(dir);
  420. return notes.sort(date_sort_asc);
  421. }
  422. this.getFiles = function(dir) {
  423. getAppData();
  424. if (typeof(dir)==='undefined') dir = notes_dir;
  425. notes = getFilesFromFolder(dir);
  426. return notes.sort(date_sort_asc);
  427. }
  428. this.getAllNotes = function() {
  429. getAppData();
  430. notes = getAllFilesFromFolder();
  431. notes = filterNotes(notes);
  432. return notes.sort(date_sort_asc);
  433. }
  434. this.getNote = function(path) {
  435. return getNote(path);
  436. }
  437. this.getCurrentNote = function() {
  438. return current_note;
  439. }
  440. this.setCurrentNote = function(note) {
  441. //console.log("searcing for: " + note_id)
  442. current_note = note;
  443. if((note_history.length -1) != note_history_index){
  444. var dif = note_history.length - note_history_index - 1;
  445. for (var i = 0; i < dif; i++) {
  446. note_history.pop();
  447. }
  448. }
  449. note_history.push(current_note);
  450. note_history_index = note_history.length -1;
  451. //console.log(current_note);
  452. //console.log("Current_note: " + current_note.title)
  453. }
  454. this.goToPreviousNote = function(){
  455. if(note_history_index > 0) {
  456. note_history_index = note_history_index - 1;
  457. current_note = note_history[note_history_index];
  458. }
  459. console.log(current_note);
  460. }
  461. this.goToNextNote = function(){
  462. if(note_history_index < (note_history.length - 1)){
  463. note_history_index = note_history_index + 1;
  464. current_note = note_history[note_history_index];
  465. }
  466. }
  467. this.setNotesDir = function(dir) {
  468. appData.UserDataDirectory = dir[0];
  469. saveAppData(appData);
  470. notes_dir = dir[0];
  471. }
  472. this.getNotesDir = function() {
  473. return notes_dir;
  474. }
  475. this.getDefaultNotesDir = function() {
  476. return default_notes_dir;
  477. }
  478. this.getDefaultNote = function() {
  479. return getNote(default_home_note);
  480. }
  481. this.changeController = function(){
  482. switch (current_note.type) {
  483. case "Markdown":
  484. $state.go("note-view");
  485. break;
  486. case "Folder":
  487. $state.go("index");
  488. break;
  489. }
  490. }
  491. this.deleteFile = function(file) {
  492. return deleteFile(file);
  493. }
  494. }])