'From Squeak2.8 of 13 June 2000 [latest update: #2359] on 8 August 2006 at 7:26:56 pm'! "Change Set: MuSwiki3 Date: 26 February 1999 Author: Lex Spoon http://www.cc.gatech.edu/projects/squeakers/10.html"! Model subclass: #MuSwikiBrowser instanceVariableNames: 'serverName serverPort userName password connection editting pageTitle pageAppearance pageLockedBy allTitles statusLine ' classVariableNames: 'DefaultServer DefaultServerPort DefaultUserName ' poolDictionaries: '' category: 'MuSwiki-Client'! Object subclass: #MuSwikiConnection instanceVariableNames: 'stringSocket ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki'! MuSwikiConnection subclass: #MuSwikiClientConnection instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Client'! Object subclass: #MuSwikiConnectionDialogue instanceVariableNames: 'browser server port userName password windows inputs ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Client'! !MuSwikiConnectionDialogue commentStamp: '' prior: 0! a dialogue for connecting a MuSwikiBrowser to a server. ! BorderedMorph subclass: #MuSwikiEditButtons instanceVariableNames: 'model editButton acceptButton cancelButton ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Client'! !MuSwikiEditButtons commentStamp: '' prior: 0! a morph which displays either a single "EDIT" button, or both "ACCEPT" and "CANCEL" buttons, for a MuSwiki browser. BUGS When the morph gets big, there is a lot of dead space. You should be able to click anywhere in edit mode, and hit either "accept" or "cancel". When the morph gets really small, the strings go past the edge of the morph. First, they should degenerate to "A", "C", and "E" buttons; then, they should still not overwrite the edge of the morph, one way or another.! RectangleMorph subclass: #MuSwikiLink instanceVariableNames: 'pageTitle linkMorph ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-MuSwiki'! Object subclass: #MuSwikiMessage instanceVariableNames: '' classVariableNames: 'CommandTable ' poolDictionaries: '' category: 'MuSwiki-Messages'! !MuSwikiMessage commentStamp: '' prior: 0! abstract superclass for messages to be sent over the network. ! MuSwikiMessage subclass: #MSMError instanceVariableNames: 'message ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMLock instanceVariableNames: 'title ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMLocked instanceVariableNames: 'title appearancePickle ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMLoggedIn instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMLogin instanceVariableNames: 'protocolVersion clientName password ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMMalformed instanceVariableNames: 'stringArray reason ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMPage instanceVariableNames: 'title appearancePickle lockedBy ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! !MSMPage commentStamp: '' prior: 0! a message from server to client containing a page! MuSwikiMessage subclass: #MSMPageRequest instanceVariableNames: 'title ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMTableOfContents instanceVariableNames: 'titles ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMUnlock instanceVariableNames: 'title ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMWho instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! MuSwikiMessage subclass: #MSMWhoReply instanceVariableNames: 'clientNames ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Messages'! Object subclass: #MuSwikiPage instanceVariableNames: 'title appearance appearancePickle ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki'! !MuSwikiPage commentStamp: '' prior: 0! a page in a MuSwiki. It has a title and a morph (it's "appearance"). On a server, it can be locked by a single connection.! Object subclass: #MuSwikiPageRecord instanceVariableNames: 'title lockedBy ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki'! !MuSwikiPageRecord commentStamp: '' prior: 0! Holds basic info on a page; for use by the server. It doesn't hold the actual appearance morph, so it takes much less memory than an actual MuSwikiPage.! MorphicModel subclass: #MuSwikiPageViewer instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki-Client'! !MuSwikiPageViewer commentStamp: '' prior: 0! This morph displays the page that a browser wants to be currently viewed. It also allows the page to be editted.! GenericServer subclass: #MuSwikiServer instanceVariableNames: 'pageRecs pageDB pageDBDirtyTime password loggedInConnections tocPage logStream ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki'! !MuSwikiServer commentStamp: '' prior: 0! A MuSwiki server. ! MuSwikiConnection subclass: #MuSwikiServerConnection instanceVariableNames: 'clientName lastPageRetrieved pageLocked ' classVariableNames: '' poolDictionaries: '' category: 'MuSwiki'! !Object methodsFor: 'testing' stamp: 'ls 2/2/1999 14:06'! isMuSwikiBrowser ^false! ! !HandMorph methodsFor: 'world menu' stamp: 'ls 2/16/1999 13:39'! openMuSwiki "open a MuSwiki browser" "add this to #openMenu to get it on the world menu" MuSwikiBrowser new openInMorphic ! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/21/1999 13:57'! pageAppearance ^pageAppearance! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:23'! password ^password! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:32'! password: aStringOrNil password _ aStringOrNil! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:30'! serverName ^serverName! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:38'! serverName: aString serverName _ aString. DefaultServer _ aString! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:31'! serverPort ^serverPort! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:38'! serverPort: anInteger serverPort _ anInteger. DefaultServerPort _ serverPort! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:23'! userName ^userName! ! !MuSwikiBrowser methodsFor: 'access' stamp: 'ls 1/20/1999 10:39'! userName: aString userName _ aString. DefaultUserName _ aString! ! !MuSwikiBrowser methodsFor: 'private' stamp: 'ls 4/10/1999 10:21'! initialize editting _ false. pageAppearance _ nil. pageTitle _ nil. pageLockedBy _ nil. serverName _ DefaultServer. serverPort _ DefaultServerPort. userName _ DefaultUserName. password _ nil. allTitles _ #(). self status: 'UNCONNECTED'.! ! !MuSwikiBrowser methodsFor: 'messages' stamp: 'ls 8/8/2006 19:17'! pageArrived: newTitle appearancePickle: newAppearancePickle lockedBy: newLockedBy "a new page has arrived; display it" self isEditting ifTrue: [ ^self ]. pageTitle _ newTitle. pageAppearance _ (RWBinaryOrTextStream with: newAppearancePickle) reset fileInObjectAndCode. pageAppearance enableDragNDrop: false. "disallow (blatant) page changes outside of edit mode" pageLockedBy _ newLockedBy. self status: 'VIEWING: ', pageTitle. self changed: #pageAppearance.! ! !MuSwikiBrowser methodsFor: 'messages' stamp: 'ls 8/8/2006 19:17'! pageLocked: newTitle appearance: newAppearance "we have obtained a lock on a page; start editting that page" self isEditting ifTrue: [ self error: 'lock recieved, but we''re already editting!!' ]. pageTitle _ newTitle. pageAppearance _ newAppearance. pageAppearance enableDragNDrop: true. pageLockedBy _ userName. editting _ true. self status: 'EDITTING: ', pageTitle. self changed: #pageAppearance. self changed: #isEditting.! ! !MuSwikiBrowser methodsFor: 'messages' stamp: 'ls 3/8/1999 16:52'! serverError: message self setSaneStatus. self notify: 'ERROR: ', message.! ! !MuSwikiBrowser methodsFor: 'messages' stamp: 'ls 2/12/1999 16:12'! tableOfContents: titles allTitles _ titles asSortedCollection asArray! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 2/16/1999 13:40'! connect "start connecting to the given server" | serverAddress socket | Socket initializeNetwork. self disconnect. serverAddress _ NetNameResolver addressForName: serverName. serverAddress ifNil: [ ^self notify: 'could not find address for ', serverName ]. socket _ Socket new. socket connectTo: serverAddress port: serverPort. socket waitForConnectionUntil: Socket standardDeadline. socket isConnected ifFalse: [ self notify: 'failed to connect to: ', serverName, '/', serverPort asString. ^self ]. connection _ MuSwikiClientConnection onSocket: socket. connection logInAs: userName password: password. self getPage: 'Top'. ! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 4/10/1999 10:19'! disconnect "disconnect from the server" connection ifNil: [ ^self ]. self isEditting ifTrue: [ (self confirm: 'editting a page--disconnect anyway?') ifFalse: [ ^self ] ]. connection destroy. connection _ nil. "do a mini-reset..." pageTitle _ pageAppearance _ pageLockedBy _ nil. "forget which page we are viewing" editting _ false. "no, we're not editting" allTitles _ #(). self changed: self. self status: 'UNCONNECTED'! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 2/9/1999 10:47'! isConnected ^connection ~= nil and: [ connection isConnected ]! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 2/26/1999 13:17'! processIO | message | connection ifNil: [ ^self ]. connection isConnected ifFalse: [ "lost connection" connection destroy. connection _ nil. self setSaneStatus. ^self ]. connection processIO. [ message _ connection nextMessageOrNil. message ~~ nil ] whileTrue: [ message applyToClient: self ]! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 1/18/1999 12:02'! step self processIO! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 8/8/2006 19:24'! stepTime ^10! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 8/8/2006 19:24'! stepTimeIn: window ^self stepTime! ! !MuSwikiBrowser methodsFor: 'network IO' stamp: 'ls 1/20/1999 10:49'! wantsSteps ^true! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 2/16/1999 13:28'! defaultBackgroundColor "this is kinda ugly, but it's better than stark white..." ^(Color r: 1.0 g: 1.0 b: 0.774)! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 4/10/1999 11:04'! initialExtent ^580@590! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 1/18/1999 11:51'! isEditting "whether there is a morph being editted right now" ^editting! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 2/16/1999 13:29'! labelString "return an informative label for the window" ^'MuSwiki 0'! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 4/10/1999 09:43'! morphicView "create and return a morphic SystemWindow for interacting with this browser" | win titleView buttons pageView | win _ SystemWindow new. win setLabel: 'MuSwiki'. win model: self. titleView _ PluggableTextMorph on: self text: #status accept: #getPage:. titleView acceptOnCR: true. win addMorph: titleView frame: (0@0 extent: 0.7@0.1). buttons _ MuSwikiEditButtons new. buttons model: self. win addMorph: buttons frame: (0.7@0 extent: 0.3@0.1). pageView _ MuSwikiPageViewer new. pageView model: self. win addMorph: pageView frame: (0@0.1 extent: 1@0.9). ^win! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 1/20/1999 10:22'! openConnectDialogue "open a dialogue which allows the user to connect us to a server" MuSwikiConnectionDialogue openOn: self! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 1/20/1999 10:23'! openInMorphic "open a morphic interface" self morphicView openInWorld. self openConnectDialogue! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 2/9/1999 10:48'! pageTitle "title of the page being viewed" ^pageTitle ifNil: [ self isConnected ifTrue: ['(no page being viewed)' ] ifFalse: ['(not connected)' ] ] ! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 4/10/1999 10:22'! setSaneStatus "set the status to something reasonable; this happens, for instance, after an error occurs" self isConnected ifFalse: [ ^self status: 'UNCONNECTED' ]. pageTitle ifNotNil: [ ^self status: 'VIEWING: ', pageTitle ]. self status: '(connected to ', serverName, ')'.! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 2/16/1999 13:29'! status "return the curretn status line" ^statusLine ! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 4/10/1999 10:20'! status: aStringOrText "change the status line" statusLine _ aStringOrText. (statusLine includes: $:) ifTrue:[ "boldface the part up to the colon, for extra visibility" statusLine _ statusLine asText. statusLine addAttribute: TextEmphasis bold from: 1 to: (statusLine indexOf: $:)-1 ]. self changed: #status.! ! !MuSwikiBrowser methodsFor: 'interface' stamp: 'ls 1/20/1999 13:38'! windowIsClosing self disconnect.! ! !MuSwikiBrowser methodsFor: 'editting' stamp: 'ls 4/10/1999 10:21'! cancelEdit self isEditting ifFalse: [ ^self error: 'not editting!!' ]. connection unlockPage: pageTitle. connection requestPage: pageTitle. "download the original version of the page" editting _ false. self changed: #isEditting. self status: 'CANCELING EDIT of: ', pageTitle.! ! !MuSwikiBrowser methodsFor: 'editting' stamp: 'ls 4/10/1999 10:21'! edit "go into edit mode for this page" self isConnected ifFalse: [ ^self inform: 'must connect first' ]. self isEditting ifTrue: [ ^self inform: 'already editting a page; cancel current edit, first' ]. pageTitle ifNil: [ ^self error: 'no page being viewed yet' ]. connection lockPage: pageTitle. self status: 'LOCKING: ', pageTitle.! ! !MuSwikiBrowser methodsFor: 'editting' stamp: 'ls 1/27/1999 11:27'! save self isEditting ifFalse: [ ^self error: 'not editting!!' ]. connection savePage: pageTitle appearance: pageAppearance. editting _ false. self changed: #isEditting.! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'ls 2/26/1999 13:12'! getPage | pageName | self isEditting ifTrue: [ ^self notify: 'unable to request a page while another page is being editted' ]. pageName _ self selectPageName. pageName ifNil: [ ^self ]. ^self getPage: pageName! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'ls 4/10/1999 10:36'! getPage: pageName | requestName | self isEditting ifTrue: [ ^self notify: 'unable to request a page while another page is being editted' ]. self isConnected ifFalse: [ ^self notify: 'not connected -- connect to server first' ]. requestName _ pageName asString withBlanksTrimmed. self status: 'DOWNLOADING: ', requestName. connection requestPage: requestName. ^true! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'ls 2/15/1999 22:37'! getPage: pageName onServer: server port: port self isEditting ifTrue: [ ^self notify: 'unable to request a page while another page is being editted' ]. self isConnected ifFalse: [ ^self notify: 'not connected -- connect to server first' ]. self disconnect. serverName _ server. serverPort _ port. password _ nil. self connect. self isConnected ifTrue: [ connection requestPage: pageName asString withBlanksTrimmed. ]. ^true! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'ls 4/10/1999 09:52'! menu: menu shiftState: shiftState ^menu labels: 'get page...\connect\disconnect\save reference\take picture as GIF' withCRs lines: #(1) selections: #(getPage openConnectDialogue disconnect saveReference saveGIF)! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'ls 1/20/1999 11:48'! perform: aMessage orSendTo: ignored self perform: aMessage! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'mjg 4/7/1999 13:39'! saveGIF GIFReadWriter putForm: pageAppearance imageForm onFileNamed: pageTitle,'.gif'. self notify: 'To make a link of this image, upload the picture and the reference and insert: *!!',pageTitle,'.gif','!!* '. ! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'mjg 4/6/1999 10:46'! saveReference | f | f := FileStream fileNamed: pageTitle,'.msw'. f nextPutAll: pageTitle,'|',serverName,'|',serverPort printString; cr. f close. ! ! !MuSwikiBrowser methodsFor: 'menu' stamp: 'ls 2/26/1999 13:12'! selectPageName "select a page name, offering a list of existing pages but allowing entry of a new page name, too. Returns nil if the user cancels" | pageName | pageName _ (SelectionMenu selections: (Array with: ' (new page) '), allTitles) startUp. pageName = ' (new page) ' ifTrue: [ pageName _ FillInTheBlankMorph request: 'name of page to create?'. pageName _ pageName withBlanksTrimmed. ]. pageName ifNil: [ ^nil ]. pageName isEmpty ifTrue: [ ^nil ]. ^pageName! ! !MuSwikiBrowser methodsFor: 'testing' stamp: 'ls 2/2/1999 14:06'! isMuSwikiBrowser ^true! ! !MuSwikiBrowser class methodsFor: 'instance creation' stamp: 'mjg 4/6/1999 10:35'! grabURL: aURL self openFromString: (HTTPSocket httpGet: aURL) contents.! ! !MuSwikiBrowser class methodsFor: 'instance creation' stamp: 'ls 1/18/1999 11:50'! new ^super new initialize! ! !MuSwikiBrowser class methodsFor: 'instance creation' stamp: 'mjg 4/6/1999 10:44'! openFromString: aString |m s| Transcript show: 'MSW: ',aString; cr. s := aString findTokens: '|'. Transcript show: 'Array: ',(s printString); cr. m := MuSwikiBrowser new. m serverName: 'guzdial.cc.gatech.edu'. m serverPort: 9090. m userName: (FillInTheBlank request: 'Your username?'). m connect.m morphicView openInWorld. m getPage: (s at: 1) onServer: (s at: 2) port: (s at: 3) asNumber. ! ! !MuSwikiConnection methodsFor: 'as yet unclassified' stamp: 'ls 1/20/1999 11:04'! destroy stringSocket destroy. stringSocket _ nil.! ! !MuSwikiConnection methodsFor: 'as yet unclassified' stamp: 'ls 1/20/1999 10:57'! isConnected stringSocket ifNil: [ ^false ]. ^stringSocket isConnected! ! !MuSwikiConnection methodsFor: 'as yet unclassified' stamp: 'ls 1/18/1999 11:53'! processIO stringSocket ifNotNil: [ stringSocket processIO ].! ! !MuSwikiConnection methodsFor: 'initialization' stamp: 'ls 1/18/1999 11:54'! socket: socket stringSocket _ (StringSocket on: socket).! ! !MuSwikiConnection methodsFor: 'messages' stamp: 'ls 1/18/1999 11:54'! nextMessageOrNil "return the next incoming message, or return nil if there are none" | array | array _ stringSocket nextOrNil. array ifNil: [ ^nil ]. ^MuSwikiMessage fromStringArray: array! ! !MuSwikiConnection methodsFor: 'messages' stamp: 'ls 1/18/1999 11:54'! sendMessage: aMessage "send the given MuSwiki message" stringSocket nextPut: aMessage asStringArray! ! !MuSwikiClientConnection methodsFor: 'messages' stamp: 'ls 1/21/1999 14:02'! lockPage: title self sendMessage: (MSMLock pageTitle: title)! ! !MuSwikiClientConnection methodsFor: 'messages' stamp: 'ls 2/12/1999 16:13'! logInAs: userName password: password self sendMessage: (MSMLogin protocolVersion: 2 clientName: userName password: password)! ! !MuSwikiClientConnection methodsFor: 'messages' stamp: 'ls 1/20/1999 13:30'! requestPage: pageName self sendMessage: (MSMPageRequest title: pageName)! ! !MuSwikiClientConnection methodsFor: 'messages' stamp: 'ls 1/27/1999 11:27'! savePage: title appearance: appearance self sendMessage: (MSMPage title: title appearance: appearance)! ! !MuSwikiClientConnection methodsFor: 'messages' stamp: 'ls 2/1/1999 13:42'! unlockPage: title self sendMessage: (MSMUnlock pageTitle: title)! ! !MuSwikiConnection class methodsFor: 'protocol specs' stamp: 'ls 4/10/1999 09:55'! validClientName: aString "return whether the specified client name is valid or not" aString size > 20 ifTrue: [ "name is too long" ^false ]. aString do: [ :c | c isAlphaNumeric ifFalse: [ "name contains wierd characters" ^false ] ]. ^true! ! !MuSwikiConnection class methodsFor: 'as yet unclassified' stamp: 'ls 1/18/1999 11:55'! onSocket: socket ^self new socket: socket! ! !MuSwikiConnectionDialogue methodsFor: 'UI' stamp: 'ls 2/1/1999 13:30'! cancel windows do: [ :w | w delete]. ! ! !MuSwikiConnectionDialogue methodsFor: 'UI' stamp: 'ls 1/20/1999 10:32'! connect inputs do: [ :m | m hasUnacceptedEdits ifTrue: [ m accept ] ]. self flag: #XXX. "this doesn't check the case that a non-numeric port was enterred" browser serverName: server. browser serverPort: port. browser userName: userName. browser password: (password isEmpty ifTrue: [ nil ] ifFalse: [ password ]). windows do: [ :w | w delete]. browser connect.! ! !MuSwikiConnectionDialogue methodsFor: 'UI' stamp: 'ls 1/27/1999 11:28'! initialExtent ^200@300! ! !MuSwikiConnectionDialogue methodsFor: 'UI' stamp: 'ls 1/20/1999 10:26'! open | win level button input | win _ SystemWindow new. win model: self. "add the input entries for server, port, etc." level _ 0. #( 'Server' server 'Port' portText 'Login name' userName 'Password' password ) pairsDo: [ :desc :method | button _ PluggableButtonMorph on: self getState: nil action: nil. button label: desc. win addMorph: button frame: (0@(level*0.2) extent: 0.5@0.2). input _ PluggableTextMorph on: self text: method accept: (method, ':') asSymbol. input acceptOnCR: true. win addMorph: input frame: ((0.5@(level*0.2)) extent: 0.5@0.2). inputs add: input. level _ level + 1. ]. "add the connect and cancel buttons" button _ PluggableButtonMorph on: self getState: nil action: #connect. button label: 'Connect'. win addMorph: button frame: (0@0.8 extent: 0.5@0.2). button _ PluggableButtonMorph on: self getState: nil action: #cancel. button label: 'Cancel'. win addMorph: button frame: (0.5@0.8 extent: 0.5@0.2). win openInWorld. windows add: win.! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:10'! password ^password! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:13'! password: aString password _ aString. ^true! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:10'! port ^port! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:12'! portText ^port asString asText! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:33'! portText: aText | newPort | newPort _ Integer readFrom: (ReadStream on: aText asString withBlanksTrimmed). newPort > 0 ifTrue: [ port _ newPort. ^true ]. ^false! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:06'! server ^server ifNil: [ '' ]! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:11'! server: aText server _ aText asString. ^true! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:10'! userName ^userName! ! !MuSwikiConnectionDialogue methodsFor: 'access' stamp: 'ls 1/20/1999 10:13'! userName: newName userName _ newName asString. ^true! ! !MuSwikiConnectionDialogue methodsFor: 'initialization' stamp: 'ls 1/20/1999 10:31'! browser: aBrowser browser _ aBrowser. server _ aBrowser serverName. port _ aBrowser serverPort. userName _ aBrowser userName. password _ aBrowser password ifNil: [ '' ]. windows _ Set new. inputs _ Set new.! ! !MuSwikiConnectionDialogue class methodsFor: 'instance creation' stamp: 'ls 1/20/1999 10:04'! on: aBrowser ^self new browser: aBrowser! ! !MuSwikiConnectionDialogue class methodsFor: 'instance creation' stamp: 'ls 1/20/1999 10:04'! openOn: aBrowser (self on: aBrowser) open! ! !MuSwikiEditButtons methodsFor: 'morphic' stamp: 'ls 1/18/1999 10:22'! layoutChanged super layoutChanged. self modelIsEditting ifTrue: [ self positionEdittingButtons ] ifFalse: [ self positionNonEdittingButton ]. ! ! !MuSwikiEditButtons methodsFor: 'morphic' stamp: 'ls 4/10/1999 11:06'! positionEdittingButtons "layout changed and we are in editting mode. Reposition the save and cancel button" | newWidth | newWidth _ self bounds width / 2. newWidth _ newWidth asInteger. acceptButton bounds: (self bounds withWidth: newWidth). cancelButton bounds: (self bounds withLeft: acceptButton innerBounds right).! ! !MuSwikiEditButtons methodsFor: 'morphic' stamp: 'ls 4/10/1999 10:53'! positionNonEdittingButton "layout changed and we are in non-editting mode. Reposition the edit button" editButton bounds: self bounds. ! ! !MuSwikiEditButtons methodsFor: 'morphic' stamp: 'ls 1/18/1999 11:08'! setButtons "set up our list of buttons" self removeAllMorphs. self modelIsEditting ifTrue: [ "add ACCEPT and CANCEL buttons" self addAllMorphs: (Array with: acceptButton with: cancelButton). ] ifFalse: [ "add EDIT button" self addMorph: editButton ]. "layoutChanged is implicitly called"! ! !MuSwikiEditButtons methodsFor: 'button presses' stamp: 'ls 1/20/1999 13:23'! cancel model cancelEdit! ! !MuSwikiEditButtons methodsFor: 'button presses' stamp: 'ls 1/20/1999 13:23'! edit model edit! ! !MuSwikiEditButtons methodsFor: 'button presses' stamp: 'ls 1/20/1999 13:23'! save model save! ! !MuSwikiEditButtons methodsFor: 'private' stamp: 'ls 1/18/1999 09:20'! modelIsEditting "return whether the model is currently editting a page or not" "returns false if there is no model" model ifNil: [ ^false ]. ^model isEditting! ! !MuSwikiEditButtons methodsFor: 'private' stamp: 'ls 1/20/1999 13:35'! update: aSymbol (aSymbol == #isEditting or: [ aSymbol == model ]) ifTrue: [ self setButtons ].! ! !MuSwikiEditButtons methodsFor: 'initialization' stamp: 'ls 4/10/1999 10:53'! createButtons "create editButton, etc." | buttonColor | editButton _ PluggableButtonMorph new. editButton label: 'EDIT'. editButton action: #edit. acceptButton _ PluggableButtonMorph new. acceptButton label: 'ACCEPT'. acceptButton action: #save. cancelButton _ PluggableButtonMorph new. cancelButton label: 'CANCEL'. cancelButton action: #cancel. buttonColor _ Color transparent. { editButton. acceptButton. cancelButton } do: [ :button | button model: self. button color: buttonColor. button onColor: buttonColor offColor: buttonColor ].! ! !MuSwikiEditButtons methodsFor: 'initialization' stamp: 'ls 1/18/1999 10:57'! initialize super initialize. self color: Color veryLightGray. self createButtons. self setButtons.! ! !MuSwikiEditButtons methodsFor: 'model access' stamp: 'ls 1/18/1999 11:12'! model: aBrowser "set the browser we are modelling" model _ aBrowser. aBrowser addDependent: self. self setButtons.! ! !MuSwikiLink methodsFor: 'private' stamp: 'ls 2/2/1999 14:12'! findBrowser "figure out what browser we are a part of. Currently, this traces up the morph hierarchy until it finds a morph who's model is a MuSwikiClient. If no browser is found, returns nil" | m | m _ self owner. [ m ~~ nil and: [ (m respondsTo: #model) not or: [m model isMuSwikiBrowser not ]]] whileTrue: [ m _ m owner ]. m ifNil: [ ^nil ]. ^m model! ! !MuSwikiLink methodsFor: 'action' stamp: 'ls 2/3/1999 13:45'! handlesMouseDown: evt ^true! ! !MuSwikiLink methodsFor: 'action' stamp: 'ls 2/26/1999 13:14'! modifySelection | newSelection | newSelection _ self findBrowser selectPageName. newSelection ifNil: [ ^self ]. self pageTitle: newSelection! ! !MuSwikiLink methodsFor: 'action' stamp: 'ls 2/3/1999 13:45'! mouseDown: evt self selected! ! !MuSwikiLink methodsFor: 'action' stamp: 'ls 2/15/1999 22:51'! selected | browser parsed | browser _ self findBrowser. browser ifNil: [ ^self ]. browser isEditting ifTrue: [ "in edit mode, offer to let the link be modified" self modifySelection. ^self ]. (pageTitle beginsWith: 'msw://') ifFalse: [ "link to another page on the same server" browser getPage: pageTitle. ^self ]. parsed _ self class parseLink: pageTitle. browser getPage: (parsed at: 3) onServer: (parsed at: 1) port: (parsed at: 2).! ! !MuSwikiLink methodsFor: 'access' stamp: 'ls 2/24/1999 13:43'! pageTitle: newTitle pageTitle _ newTitle asString withBlanksTrimmed. linkMorph contents: pageTitle. self extent: (linkMorph extent + (6@6))! ! !MuSwikiLink methodsFor: 'initialization' stamp: 'ls 2/24/1999 13:44'! initialize super initialize. linkMorph _ StringMorph new. linkMorph position: 3@3. linkMorph lock. self addMorph: linkMorph. self pageTitle: 'Top'. self color: Color cyan.! ! !MuSwikiLink class methodsFor: 'parsing' stamp: 'ls 2/16/1999 14:07'! parseLink: link "parse a link of the form 'msw://server:port/page+name'. Not incorporated into the real URL hierarchy yet, but it might be eventually. The return value is a 3-array with server, port, and page name" | rest i server port pageName | rest _ link copyFrom: 'msw://' size+1 to: link size. "extract the server name" i _ rest indexOf: $:. i = 0 ifTrue: [ "no port specified -- error for now" self error: 'no port specified on the link'. ^nil ]. server _ rest copyFrom: 1 to: i-1. rest _ rest copyFrom: i+1 to: rest size. "extract the port" i _ rest indexOf: $/. i = 0 ifTrue: [ self error: 'no page name specified'. ^nil ]. port _ Integer readFrom: (ReadStream on: (rest copyFrom: 1 to: i-1)). port = 0 ifTrue: [ self error: 'error parsing port number'. ^nil ]. rest _ rest copyFrom: i+1 to: rest size. "extract the page name" pageName _ rest. ^Array with: server with: port with: pageName! ! !MuSwikiMessage methodsFor: 'as yet unclassified' stamp: 'ls 1/20/1999 11:30'! applyToClient: client "apply to the given client" ^self subclassResponsibility! ! !MuSwikiMessage methodsFor: 'as yet unclassified' stamp: 'ls 1/20/1999 11:31'! applyToServer: server forConnection: connection "apply to the given server, assuming we were read from the given connection" self flag: #XXX. "This should eventually generate a custom error instead of halting the server" ^self subclassResponsibility! ! !MuSwikiMessage methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:18'! asStringArray "convert this to a array of strings which can be read back with fromStringArray:" ^self subclassResponsibility! ! !MuSwikiMessage methodsFor: 'testing' stamp: 'ls 1/13/1999 11:38'! isMalformed ^false! ! !MSMError methodsFor: 'initializing' stamp: 'ls 2/1/1999 13:26'! message: aString message _ aString! ! !MSMError methodsFor: 'converting' stamp: 'ls 2/1/1999 13:26'! asStringArray ^Array with: 'ERROR' with: message! ! !MSMError methodsFor: 'applying' stamp: 'ls 2/1/1999 13:28'! applyToClient: client client serverError: message! ! !MSMLock methodsFor: 'conversion' stamp: 'ls 1/27/1999 11:00'! asStringArray ^Array with: 'LOCK' with: title! ! !MSMLock methodsFor: 'initialization' stamp: 'ls 1/27/1999 10:59'! pageTitle: aString title _ aString! ! !MSMLock methodsFor: 'applying' stamp: 'ls 1/27/1999 11:01'! applyToServer: server forConnection: connection server lockPage: title forConnection: connection! ! !MSMLocked methodsFor: 'conversion' stamp: 'ls 3/8/1999 16:24'! asStringArray ^Array with: 'LOCKED' with: title with: appearancePickle! ! !MSMLocked methodsFor: 'applying' stamp: 'ls 3/8/1999 16:25'! applyToClient: client client pageLocked: title appearance: self appearance! ! !MSMLocked methodsFor: 'initialization' stamp: 'ls 3/8/1999 16:25'! title: aString appearancePickle: anotherString title _ aString. appearancePickle _ anotherString.! ! !MSMLocked methodsFor: 'access' stamp: 'ls 3/8/1999 16:25'! appearance ^(RWBinaryOrTextStream with: appearancePickle) binary reset fileInObjectAndCode! ! !MSMLoggedIn methodsFor: 'as yet unclassified' stamp: 'ls 1/20/1999 13:34'! applyToClient: client "nothing..."! ! !MSMLoggedIn methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:50'! asStringArray ^#('LOGGED IN')! ! !MSMLogin methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:28'! applyTo: server forConnection: connection server login: clientName onConnection: connection protocolVersion: protocolVersion password: password! ! !MSMLogin methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:47'! applyToServer: server forConnection: connection server login: clientName onConnection: connection protocolVersion: protocolVersion password: password! ! !MSMLogin methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:22'! asStringArray ^ Array with: 'LOGIN' with: protocolVersion asString with: clientName with: (password ifNil: ['']).! ! !MSMLogin methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:24'! protocolVersion: protocolVersion0 clientName: clientName0 password: password0 protocolVersion _ protocolVersion0. clientName _ clientName0. password _ password0.! ! !MSMMalformed methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 10:58'! applyToServer: server forConnection: connection server badMessage: stringArray fromConnection: connection! ! !MSMMalformed methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:26'! stringArray: stringArray0 reason: reason0 stringArray _ stringArray0. reason _ reason0.! ! !MSMMalformed methodsFor: 'testing' stamp: 'ls 1/13/1999 11:38'! isMalformed ^true! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:41'! appearance ^(RWBinaryOrTextStream with: appearancePickle) reset fileInObjectAndCode! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:25'! appearancePickle ^appearancePickle! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:25'! applyToClient: client client pageArrived: title appearancePickle: appearancePickle lockedBy: lockedBy! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:26'! applyToServer: server forConnection: connection server pageArrivedTitle: title appearancePickle: appearancePickle fromConnection: connection ! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:26'! asStringArray ^lockedBy ifNil: [ Array with: 'PAGE' with: title with: appearancePickle ] ifNotNil: [ Array with: 'PAGE' with: title with: appearancePickle with: lockedBy ]! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/1/1999 13:48'! title ^title! ! !MSMPage methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:27'! title: aString appearancePickle: anotherString lockedBy: aName title _ aString. appearancePickle _ anotherString. lockedBy _ aName! ! !MSMPageRequest methodsFor: 'access' stamp: 'ls 1/13/1999 10:53'! title ^title! ! !MSMPageRequest methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 10:53'! applyToServer: server forConnection: connection server pageRequest: title byConnection: connection! ! !MSMPageRequest methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:44'! asStringArray ^Array with: 'GET PAGE' with: title! ! !MSMPageRequest methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 10:52'! title: aString title _ aString! ! !MSMTableOfContents methodsFor: 'applying' stamp: 'ls 2/12/1999 16:06'! applyToClient: aClient aClient tableOfContents: titles! ! !MSMTableOfContents methodsFor: 'initialization' stamp: 'ls 2/12/1999 16:06'! titles: aList titles _ aList! ! !MSMTableOfContents methodsFor: 'conversion' stamp: 'ls 2/12/1999 16:05'! asStringArray ^Array streamContents: [ :str | str nextPut: 'TOC'. titles do: [ :title | str nextPut: title ] ]! ! !MSMUnlock methodsFor: 'conversion' stamp: 'ls 2/1/1999 13:37'! asStringArray ^Array with: 'UNLOCK' with: title! ! !MSMUnlock methodsFor: 'initialization' stamp: 'ls 2/1/1999 13:37'! pageTitle: aString title _ aString! ! !MSMUnlock methodsFor: 'applying' stamp: 'ls 2/1/1999 13:37'! applyToServer: server forConnection: connection server unlockPage: title forConnection: connection! ! !MSMWho methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:44'! applyToServer: server forConnection: connection server processWhoFor: connection! ! !MSMWho methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:39'! asStringArray ^#('WHO')! ! !MSMWhoReply methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:45'! asStringArray ^(Array with: 'WHO REPLY'), clientNames asArray! ! !MSMWhoReply methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:26'! clientNames ^clientNames! ! !MSMWhoReply methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:45'! clientNames: names clientNames _ names! ! !MuSwikiMessage class methodsFor: 'initialization' stamp: 'ls 2/12/1999 16:07'! initialize "MuSwikiMessage initialize" CommandTable _ Dictionary new. #( 'LOGIN' MSMLogin 'LOGGED IN' MSMLoggedIn 'PAGE' MSMPage 'GET PAGE' MSMPageRequest 'LOCK' MSMLock 'LOCKED' MSMLocked 'UNLOCK' MSMUnlock 'WHO' MSMWho 'WHO REPLY' MSMWhoReply 'TOC' MSMTableOfContents 'ERROR' MSMError ) pairsDo: [ :command :className | CommandTable at: (command asLowercase) put: (Smalltalk at: className) ]! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 11:13'! test "test out message encoding and parsing" " MuSwikiMessage test " #(testWho testWhoReply testPage testPageRequest) do: [ :m | (self perform: m) ifFalse: [ self error: m asString ] ]! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 11:12'! testLoggedIn ^true! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 11:12'! testLogin ^true! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 12:03'! testPage | testPage testPageEncoded msg array recoveredPage | testPage _ TextMorph new contents: 'hiya' asText. testPageEncoded _ RWBinaryOrTextStream on: (String new). (SmartRefStream on: testPageEncoded) nextPut: testPage. testPageEncoded _ testPageEncoded contents asString. msg _ self fromStringArray: (Array with: 'PAGE' with: testPageEncoded). msg class = MSMPage ifFalse: [ ^false ]. msg page class = TextMorph ifFalse: [ ^false ]. msg page contents asString = 'hiya' ifFalse: [ ^false ]. array _ (MSMPage page: testPage) asStringArray. (array at: 1) = 'PAGE' ifFalse: [ ^false ]. recoveredPage _ (RWBinaryOrTextStream with: array second) reset fileInObjectAndCode. recoveredPage class = TextMorph ifFalse: [ ^false ]. recoveredPage contents asString = 'hiya' ifFalse:[ ^false ]. ^true! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 11:44'! testPageRequest | msg array | msg _ self fromStringArray: #('GET PAGE' 'Cool ness'). msg class = MSMPageRequest ifFalse: [ ^false ]. msg title = 'Cool ness' ifFalse: [ ^false ]. array _ (MSMPageRequest title: 'zip boo boP') asStringArray. array = #('GET PAGE' 'zip boo boP') ifFalse: [^false ]. ^true! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 11:39'! testWho | msg array | msg _ self fromStringArray: #('WHO'). msg class == MSMWho ifFalse: [ ^false ]. array _ (MSMWho new) asStringArray. array = #('WHO') ifFalse: [^false ]. ^true! ! !MuSwikiMessage class methodsFor: 'testing' stamp: 'ls 1/13/1999 11:37'! testWhoReply | msg array | msg _ self fromStringArray: #('WHO REPLY' 'abc' 'def'). msg clientNames asSortedCollection asArray = #('abc' 'def') ifFalse: [ ^false ]. array _ (MSMWhoReply clientNames: #('abc' 'def')) asStringArray. array = #('WHO REPLY' 'abc' 'def') ifFalse: [^false ]. ^true! ! !MuSwikiMessage class methodsFor: 'parsing' stamp: 'ls 1/13/1999 11:36'! fromStringArray: stringArray "given an array of strings, parse a message. Always succeeds, although it may return a MalformedMuSwikiMessage" | command class | stringArray isEmpty ifTrue: [ ^self malformedMessage: stringArray reason: 'empty message' ]. command _ (stringArray at: 1) asLowercase. class _ CommandTable at: command ifAbsent: [ ^self malformedMessage: stringArray reason: 'unknown command (', command, ')' ]. ^class fromStringArray: stringArray! ! !MuSwikiMessage class methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:02'! badArgumentCountMessage: stringArray ^self malformedMessage: stringArray reason: 'incorrect number of arguments'! ! !MuSwikiMessage class methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:25'! malformedMessage: stringArray "generate a MSMMalformed for the given string array" ^self malformedMessage: stringArray reason: 'no explanation specified'! ! !MuSwikiMessage class methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:37'! malformedMessage: stringArray reason: aString "generate a MSMMalformed for the given string array" ^MSMMalformed stringArray: stringArray reason: aString! ! !MSMError class methodsFor: 'parsing' stamp: 'ls 2/1/1999 13:28'! fromStringArray: anArray anArray size = 2 ifFalse: [ ^self badArgumentCountMessage: anArray ]. ^self message: (anArray at: 2)! ! !MSMError class methodsFor: 'instance creation' stamp: 'ls 2/1/1999 13:26'! message: aString ^self new message: aString! ! !MSMLock class methodsFor: 'instance creation' stamp: 'ls 1/21/1999 14:01'! fromStringArray: array (array size = 2) ifFalse: [ ^self badArgumentCountMessage: array ]. ^self pageTitle: (array at: 2)! ! !MSMLock class methodsFor: 'instance creation' stamp: 'ls 1/27/1999 11:00'! pageTitle: title ^super new pageTitle: title! ! !MSMLocked class methodsFor: 'instance creation' stamp: 'ls 3/8/1999 16:26'! title: title appearancePickle: appearancePickle ^self new title: title appearancePickle: appearancePickle! ! !MSMLocked class methodsFor: 'parsing' stamp: 'ls 3/8/1999 16:50'! fromStringArray: array array size = 3 ifFalse: [ ^self malformedMessage: array ]. ^self title: (array at: 2) appearancePickle: (array at: 3)! ! !MSMLoggedIn class methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:51'! fromStringArray: stringArray (stringArray size ~= 1) ifTrue: [ ^self malformedMessage: stringArray ]. ^self new! ! !MSMLogin class methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:24'! fromStringArray: array | protocolVersion clientName password | array size < 3 ifTrue: [ ^self malformedMessage: array ]. protocolVersion _ array at: 2. clientName _ array at: 3. password _ array at: 4 ifAbsent: [ nil ]. protocolVersion _ Integer readFrom: (ReadStream on: protocolVersion). ^self new protocolVersion: protocolVersion clientName: clientName password: password! ! !MSMLogin class methodsFor: 'as yet unclassified' stamp: 'ls 1/18/1999 12:08'! protocolVersion: protocolVersion clientName: clientName password: password ^self new protocolVersion: protocolVersion clientName: clientName password: password ! ! !MSMMalformed class methodsFor: 'instance creation' stamp: 'ls 1/11/1999 11:26'! stringArray: stringArray reason: reason "create an instance with the given string array and explanation" ^self new stringArray: stringArray reason: reason! ! !MSMPage class methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:28'! fromStringArray: stringArray | title lockedBy appearancePickle | (stringArray size between: 3 and: 4) ifFalse: [ ^self badArgumentCountMessage: stringArray ]. title _ stringArray at: 2. appearancePickle _ stringArray at: 3. lockedBy _ (stringArray size > 3) ifTrue: [ stringArray at: 4 ] ifFalse: [ nil ]. ^self title: title appearancePickle: appearancePickle lockedBy: lockedBy! ! !MSMPage class methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:39'! title: aString appearance: aMorph ^self title: aString appearance: aMorph lockedBy: nil! ! !MSMPage class methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:42'! title: aString appearance: aMorph lockedBy: aName | appearancePickleStream | appearancePickleStream _ RWBinaryOrTextStream on: String new. (SmartRefStream on: appearancePickleStream) nextPut: aMorph. ^self title: aString appearancePickle: appearancePickleStream contents lockedBy: aName ! ! !MSMPage class methodsFor: 'as yet unclassified' stamp: 'ls 3/15/1999 12:31'! title: aString appearancePickle: anotherString ^self title: aString appearancePickle: anotherString lockedBy: nil! ! !MSMPage class methodsFor: 'as yet unclassified' stamp: 'ls 2/26/1999 12:43'! title: aString appearancePickle: anotherString lockedBy: aName ^self new title: aString appearancePickle: anotherString lockedBy: aName! ! !MSMPageRequest class methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 10:52'! fromStringArray: array array size = 2 ifFalse: [ ^self malformedMessage: array ]. ^self new title: (array at: 2)! ! !MSMPageRequest class methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:09'! title: aString "create a page request for the given title" ^self new title: aString! ! !MSMTableOfContents class methodsFor: 'instance creation' stamp: 'ls 2/12/1999 16:10'! titles: list ^super new titles: list! ! !MSMTableOfContents class methodsFor: 'parsing' stamp: 'ls 2/12/1999 16:10'! fromStringArray: array array size < 1 ifTrue: [ ^self malformedMessage: array ]. ^self titles: (array copyFrom: 2 to: array size)! ! !MSMUnlock class methodsFor: 'instance creation' stamp: 'ls 2/1/1999 13:38'! fromStringArray: array array size = 2 ifFalse: [ ^self badArgumentCountMessage: array ]. ^self pageTitle: (array at: 2)! ! !MSMUnlock class methodsFor: 'instance creation' stamp: 'ls 2/1/1999 13:38'! pageTitle: title ^self new pageTitle: title! ! !MSMWho class methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:43'! fromStringArray: stringArray (stringArray size ~= 1) ifTrue: [ ^self malformedMessage: stringArray ]. ^self new! ! !MSMWhoReply class methodsFor: 'as yet unclassified' stamp: 'ls 1/11/1999 11:45'! clientNames: listOfNames ^self new clientNames: listOfNames! ! !MSMWhoReply class methodsFor: 'as yet unclassified' stamp: 'ls 1/13/1999 11:29'! fromStringArray: stringArray ^self clientNames: (stringArray copyFrom: 2 to: stringArray size)! ! !MuSwikiPage methodsFor: 'private-initialization' stamp: 'ls 2/26/1999 12:58'! entitled: aString title _ aString. self appearance: self initialAppearance! ! !MuSwikiPage methodsFor: 'private-initialization' stamp: 'ls 2/5/1999 12:13'! initialAppearance "generate the morph which we initially look like" "(note, the server normally provides some other method for giving an initial appearance....)" ^PasteUpMorph new extent: 300@200; color: (Color white); borderColor: Color black! ! !MuSwikiPage methodsFor: 'access' stamp: 'ls 2/26/1999 13:06'! appearance "the morph that describes our appearance" ^(RWBinaryOrTextStream with: appearancePickle) reset fileInObjectAndCode! ! !MuSwikiPage methodsFor: 'access' stamp: 'ls 2/26/1999 13:04'! appearance: aMorph "change the appearance of this page" | str | self assert: [ aMorph isKindOf: Morph ]. str _ RWBinaryOrTextStream on: String new. (SmartRefStream on: str) nextPut: aMorph. self appearancePickle: str contents! ! !MuSwikiPage methodsFor: 'access' stamp: 'ls 2/26/1999 13:03'! appearancePickle "return a SmartRefStream pickle of our appearance" ^appearancePickle! ! !MuSwikiPage methodsFor: 'access' stamp: 'ls 2/26/1999 12:56'! appearancePickle: aString "change the appearance of this page" appearancePickle _ aString! ! !MuSwikiPage methodsFor: 'access' stamp: 'ls 1/13/1999 10:47'! title ^title! ! !MuSwikiPage methodsFor: 'assert' stamp: 'ls 3/15/1999 12:11'! assert: aBoolean aBoolean value ifFalse: [ self halt: 'assertion failed' ]! ! !MuSwikiPage class methodsFor: 'instance creation' stamp: 'ls 1/14/1999 18:42'! entitled: aString ^self new entitled: aString! ! !MuSwikiPageRecord methodsFor: 'initialization' stamp: 'ls 2/5/1999 10:17'! title: aString title _ aString! ! !MuSwikiPageRecord methodsFor: 'locking' stamp: 'ls 2/5/1999 10:18'! isLocked "test whether this page is locked by anyone" ^self lockedBy ~= nil! ! !MuSwikiPageRecord methodsFor: 'locking' stamp: 'ls 2/5/1999 10:18'! lockBy: aClient "lock the page for the given client. Overrides any previously existing lock. If aClient is nil, then unlock the page" lockedBy _ aClient! ! !MuSwikiPageRecord methodsFor: 'locking' stamp: 'ls 2/5/1999 10:18'! lockedBy "return who the page is locked by, or nil if no one" ^lockedBy! ! !MuSwikiPageRecord methodsFor: 'locking' stamp: 'ls 2/5/1999 10:18'! unlock "unlock the page" self lockBy: nil! ! !MuSwikiPageRecord methodsFor: 'access' stamp: 'ls 2/5/1999 12:20'! title ^title! ! !MuSwikiPageRecord class methodsFor: 'instance creation' stamp: 'ls 2/5/1999 10:29'! title: aString "create a record with the given string as title" ^super new title: aString! ! !MuSwikiPageViewer methodsFor: 'morphic' stamp: 'ls 4/9/1999 16:53'! canvasForSubmorphs: canvasForMe "Provide a clipping canvas for drawing my submorphs." ^ canvasForMe copyClipRect: self innerBounds! ! !MuSwikiPageViewer methodsFor: 'morphic' stamp: 'ls 4/10/1999 15:21'! handlesMouseDown: evt ^evt yellowButtonPressed! ! !MuSwikiPageViewer methodsFor: 'morphic' stamp: 'ls 2/9/1999 11:21'! layoutChanged | morph newX newY newPosition innerBounds | super layoutChanged. self pageHolder submorphs isEmpty ifTrue: [ ^self ]. morph _ self pageHolder submorphs first. innerBounds _ self pageHolder innerBounds. newX _ innerBounds width - morph width // 2 + innerBounds left. newY _ innerBounds height - morph height // 2 + innerBounds top. newX _ newX max: innerBounds left. newY _ newY max: innerBounds top. newPosition _ newX @ newY. newPosition ~= morph position ifTrue: [ morph position: newPosition "- innerBounds topLeft" ].! ! !MuSwikiPageViewer methodsFor: 'morphic' stamp: 'ls 4/10/1999 10:36'! mouseDown: evt evt yellowButtonPressed ifFalse: [ ^self ]. ^self displayMenu: evt.! ! !MuSwikiPageViewer methodsFor: 'morphic' stamp: 'ls 1/21/1999 13:22'! update: aSymbol (aSymbol == #pageAppearance or: [ aSymbol == model ]) ifTrue: [ self pageChanged ]! ! !MuSwikiPageViewer methodsFor: 'menu' stamp: 'ls 4/10/1999 10:35'! displayMenu: evt | menu | menu _ self getMenu: evt shiftPressed. menu setInvokingView: self. menu popUpAt: evt cursorPoint event: evt! ! !MuSwikiPageViewer methodsFor: 'menu' stamp: 'ls 2/9/1999 11:37'! getMenu: shiftKeyState | menu | menu _ MenuMorph new defaultTarget: model. model menu: menu shiftState: shiftKeyState. ^menu! ! !MuSwikiPageViewer methodsFor: 'private' stamp: 'ls 1/21/1999 13:20'! getPage model ifNil: [ ^nil ]. ^model pageAppearance! ! !MuSwikiPageViewer methodsFor: 'private' stamp: 'ls 2/9/1999 11:22'! initialize super initialize. "getMenuSelector _ #menu:shiftState:." self borderColor: Color black.! ! !MuSwikiPageViewer methodsFor: 'private' stamp: 'bolot 4/5/1999 23:42'! pageChanged "the model has gotten a new page. Draw the new page" | appearance | self pageHolder removeAllMorphs. appearance _ self getPage. appearance ifNil: [ ^self ]. appearance position: (0@0). self world startSteppingSubmorphsOf: appearance. self pageHolder addMorph: appearance! ! !MuSwikiPageViewer methodsFor: 'private' stamp: 'ls 2/9/1999 11:19'! pageHolder ^self! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 1/20/1999 09:58'! clientListText "return the list of clients as a Text" | string | string _ String streamContents: [ :s | loggedInConnections keysDo: [ :k | s nextPutAll: k. s cr ] ]. ^string asText! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 1/11/1999 11:11'! defaultBackgroundColor ^Color cyan! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 1/11/1999 11:12'! initialExtent ^200@200! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 1/20/1999 09:59'! openAdminMorph "open an admin window" "MuSwikiServer new openAdminMorph" | win loginList | win _ SystemWindow new. win model: self. win setLabel: 'MuSwiki Server Control Panel'. loginList _ PluggableTextMorph on: self text: #clientListText accept: nil. win addMorph: loginList frame: (0@0 extent: 1@1). win openInWorld. ! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 2/5/1999 10:27'! step "perform periodic activities" self processIO. "check for network activity" self possiblyFlushPageDB. "periodically flush the appearance DB" ! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 8/8/2006 19:24'! stepTime ^10! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 8/8/2006 19:24'! stepTimeIn: window ^self stepTime! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 1/20/1999 10:49'! wantsSteps ^true! ! !MuSwikiServer methodsFor: 'admin interface' stamp: 'ls 2/24/1999 11:04'! windowIsClosing self isListening ifTrue: [ (self confirm: 'stop listening for connections?') ifTrue: [ self flag: #XXX. "we should close all existing connections, too" self stopListening ] ]. self flushDB.! ! !MuSwikiServer methodsFor: 'private' stamp: 'ls 8/8/2006 19:21'! initializeOn: name "open the page database" pageDB _ FileDictionary fileNamed: name. pageDB open. "make sure a template page exists" (pageDB keys includes: 'template') ifFalse: [ pageDB at: 'template' put: (MuSwikiPage entitled: 'Template') ]. "initialize a record for each page" pageRecs _ Dictionary new. pageDB do: [ :pageAssoc | | page | page := pageAssoc value. pageRecs at: page title asLowercase put: (MuSwikiPageRecord title: page title) ]. "initialize the list of logged-in connections" loggedInConnections _ Dictionary new. "maps clientName --> connection" ! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:14'! badMessage: array fromConnection: connection connection clientName ifNil: [ "not logged in" ^connection destroy ]. connection sendError: 'recieved an invalid message'.! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 2/12/1999 16:24'! broadcastTOC | titles | titles _ self titleList. loggedInConnections do: [ :conn | conn sendTOC: titles ].! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:13'! lockPage: title forConnection: connection | pageRec | connection clientName ifNil: [ "not logged in" ^self ]. "check if the client is already locking a page" connection pageLocked ifNotNil: [ connection sendError: 'You already have a page locked'. ^self ]. "check that the requested page exists" (pageRecs includesKey: title asLowercase) ifFalse: [ connection sendError: 'There is no page named ', title. ^self ]. pageRec _ pageRecs at: title asLowercase. "check that the page isn't already locked" pageRec lockedBy ifNotNil: [ connection sendError: 'page already locked by ', pageRec lockedBy clientName. self logPageLockAttempt: pageRec title byUser: connection clientName. ^self ]. "okay, lock the page" pageRec lockBy: connection. "send the lock confirmation, along with a canonical copy of the page" connection lockConfirm: title appearancePickle: (self pageTitled: title) appearancePickle. connection pageLocked: pageRec. self logPageLock: pageRec title byUser: connection clientName! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:05'! login: clientName onConnection: connection protocolVersion: protocolVersion password: ignored "a client wishes to log in" protocolVersion ~= 2 ifTrue: [ connection badProtocol. ^self ]. (MuSwikiConnection validClientName: clientName) ifFalse: [ connection invalidClientName. ^self ]. (loggedInConnections includesKey: clientName) ifTrue: [ connection alreadyLoggedIn. self log: clientName, ' tried to log in twice!!'. ^self ]. connection clientName: clientName. loggedInConnections at: clientName asLowercase put: connection. connection loggedIn. self changed: #clientListText. connection sendTOC: self titleList. self logLogin: clientName.! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:13'! pageArrivedTitle: title appearancePickle: appearancePickle fromConnection: connection "a page has arrived; save it" |pageRec page | connection clientName ifNil: [ "not logged in" ^self ]. "check whether the client is allowed to update the page" (pageRecs includesKey: title asLowercase) ifFalse: [ ^connection sendError: 'no such page' ]. pageRec _ pageRecs at: title asLowercase. pageRec lockedBy == connection ifFalse: [ ^connection sendError: 'page is not locked by you!!' ]. "unlock the page" pageRec unlock. connection pageLocked: nil. "update the page" page _ self pageTitled: title. page appearancePickle: appearancePickle. self setPageTitled: title to: page. "notify other browsers that the page has changed" self connections do: [ :c | c lastPageRetrieved asLowercase = pageRec title asLowercase ifTrue: [ c sendPage: page lockedBy: nil] ]. self logPageSubmission: title byUser: connection clientName! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:12'! pageRequest: title byConnection: connection "a connection has requested the given page" |page pageRec | connection clientName ifNil: [ "not logged in" ^self ]. (pageRecs includesKey: title asLowercase) ifTrue: [ pageRec _ pageRecs at: title asLowercase. page _ pageDB at: title asLowercase ] ifFalse: [ "create a new page" pageRec _ MuSwikiPageRecord title: title. page _ (MuSwikiPage entitled: title) appearance: (self pageTitled: 'Template') appearance. pageRecs at: title asLowercase put: pageRec. self setPageTitled: title to: page. self broadcastTOC. ]. connection sendPage: page lockedBy: (pageRec lockedBy ifNotNil: [ pageRec lockedBy clientName ]). self logPageRequest: title byUser: connection clientName! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:12'! processWhoFor: connection connection clientName ifNil: [ "not logged in" ^self ]. connection whoReply: (loggedInConnections keys).! ! !MuSwikiServer methodsFor: 'messages' stamp: 'ls 4/10/1999 10:12'! unlockPage: title forConnection: connection | pageRec | connection clientName ifNil: [ "not logged in" ^self ]. pageRec _ pageRecs at: title asLowercase ifAbsent: [ self sendError: 'cannot unlock a page that does not exist!! (', title, ')'. ^self ]. (pageRec lockedBy == connection) ifFalse: [ connection sendError: 'you don''t have that page (', title, ') locked'. ^self ]. connection pageLocked: nil. pageRec lockBy: nil. "no notification really needed...." self logPageEditCancel: title byUser: connection clientName! ! !MuSwikiServer methodsFor: 'networking' stamp: 'ls 2/24/1999 10:50'! connectionForSocket: socket ^MuSwikiServerConnection onSocket: socket! ! !MuSwikiServer methodsFor: 'networking' stamp: 'ls 3/15/1999 12:40'! connectionQuitting: aConnection "a client is quitting. remove our record of them, and release any resources it held" | name | aConnection clientName ifNil: [ "not logged in; nothing to be done" ^self ]. "remove them from thelogin list" name _ aConnection clientName. loggedInConnections removeKey: name ifAbsent: [ ]. "unlock any page the client held" (aConnection pageLocked) ifNotNil: [ aConnection pageLocked unlock. aConnection pageLocked: nil ]. self changed: #clientListText. ! ! !MuSwikiServer methodsFor: 'networking' stamp: 'ls 2/24/1999 10:55'! processMessage: message fromConnection: connection message applyToServer: self forConnection: connection! ! !MuSwikiServer methodsFor: 'networking' stamp: 'ls 4/5/1999 23:56'! startListeningOnPort: port super startListeningOnPort: port. self log: 'STARTING on port ', port printString.! ! !MuSwikiServer methodsFor: 'networking' stamp: 'ls 4/5/1999 23:56'! stopListening super stopListening. self log: 'STOPPING'! ! !MuSwikiServer methodsFor: 'page DB' stamp: 'ls 2/10/1999 11:20'! flushDB Transcript show: 'flushing page DB'; cr. pageDB close open. pageDBDirtyTime _ nil! ! !MuSwikiServer methodsFor: 'page DB' stamp: 'ls 2/5/1999 10:23'! pageTitled: title "return the actual Page for the given title" ^pageDB at: title asLowercase! ! !MuSwikiServer methodsFor: 'page DB' stamp: 'ls 2/10/1999 11:20'! possiblyFlushPageDB "flush the page DB, if it is dirty and hasn't been flushed in a while" (pageDBDirtyTime ~= nil and: [ pageDBDirtyTime + 60 <= Time totalSeconds ]) ifTrue: [ self flushDB ].! ! !MuSwikiServer methodsFor: 'page DB' stamp: 'ls 2/5/1999 10:24'! setPageTitled: title to: page "change the actual Page for the given title" pageDB at: title asLowercase put: page. pageDBDirtyTime ifNil: [ Transcript show: 'page DB became dirty'; cr. pageDBDirtyTime _ Time totalSeconds ].! ! !MuSwikiServer methodsFor: 'page DB' stamp: 'ls 2/12/1999 16:18'! titleList "return a collection of all the page titles" ^pageRecs collect: [ :rec | rec title ]! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/6/1999 00:00'! log: aString "send the given string to the logfile, with a time stamp. does nothing if no logStream has been set" logStream ifNil: [ ^self ]. logStream nextPutAll: Time now printString; tab; nextPutAll: Date today printString; tab; nextPutAll: aString; cr. (logStream respondsTo: #flush) ifTrue: [ logStream flush ]! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:49'! logLogin: userName "log that a user has logged in" self log: userName, ' logs in'! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:49'! logLogoff: userName "log that a user has logged off" self log: userName, ' logs off'! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:51'! logPageEditCancel: pageName byUser: userName "log that a user has finished editting a page" self log: userName, ' stops editting page ', pageName! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:50'! logPageLock: pageName byUser: userName "log that a user has started editting a page" self log: userName, ' locks page ', pageName! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:50'! logPageLockAttempt: pageName byUser: userName "log that a user has tried to started editting a page" self log: userName, ' tries to locks page ', pageName! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:53'! logPageRequest: pageName byUser: userName "log that a user has started looking at a page" self log: userName, ' starts looking at page ', pageName! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:50'! logPageSubmission: pageName byUser: userName "log that a user has finished editting a page" self log: userName, ' submits a new version of page ', pageName! ! !MuSwikiServer methodsFor: 'logging' stamp: 'ls 4/5/1999 23:46'! logToFile: fileName logStream _ FileStream fileNamed: fileName. logStream setToEnd. ! ! !MuSwikiServer class methodsFor: 'instance creation' stamp: 'ls 2/5/1999 10:45'! new ^super error: 'create a MuSwikiServer using on:, not new'! ! !MuSwikiServer class methodsFor: 'instance creation' stamp: 'bolot 4/5/1999 23:36'! on: fileName "open a server on the given file name" ^super new initialize initializeOn: fileName " | s | s _ MuSwikiServer on: 'MuSwikiPages'. s startListeningOnPort: 9090. s openAdminMorph. " ! ! !MuSwikiServerConnection methodsFor: 'as yet unclassified' stamp: 'ls 1/14/1999 18:34'! clientName: aString "set the name the client has logged in as" clientName _ aString! ! !MuSwikiServerConnection methodsFor: 'access' stamp: 'ls 1/20/1999 11:08'! clientName ^clientName! ! !MuSwikiServerConnection methodsFor: 'access' stamp: 'ls 1/27/1999 11:35'! lastPageRetrieved "return the last page this client retrieved, or nil if they haven't retrieved any pages yet" ^lastPageRetrieved! ! !MuSwikiServerConnection methodsFor: 'access' stamp: 'ls 2/1/1999 13:48'! pageLocked "return the current page this client has locked" ^pageLocked! ! !MuSwikiServerConnection methodsFor: 'access' stamp: 'ls 2/1/1999 13:53'! pageLocked: page pageLocked _ page! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 4/6/1999 00:05'! alreadyLoggedIn self sendError: 'someone is already logged in under that name'! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 2/12/1999 16:13'! badProtocol self sendError: 'client protocol and server protocol are not the same!!'! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 3/12/1999 17:51'! invalidClientName self sendError: 'invalid user name selected. Usernames must be all lowercase and include no spaces or other special characters'! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 3/8/1999 16:27'! lockConfirm: title appearancePickle: appearancePickle self sendMessage: (MSMLocked title: title appearancePickle: appearancePickle)! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 1/11/1999 11:51'! loggedIn stringSocket nextPut: MSMLoggedIn new asStringArray! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 2/1/1999 13:31'! sendError: message self sendMessage: (MSMError message: message)! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 3/15/1999 12:30'! sendPage: aPage lockedBy: aClientName "send a muswiki page to the client" | message | message _ MSMPage title: aPage title appearancePickle: aPage appearancePickle lockedBy: aClientName. self sendMessage: message. lastPageRetrieved _ aPage title.! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 2/12/1999 16:16'! sendTOC: titleList self sendMessage: (MSMTableOfContents titles: titleList)! ! !MuSwikiServerConnection methodsFor: 'messages' stamp: 'ls 1/11/1999 11:49'! whoReply: clientNames | message | message _ MSMWhoReply clientNames: clientNames. stringSocket nextPut: message asStringArray! ! !MuSwikiServerConnection reorganize! ('as yet unclassified' clientName:) ('access' clientName lastPageRetrieved pageLocked pageLocked:) ('messages' alreadyLoggedIn badProtocol invalidClientName lockConfirm:appearancePickle: loggedIn sendError: sendPage:lockedBy: sendTOC: whoReply:) ! !MuSwikiServer reorganize! ('admin interface' clientListText defaultBackgroundColor initialExtent openAdminMorph step stepTime stepTimeIn: wantsSteps windowIsClosing) ('private' initializeOn:) ('messages' badMessage:fromConnection: broadcastTOC lockPage:forConnection: login:onConnection:protocolVersion:password: pageArrivedTitle:appearancePickle:fromConnection: pageRequest:byConnection: processWhoFor: unlockPage:forConnection:) ('networking' connectionForSocket: connectionQuitting: processMessage:fromConnection: startListeningOnPort: stopListening) ('page DB' flushDB pageTitled: possiblyFlushPageDB setPageTitled:to: titleList) ('logging' log: logLogin: logLogoff: logPageEditCancel:byUser: logPageLock:byUser: logPageLockAttempt:byUser: logPageRequest:byUser: logPageSubmission:byUser: logToFile:) ! !MuSwikiPageViewer reorganize! ('morphic' canvasForSubmorphs: handlesMouseDown: layoutChanged mouseDown: update:) ('menu' displayMenu: getMenu:) ('private' getPage initialize pageChanged pageHolder) ! MuSwikiMessage initialize! !MuSwikiMessage class reorganize! ('initialization' initialize) ('testing' test testLoggedIn testLogin testPage testPageRequest testWho testWhoReply) ('parsing' fromStringArray:) ('as yet unclassified' badArgumentCountMessage: malformedMessage: malformedMessage:reason:) ! !MSMUnlock reorganize! ('conversion' asStringArray) ('initialization' pageTitle:) ('applying' applyToServer:forConnection:) ! !MSMTableOfContents reorganize! ('applying' applyToClient:) ('initialization' titles:) ('conversion' asStringArray) ! !MuSwikiEditButtons reorganize! ('morphic' layoutChanged positionEdittingButtons positionNonEdittingButton setButtons) ('button presses' cancel edit save) ('private' modelIsEditting update:) ('initialization' createButtons initialize) ('model access' model:) ! !MuSwikiBrowser reorganize! ('access' pageAppearance password password: serverName serverName: serverPort serverPort: userName userName:) ('private' initialize) ('messages' pageArrived:appearancePickle:lockedBy: pageLocked:appearance: serverError: tableOfContents:) ('network IO' connect disconnect isConnected processIO step stepTime stepTimeIn: wantsSteps) ('interface' defaultBackgroundColor initialExtent isEditting labelString morphicView openConnectDialogue openInMorphic pageTitle setSaneStatus status status: windowIsClosing) ('editting' cancelEdit edit save) ('menu' getPage getPage: getPage:onServer:port: menu:shiftState: perform:orSendTo: saveGIF saveReference selectPageName) ('testing' isMuSwikiBrowser) !