2018-01-29 12:16:50 +08:00
"use strict" ;
2018-01-30 07:34:59 +08:00
const Entity = require ( './entity' ) ;
2018-08-13 16:59:31 +08:00
const Attribute = require ( './attribute' ) ;
2018-04-20 12:12:01 +08:00
const protectedSessionService = require ( '../services/protected_session' ) ;
2018-03-31 22:51:37 +08:00
const repository = require ( '../services/repository' ) ;
2018-04-03 08:46:46 +08:00
const dateUtils = require ( '../services/date_utils' ) ;
2018-01-29 12:16:50 +08:00
2018-08-16 04:06:49 +08:00
const LABEL = 'label' ;
2018-11-13 19:50:08 +08:00
const LABEL _DEFINITION = 'label-definition' ;
2018-08-16 04:06:49 +08:00
const RELATION = 'relation' ;
2018-11-13 19:50:08 +08:00
const RELATION _DEFINITION = 'relation-definition' ;
2018-08-16 04:06:49 +08:00
2018-08-23 05:37:06 +08:00
/ * *
* This represents a Note which is a central object in the Trilium Notes project .
*
* @ property { string } noteId - primary key
* @ property { string } type - one of "text" , "code" , "file" or "render"
* @ property { string } mime - MIME type , e . g . "text/html"
* @ property { string } title - note title
* @ property { string } content - note content - e . g . HTML text for text notes , file payload for files
* @ property { boolean } isProtected - true if note is protected
* @ property { boolean } isDeleted - true if note is deleted
* @ property { string } dateCreated
* @ property { string } dateModified
*
* @ extends Entity
* /
2018-01-30 07:34:59 +08:00
class Note extends Entity {
2018-08-17 05:00:04 +08:00
static get entityName ( ) { return "notes" ; }
2018-01-31 09:12:19 +08:00
static get primaryKeyName ( ) { return "noteId" ; }
2018-08-13 02:04:48 +08:00
static get hashedProperties ( ) { return [ "noteId" , "title" , "content" , "type" , "isProtected" , "isDeleted" ] ; }
2018-01-30 12:35:36 +08:00
2018-08-23 05:37:06 +08:00
/ * *
* @ param row - object containing database row from "notes" table
* /
2018-03-31 22:51:37 +08:00
constructor ( row ) {
super ( row ) ;
2018-01-30 09:57:55 +08:00
2018-08-07 17:38:00 +08:00
this . isProtected = ! ! this . isProtected ;
2018-10-31 05:18:20 +08:00
/ * t r u e i f c o n t e n t ( m e a n i n g a n y k i n d o f p o t e n t i a l l y e n c r y p t e d c o n t e n t ) i s e i t h e r n o t e n c r y p t e d
* or encrypted , but with available protected session ( so effectively decrypted ) * /
this . isContentAvailable = true ;
2018-08-07 17:38:00 +08:00
2018-04-09 00:27:10 +08:00
// check if there's noteId, otherwise this is a new entity which wasn't encrypted yet
if ( this . isProtected && this . noteId ) {
2018-10-31 05:18:20 +08:00
this . isContentAvailable = protectedSessionService . isProtectedSessionAvailable ( ) ;
2018-04-20 12:12:01 +08:00
protectedSessionService . decryptNote ( this ) ;
2018-01-30 12:35:36 +08:00
}
2018-04-11 12:10:11 +08:00
this . setContent ( this . content ) ;
2018-01-30 09:57:55 +08:00
}
2018-04-08 20:31:19 +08:00
setContent ( content ) {
this . content = content ;
2018-04-11 12:10:11 +08:00
try {
2018-04-08 20:31:19 +08:00
this . jsonContent = JSON . parse ( this . content ) ;
}
2018-04-11 12:10:11 +08:00
catch ( e ) { }
2018-04-08 20:31:19 +08:00
}
2018-08-23 05:37:06 +08:00
/** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */
2018-08-01 15:26:02 +08:00
isRoot ( ) {
return this . noteId === 'root' ;
}
2018-08-23 05:37:06 +08:00
/** @returns {boolean} true if this note is of application/json content type */
2018-01-30 12:17:44 +08:00
isJson ( ) {
2018-03-26 11:25:17 +08:00
return this . mime === "application/json" ;
2018-01-30 12:17:44 +08:00
}
2018-08-23 05:37:06 +08:00
/** @returns {boolean} true if this note is JavaScript (code or attachment) */
2018-02-18 23:47:02 +08:00
isJavaScript ( ) {
2018-03-02 11:30:06 +08:00
return ( this . type === "code" || this . type === "file" )
2018-03-06 11:08:45 +08:00
&& ( this . mime . startsWith ( "application/javascript" ) || this . mime === "application/x-javascript" ) ;
2018-02-18 23:47:02 +08:00
}
2018-08-23 05:37:06 +08:00
/** @returns {boolean} true if this note is HTML */
2018-03-05 10:05:14 +08:00
isHtml ( ) {
2018-05-27 07:27:47 +08:00
return ( this . type === "code" || this . type === "file" || this . type === "render" ) && this . mime === "text/html" ;
2018-03-05 10:05:14 +08:00
}
2018-08-23 05:37:06 +08:00
/** @returns {string} JS script environment - either "frontend" or "backend" */
2018-03-06 12:09:36 +08:00
getScriptEnv ( ) {
if ( this . isHtml ( ) || ( this . isJavaScript ( ) && this . mime . endsWith ( 'env=frontend' ) ) ) {
return "frontend" ;
}
2018-03-07 13:17:18 +08:00
if ( this . type === 'render' ) {
return "frontend" ;
}
2018-03-06 12:09:36 +08:00
if ( this . isJavaScript ( ) && this . mime . endsWith ( 'env=backend' ) ) {
return "backend" ;
}
return null ;
}
2018-08-23 05:37:06 +08:00
/ * *
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Attribute [ ] > } attributes belonging to this specific note ( excludes inherited attributes )
2018-08-23 05:37:06 +08:00
* /
2018-08-07 19:33:10 +08:00
async getOwnedAttributes ( ) {
return await repository . getEntities ( ` SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ` , [ this . noteId ] ) ;
2018-01-29 12:16:50 +08:00
}
2018-11-01 17:23:21 +08:00
/ * *
* @ returns { Promise < Attribute [ ] > } relations targetting this specific note
* /
async getTargetRelations ( ) {
return await repository . getEntities ( "SELECT * FROM attributes WHERE type = 'relation' AND isDeleted = 0 AND value = ?" , [ this . noteId ] ) ;
}
2018-09-01 19:18:55 +08:00
/ * *
* @ param { string } [ name ] - attribute name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s attributes , including inherited ones
* /
async getAttributes ( name ) {
2018-08-13 23:16:06 +08:00
if ( ! this . _ _attributeCache ) {
await this . loadAttributesToCache ( ) ;
}
2018-09-01 19:18:55 +08:00
if ( name ) {
return this . _ _attributeCache . filter ( attr => attr . name === name ) ;
}
else {
return this . _ _attributeCache ;
}
2018-08-13 23:16:06 +08:00
}
2018-09-01 19:18:55 +08:00
/ * *
* @ param { string } [ name ] - label name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s labels ( attributes with type label ) , including inherited ones
* /
async getLabels ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === LABEL ) ;
2018-08-16 04:06:49 +08:00
}
2018-11-13 19:50:08 +08:00
/ * *
* @ param { string } [ name ] - label name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s label definitions , including inherited ones
* /
async getLabelDefinitions ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === LABEL _DEFINITION ) ;
}
2018-09-01 19:18:55 +08:00
/ * *
* @ param { string } [ name ] - relation name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s relations ( attributes with type relation ) , including inherited ones
* /
async getRelations ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === RELATION ) ;
2018-08-16 04:06:49 +08:00
}
2018-11-13 19:50:08 +08:00
/ * *
* @ param { string } [ name ] - relation name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s relation definitions including inherited ones
* /
async getRelationDefinitions ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === RELATION _DEFINITION ) ;
}
2018-08-23 05:37:06 +08:00
/ * *
* Clear note ' s attributes cache to force fresh reload for next attribute request .
* Cache is note instance scoped .
* /
2018-08-13 23:16:06 +08:00
invalidateAttributeCache ( ) {
this . _ _attributeCache = null ;
}
2018-08-23 05:37:06 +08:00
/** @returns {Promise<void>} */
2018-08-13 23:16:06 +08:00
async loadAttributesToCache ( ) {
2018-08-07 19:33:10 +08:00
const attributes = await repository . getEntities ( `
2018-08-13 23:05:16 +08:00
WITH RECURSIVE
tree ( noteId , level ) AS (
SELECT ? , 0
UNION
SELECT branches . parentNoteId , tree . level + 1 FROM branches
JOIN tree ON branches . noteId = tree . noteId
JOIN notes ON notes . noteId = branches . parentNoteId
WHERE notes . isDeleted = 0
AND branches . isDeleted = 0
) ,
treeWithAttrs ( noteId , level ) AS (
SELECT * FROM tree
UNION
SELECT attributes . value , treeWithAttrs . level + 1 FROM attributes
JOIN treeWithAttrs ON treeWithAttrs . noteId = attributes . noteId
WHERE attributes . isDeleted = 0
AND attributes . type = 'relation'
2018-08-21 18:52:11 +08:00
AND attributes . name = 'template'
2018-08-14 19:02:17 +08:00
AND ( attributes . noteId = ? OR attributes . isInheritable = 1 )
2018-08-13 23:05:16 +08:00
)
SELECT attributes . * FROM attributes JOIN treeWithAttrs ON attributes . noteId = treeWithAttrs . noteId
WHERE attributes . isDeleted = 0 AND ( attributes . isInheritable = 1 OR attributes . noteId = ? )
2018-08-14 19:02:17 +08:00
ORDER BY level , noteId , position ` , [this.noteId, this.noteId, this.noteId]);
2018-08-07 19:33:10 +08:00
// attributes are ordered so that "closest" attributes are first
// we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter.
const filteredAttributes = attributes . filter ( ( attr , index ) => {
if ( attr . isDefinition ( ) ) {
const firstDefinitionIndex = attributes . findIndex ( el => el . type === attr . type && el . name === attr . name ) ;
// keep only if this element is the first definition for this type & name
return firstDefinitionIndex === index ;
}
else {
const definitionAttr = attributes . find ( el => el . type === attr . type + '-definition' && el . name === attr . name ) ;
if ( ! definitionAttr ) {
return true ;
}
const definition = definitionAttr . value ;
if ( definition . multiplicityType === 'multivalue' ) {
return true ;
}
else {
const firstAttrIndex = attributes . findIndex ( el => el . type === attr . type && el . name === attr . name ) ;
// in case of single-valued attribute we'll keep it only if it's first (closest)
return firstAttrIndex === index ;
}
}
} ) ;
for ( const attr of filteredAttributes ) {
attr . isOwned = attr . noteId === this . noteId ;
2018-03-03 22:11:41 +08:00
}
2018-08-13 23:16:06 +08:00
this . _ _attributeCache = filteredAttributes ;
2018-03-03 22:11:41 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { Promise < boolean > } true if note has an attribute with given type and name ( including inherited )
* /
2018-08-16 04:06:49 +08:00
async hasAttribute ( type , name ) {
return ! ! await this . getAttribute ( type , name ) ;
2018-03-05 11:09:51 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { Promise < Attribute > } attribute of given type and name . If there 's more such attributes, first is returned. Returns null if there' s no such attribute belonging to this note .
* /
2018-08-16 04:06:49 +08:00
async getAttribute ( type , name ) {
2018-08-07 19:33:10 +08:00
const attributes = await this . getAttributes ( ) ;
2018-08-16 04:06:49 +08:00
return attributes . find ( attr => attr . type === type && attr . name === name ) ;
2018-01-30 06:41:59 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { Promise < string > } attribute value of given type and name or null if no such attribute exists .
* /
2018-08-16 04:06:49 +08:00
async getAttributeValue ( type , name ) {
const attr = await this . getAttribute ( type , name ) ;
2018-08-13 16:59:31 +08:00
2018-08-16 04:06:49 +08:00
return attr ? attr . value : null ;
2018-08-13 16:59:31 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
* Based on enabled , attribute is either set or removed .
*
* @ param { string } type - attribute type ( 'relation' , 'label' etc . )
* @ param { boolean } enabled - toggle On or Off
* @ param { string } name - attribute name
* @ param { string } [ value ] - attribute value ( optional )
* @ returns { Promise < void > }
* /
async toggleAttribute ( type , enabled , name , value ) {
2018-08-13 19:53:08 +08:00
if ( enabled ) {
2018-08-16 04:06:49 +08:00
await this . setAttribute ( type , name , value ) ;
2018-08-13 19:53:08 +08:00
}
else {
2018-08-16 04:06:49 +08:00
await this . removeAttribute ( type , name , value ) ;
2018-08-13 19:53:08 +08:00
}
}
2018-08-23 05:37:06 +08:00
/ * *
* Creates given attribute name - value pair if it doesn ' t exist .
*
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ param { string } [ value ] - attribute value ( optional )
* @ returns { Promise < void > }
* /
async setAttribute ( type , name , value ) {
2018-08-13 19:53:08 +08:00
const attributes = await this . getOwnedAttributes ( ) ;
2018-08-23 05:37:06 +08:00
let attr = attributes . find ( attr => attr . type === type && ( value === undefined || attr . value === value ) ) ;
2018-08-13 16:59:31 +08:00
2018-08-16 04:06:49 +08:00
if ( ! attr ) {
attr = new Attribute ( {
2018-08-13 16:59:31 +08:00
noteId : this . noteId ,
2018-08-16 04:06:49 +08:00
type : type ,
2018-08-13 19:53:08 +08:00
name : name ,
2018-08-23 05:37:06 +08:00
value : value !== undefined ? value : ""
2018-08-13 16:59:31 +08:00
} ) ;
2018-08-16 04:06:49 +08:00
await attr . save ( ) ;
2018-08-13 23:16:06 +08:00
this . invalidateAttributeCache ( ) ;
2018-08-13 19:53:08 +08:00
}
2018-08-13 16:59:31 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
* Removes given attribute name - value pair if it exists .
*
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ param { string } [ value ] - attribute value ( optional )
* @ returns { Promise < void > }
* /
async removeAttribute ( type , name , value ) {
2018-08-13 19:53:08 +08:00
const attributes = await this . getOwnedAttributes ( ) ;
2018-08-13 16:59:31 +08:00
2018-08-13 19:53:08 +08:00
for ( const attribute of attributes ) {
2018-08-23 05:37:06 +08:00
if ( attribute . type === type && ( value === undefined || value === attribute . value ) ) {
2018-08-13 19:53:08 +08:00
attribute . isDeleted = true ;
await attribute . save ( ) ;
2018-08-13 23:16:06 +08:00
this . invalidateAttributeCache ( ) ;
2018-08-13 19:53:08 +08:00
}
2018-08-13 16:59:31 +08:00
}
}
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } name - label name
* @ returns { Promise < boolean > } true if label exists ( including inherited )
* /
2018-08-16 04:06:49 +08:00
async hasLabel ( name ) { return await this . hasAttribute ( LABEL , name ) ; }
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } name - relation name
* @ returns { Promise < boolean > } true if relation exists ( including inherited )
* /
2018-08-16 04:06:49 +08:00
async hasRelation ( name ) { return await this . hasAttribute ( RELATION , name ) ; }
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } name - label name
* @ returns { Promise < Attribute > } label if it exists , null otherwise
* /
2018-08-16 04:06:49 +08:00
async getLabel ( name ) { return await this . getAttribute ( LABEL , name ) ; }
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } name - relation name
* @ returns { Promise < Attribute > } relation if it exists , null otherwise
* /
2018-08-16 04:06:49 +08:00
async getRelation ( name ) { return await this . getAttribute ( RELATION , name ) ; }
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } name - label name
* @ returns { Promise < string > } label value if label exists , null otherwise
* /
2018-08-16 04:06:49 +08:00
async getLabelValue ( name ) { return await this . getAttributeValue ( LABEL , name ) ; }
2018-08-23 05:37:06 +08:00
/ * *
* @ param { string } name - relation name
* @ returns { Promise < string > } relation value if relation exists , null otherwise
* /
async getRelationValue ( name ) { return await this . getAttributeValue ( RELATION , name ) ; }
2018-08-16 04:06:49 +08:00
2018-08-23 05:37:06 +08:00
/ * *
* Based on enabled , label is either set or removed .
*
* @ param { boolean } enabled - toggle On or Off
* @ param { string } name - label name
* @ param { string } [ value ] - label value ( optional )
* @ returns { Promise < void > }
* /
async toggleLabel ( enabled , name , value ) { return await this . toggleAttribute ( LABEL , enabled , name , value ) ; }
/ * *
* Based on enabled , relation is either set or removed .
*
* @ param { boolean } enabled - toggle On or Off
* @ param { string } name - relation name
* @ param { string } [ value ] - relation value ( noteId )
* @ returns { Promise < void > }
* /
async toggleRelation ( enabled , name , value ) { return await this . toggleAttribute ( RELATION , enabled , name , value ) ; }
/ * *
* Create label name - value pair if it doesn ' t exist yet .
*
* @ param { string } name - label name
* @ param { string } [ value ] - label value
* @ returns { Promise < void > }
* /
async setLabel ( name , value ) { return await this . setAttribute ( LABEL , name , value ) ; }
/ * *
* Create relation name - value pair if it doesn ' t exist yet .
*
* @ param { string } name - relation name
* @ param { string } [ value ] - relation value ( noteId )
* @ returns { Promise < void > }
* /
async setRelation ( name , value ) { return await this . setAttribute ( RELATION , name , value ) ; }
/ * *
* Remove label name - value pair , if it exists .
*
* @ param { string } name - label name
* @ param { string } [ value ] - label value
* @ returns { Promise < void > }
* /
async removeLabel ( name , value ) { return await this . removeAttribute ( LABEL , name , value ) ; }
/ * *
* Remove relation name - value pair , if it exists .
*
* @ param { string } name - relation name
* @ param { string } [ value ] - relation value ( noteId )
* @ returns { Promise < void > }
* /
async removeRelation ( name , value ) { return await this . removeAttribute ( RELATION , name , value ) ; }
/ * *
* @ param { string } name
* @ returns { Promise < Note > | null } target note of the relation or null ( if target is empty or note was not found )
* /
2018-08-21 18:50:43 +08:00
async getRelationTarget ( name ) {
const relation = await this . getRelation ( name ) ;
return relation ? await repository . getNote ( relation . value ) : null ;
}
2018-08-23 05:37:06 +08:00
/ * *
2018-11-15 00:26:07 +08:00
* Finds child notes with given attribute name and value . Only own attributes are considered , not inherited ones
2018-08-23 05:37:06 +08:00
*
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ param { string } [ value ] - attribute value
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Note [ ] > }
2018-08-23 05:37:06 +08:00
* /
2018-11-15 00:26:07 +08:00
async findChildNotesWithAttribute ( type , name , value ) {
2018-08-21 18:50:43 +08:00
const params = [ this . noteId , name ] ;
let valueCondition = "" ;
if ( value !== undefined ) {
params . push ( value ) ;
valueCondition = " AND attributes.value = ?" ;
}
const notes = await repository . getEntities ( `
WITH RECURSIVE
tree ( noteId ) AS (
SELECT ?
UNION
SELECT branches . noteId FROM branches
JOIN tree ON branches . parentNoteId = tree . noteId
JOIN notes ON notes . noteId = branches . noteId
WHERE notes . isDeleted = 0
AND branches . isDeleted = 0
)
SELECT notes . * FROM notes
JOIN tree ON tree . noteId = notes . noteId
JOIN attributes ON attributes . noteId = notes . noteId
WHERE attributes . isDeleted = 0
AND attributes . name = ?
$ { valueCondition }
ORDER BY noteId , position ` , params);
return notes ;
}
2018-08-23 05:37:06 +08:00
/ * *
* Finds notes with given label name and value . Only own labels are considered , not inherited ones
*
* @ param { string } name - label name
* @ param { string } [ value ] - label value
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Note [ ] > }
2018-08-23 05:37:06 +08:00
* /
2018-11-15 00:26:07 +08:00
async findChildNotesWithLabel ( name , value ) { return await this . findChildNotesWithAttribute ( LABEL , name , value ) ; }
2018-08-23 05:37:06 +08:00
/ * *
* Finds notes with given relation name and value . Only own relations are considered , not inherited ones
*
* @ param { string } name - relation name
* @ param { string } [ value ] - relation value
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Note [ ] > }
2018-08-23 05:37:06 +08:00
* /
2018-11-15 00:26:07 +08:00
async findChildNotesWithRelation ( name , value ) { return await this . findChildNotesWithAttribute ( RELATION , name , value ) ; }
2018-08-21 18:50:43 +08:00
2018-08-23 05:37:06 +08:00
/ * *
* Returns note revisions of this note .
*
2018-08-23 21:33:19 +08:00
* @ returns { Promise < NoteRevision [ ] > }
2018-08-23 05:37:06 +08:00
* /
2018-01-30 07:34:59 +08:00
async getRevisions ( ) {
2018-03-31 22:51:37 +08:00
return await repository . getEntities ( "SELECT * FROM note_revisions WHERE noteId = ?" , [ this . noteId ] ) ;
2018-01-30 07:34:59 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
2018-11-15 20:58:14 +08:00
* Get list of links coming out of this note .
*
2018-11-08 18:08:16 +08:00
* @ returns { Promise < Link [ ] > }
2018-08-23 05:37:06 +08:00
* /
2018-11-08 18:08:16 +08:00
async getLinks ( ) {
return await repository . getEntities ( "SELECT * FROM links WHERE noteId = ? AND isDeleted = 0" , [ this . noteId ] ) ;
2018-04-01 10:15:06 +08:00
}
2018-11-15 20:58:14 +08:00
/ * *
* Get list of links targetting this note .
*
* @ returns { Promise < Link [ ] > }
* /
async getTargetLinks ( ) {
return await repository . getEntities ( "SELECT * FROM links WHERE targetNoteId = ? AND isDeleted = 0" , [ this . noteId ] ) ;
}
2018-11-08 17:11:00 +08:00
/ * *
2018-11-08 18:08:16 +08:00
* Return all links from this note , including deleted ones .
*
2018-11-08 17:11:00 +08:00
* @ returns { Promise < Link [ ] > }
* /
2018-11-08 18:08:16 +08:00
async getLinksWithDeleted ( ) {
return await repository . getEntities ( "SELECT * FROM links WHERE noteId = ?" , [ this . noteId ] ) ;
2018-11-08 17:11:00 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Branch [ ] > }
2018-08-23 05:37:06 +08:00
* /
2018-04-01 11:08:22 +08:00
async getBranches ( ) {
2018-03-31 22:51:37 +08:00
return await repository . getEntities ( "SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ?" , [ this . noteId ] ) ;
2018-01-29 12:16:50 +08:00
}
2018-01-30 12:35:36 +08:00
2018-09-03 15:40:22 +08:00
/ * *
* @ returns { boolean } - true if note has children
* /
async hasChildren ( ) {
return ( await this . getChildNotes ( ) ) . length > 0 ;
}
2018-08-23 05:37:06 +08:00
/ * *
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Note [ ] > } child notes of this note
2018-08-23 05:37:06 +08:00
* /
2018-04-01 11:08:22 +08:00
async getChildNotes ( ) {
2018-03-31 22:51:37 +08:00
return await repository . getEntities ( `
2018-02-25 03:42:52 +08:00
SELECT notes . *
2018-03-25 09:39:15 +08:00
FROM branches
2018-02-25 03:42:52 +08:00
JOIN notes USING ( noteId )
WHERE notes . isDeleted = 0
2018-03-25 09:39:15 +08:00
AND branches . isDeleted = 0
AND branches . parentNoteId = ?
ORDER BY branches . notePosition ` , [this.noteId]);
2018-02-25 03:42:52 +08:00
}
2018-08-23 05:37:06 +08:00
/ * *
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Branch [ ] > } child branches of this note
2018-08-23 05:37:06 +08:00
* /
2018-04-01 11:08:22 +08:00
async getChildBranches ( ) {
return await repository . getEntities ( `
SELECT branches . *
FROM branches
WHERE branches . isDeleted = 0
AND branches . parentNoteId = ?
ORDER BY branches . notePosition ` , [this.noteId]);
}
2018-08-23 05:37:06 +08:00
/ * *
2018-08-23 21:33:19 +08:00
* @ returns { Promise < Note [ ] > } parent notes of this note ( note can have multiple parents because of cloning )
2018-08-23 05:37:06 +08:00
* /
2018-04-01 11:08:22 +08:00
async getParentNotes ( ) {
2018-03-31 22:51:37 +08:00
return await repository . getEntities ( `
2018-02-25 03:42:52 +08:00
SELECT parent _notes . *
FROM
2018-03-25 09:39:15 +08:00
branches AS child _tree
2018-02-25 03:42:52 +08:00
JOIN notes AS parent _notes ON parent _notes . noteId = child _tree . parentNoteId
WHERE child _tree . noteId = ?
AND child _tree . isDeleted = 0
AND parent _notes . isDeleted = 0 ` , [this.noteId]);
}
2018-01-30 12:35:36 +08:00
beforeSaving ( ) {
2018-04-11 12:10:11 +08:00
if ( this . isJson ( ) && this . jsonContent ) {
2018-04-01 10:15:06 +08:00
this . content = JSON . stringify ( this . jsonContent , null , '\t' ) ;
}
2018-01-30 12:35:36 +08:00
2018-08-28 05:04:52 +08:00
// we do this here because encryption needs the note ID for the IV
this . generateIdIfNecessary ( ) ;
2018-01-30 12:35:36 +08:00
if ( this . isProtected ) {
2018-04-20 12:12:01 +08:00
protectedSessionService . encryptNote ( this ) ;
2018-01-30 12:35:36 +08:00
}
2018-04-01 10:15:06 +08:00
2018-04-02 05:38:24 +08:00
if ( ! this . isDeleted ) {
this . isDeleted = false ;
}
2018-04-01 10:15:06 +08:00
if ( ! this . dateCreated ) {
2018-04-03 08:46:46 +08:00
this . dateCreated = dateUtils . nowDate ( ) ;
2018-04-01 10:15:06 +08:00
}
2018-08-13 02:04:48 +08:00
super . beforeSaving ( ) ;
if ( this . isChanged ) {
this . dateModified = dateUtils . nowDate ( ) ;
}
2018-01-30 12:35:36 +08:00
}
2018-01-29 12:16:50 +08:00
}
module . exports = Note ;