TiddlyWikiPharo/repository/TiddlyWiki/TiddlyWiki.class.st

708 lines
19 KiB
Smalltalk

"
I model a TiddlyWiki.
More information:
https://tiddlywiki.com/
"
Class {
#name : #TiddlyWiki,
#superclass : #Object,
#instVars : [
'name',
'file',
'remote',
'jsonFile',
'tiddlers',
'folder'
],
#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 updateFromHtml.
recentTiddlers := self changesAfter: repository checkoutDateAndTime.
docsSton := recentTiddlers collect: [:each | each exportSTONFileOptimized ].
"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 : #accessing }
TiddlyWiki >> addUnversionedLargeTiddlersToRepo [
| stonfiles largeTiddlersFileReference repository |
repository := self repository.
stonfiles := (self file parent / 'largeTiddlers') files
select: [ :each | each fullName endsWith: '.ston' ].
largeTiddlersFileReference := stonfiles collect: [ :each |
each fullName withoutPrefix:
repository local fullName , '/' ].
largeTiddlersFileReference do: [ :each | repository addUnversioned: each ].
^ largeTiddlersFileReference
]
{ #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 commonTiddlers.
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 >> commonTiddlers [
| 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 >> 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 >> 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 >> downloadHTML [
| htmlLink localCopy |
self isInTiddlyHost ifTrue: [
htmlLink := self remote asString
].
localCopy := self folder / 'index.html'.
localCopy exists ifTrue: [ | timestamp |
timestamp := (DateAndTime now asString) copyReplaceAll: ':' with: '_'.
localCopy renameTo: 'index', timestamp, '.html'].
ZnClient new
url: htmlLink;
downloadTo: self folder / 'index.html'.
^ self folder
]
{ #category : #accessing }
TiddlyWiki >> exportCommonTiddlers [
| content |
content := self commonTiddlers.
content do: [ :each |
each exportSTONFileOptimized ].
^ self tiddlersFolder
]
{ #category : #accessing }
TiddlyWiki >> exportContentShadowAndLargeTiddlersSTONFiles [
self exportSTONFiles; exportLargeTiddlers
]
{ #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 commonTiddlers.
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 >> folder [
folder ifNotNil: [ ^ folder ].
self file
ifNotNil: [ folder := self file parent ]
ifNil: [ folder := FileLocator temp ]
]
{ #category : #accessing }
TiddlyWiki >> folder: anObject [
folder := anObject
]
{ #category : #accessing }
TiddlyWiki >> fromDictionary: tiddlersDict [
self tiddlers: (tiddlersDict collect: [ :each |
Tiddler new
fromDictionary: each;
wiki: self ])
]
{ #category : #accessing }
TiddlyWiki >> fromString: aJSONString [
| tiddlersDict |
tiddlersDict := STONJSON fromString: aJSONString.
self fromDictionary: tiddlersDict
]
{ #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
#exportJSONFileOptimized or #exportJSONFile"
self tiddlersJSONFile ifNil: [ ^ self ].
self fromString: self tiddlersJSONFile contents.
]
{ #category : #accessing }
TiddlyWiki >> importJSONLink [
^ self fromString: (self remote asString, '/tiddlers.json') asUrl retrieveContents
]
{ #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 >> isInTiddlyHost [
^ self remote host endsWith: 'tiddlyhost.com'
]
{ #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 asArray 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 ].
(self remote host endsWith: 'tiddlyhost.com')
ifTrue: [ ^ name := (self remote host splitOn: '.') first ].
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 >> oldestCreatedTiddler [
| tiddlersTemp oldestDate |
tiddlersTemp := self tiddlers.
oldestDate := (tiddlersTemp collect: [ :tiddler | tiddler created asDateAndTimeForTiddler ]) asSortedCollection first.
^ (tiddlersTemp select:[ :tiddler | tiddler created asDateAndTimeForTiddler = oldestDate ]) first.
]
{ #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) 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 largeTiddlersRebuild |
largeTiddlersRebuild := (((self largeTiddlersFolder files)
reject: [ :each | each basename beginsWith: '_shadow' ])
collect: [ :each | STONJSON fromString: each contents ]).
stonTiddlers := OrderedCollection new.
stonTiddlers
addAll: largeTiddlersRebuild;
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 >> resynchronizeWithRepository [
| repository return |
repository := self repository.
repository update.
repository syncUnversioned.
return := Dictionary new
at: 'status' put: self addRecentChangesToRepo.
self exportSTONFiles;
exportCommonTiddlers;
exportLargeTiddlers.
^ return
at: 'file' put: self rebuildTiddlersJSON;
yourself.
]
{ #category : #accessing }
TiddlyWiki >> selectByTagsIncludes: string [
^ (self tiddlers select: [ :tiddler | tiddler tags notNil ])
select: [ :tiddler | tiddler tags includesSubstring: string ]
]
{ #category : #accessing }
TiddlyWiki >> selectContentType: mimeType [
^ self tiddlers select: [ :tiddler | tiddler type isNotNil and: [tiddler type beginsWith: mimeType ]]
]
{ #category : #accessing }
TiddlyWiki >> selectTitleIncludes: string [
^ self tiddlers select: [ :tiddler | tiddler title includesSubstring: string ]
]
{ #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 exportCommonTiddlers.
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 [
(self isInTiddlyHost and: [^ tiddlers isNil]) ifTrue: [ ^ self importJSONLink].
^ 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 >> updateFromHtml [
self
exportJSONFileOptimized;
importJSONFile.
]
{ #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'
]