2021-10-16 22:13:34 +02:00
"use strict" ;
2021-10-17 14:44:59 +02:00
const sql = require ( '../../sql' ) ;
const utils = require ( '../../../services/utils' ) ;
2023-01-03 13:52:37 +01:00
const AbstractShacaEntity = require ( './abstract_shaca_entity' ) ;
2022-11-01 22:49:37 +01:00
const escape = require ( 'escape-html' ) ;
2021-10-16 22:13:34 +02:00
const LABEL = 'label' ;
const RELATION = 'relation' ;
2022-07-31 21:45:32 +02:00
const CREDENTIALS = 'shareCredentials' ;
const isCredentials = attr => attr . type === 'label' && attr . name === CREDENTIALS ;
2021-10-16 22:13:34 +02:00
2023-01-03 13:40:21 +01:00
class SNote extends AbstractShacaEntity {
2022-10-27 20:34:53 +02:00
constructor ( [ noteId , title , type , mime , utcDateModified , isProtected ] ) {
2021-10-17 14:44:59 +02:00
super ( ) ;
2021-10-16 22:13:34 +02:00
/** @param {string} */
this . noteId = noteId ;
/** @param {string} */
2022-10-27 20:34:53 +02:00
this . title = isProtected ? "[protected]" : title ;
2021-10-16 22:13:34 +02:00
/** @param {string} */
this . type = type ;
/** @param {string} */
this . mime = mime ;
2021-12-06 22:53:17 +01:00
/** @param {string} */
this . utcDateModified = utcDateModified ; // used for caching of images
2022-10-27 20:34:53 +02:00
/** @param {boolean} */
this . isProtected = isProtected ;
2021-10-16 22:13:34 +02:00
2023-01-03 13:40:21 +01:00
/** @param {SBranch[]} */
2021-10-16 22:13:34 +02:00
this . parentBranches = [ ] ;
2023-01-03 13:40:21 +01:00
/** @param {SNote[]} */
2021-10-16 22:13:34 +02:00
this . parents = [ ] ;
2023-01-03 13:40:21 +01:00
/** @param {SNote[]} */
2021-10-16 22:13:34 +02:00
this . children = [ ] ;
2023-01-03 13:40:21 +01:00
/** @param {SAttribute[]} */
2021-10-16 22:13:34 +02:00
this . ownedAttributes = [ ] ;
2023-01-03 13:40:21 +01:00
/** @param {SAttribute[]|null} */
2021-10-16 22:13:34 +02:00
this . _ _attributeCache = null ;
2023-01-03 13:40:21 +01:00
/** @param {SAttribute[]|null} */
2021-10-16 22:13:34 +02:00
this . inheritableAttributeCache = null ;
2023-01-03 13:40:21 +01:00
/** @param {SAttribute[]} */
2021-10-16 22:13:34 +02:00
this . targetRelations = [ ] ;
2021-10-17 14:44:59 +02:00
this . shaca . notes [ this . noteId ] = this ;
2021-10-16 22:13:34 +02:00
}
2023-01-03 13:40:21 +01:00
/** @returns {SBranch[]} */
2021-10-16 22:13:34 +02:00
getParentBranches ( ) {
return this . parentBranches ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SBranch[]} */
2021-10-16 22:13:34 +02:00
getBranches ( ) {
return this . parentBranches ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SBranch[]} */
2022-11-01 22:49:37 +01:00
getChildBranches ( ) {
return this . children . map ( childNote => this . shaca . getBranchFromChildAndParent ( childNote . noteId , this . noteId ) ) ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SNote[]} */
2021-10-16 22:13:34 +02:00
getParentNotes ( ) {
return this . parents ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SNote[]} */
2021-10-16 22:13:34 +02:00
getChildNotes ( ) {
return this . children ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SNote[]} */
2022-02-16 22:16:15 +01:00
getVisibleChildNotes ( ) {
2022-03-22 23:17:47 +01:00
return this . getChildBranches ( )
. filter ( branch => ! branch . isHidden )
. map ( branch => branch . getNote ( ) )
2022-10-27 20:34:53 +02:00
. filter ( childNote => ! childNote . hasLabel ( 'shareHiddenFromTree' ) ) ;
2022-02-16 22:16:15 +01:00
}
2022-11-01 22:49:37 +01:00
/** @returns {boolean} */
2021-10-16 22:13:34 +02:00
hasChildren ( ) {
return this . children && this . children . length > 0 ;
}
2022-11-01 22:49:37 +01:00
/** @returns {boolean} */
2022-02-16 22:16:15 +01:00
hasVisibleChildren ( ) {
2022-03-22 23:17:47 +01:00
return this . getVisibleChildNotes ( ) . length > 0 ;
2022-02-16 22:16:15 +01:00
}
2021-10-16 22:13:34 +02:00
getContent ( silentNotFoundError = false ) {
const row = sql . getRow ( ` SELECT content FROM note_contents WHERE noteId = ? ` , [ this . noteId ] ) ;
if ( ! row ) {
if ( silentNotFoundError ) {
return undefined ;
}
else {
2022-12-21 15:19:05 +01:00
throw new Error ( ` Cannot find note content for noteId= ${ this . noteId } ` ) ;
2021-10-16 22:13:34 +02:00
}
}
let content = row . content ;
if ( this . isStringNote ( ) ) {
return content === null
? ""
: content . toString ( "UTF-8" ) ;
}
else {
return content ;
}
}
/** @returns {boolean} true if the note has string content (not binary) */
isStringNote ( ) {
return utils . isStringNote ( this . type , this . mime ) ;
}
/ * *
* @ param { string } [ type ] - ( optional ) attribute type to filter
* @ param { string } [ name ] - ( optional ) attribute name to filter
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute [ ] } all note ' s attributes , including inherited ones
2021-10-16 22:13:34 +02:00
* /
getAttributes ( type , name ) {
this . _ _getAttributes ( [ ] ) ;
if ( type && name ) {
2022-07-31 21:45:32 +02:00
return this . _ _attributeCache . filter ( attr => attr . type === type && attr . name === name && ! isCredentials ( attr ) ) ;
2021-10-16 22:13:34 +02:00
}
else if ( type ) {
2022-07-31 21:45:32 +02:00
return this . _ _attributeCache . filter ( attr => attr . type === type && ! isCredentials ( attr ) ) ;
2021-10-16 22:13:34 +02:00
}
else if ( name ) {
2022-07-31 21:45:32 +02:00
return this . _ _attributeCache . filter ( attr => attr . name === name && ! isCredentials ( attr ) ) ;
2021-10-16 22:13:34 +02:00
}
else {
2022-07-31 21:45:32 +02:00
return this . _ _attributeCache . filter ( attr => ! isCredentials ( attr ) ) ;
2021-10-16 22:13:34 +02:00
}
}
2023-01-03 13:40:21 +01:00
/** @returns {SAttribute[]} */
2022-07-31 21:45:32 +02:00
getCredentials ( ) {
this . _ _getAttributes ( [ ] ) ;
return this . _ _attributeCache . filter ( isCredentials ) ;
}
2021-10-16 22:13:34 +02:00
_ _getAttributes ( path ) {
if ( path . includes ( this . noteId ) ) {
return [ ] ;
}
if ( ! this . _ _attributeCache ) {
const parentAttributes = this . ownedAttributes . slice ( ) ;
const newPath = [ ... path , this . noteId ] ;
if ( this . noteId !== 'root' ) {
for ( const parentNote of this . parents ) {
parentAttributes . push ( ... parentNote . _ _getInheritableAttributes ( newPath ) ) ;
}
}
const templateAttributes = [ ] ;
for ( const ownedAttr of parentAttributes ) { // parentAttributes so we process also inherited templates
if ( ownedAttr . type === 'relation' && ownedAttr . name === 'template' ) {
2021-10-17 14:44:59 +02:00
const templateNote = this . shaca . notes [ ownedAttr . value ] ;
2021-10-16 22:13:34 +02:00
if ( templateNote ) {
templateAttributes . push ( ... templateNote . _ _getAttributes ( newPath ) ) ;
}
}
}
this . _ _attributeCache = [ ] ;
const addedAttributeIds = new Set ( ) ;
for ( const attr of parentAttributes . concat ( templateAttributes ) ) {
if ( ! addedAttributeIds . has ( attr . attributeId ) ) {
addedAttributeIds . add ( attr . attributeId ) ;
this . _ _attributeCache . push ( attr ) ;
}
}
this . inheritableAttributeCache = [ ] ;
for ( const attr of this . _ _attributeCache ) {
if ( attr . isInheritable ) {
this . inheritableAttributeCache . push ( attr ) ;
}
}
}
return this . _ _attributeCache ;
}
2023-01-03 13:40:21 +01:00
/** @return {SAttribute[]} */
2021-10-16 22:13:34 +02:00
_ _getInheritableAttributes ( path ) {
if ( path . includes ( this . noteId ) ) {
return [ ] ;
}
if ( ! this . inheritableAttributeCache ) {
this . _ _getAttributes ( path ) ; // will refresh also this.inheritableAttributeCache
}
return this . inheritableAttributeCache ;
}
2022-11-01 22:49:37 +01:00
/** @returns {boolean} */
2021-10-16 22:13:34 +02:00
hasAttribute ( type , name ) {
return ! ! this . getAttributes ( ) . find ( attr => attr . type === type && attr . name === name ) ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SNote|null} */
2021-10-16 22:13:34 +02:00
getRelationTarget ( name ) {
const relation = this . getAttributes ( ) . find ( attr => attr . type === 'relation' && attr . name === name ) ;
return relation ? relation . targetNote : null ;
}
/ * *
* @ param { string } name - label name
* @ returns { boolean } true if label exists ( including inherited )
* /
hasLabel ( name ) { return this . hasAttribute ( LABEL , name ) ; }
/ * *
* @ param { string } name - label name
* @ returns { boolean } true if label exists ( excluding inherited )
* /
hasOwnedLabel ( name ) { return this . hasOwnedAttribute ( LABEL , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { boolean } true if relation exists ( including inherited )
* /
hasRelation ( name ) { return this . hasAttribute ( RELATION , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { boolean } true if relation exists ( excluding inherited )
* /
hasOwnedRelation ( name ) { return this . hasOwnedAttribute ( RELATION , name ) ; }
/ * *
* @ param { string } name - label name
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute | null } label if it exists , null otherwise
2021-10-16 22:13:34 +02:00
* /
getLabel ( name ) { return this . getAttribute ( LABEL , name ) ; }
/ * *
* @ param { string } name - label name
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute | null } label if it exists , null otherwise
2021-10-16 22:13:34 +02:00
* /
getOwnedLabel ( name ) { return this . getOwnedAttribute ( LABEL , name ) ; }
/ * *
* @ param { string } name - relation name
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute | null } relation if it exists , null otherwise
2021-10-16 22:13:34 +02:00
* /
getRelation ( name ) { return this . getAttribute ( RELATION , name ) ; }
/ * *
* @ param { string } name - relation name
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute | null } relation if it exists , null otherwise
2021-10-16 22:13:34 +02:00
* /
getOwnedRelation ( name ) { return this . getOwnedAttribute ( RELATION , name ) ; }
/ * *
* @ param { string } name - label name
* @ returns { string | null } label value if label exists , null otherwise
* /
getLabelValue ( name ) { return this . getAttributeValue ( LABEL , name ) ; }
/ * *
* @ param { string } name - label name
* @ returns { string | null } label value if label exists , null otherwise
* /
getOwnedLabelValue ( name ) { return this . getOwnedAttributeValue ( LABEL , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { string | null } relation value if relation exists , null otherwise
* /
getRelationValue ( name ) { return this . getAttributeValue ( RELATION , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { string | null } relation value if relation exists , null otherwise
* /
getOwnedRelationValue ( name ) { return this . getOwnedAttributeValue ( RELATION , name ) ; }
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { boolean } true if note has an attribute with given type and name ( excluding inherited )
* /
hasOwnedAttribute ( type , name ) {
return ! ! this . getOwnedAttribute ( type , name ) ;
}
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute } 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 .
2021-10-16 22:13:34 +02:00
* /
getAttribute ( type , name ) {
const attributes = this . getAttributes ( ) ;
return attributes . find ( attr => attr . type === type && attr . name === name ) ;
}
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { string | null } attribute value of given type and name or null if no such attribute exists .
* /
getAttributeValue ( type , name ) {
const attr = this . getAttribute ( type , name ) ;
return attr ? attr . value : null ;
}
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { string | null } attribute value of given type and name or null if no such attribute exists .
* /
getOwnedAttributeValue ( type , name ) {
const attr = this . getOwnedAttribute ( type , name ) ;
return attr ? attr . value : null ;
}
/ * *
* @ param { string } [ name ] - label name to filter
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute [ ] } all note ' s labels ( attributes with type label ) , including inherited ones
2021-10-16 22:13:34 +02:00
* /
getLabels ( name ) {
return this . getAttributes ( LABEL , name ) ;
}
/ * *
* @ param { string } [ name ] - label name to filter
* @ returns { string [ ] } all note ' s label values , including inherited ones
* /
getLabelValues ( name ) {
return this . getLabels ( name ) . map ( l => l . value ) ;
}
/ * *
* @ param { string } [ name ] - label name to filter
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute [ ] } all note ' s labels ( attributes with type label ) , excluding inherited ones
2021-10-16 22:13:34 +02:00
* /
getOwnedLabels ( name ) {
return this . getOwnedAttributes ( LABEL , name ) ;
}
/ * *
* @ param { string } [ name ] - label name to filter
* @ returns { string [ ] } all note ' s label values , excluding inherited ones
* /
getOwnedLabelValues ( name ) {
return this . getOwnedAttributes ( LABEL , name ) . map ( l => l . value ) ;
}
/ * *
* @ param { string } [ name ] - relation name to filter
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute [ ] } all note ' s relations ( attributes with type relation ) , including inherited ones
2021-10-16 22:13:34 +02:00
* /
getRelations ( name ) {
return this . getAttributes ( RELATION , name ) ;
}
/ * *
* @ param { string } [ name ] - relation name to filter
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute [ ] } all note ' s relations ( attributes with type relation ) , excluding inherited ones
2021-10-16 22:13:34 +02:00
* /
getOwnedRelations ( name ) {
return this . getOwnedAttributes ( RELATION , name ) ;
}
/ * *
* @ param { string } [ type ] - ( optional ) attribute type to filter
* @ param { string } [ name ] - ( optional ) attribute name to filter
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute [ ] } note ' s "owned" attributes - excluding inherited ones
2021-10-16 22:13:34 +02:00
* /
getOwnedAttributes ( type , name ) {
// it's a common mistake to include # or ~ into attribute name
if ( name && [ "#" , "~" ] . includes ( name [ 0 ] ) ) {
name = name . substr ( 1 ) ;
}
if ( type && name ) {
return this . ownedAttributes . filter ( attr => attr . type === type && attr . name === name ) ;
}
else if ( type ) {
return this . ownedAttributes . filter ( attr => attr . type === type ) ;
}
else if ( name ) {
return this . ownedAttributes . filter ( attr => attr . name === name ) ;
}
else {
return this . ownedAttributes . slice ( ) ;
}
}
/ * *
2023-01-03 13:40:21 +01:00
* @ returns { SAttribute } attribute belonging to this specific note ( excludes inherited attributes )
2021-10-16 22:13:34 +02:00
*
* This method can be significantly faster than the getAttribute ( )
* /
getOwnedAttribute ( type , name ) {
const attrs = this . getOwnedAttributes ( type , name ) ;
return attrs . length > 0 ? attrs [ 0 ] : null ;
}
2022-11-01 22:49:37 +01:00
/** @returns {boolean} */
2021-10-16 22:13:34 +02:00
get isArchived ( ) {
return this . hasAttribute ( 'label' , 'archived' ) ;
}
2022-11-01 22:49:37 +01:00
/** @returns {boolean} */
2021-10-16 22:13:34 +02:00
hasInheritableOwnedArchivedLabel ( ) {
return ! ! this . ownedAttributes . find ( attr => attr . type === 'label' && attr . name === 'archived' && attr . isInheritable ) ;
}
2022-11-01 22:49:37 +01:00
/** @returns {boolean} */
2021-10-16 22:13:34 +02:00
isTemplate ( ) {
return ! ! this . targetRelations . find ( rel => rel . name === 'template' ) ;
}
2023-01-03 13:40:21 +01:00
/** @returns {SAttribute[]} */
2021-10-16 22:13:34 +02:00
getTargetRelations ( ) {
return this . targetRelations ;
}
2022-11-01 22:49:37 +01:00
/** @returns {string} */
2021-12-22 10:57:02 +01:00
get shareId ( ) {
2022-01-17 23:13:56 +01:00
if ( this . hasOwnedLabel ( 'shareRoot' ) ) {
return "" ;
}
2021-12-22 10:57:02 +01:00
const sharedAlias = this . getOwnedLabelValue ( "shareAlias" ) ;
return sharedAlias || this . noteId ;
}
2022-01-01 13:23:09 +01:00
2022-11-01 22:49:37 +01:00
get escapedTitle ( ) {
return escape ( this . title ) ;
}
2022-01-01 13:23:09 +01:00
getPojoWithAttributes ( ) {
return {
noteId : this . noteId ,
title : this . title ,
type : this . type ,
mime : this . mime ,
utcDateModified : this . utcDateModified ,
2022-12-19 21:39:12 +01:00
attributes : this . getAttributes ( )
// relations could link across shared subtrees which might leak them
// individual relations might be whitelisted based on needs #3434
. filter ( attr => attr . type === 'label' )
. map ( attr => attr . getPojo ( ) ) ,
2022-01-01 13:23:09 +01:00
parentNoteIds : this . parents . map ( parentNote => parentNote . noteId ) ,
childNoteIds : this . children . map ( child => child . noteId )
} ;
}
2021-10-16 22:13:34 +02:00
}
2023-01-03 13:40:21 +01:00
module . exports = SNote ;