" I model a Tiddler object in [TiddlyWiki](https://tiddlywiki.com/). I implement the standard fields as described in the standard documentation at: " Class { #name : #Tiddler, #superclass : #Object, #instVars : [ 'title', 'text', 'modified', 'created', 'creator', 'tags', 'type', 'list', 'caption', 'modifier', 'wiki', 'customFields', 'bag', 'revision' ], #category : #'TiddlyWiki-Model' } { #category : #'instance creation' } Tiddler class >> nowLocal [ ^ ((ZTimestampFormat fromString: '20010203160506700') format: (ZTimestamp fromString: Time nowLocal asDateAndTime asString)) copyFrom: 1 to: 17 ] { #category : #accessing } Tiddler >> asDictionary [ | response | response := Dictionary new at: 'title' put: self title; at: 'text' put: self text; at: 'created' put: self created; at: 'tags' put: self tags; at: 'type' put: self type; at: 'creator' put: self creator; at: 'modifier' put: self modifier; at: 'modified' put: self modified; at: 'bag' put: self bag; at: 'revision' put: self revision; yourself. self customFields ifNotEmpty: [ self customFields keysAndValuesDo: [:k :v | response at: k put: v ] ]. ^ response ] { #category : #converting } Tiddler >> asJson [ ^ STONJSON toStringPretty: { self asDictionary } ] { #category : #converting } Tiddler >> asJsonString [ ^ STONJSON toStringPretty: self asDictionary ] { #category : #accessing } Tiddler >> asJsonTempFile [ ^ MarkupFile exportAsFileOn: FileLocator temp / self title, 'json' containing:self asJson ] { #category : #accessing } Tiddler >> asStonStringPretty [ | output temp | temp := self copy. temp wiki: nil. output := '' writeStream. (STON writer on: output) newLine: String crlf; prettyPrint: true; keepNewLines: true; nextPut: temp. ^ output contents ] { #category : #accessing } Tiddler >> bag [ ^ bag ] { #category : #accessing } Tiddler >> bag: aString [ bag := aString ] { #category : #accessing } Tiddler >> caption [ ^ caption ] { #category : #accessing } Tiddler >> caption: anObject [ caption := anObject ] { #category : #accessing } Tiddler >> created [ ^ created ifNil: [ created := self class nowLocal ] ] { #category : #accessing } Tiddler >> created: anObject [ created := anObject ] { #category : #accessing } Tiddler >> createdAsTWFormat [ (self created beginsWith: 'NaN') ifTrue: [ self created: self class nowLocal]. ^ ((ZTimestampFormat fromString: '20010203160506700') format: (ZTimestamp fromString: self created)) copyFrom: 1 to: 17 ] { #category : #accessing } Tiddler >> createdReversableEncoded [ "I encode the tiddler creation date with miliseconds precision in a shorter reversable way (from 17 characters to 10). But when tiddlers are created with the same exact date (for example programmatically) I produce the same encoding (because of reversability). I recommend to use nanoID instead to get unique visible different identifiers " | output longDate | longDate := self createdAsTWFormat. output := WriteStream on: ''. 1 to: 14 by: 2 do: [ :i | output nextPutAll: (longDate copyFrom: i to: i +1) greaseInteger asCharacterDigit greaseString ]. output nextPutAll: (longDate copyFrom: 15 to: 17). ^ output contents ] { #category : #accessing } Tiddler >> creator [ ^ creator ] { #category : #accessing } Tiddler >> creator: anObject [ creator := anObject ] { #category : #accessing } Tiddler >> customFields [ ^ customFields ifNil: [ customFields := Dictionary new] ] { #category : #accessing } Tiddler >> deleteUid [ self customFields deleteKey: 'uid'. ] { #category : #accessing } Tiddler >> exportJSONFile [ | jsonFile folder | folder := self wiki folder. jsonFile := folder / 'tiddlers' / ((self fileName removeSuffix: '.ston'), '.json'). ^ MarkupFile exportAsFileOn: jsonFile containing:self asJson ] { #category : #accessing } Tiddler >> exportSTONFile [ ^ self exportSTONFileInto: 'tiddlers' ] { #category : #accessing } Tiddler >> exportSTONFileInto: subfolder [ | stonFile | stonFile := self wiki folder / subfolder / self fileName. ^ MarkupFile exportAsFileOn: stonFile containing: self asStonStringPretty ] { #category : #accessing } Tiddler >> exportSTONFileOptimized [ | exporter wikiFolder tiddlersFolder | wikiFolder := self wiki folder. exporter := wikiFolder / 'scripts' / 'stringAsFileInto'. exporter exists ifFalse: [ self installTiddlerExporter ]. tiddlersFolder := wikiFolder / 'tiddlers'. tiddlersFolder exists ifFalse: [ tiddlersFolder ensureCreateDirectory ]. OSSUnixSubprocess new command: exporter fullName; arguments: { self asStonStringPretty . self fileName }; workingDirectory: tiddlersFolder fullName; runAndWaitOnExitDo: [ :process :outString | ^ tiddlersFolder / self fileName ] ] { #category : #accessing } Tiddler >> exportWithTemplate: aTemplate [ ^ aTemplate asMustacheTemplate value: self asDictionary ] { #category : #accessing } Tiddler >> fileName [ | dashedTitle sanitized | dashedTitle := '-' join: (self title substrings collect: [ :each | each ]). sanitized := dashedTitle copyWithoutAll: #($¿ $? $! $¡ $/). ^ sanitized , '--', (self uid copyFrom: 1 to: 12), '.ston'. ] { #category : #accessing } Tiddler >> fromDictionary: aDictionary [ | customKeys | self title: (aDictionary at: 'title'); text: (aDictionary at: 'text' ifAbsentPut: [ nil ]); tags: (aDictionary at: 'tags' ifAbsentPut: [ nil ]); created: (aDictionary at: 'created' ifAbsentPut: [ self class nowLocal ]); creator: (aDictionary at: 'creator' ifAbsentPut: [ nil ]); modified: (aDictionary at: 'modified' ifAbsentPut: [ nil ]); modifier: (aDictionary at: 'modifier' ifAbsentPut: [ nil ]); type: (aDictionary at: 'type' ifAbsentPut: [ nil ]); caption: (aDictionary at: 'caption' ifAbsentPut: [ nil ]); bag: (aDictionary at: 'bag' ifAbsentPut: [ nil ]); list: (aDictionary at: 'list' ifAbsentPut: [ nil ]); revision: (aDictionary at: 'revision' ifAbsentPut: [ nil ]). customKeys := aDictionary keys copyWithoutAll: (self class instanceVariables collect: [ :each | each name ]). "(customKeys includes: 'uid') ifFalse: [ self uidGenerator ]." customKeys do: [:key | | valueTemp | valueTemp := aDictionary at: key. valueTemp class = Array ifTrue: [ self customFields at: key put: (self tiddlersListFrom: valueTemp) ] ifFalse: [ self customFields at: key put: valueTemp ]. valueTemp class ]. ] { #category : #'instance creation' } Tiddler >> fromMarkdownParsedItems: aCollection [ | outputStream | outputStream := '' writeStream. aCollection children do: [ :each | each children ifEmpty: [ self itemContentsStringFor: each into: outputStream ] ifNotEmpty: [ each children do: [ :child | self itemContentsStringFor: child into: outputStream ] ] ] ] { #category : #accessing } Tiddler >> gtTextFor: aView [ ^ aView textEditor title: 'Text'; text: [ text ] ] { #category : #testing } Tiddler >> hasUID [ ^ self customFields includesKey: 'uid' ] { #category : #accessing } Tiddler >> importFedWikiPage: pageViewUrlString [ | pageTitle pageViewUrl pageData | pageViewUrl := pageViewUrlString asZnUrl. pageTitle := pageViewUrl segments second. pageData := (pageViewUrl scheme, '://', pageViewUrl host, '/', pageTitle, '.json') asZnUrl. ^ STONJSON fromString: pageData retrieveContents ] { #category : #accessing } Tiddler >> installTiddlerExporter [ | folder | folder := (self wiki folder). folder := (folder / 'scripts') ensureCreateDirectory. ZnClient new url: 'https://mutabit.com/repos.fossil/mutabit/uv/wiki/scripts/stringAsFileInto'; downloadTo: folder / 'stringAsFileInto'. ZnClient new url: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/wiki/scripts/stringAsFileInto.nim'; downloadTo: folder / 'stringAsFileInto.nim'. OSSUnixSubprocess new command: 'chmod'; arguments: { '+x' . (folder / 'stringAsFileInto') fullName }; workingDirectory: folder fullName; redirectStdout; redirectStderr; runAndWaitOnExitDo: [ :process :outString | ^ outString ] ] { #category : #testing } Tiddler >> isImage [ ^ self type ifNil: [ ^ false ]; beginsWith: 'image/' ] { #category : #testing } Tiddler >> isJavascript [ ^ self type = 'application/javascript' ] { #category : #testing } Tiddler >> isMarkdown [ ^ self type = 'text/x-markdown' ] { #category : #testing } Tiddler >> isNilType [ ^ self type = nil ] { #category : #testing } Tiddler >> isPDF [ ^ self type = 'application/pdf' ] { #category : #testing } Tiddler >> isShadow [ ^ self title beginsWith: '$:/' ] { #category : #testing } Tiddler >> isTW5Type [ ^ self type = 'text/vnd.tiddlywiki' ] { #category : #testing } Tiddler >> isTextPlain [ ^ self type = 'text/plain' ] { #category : #testing } Tiddler >> isXTiddlerDictionary [ ^ self type = 'application/x-tiddler-dictionary' ] { #category : #utilities } Tiddler >> itemContentsStringFor: item into: stream [ stream nextPutAll: item text; nextPut: Character cr; nextPut: Character cr ] { #category : #accessing } Tiddler >> linkedTiddlers [ "At the begining we are going to introduce 'pureTiddlers' as thos included in the wiki which are not linked via aliases. Future versions of this method sould included internal aliased tiddlers." | pureTiddlersTitles | self rawLinks ifNil: [ ^nil ]. pureTiddlersTitles := self rawLinks difference: self rawAliasedLinks. ^ self wiki tiddlers select: [:tiddler | pureTiddlersTitles includes: tiddler title ]. ] { #category : #accessing } Tiddler >> list [ ^ list ] { #category : #accessing } Tiddler >> list: anObject [ list := anObject ] { #category : #accessing } Tiddler >> listedTiddlers [ "I export all tiddlers in the list field as an alphabetic collection. Future versions should preserve the order in the list. Notice that while '#list' only gives the titles of the listed tiddlers, I return them as proper tiddler objects." | remainList remainListArray listedTiddlers | self list ifNil: [^ nil ]. remainList := self list copy. self manualLinksList do: [:manualLink | remainList := remainList copyReplaceAll: manualLink with: '' ]. remainListArray := (remainList copyReplaceAll: '[[]]' with: '') withBlanksCondensed splitOn: Character space. listedTiddlers := self manualLinksList, remainListArray. ^ self wiki tiddlers select: [:tiddler | listedTiddlers includes: tiddler title ]. ] { #category : #accessing } Tiddler >> manualLinksList [ self list ifNil: [^ nil]. ^ WikiTextGrammar new linkSea star parse: self list. ] { #category : #utilities } Tiddler >> markdownLinksAsWikiText [ "I'm useful to convert _internal_ links between formats, as is a common pattern found when migrating content from Markdown to TiddlyWiki's WikiText. I DON'T work on external links. A better regex could be used for that. See: - https://davidwells.io/snippets/regex-match-markdown-links - http://blog.michaelperrin.fr/2019/02/04/advanced-regular-expressions/" | markdownLinks | markdownLinks := (self text splitOn: Character space) select: [:each | each matchesRegex: '\[(.+)\)']. markdownLinks ifEmpty: [^ self]. ^ markdownLinks ] { #category : #accessing } Tiddler >> modified [ ^ modified ] { #category : #accessing } Tiddler >> modified: anObject [ modified := anObject ] { #category : #accessing } Tiddler >> modifier [ ^ modifier ] { #category : #accessing } Tiddler >> modifier: anObject [ modifier := anObject ] { #category : #accessing } Tiddler >> printOn: aStream [ super printOn: aStream. aStream nextPutAll: '( ', self title, ' )' ] { #category : #accessing } Tiddler >> rawAliasedLinks [ ^ self rawLinks select: [ :each | each includesSubstring: '|' ] ] { #category : #accessing } Tiddler >> rawLinks [ self text ifNil: [ ^ Set new ]. ^ (WikiTextGrammar new linkSea star parse: self text) asSet ] { #category : #accessing } Tiddler >> revision [ ^ revision ] { #category : #accessing } Tiddler >> revision: aNumberString [ revision := aNumberString ] { #category : #accessing } Tiddler >> tags [ ^ tags ] { #category : #accessing } Tiddler >> tags: anObject [ tags := anObject ] { #category : #accessing } Tiddler >> text [ ^ text ] { #category : #accessing } Tiddler >> text: anObject [ text := anObject ] { #category : #accessing } Tiddler >> tiddlersListFrom: anArray [ | output | output := '' writeStream. anArray doWithIndex: [:each :i | output nextPutAll: '[[', each asString, ']]'. i = anArray size ifFalse: [ output nextPutAll: Character space asString ]. ]. ^ output contents. ] { #category : #accessing } Tiddler >> title [ ^ title ] { #category : #accessing } Tiddler >> title: anObject [ title := anObject ] { #category : #accessing } Tiddler >> type [ ^ type ] { #category : #accessing } Tiddler >> type: anObject [ type := anObject ] { #category : #accessing } Tiddler >> uid [ ^ self customFields at: 'uid' ifAbsentPut: [ self uidGenerator ]. ] { #category : #accessing } Tiddler >> uidGenerator [ ^ self customFields at: 'uid' put: NanoID generate. ] { #category : #accessing } Tiddler >> wiki [ ^ wiki ] { #category : #accessing } Tiddler >> wiki: aTiddlyWiki [ wiki := aTiddlyWiki ]