becca_entities_brevision.js.html 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: becca/entities/brevision.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: becca/entities/brevision.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>"use strict";
  20. const protectedSessionService = require('../../services/protected_session');
  21. const utils = require('../../services/utils');
  22. const dateUtils = require('../../services/date_utils');
  23. const becca = require('../becca');
  24. const AbstractBeccaEntity = require("./abstract_becca_entity");
  25. const sql = require("../../services/sql");
  26. const BAttachment = require("./battachment");
  27. /**
  28. * Revision represents a snapshot of note's title and content at some point in the past.
  29. * It's used for seamless note versioning.
  30. *
  31. * @extends AbstractBeccaEntity
  32. */
  33. class BRevision extends AbstractBeccaEntity {
  34. static get entityName() { return "revisions"; }
  35. static get primaryKeyName() { return "revisionId"; }
  36. static get hashedProperties() { return ["revisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated",
  37. "utcDateLastEdited", "utcDateCreated", "utcDateModified", "blobId"]; }
  38. constructor(row, titleDecrypted = false) {
  39. super();
  40. /** @type {string} */
  41. this.revisionId = row.revisionId;
  42. /** @type {string} */
  43. this.noteId = row.noteId;
  44. /** @type {string} */
  45. this.type = row.type;
  46. /** @type {string} */
  47. this.mime = row.mime;
  48. /** @type {boolean} */
  49. this.isProtected = !!row.isProtected;
  50. /** @type {string} */
  51. this.title = row.title;
  52. /** @type {string} */
  53. this.blobId = row.blobId;
  54. /** @type {string} */
  55. this.dateLastEdited = row.dateLastEdited;
  56. /** @type {string} */
  57. this.dateCreated = row.dateCreated;
  58. /** @type {string} */
  59. this.utcDateLastEdited = row.utcDateLastEdited;
  60. /** @type {string} */
  61. this.utcDateCreated = row.utcDateCreated;
  62. /** @type {string} */
  63. this.utcDateModified = row.utcDateModified;
  64. /** @type {int} */
  65. this.contentLength = row.contentLength;
  66. if (this.isProtected &amp;&amp; !titleDecrypted) {
  67. this.title = protectedSessionService.isProtectedSessionAvailable()
  68. ? protectedSessionService.decryptString(this.title)
  69. : "[protected]";
  70. }
  71. }
  72. getNote() {
  73. return becca.notes[this.noteId];
  74. }
  75. /** @returns {boolean} true if the note has string content (not binary) */
  76. hasStringContent() {
  77. return utils.isStringNote(this.type, this.mime);
  78. }
  79. isContentAvailable() {
  80. return !this.revisionId // new note which was not encrypted yet
  81. || !this.isProtected
  82. || protectedSessionService.isProtectedSessionAvailable()
  83. }
  84. /*
  85. * Note revision content has quite special handling - it's not a separate entity, but a lazily loaded
  86. * part of Revision entity with its own sync. The reason behind this hybrid design is that
  87. * content can be quite large, and it's not necessary to load it / fill memory for any note access even
  88. * if we don't need a content, especially for bulk operations like search.
  89. *
  90. * This is the same approach as is used for Note's content.
  91. */
  92. /** @returns {string|Buffer} */
  93. getContent() {
  94. return this._getContent();
  95. }
  96. /**
  97. * @returns {*}
  98. * @throws Error in case of invalid JSON */
  99. getJsonContent() {
  100. const content = this.getContent();
  101. if (!content || !content.trim()) {
  102. return null;
  103. }
  104. return JSON.parse(content);
  105. }
  106. /** @returns {*|null} valid object or null if the content cannot be parsed as JSON */
  107. getJsonContentSafely() {
  108. try {
  109. return this.getJsonContent();
  110. }
  111. catch (e) {
  112. return null;
  113. }
  114. }
  115. /**
  116. * @param content
  117. * @param {object} [opts]
  118. * @param {object} [opts.forceSave=false] - will also save this BRevision entity
  119. */
  120. setContent(content, opts) {
  121. this._setContent(content, opts);
  122. }
  123. /** @returns {BAttachment[]} */
  124. getAttachments() {
  125. return sql.getRows(`
  126. SELECT attachments.*
  127. FROM attachments
  128. WHERE ownerId = ?
  129. AND isDeleted = 0`, [this.revisionId])
  130. .map(row => new BAttachment(row));
  131. }
  132. /** @returns {BAttachment|null} */
  133. getAttachmentById(attachmentId, opts = {}) {
  134. opts.includeContentLength = !!opts.includeContentLength;
  135. const query = opts.includeContentLength
  136. ? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength
  137. FROM attachments
  138. JOIN blobs USING (blobId)
  139. WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`
  140. : `SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`;
  141. return sql.getRows(query, [this.revisionId, attachmentId])
  142. .map(row => new BAttachment(row))[0];
  143. }
  144. /** @returns {BAttachment[]} */
  145. getAttachmentsByRole(role) {
  146. return sql.getRows(`
  147. SELECT attachments.*
  148. FROM attachments
  149. WHERE ownerId = ?
  150. AND role = ?
  151. AND isDeleted = 0
  152. ORDER BY position`, [this.revisionId, role])
  153. .map(row => new BAttachment(row));
  154. }
  155. /** @returns {BAttachment} */
  156. getAttachmentByTitle(title) {
  157. // cannot use SQL to filter by title since it can be encrypted
  158. return this.getAttachments().filter(attachment => attachment.title === title)[0];
  159. }
  160. beforeSaving() {
  161. super.beforeSaving();
  162. this.utcDateModified = dateUtils.utcNowDateTime();
  163. }
  164. getPojo() {
  165. return {
  166. revisionId: this.revisionId,
  167. noteId: this.noteId,
  168. type: this.type,
  169. mime: this.mime,
  170. isProtected: this.isProtected,
  171. title: this.title,
  172. blobId: this.blobId,
  173. dateLastEdited: this.dateLastEdited,
  174. dateCreated: this.dateCreated,
  175. utcDateLastEdited: this.utcDateLastEdited,
  176. utcDateCreated: this.utcDateCreated,
  177. utcDateModified: this.utcDateModified,
  178. content: this.content, // used when retrieving full note revision to frontend
  179. contentLength: this.contentLength
  180. };
  181. }
  182. getPojoToSave() {
  183. const pojo = this.getPojo();
  184. delete pojo.content; // not getting persisted
  185. delete pojo.contentLength; // not getting persisted
  186. if (pojo.isProtected) {
  187. if (protectedSessionService.isProtectedSessionAvailable()) {
  188. pojo.title = protectedSessionService.encrypt(this.title);
  189. }
  190. else {
  191. // updating protected note outside of protected session means we will keep original ciphertexts
  192. delete pojo.title;
  193. }
  194. }
  195. return pojo;
  196. }
  197. }
  198. module.exports = BRevision;
  199. </code></pre>
  200. </article>
  201. </section>
  202. </div>
  203. <nav>
  204. <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-sql.html">sql</a></li></ul><h3>Classes</h3><ul><li><a href="AbstractBeccaEntity.html">AbstractBeccaEntity</a></li><li><a href="BAttachment.html">BAttachment</a></li><li><a href="BAttribute.html">BAttribute</a></li><li><a href="BBranch.html">BBranch</a></li><li><a href="BEtapiToken.html">BEtapiToken</a></li><li><a href="BNote.html">BNote</a></li><li><a href="BOption.html">BOption</a></li><li><a href="BRecentNote.html">BRecentNote</a></li><li><a href="BRevision.html">BRevision</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li></ul><h3>Global</h3><ul><li><a href="global.html#api">api</a></li></ul>
  205. </nav>
  206. <br class="clear">
  207. <footer>
  208. Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.2</a>
  209. </footer>
  210. <script> prettyPrint(); </script>
  211. <script src="scripts/linenumber.js"> </script>
  212. </body>
  213. </html>