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

file-service.js 18KB

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