" I model a TiddlyWiki. More information: https://tiddlywiki.com/ " Class { #name : #TiddlyWiki, #superclass : #Object, #instVars : [ 'name', 'file', 'remote', 'jsonFile', 'tiddlers' ], #category : #'TiddlyWiki-Model' } { #category : #accessing } TiddlyWiki class >> fromJSONUrl: anUrlString [ | rawContents contentsString | rawContents := anUrlString asUrl retrieveContents. rawContents class = ByteArray ifTrue: [ contentsString := rawContents utf8Decoded ] ifFalse: [ contentsString := rawContents ]. ^ self new fromDictionary: (STONJSON fromString: contentsString); remote: anUrlString; name: anUrlString ] { #category : #accessing } TiddlyWiki >> addRecentChangesToRepo [ | docsSton docsStonSanitized recentTiddlers repository | repository := self repository. self exportJSONFile; importJSONFile. recentTiddlers := self changesAfter: repository checkoutDateAndTime. docsSton := recentTiddlers collect: [:each | each exportSTONFile]. "Collecting tiddlers file reference as string and adding to the repo" docsStonSanitized := docsSton collect: [ :each | (each fullName removePrefix: repository localRoot) allButFirst ]. docsStonSanitized do: [ :each | repository add: each ]. ^ repository status ] { #category : #accessing } TiddlyWiki >> addToConfigFile [ | cleaned newConfig | cleaned := self copy. cleaned tiddlers: nil. newConfig := self configDictonary at: cleaned name put: cleaned; yourself. ^ MarkupFile exportAsFileOn: self configFile containing:(STON toStringPretty: newConfig) ] { #category : #testing } TiddlyWiki >> belongsToLocalRepository [ | localFolder tempRepo relativeName | localFolder := self detectRepositoryLocal ifNil: [ ^ false ]. tempRepo := FossilRepo new local: localFolder. relativeName := self file fullName withoutPrefix: (tempRepo local fullName, '/'). ^ tempRepo listUnversioned hasLiteral: relativeName ] { #category : #accessing } TiddlyWiki >> changesAfter: aDateString [ | recent created modified wiki aDate | aDate := aDateString asZTimestamp. wiki := self contentTiddlersWithoutLargeTiddlers. created := wiki select: [ :tiddler | tiddler created asZTimestamp > aDate ]. modified := wiki select: [ :tiddler | tiddler modified isNotNil and: [ tiddler modified asZTimestamp > aDate ] ]. recent := OrderedCollection new. recent addAll: created; addAll: modified. ^ recent asSet. ] { #category : #accessing } TiddlyWiki >> configDictonary [ ^ STONJSON fromString: self configFile contents. ] { #category : #accessing } TiddlyWiki >> configFile [ | tempFile | tempFile := FileLocator home / '.config' / 'TiddlyWikiPharo' / 'tiddlywiki.conf.ston'. tempFile ensureCreateFile. tempFile contents isEmpty ifTrue: [ MarkupFile exportAsFileOn: tempFile containing: ( STON toStringPretty: Dictionary new) ]. ^ tempFile ] { #category : #accessing } TiddlyWiki >> contentTiddlers [ ^ self tiddlers copyWithoutAll: self shadow ] { #category : #accessing } TiddlyWiki >> contentTiddlersWithoutLargeTiddlers [ | content | content := OrderedCollection new. content addAll: (self contentTiddlers select: [ :each | each isTW5Type or: [ each isNilType ]]); addAll: (self contentTiddlers select: [ :each | each isJavascript ]); addAll: (self contentTiddlers select: [ :each | each isXTiddlerDictionary ]); addAll: (self contentTiddlers select: [ :each | each isTextPlain ]); addAll: (self contentTiddlers select: [ :each | each isMarkdown ]). ^ content. ] { #category : #accessing } TiddlyWiki >> detectRepositoryLocal [ | folder folderItems | folder := self file parent. folderItems := folder children. [(folderItems select: [ :path | (path basename beginsWith: '.fslckout') or: [ path basename beginsWith: '.fossil']]) isEmpty and:[ (folder = FileLocator root) not ]] whileTrue: [folder := folder parent. folderItems := folder children.]. folder = FileLocator root ifTrue: [ ^ nil ]. ^ folder ] { #category : #accessing } TiddlyWiki >> exportContentShadowAndLargeTiddlersSTONFiles [ self exportSTONFiles; exportLargeTiddlers ] { #category : #accessing } TiddlyWiki >> exportContentTiddlers [ | content | content := self contentTiddlersWithoutLargeTiddlers. ^ content do: [ :each | each exportSTONFileInto: 'tiddlers' ]. ] { #category : #accessing } TiddlyWiki >> exportContentType: aMimeType [ | filteredTiddlers tempWiki | filteredTiddlers := self selectContentType: aMimeType. filteredTiddlers do: [ :each | each exportSTONFileInto: self largeTiddlersFolderName ]. ^ self largeTiddlersFolder ] { #category : #accessing } TiddlyWiki >> exportJSONFile [ | docTree rawJsonTiddlers | self htmlFileExists. docTree := XMLHTMLParser parse: self file contents. rawJsonTiddlers := (docTree xpath: '//script[@class="tiddlywiki-tiddler-store"]') stringValue. ^ MarkupFile exportAsFileOn: self jsonFile containing: rawJsonTiddlers ] { #category : #accessing } TiddlyWiki >> exportJSONFileOptimized [ | exporter wikiFolder | wikiFolder := self file parent. exporter := wikiFolder / 'scripts' / 'exportJsonFile'. exporter exists ifFalse: [ self installJsonExporter]. OSSUnixSubprocess new command: exporter fullName; workingDirectory: exporter parent fullName; runAndWaitOnExitDo: [ :process :outString | ^ self jsonFile ] ] { #category : #accessing } TiddlyWiki >> exportJSONSubtiddlers: subtiddlersCollection [ ^ self exportJSONSubtiddlers: subtiddlersCollection as: 'subtiddlers' ] { #category : #accessing } TiddlyWiki >> exportJSONSubtiddlers: subtiddlersCollection as: aName [ ^ MarkupFile exportAsFileOn: self file parent / aName, 'json' containing: (self jsonSubtiddlers: subtiddlersCollection) ] { #category : #accessing } TiddlyWiki >> exportJSTiddlers [ | jsTiddlers jsNotShadow | jsTiddlers := self tiddlers select: [ :each | each isJavascript ]. jsNotShadow := jsTiddlers reject: [ :each | each isShadow ]. ^ jsNotShadow do: [ :each | each exportSTONFileInto: 'tiddlers' ] ] { #category : #accessing } TiddlyWiki >> exportLargeTiddlers [ ^ self largeTiddlers do: [ :each | each exportSTONFileInto: self largeTiddlersFolderName ]. ] { #category : #accessing } TiddlyWiki >> exportSTONFiles [ | stonFile wikiTemp shadowFile | self tiddlersJSONFile ifNil: [ self inform: 'No JSON Tiddlers file found. If you have one, please provide its location'. stonFile := FileLocator temp / 'tiddlers.ston' ] ifNotNil: [ stonFile := self tiddlersJSONFile withoutExtension , 'ston' ]. shadowFile := self largeTiddlersFolder / '_shadow.ston'. wikiTemp := self copy. wikiTemp tiddlers: self contentTiddlersWithoutLargeTiddlers. "wikiTemp := wikiTemp withoutImages. wikiTemp := wikiTemp withoutPDFs." GrafoscopioUtils exportAsSton: self shadow on: shadowFile. ^ GrafoscopioUtils exportAsSton: wikiTemp on: stonFile ] { #category : #accessing } TiddlyWiki >> exportSTONTiddlers: aCollection [ aCollection do: [:each | each exportSTONFile ] ] { #category : #accessing } TiddlyWiki >> exportTW5Tiddlers [ | tw5Tiddlers tw5ExplicitTiddlers notShadowTiddlers | tw5Tiddlers := self tiddlers select: [ :each | each isNilType ]. tw5ExplicitTiddlers := self tiddlers select: [ :each | each isTW5Type ]. notShadowTiddlers := OrderedCollection new. notShadowTiddlers addAll: (tw5ExplicitTiddlers reject: [ :each | each isShadow ]). notShadowTiddlers addAll: (tw5Tiddlers reject: [ :each | each isShadow ]). ^ notShadowTiddlers do: [ :each | each exportSTONFileInto: 'tiddlers' ] ] { #category : #accessing } TiddlyWiki >> file [ ^ file ] { #category : #accessing } TiddlyWiki >> file: anObject [ file := anObject ] { #category : #accessing } TiddlyWiki >> fromDictionary: tiddlersDict [ self tiddlers: (tiddlersDict collect: [ :each | Tiddler new fromDictionary: each; wiki: self ]) ] { #category : #accessing } TiddlyWiki >> fromUrl: anUrlString [ | docTree rawJsonTiddlers tiddlersDictionary | self remote: anUrlString. docTree := XMLHTMLParser parse: (self remote retrieveContents). rawJsonTiddlers := (docTree xpath: '//script[@class="tiddlywiki-tiddler-store"]') stringValue. tiddlersDictionary := STONJSON fromString: rawJsonTiddlers. self fromDictionary: tiddlersDictionary ] { #category : #accessing } TiddlyWiki >> htmlFileExists [ self file ifNil: [ self inform: 'No TiddlyWiki HTML file found. If you have one, please provide its location.'. ^ nil ]. ] { #category : #accessing } TiddlyWiki >> importJSONFile [ "I import a JSON representation of my tiddlers data, that has been previosly exported by the TiddlyWiki HTML self contained file. Such file is called, by convention, 'tiddlers.json' and stored in the same folder where the HTML file is located." | tiddlersDict | self tiddlersJSONFile ifNil: [ ^ self ]. tiddlersDict := STONJSON fromString: self tiddlersJSONFile contents. self fromDictionary: tiddlersDict ] { #category : #accessing } TiddlyWiki >> installJsonExporter [ | folder | folder := (self file parent / 'scripts') ensureCreateDirectory. ZnClient new url: 'https://mutabit.com/repos.fossil/mutabit/uv/wiki/scripts/exportJsonFile'; downloadTo: folder / 'exportJsonFile'. ZnClient new url: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/wiki/scripts/exportJsonFile.nim'; downloadTo: folder / 'exportJsonFile.nim'. OSSUnixSubprocess new command: 'chmod'; arguments: { '+x' . (folder / 'exportJsonFile') fullName }; workingDirectory: folder fullName; redirectStdout; redirectStderr; runAndWaitOnExitDo: [ :process :outString | ^ outString ] ] { #category : #accessing } TiddlyWiki >> jsonFile [ ^ jsonFile ifNil: [ self htmlFileExists. jsonFile := file parent / 'tiddlers.json'.] ] { #category : #accessing } TiddlyWiki >> jsonFile: aFileLocator [ "I contain the tiddlers representation of the wiki data in JSON format." jsonFile := aFileLocator ] { #category : #accessing } TiddlyWiki >> jsonSubtiddlers: subtiddlersCollection [ | subtiddlersDict | subtiddlersDict := subtiddlersCollection collect: [:tiddler | tiddler asDictionary ]. ^ STONJSON toStringPretty: subtiddlersDict ] { #category : #accessing } TiddlyWiki >> largeTiddlers [ | wikiImages wikiPDFs wikiLargeTiddlers | wikiImages := self selectContentType: 'image/'. wikiPDFs := self selectContentType: 'application/pdf'. wikiLargeTiddlers := OrderedCollection new. wikiLargeTiddlers addAll: wikiImages; addAll: wikiPDFs. ^ wikiLargeTiddlers ] { #category : #accessing } TiddlyWiki >> largeTiddlersFolder [ "I store all shadow tiddlers, i.e. tiddlers that provide functionality to TiddlyWiki, for example the ones that come in the plugins or in system tiddlers. For more information about shadow tiddlers, see #shadow method." | folder | folder := self file parent / self largeTiddlersFolderName. folder ensureCreateDirectory. ^ folder ] { #category : #accessing } TiddlyWiki >> largeTiddlersFolderName [ ^ 'largeTiddlers' ] { #category : #accessing } TiddlyWiki >> loadFromConfig: wikiname [ ^ (self configDictonary at: wikiname) importJSONFile. ] { #category : #accessing } TiddlyWiki >> local [ ^ self file ] { #category : #accessing } TiddlyWiki >> local: aFileRefence [ ^ self file:aFileRefence ] { #category : #accessing } TiddlyWiki >> name [ | tempName suffix | name ifNotNil: [ ^ name ]. self file ifNotNil: [ ^ name := self file basenameWithoutExtension ]. self remote ifNil: [ ^ name := nil ]. tempName := self remote file. (tempName endsWithAnyOf: #('.html' '.htm')) ifTrue: [ suffix := (tempName splitOn: '.') last. tempName := tempName removeSuffix: '.', suffix. ]. name := tempName ] { #category : #accessing } TiddlyWiki >> name: aString [ name := aString ] { #category : #accessing } TiddlyWiki >> networkView [ | view | view := GtMondrian new. view nodes with: self tiddlers. view edges connectFromAll: #linkedTiddlers. view layout force. ^ view ] { #category : #accessing } TiddlyWiki >> printOn: aStream [ super printOn: aStream. aStream nextPutAll: '( ', self name ,' )' ] { #category : #accessing } TiddlyWiki >> rebuildTiddlers [ | stonTiddlers shadowTiddlersFile | shadowTiddlersFile := self largeTiddlersFolder asFileReference children select: [ :each | each basename beginsWith: '_shadow.ston' ]. stonTiddlers := OrderedCollection new. stonTiddlers addAll: self rebuildTiddlersWithoutShadows ; addAll: (STON fromString:shadowTiddlersFile first contents). ^ stonTiddlers ] { #category : #accessing } TiddlyWiki >> rebuildTiddlersJSON [ self tiddlers: self rebuildTiddlersWithoutShadows. ^ self exportJSONSubtiddlers: (self rebuildTiddlersWithoutShadows asArray) as: 'rebuildedTiddlers'. ] { #category : #accessing } TiddlyWiki >> rebuildTiddlersWithoutLargeTiddlers [ | stonTiddlers contentTiddlersFiles | contentTiddlersFiles := self tiddlersFolder files select: [ :each | each basename endsWith: 'ston' ]. stonTiddlers := OrderedCollection new. stonTiddlers addAll: (contentTiddlersFiles collect:[ :each | STONJSON fromString: each contents ]). ^ stonTiddlers ] { #category : #accessing } TiddlyWiki >> rebuildTiddlersWithoutShadows [ | stonTiddlers largeTiddlers | largeTiddlers := (((self largeTiddlersFolder files) reject: [ :each | each basename beginsWith: '_shadow.ston' ]) collect: [ :each | STONJSON fromString: each contents ]). stonTiddlers := OrderedCollection new. stonTiddlers addAll: largeTiddlers; addAll: self rebuildTiddlersWithoutLargeTiddlers. ^ stonTiddlers ] { #category : #accessing } TiddlyWiki >> remote [ ^ remote ] { #category : #accessing } TiddlyWiki >> remote: aUrlString [ remote := aUrlString asZnUrl ] { #category : #accessing } TiddlyWiki >> repository [ | repo | self belongsToLocalRepository ifFalse: [ ^ self ]. repo := FossilRepo new local: self detectRepositoryLocal. repo repository. ^ repo ] { #category : #accessing } TiddlyWiki >> resynchronize [ | repository return | repository := self repository. repository update. return := Dictionary new at: 'status' put: self addRecentChangesToRepo. self exportSTONFiles. self exportContentTiddlers. self exportLargeTiddlers. ^ return at: 'file' put: self rebuildTiddlersJSON; yourself. ] { #category : #accessing } TiddlyWiki >> selectContentType: mimeType [ ^ self tiddlers select: [ :tiddler | tiddler type isNotNil and: [tiddler type beginsWith: mimeType ]] ] { #category : #accessing } TiddlyWiki >> shadow [ "Shadow tiddlers are tiddlers that are loaded from plugins. For more information about them, see: - https://tiddlywiki.com/static/ShadowTiddlers.html - https://groups.google.com/g/TiddlyWiki/c/HuyZmaRJTxI" ^ self tiddlers select: [:tiddler | tiddler title beginsWith: '$:/'] ] { #category : #accessing } TiddlyWiki >> syncRemoteLocalDestructive [ | repository | repository := self repository. repository update. repository revertRemoteUnversioned. repository exportHTMLUnversioned. self exportJSONFile; importJSONFile. self exportSTONFiles. self exportContentTiddlers. self exportLargeTiddlers. ^ Dictionary new at: 'status' put: repository status; at: 'tiddlers from STON' put: self rebuildTiddlers; yourself. ] { #category : #accessing } TiddlyWiki >> taggedWith: aTag [ ^ self tiddlers select: [:tiddler | tiddler tags isNotNil and: [tiddler tags includesSubstring: aTag ] ] ] { #category : #accessing } TiddlyWiki >> tiddlers [ ^ tiddlers ifNil: [ tiddlers := OrderedCollection new ] ] { #category : #accessing } TiddlyWiki >> tiddlers: anOrderedCollection [ tiddlers := anOrderedCollection ] { #category : #accessing } TiddlyWiki >> tiddlersFolder [ ^ self file parent / 'tiddlers' ] { #category : #accessing } TiddlyWiki >> tiddlersJSONFile [ self jsonFile exists ifFalse: [ self inform: 'You need to export tiddlers as JSON from TiddlyWiki and locate it in the same folder as the HTML file'. ^ nil ]. ^ jsonFile ] { #category : #accessing } TiddlyWiki >> tiddlersJSONUrl [ self remote ifNil: [^ nil]. ] { #category : #accessing } TiddlyWiki >> withoutContentType: application [ | filteredTiddlers tempWiki | filteredTiddlers := self tiddlers reject: [:tiddler | tiddler type isNotNil and: [tiddler type beginsWith: application ]]. tempWiki := self copy tiddlers: filteredTiddlers. tempWiki tiddlers do: [:tiddler | tiddler wiki: tempWiki ]. ^ tempWiki ] { #category : #accessing } TiddlyWiki >> withoutImages [ ^ self withoutContentType: 'image/' ] { #category : #accessing } TiddlyWiki >> withoutPDFs [ ^ self withoutContentType: 'application/pdf' ]