diff options
| author | Eugen Wissner <belka@caraus.de> | 2025-11-21 22:20:10 +0100 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2025-11-21 22:20:10 +0100 |
| commit | c03c9f8b886c0e8eca5a701c0ca941e05e0b6285 (patch) | |
| tree | 5f27293563caa042b182de97fe59b22e2cbd533a /pharo-mooc/tiny-blog/src/TinyBlog-Components | |
| parent | c0336dac8effb57c04221f23b72bff70170933c5 (diff) | |
| download | book-exercises-c03c9f8b886c0e8eca5a701c0ca941e05e0b6285.tar.gz | |
Add pharo tiny blog
Diffstat (limited to 'pharo-mooc/tiny-blog/src/TinyBlog-Components')
11 files changed, 634 insertions, 0 deletions
diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAdminComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAdminComponent.class.st new file mode 100644 index 0000000..c35cc95 --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAdminComponent.class.st @@ -0,0 +1,24 @@ +Class { + #name : 'TBAdminComponent', + #superclass : 'TBScreenComponent', + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'initialization' } +TBAdminComponent >> createHeaderComponent [ + ^ TBAdminHeaderComponent from: self +] + +{ #category : 'rendering' } +TBAdminComponent >> goToPostListView [ + self answer +] + +{ #category : 'acccessing' } +TBAdminComponent >> renderContentOn: html [ + super renderContentOn: html. + html container: [ + html heading: 'Blog Admin'. + html horizontalRule ] +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAdminHeaderComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAdminHeaderComponent.class.st new file mode 100644 index 0000000..cf7e8cd --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAdminHeaderComponent.class.st @@ -0,0 +1,34 @@ +Class { + #name : 'TBAdminHeaderComponent', + #superclass : 'TBHeaderComponent', + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'rendering' } +TBAdminHeaderComponent >> renderButtonsOn: html [ + html form: [ + self renderDisconnectButtonOn: html. + self renderPublicViewButton: html ] +] + +{ #category : 'rendering' } +TBAdminHeaderComponent >> renderDisconnectButtonOn: html [ + html formButton + beSecondary; + callback: [ self session reset ]; + with: [ + html text: 'Disconnect'. + html span class: 'glyphicon glyphicon-logout' ] +] + +{ #category : 'rendering' } +TBAdminHeaderComponent >> renderPublicViewButton: html [ + self session isLogged ifTrue: [ + html formButton + beSecondary; + callback: [ component goToPostListView ]; + with: [ + html span class: 'glyphicon glyphicon-eye-open'. + html text: ' Public View' ] ] +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBApplicationRootComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBApplicationRootComponent.class.st new file mode 100644 index 0000000..6c8d80c --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBApplicationRootComponent.class.st @@ -0,0 +1,57 @@ +Class { + #name : 'TBApplicationRootComponent', + #superclass : 'WAComponent', + #instVars : [ + 'main' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'testing' } +TBApplicationRootComponent class >> canBeRoot [ + ^ true +] + +{ #category : 'initialization' } +TBApplicationRootComponent class >> initialize [ + "self initialize" + | app | + app := WAAdmin register: self asApplicationAt: 'TinyBlog'. + app + preferenceAt: #sessionClass put: TBSession. + app + addLibrary: JQDeploymentLibrary; + addLibrary: JQUiDeploymentLibrary; + addLibrary: SBSDeploymentLibrary +] + +{ #category : 'rendering' } +TBApplicationRootComponent >> children [ + ^ { main } +] + +{ #category : 'initialization' } +TBApplicationRootComponent >> initialize [ + super initialize. + main := TBPostsListComponent new +] + +{ #category : 'initialization' } +TBApplicationRootComponent >> main: aComponent [ + + main := aComponent +] + +{ #category : 'rendering' } +TBApplicationRootComponent >> renderContentOn: html [ + html render: main. + SBSDeploymentLibrary addLoadScriptTo: html +] + +{ #category : 'rendering' } +TBApplicationRootComponent >> updateRoot: anHtmlRoot [ + super updateRoot: anHtmlRoot. + anHtmlRoot beHtml5. + anHtmlRoot title: 'TinyBlog' +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAuthenticationComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAuthenticationComponent.class.st new file mode 100644 index 0000000..b76cb6b --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBAuthenticationComponent.class.st @@ -0,0 +1,125 @@ +Class { + #name : 'TBAuthenticationComponent', + #superclass : 'SBSComponent', + #instVars : [ + 'password', + 'account', + 'component' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'instance creation' } +TBAuthenticationComponent class >> from: aComponent [ + ^ self new + component: aComponent; + yourself +] + +{ #category : 'accessing' } +TBAuthenticationComponent >> account [ + + ^ account +] + +{ #category : 'accessing' } +TBAuthenticationComponent >> account: anObject [ + + account := anObject +] + +{ #category : 'accessing' } +TBAuthenticationComponent >> component [ + + ^ component +] + +{ #category : 'accessing' } +TBAuthenticationComponent >> component: anObject [ + + component := anObject +] + +{ #category : 'accessing' } +TBAuthenticationComponent >> password [ + + ^ password +] + +{ #category : 'accessing' } +TBAuthenticationComponent >> password: anObject [ + + password := anObject +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> renderAccountFieldOn: html [ + html + formGroup: [ html label with: 'Account'. + html textInput + formControl; + attributeAt: 'autofocus' put: 'true'; + callback: [ :value | account := value ]; + value: account ] +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> renderBodyOn: html [ + html + modalBody: [ + html form: [ + self renderAccountFieldOn: html. + self renderPasswordFieldOn: html. + html modalFooter: [ self renderButtonsOn: html ] ] ] +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> renderButtonsOn: html [ + html formButton + beSecondary; + dataDismiss: 'modal'; + value: 'Cancel'. + html formButton + "beSubmit;" + bePrimary; + callback: [ self validate ]; + value: 'SignIn' +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> renderContentOn: html [ + html modal + id: 'myAuthDialog'; + fade; + with: [ + html modalDialog: [ + html modalContent: [ + self renderHeaderOn: html. + self renderBodyOn: html ] ] ] +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> renderHeaderOn: html [ + html + modalHeader: [ + html modalCloseButton. + html modalTitle + level: 4; + with: 'Authentication' ] +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> renderPasswordFieldOn: html [ + html + formGroup: [ html label with: 'Password'. + html passwordInput + formControl; + callback: [ :value | password := value ]; + value: password ] +] + +{ #category : 'rendering' } +TBAuthenticationComponent >> validate [ + ^ component tryConnectionWithLogin: self account andPassword: self password +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBCategoriesComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBCategoriesComponent.class.st new file mode 100644 index 0000000..6776186 --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBCategoriesComponent.class.st @@ -0,0 +1,61 @@ +Class { + #name : 'TBCategoriesComponent', + #superclass : 'TBScreenComponent', + #instVars : [ + 'categories', + 'postsList' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'instance creation' } +TBCategoriesComponent class >> categories: categories postsList: aTBScreen [ + ^ self new categories: categories; postsList: aTBScreen +] + +{ #category : 'accessing' } +TBCategoriesComponent >> categories [ + + ^ categories +] + +{ #category : 'accessing' } +TBCategoriesComponent >> categories: anObject [ + + categories := anObject +] + +{ #category : 'accessing' } +TBCategoriesComponent >> postsList [ + + ^ postsList +] + +{ #category : 'accessing' } +TBCategoriesComponent >> postsList: anObject [ + + postsList := anObject +] + +{ #category : 'actions' } +TBCategoriesComponent >> renderCategoryLinkOn: html with: aCategory [ + html listGroupLinkedItem + class: 'active' if: aCategory = self postsList currentCategory; + callback: [ self selectCategory: aCategory ]; + with: aCategory +] + +{ #category : 'actions' } +TBCategoriesComponent >> renderContentOn: html [ + html listGroup: [ + html listGroupItem + with: [ html strong: 'Categories' ]. + categories do: [ :cat | + self renderCategoryLinkOn: html with: cat ] ] +] + +{ #category : 'actions' } +TBCategoriesComponent >> selectCategory: aCategory [ + postsList currentCategory: aCategory +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBHeaderComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBHeaderComponent.class.st new file mode 100644 index 0000000..4a95f2a --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBHeaderComponent.class.st @@ -0,0 +1,78 @@ +Class { + #name : 'TBHeaderComponent', + #superclass : 'SBSComponent', + #instVars : [ + 'component' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'instance creation' } +TBHeaderComponent class >> from: aComponent [ + ^ self new + component: aComponent; + yourself +] + +{ #category : 'accessing' } +TBHeaderComponent >> component [ + + ^ component +] + +{ #category : 'accessing' } +TBHeaderComponent >> component: anObject [ + + component := anObject +] + +{ #category : 'rendering' } +TBHeaderComponent >> renderBrandOn: html [ + html navigationBarBrand + url: self application url; + with: 'TinyBlog' +] + +{ #category : 'rendering' } +TBHeaderComponent >> renderButtonsOn: html [ + self session isLogged + ifTrue: [ self renderSimpleAdminButtonOn: html ] + ifFalse: [ self renderModalLoginButtonOn: html ] +] + +{ #category : 'rendering' } +TBHeaderComponent >> renderContentOn: html [ + | bar | + bar := html navigationBar. + + bar background beLight. + bar with: [ + html container: [ + self renderBrandOn: html. + self renderButtonsOn: html + ] + ] +] + +{ #category : 'rendering' } +TBHeaderComponent >> renderModalLoginButtonOn: html [ + html render: (TBAuthenticationComponent from: component). + html formButton beSecondary; + dataTarget: '#myAuthDialog'; + dataToggle: 'modal'; + with: [ + html span class: 'glyphicon glyphicon-lock'. + html text: 'Login' ] +] + +{ #category : 'rendering' } +TBHeaderComponent >> renderSimpleAdminButtonOn: html [ + html form: [ + html formButton + beSecondary; + callback: [ component goToAdministrationView ]; + with: [ + html span class: 'glyphicon glyphicon-list-alt'. + html text: ' Admin View' ]] +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBPostComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBPostComponent.class.st new file mode 100644 index 0000000..f534bf7 --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBPostComponent.class.st @@ -0,0 +1,42 @@ +Class { + #name : 'TBPostComponent', + #superclass : 'SBSComponent', + #instVars : [ + 'post' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'initialization' } +TBPostComponent >> date [ + ^ post date +] + +{ #category : 'initialization' } +TBPostComponent >> initialize [ + super initialize. + post := TBPost new +] + +{ #category : 'initialization' } +TBPostComponent >> post: aPost [ + post := aPost +] + +{ #category : 'initialization' } +TBPostComponent >> renderContentOn: html [ + html heading level: 2; with: self title. + html heading level: 6; with: self date. + html text: self text +] + +{ #category : 'initialization' } +TBPostComponent >> text [ + ^ post text +] + +{ #category : 'initialization' } +TBPostComponent >> title [ + ^ post title +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBPostsListComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBPostsListComponent.class.st new file mode 100644 index 0000000..82a9a73 --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBPostsListComponent.class.st @@ -0,0 +1,142 @@ +Class { + #name : 'TBPostsListComponent', + #superclass : 'TBScreenComponent', + #instVars : [ + 'postComponents', + 'currentCategory', + 'showLoginError' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'rendering' } +TBPostsListComponent >> basicRenderCategoriesOn: html [ + html render: self categoriesComponent +] + +{ #category : 'rendering' } +TBPostsListComponent >> basicRenderPostsOn: html [ + self readSelectedPosts do: [ :p | html render: (self postComponentFor: p) ] +] + +{ #category : 'rendering' } +TBPostsListComponent >> categoriesComponent [ + ^ TBCategoriesComponent + categories: self blog allCategories + postsList: self +] + +{ #category : 'initialization' } +TBPostsListComponent >> children [ + ^ self postComponents, super children +] + +{ #category : 'accessing' } +TBPostsListComponent >> currentCategory [ + + ^ currentCategory +] + +{ #category : 'accessing' } +TBPostsListComponent >> currentCategory: anObject [ + + currentCategory := anObject +] + +{ #category : 'as yet unclassified' } +TBPostsListComponent >> goToAdministrationView [ + self call: TBAdminComponent new +] + +{ #category : 'as yet unclassified' } +TBPostsListComponent >> hasLoginError [ + ^ showLoginError ifNil: [ false ] +] + +{ #category : 'initialization' } +TBPostsListComponent >> initialize [ + super initialize. + postComponents := OrderedCollection new +] + +{ #category : 'as yet unclassified' } +TBPostsListComponent >> loginErrorMessage [ + ^ 'Incorrect login and/or password' +] + +{ #category : 'as yet unclassified' } +TBPostsListComponent >> loginErrorOccurred [ + showLoginError := true +] + +{ #category : 'rendering' } +TBPostsListComponent >> postComponentFor: aPost [ + ^ TBPostComponent new post: aPost +] + +{ #category : 'initialization' } +TBPostsListComponent >> postComponents [ + postComponents := self readSelectedPosts + collect: [ :each | TBPostComponent new post: each ]. + ^ postComponents +] + +{ #category : 'initialization' } +TBPostsListComponent >> readSelectedPosts [ + ^ self currentCategory + ifNil: [ self blog allVisibleBlogPosts ] + ifNotNil: [ self blog allVisibleBlogPostsFromCategory: self currentCategory ] +] + +{ #category : 'rendering' } +TBPostsListComponent >> renderCategoryColumnOn: html [ + html column + extraSmallSize: 12; + smallSize: 2; + mediumSize: 4; + with: [ self basicRenderCategoriesOn: html ] +] + +{ #category : 'rendering' } +TBPostsListComponent >> renderContentOn: html [ + super renderContentOn: html. + html container: [ + html row + with: [ + html column + extraSmallSize: 12; + smallSize: 2; + mediumSize: 4; + with: [ html render: self categoriesComponent ]. + self renderPostColumnOn: html ] ] +] + +{ #category : 'rendering' } +TBPostsListComponent >> renderLoginErrorMessageIfAnyOn: html [ + self hasLoginError ifTrue: [ + showLoginError := false. + html alert + beDanger; + with: self loginErrorMessage ] +] + +{ #category : 'rendering' } +TBPostsListComponent >> renderPostColumnOn: html [ + html column + extraSmallSize: 12; + smallSize: 10; + mediumSize: 8; + with: [ + self renderLoginErrorMessageIfAnyOn: html. + self basicRenderPostsOn: html ] +] + +{ #category : 'as yet unclassified' } +TBPostsListComponent >> tryConnectionWithLogin: login andPassword: password [ + (login = self blog administrator login and: [ (SHA256 hashMessage: password) = self blog administrator password ]) + ifTrue: [ + self session currentAdmin: self blog administrator. + self goToAdministrationView ] + ifFalse: [ self loginErrorOccurred ] +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBScreenComponent.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBScreenComponent.class.st new file mode 100644 index 0000000..ef15b8f --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBScreenComponent.class.st @@ -0,0 +1,37 @@ +Class { + #name : 'TBScreenComponent', + #superclass : 'SBSRootComponent', + #instVars : [ + 'header' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'acccessing' } +TBScreenComponent >> blog [ + "Return the current blog. In the future we will ask the + session to return the blog of the currently logged in user." + ^ TBBlog current +] + +{ #category : 'acccessing' } +TBScreenComponent >> children [ + ^ { header } +] + +{ #category : 'initialization' } +TBScreenComponent >> createHeaderComponent [ + ^ TBHeaderComponent from: self +] + +{ #category : 'initialization' } +TBScreenComponent >> initialize [ + super initialize. + header := self createHeaderComponent +] + +{ #category : 'acccessing' } +TBScreenComponent >> renderContentOn: html [ + html render: header +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBSession.class.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBSession.class.st new file mode 100644 index 0000000..f56c40b --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/TBSession.class.st @@ -0,0 +1,33 @@ +Class { + #name : 'TBSession', + #superclass : 'WASession', + #instVars : [ + 'currentAdmin' + ], + #category : 'TinyBlog-Components', + #package : 'TinyBlog-Components' +} + +{ #category : 'accessing' } +TBSession >> currentAdmin [ + + ^ currentAdmin +] + +{ #category : 'accessing' } +TBSession >> currentAdmin: anObject [ + + currentAdmin := anObject +] + +{ #category : 'testing' } +TBSession >> isLogged [ + ^ self currentAdmin notNil +] + +{ #category : 'initialization' } +TBSession >> reset [ + currentAdmin := nil. + self requestContext redirectTo: self application url. + self unregister +] diff --git a/pharo-mooc/tiny-blog/src/TinyBlog-Components/package.st b/pharo-mooc/tiny-blog/src/TinyBlog-Components/package.st new file mode 100644 index 0000000..1f12c30 --- /dev/null +++ b/pharo-mooc/tiny-blog/src/TinyBlog-Components/package.st @@ -0,0 +1 @@ +Package { #name : 'TinyBlog-Components' } |
