Add pharo tiny blog
This commit is contained in:
3
pharo-mooc/tiny-blog/.project
Normal file
3
pharo-mooc/tiny-blog/.project
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
'srcDirectory' : 'src'
|
||||
}
|
||||
3
pharo-mooc/tiny-blog/src/.properties
Normal file
3
pharo-mooc/tiny-blog/src/.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
#format : #tonel
|
||||
}
|
||||
@@ -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 ]
|
||||
]
|
||||
@@ -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' ] ]
|
||||
]
|
||||
@@ -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'
|
||||
]
|
||||
@@ -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
|
||||
]
|
||||
@@ -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
|
||||
]
|
||||
@@ -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' ]]
|
||||
]
|
||||
@@ -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
|
||||
]
|
||||
@@ -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 ]
|
||||
]
|
||||
@@ -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
|
||||
]
|
||||
@@ -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
|
||||
]
|
||||
1
pharo-mooc/tiny-blog/src/TinyBlog-Components/package.st
Normal file
1
pharo-mooc/tiny-blog/src/TinyBlog-Components/package.st
Normal file
@@ -0,0 +1 @@
|
||||
Package { #name : 'TinyBlog-Components' }
|
||||
81
pharo-mooc/tiny-blog/src/TinyBlog-Tests/TBBlogTest.class.st
Normal file
81
pharo-mooc/tiny-blog/src/TinyBlog-Tests/TBBlogTest.class.st
Normal file
@@ -0,0 +1,81 @@
|
||||
Class {
|
||||
#name : 'TBBlogTest',
|
||||
#superclass : 'TestCase',
|
||||
#instVars : [
|
||||
'blog',
|
||||
'post',
|
||||
'first',
|
||||
'previousRepository'
|
||||
],
|
||||
#category : 'TinyBlog-Tests',
|
||||
#package : 'TinyBlog-Tests'
|
||||
}
|
||||
|
||||
{ #category : 'running' }
|
||||
TBBlogTest >> setUp [
|
||||
previousRepository := VORepository current.
|
||||
VORepository setRepository: VOMemoryRepository new.
|
||||
blog := TBBlog current.
|
||||
first := TBPost title: 'A title' text: 'A text' category: 'First Category'.
|
||||
blog writeBlogPost: first.
|
||||
|
||||
post := (TBPost title: 'Another title' text: 'Another text' category: 'Second Category') beVisible
|
||||
]
|
||||
|
||||
{ #category : 'running' }
|
||||
TBBlogTest >> tearDown [
|
||||
VORepository setRepository: previousRepository
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testAddBlogPost [
|
||||
blog writeBlogPost: post.
|
||||
self assert: blog size equals: 2
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testAllBlogPosts [
|
||||
blog writeBlogPost: post.
|
||||
self assert: blog allBlogPosts size equals: 2
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testAllBlogPostsFromCategory [
|
||||
self assert: (blog allBlogPostsFromCategory: 'First Category')
|
||||
size equals: 1
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testAllCategories [
|
||||
blog writeBlogPost: post.
|
||||
self assert: blog allCategories size equals: 2
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testAllVisibleBlogPosts [
|
||||
blog writeBlogPost: post.
|
||||
self assert: blog allVisibleBlogPosts size equals: 1
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testAllVisibleBlogPostsFromCategory [
|
||||
blog writeBlogPost: post.
|
||||
self assert: (blog allVisibleBlogPostsFromCategory: 'First Category') size equals: 0.
|
||||
self assert: (blog allVisibleBlogPostsFromCategory: 'Second Category') size equals: 1.
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testRemoveAllBlogPosts [
|
||||
blog removeAllPosts.
|
||||
self assert: blog size equals: 0
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testSize [
|
||||
self assert: blog size equals: 1
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBBlogTest >> testUnclassifiedBlogPosts [
|
||||
self assert: (blog allBlogPosts select: [ :p | p isUnclassified ]) size equals: 0
|
||||
]
|
||||
30
pharo-mooc/tiny-blog/src/TinyBlog-Tests/TBPostTest.class.st
Normal file
30
pharo-mooc/tiny-blog/src/TinyBlog-Tests/TBPostTest.class.st
Normal file
@@ -0,0 +1,30 @@
|
||||
Class {
|
||||
#name : 'TBPostTest',
|
||||
#superclass : 'TestCase',
|
||||
#category : 'TinyBlog-Tests',
|
||||
#package : 'TinyBlog-Tests'
|
||||
}
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBPostTest >> testPostIsCreatedCorrectly [
|
||||
|
||||
| post |
|
||||
post := TBPost
|
||||
title: 'Welcome to TinyBlog'
|
||||
text: 'TinyBlog is a small blog engine made with Pharo.'
|
||||
category: 'TinyBlog'.
|
||||
self assert: post title equals: 'Welcome to TinyBlog'.
|
||||
self assert: post text equals: 'TinyBlog is a small blog engine made with Pharo.'.
|
||||
]
|
||||
|
||||
{ #category : 'tests' }
|
||||
TBPostTest >> testWithoutCategoryIsUnclassified [
|
||||
|
||||
| post |
|
||||
post := TBPost
|
||||
title: 'Welcome to TinyBlog'
|
||||
text: 'TinyBlog is a small blog engine made with Pharo.'.
|
||||
self assert: post title equals: 'Welcome to TinyBlog'.
|
||||
self assert: post isUnclassified.
|
||||
self deny: post isVisible
|
||||
]
|
||||
1
pharo-mooc/tiny-blog/src/TinyBlog-Tests/package.st
Normal file
1
pharo-mooc/tiny-blog/src/TinyBlog-Tests/package.st
Normal file
@@ -0,0 +1 @@
|
||||
Package { #name : 'TinyBlog-Tests' }
|
||||
42
pharo-mooc/tiny-blog/src/TinyBlog/TBAdministrator.class.st
Normal file
42
pharo-mooc/tiny-blog/src/TinyBlog/TBAdministrator.class.st
Normal file
@@ -0,0 +1,42 @@
|
||||
Class {
|
||||
#name : 'TBAdministrator',
|
||||
#superclass : 'Object',
|
||||
#instVars : [
|
||||
'login',
|
||||
'password'
|
||||
],
|
||||
#category : 'TinyBlog',
|
||||
#package : 'TinyBlog'
|
||||
}
|
||||
|
||||
{ #category : 'as yet unclassified' }
|
||||
TBAdministrator class >> login: login password: password [
|
||||
^ self new
|
||||
login: login;
|
||||
password: password;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBAdministrator >> login [
|
||||
|
||||
^ login
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBAdministrator >> login: anObject [
|
||||
|
||||
login := anObject
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBAdministrator >> password [
|
||||
|
||||
^ password
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBAdministrator >> password: anObject [
|
||||
|
||||
password := SHA256 hashMessage: anObject
|
||||
]
|
||||
133
pharo-mooc/tiny-blog/src/TinyBlog/TBBlog.class.st
Normal file
133
pharo-mooc/tiny-blog/src/TinyBlog/TBBlog.class.st
Normal file
@@ -0,0 +1,133 @@
|
||||
Class {
|
||||
#name : 'TBBlog',
|
||||
#superclass : 'Object',
|
||||
#instVars : [
|
||||
'posts',
|
||||
'adminUser'
|
||||
],
|
||||
#category : 'TinyBlog',
|
||||
#package : 'TinyBlog'
|
||||
}
|
||||
|
||||
{ #category : 'demos' }
|
||||
TBBlog class >> createDemoPosts [
|
||||
"TBBlog createDemoPosts"
|
||||
self current
|
||||
writeBlogPost: ((TBPost title: 'Welcome in TinyBlog' text: 'TinyBlog is a small blog engine made with Pharo.' category: 'TinyBlog') visible: true);
|
||||
writeBlogPost: ((TBPost
|
||||
title: 'Report Pharo Sprint'
|
||||
text: 'Friday, June 12 there was a Pharo sprint / Moose dojo. It was a nice event with more than 15 motivated sprinters. With the help of candies, cakes and chocolate, huge work has been done'
|
||||
category: 'Pharo') visible: true);
|
||||
writeBlogPost: ((TBPost
|
||||
title: 'Brick on top of Bloc - Preview'
|
||||
text: 'We are happy to announce the first preview version of Brick, a new widget set created from scratch on top of Bloc. Brick is being developed primarily by Alex Syrel (together with Alain Plantec, Andrei Chis and myself), and the work is sponsored by ESUG. Brick is part of the Glamorous Toolkit effort and will provide the basis for the new versions of the development tools.'
|
||||
category: 'Pharo') visible: true);
|
||||
writeBlogPost: ((TBPost
|
||||
title: 'The sad story of unclassified blog posts'
|
||||
text: 'So sad that I can read this.') visible: true);
|
||||
writeBlogPost: ((TBPost
|
||||
title: 'Working with Pharo on the Raspberry Pi'
|
||||
text: 'Hardware is getting cheaper and many new small devices like the famous Raspberry Pi provide new computation power that was one once only available on regular desktop computers.'
|
||||
category: 'Pharo') visible: true)
|
||||
]
|
||||
|
||||
{ #category : 'initialization' }
|
||||
TBBlog class >> current [
|
||||
"answer the instance of the TBRepository"
|
||||
^ self selectAll
|
||||
ifNotEmpty: [ :x | x anyOne ]
|
||||
ifEmpty: [ self new save ]
|
||||
]
|
||||
|
||||
{ #category : 'as yet unclassified' }
|
||||
TBBlog class >> defaultAdminLogin [
|
||||
^ 'admin'
|
||||
]
|
||||
|
||||
{ #category : 'as yet unclassified' }
|
||||
TBBlog class >> defaultAdminPassword [
|
||||
^ 'topsecret'
|
||||
]
|
||||
|
||||
{ #category : 'initialization' }
|
||||
TBBlog class >> initialize [
|
||||
self reset
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog class >> initializeVoyageOnMemoryDB [
|
||||
VOMemoryRepository new enableSingleton
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog class >> isVoyageRoot [
|
||||
"Indicates that instances of this class are top level documents in noSQL databases"
|
||||
^ true
|
||||
]
|
||||
|
||||
{ #category : 'initialization' }
|
||||
TBBlog class >> reset [
|
||||
self initializeVoyageOnMemoryDB.
|
||||
]
|
||||
|
||||
{ #category : 'as yet unclassified' }
|
||||
TBBlog >> administrator [
|
||||
^ adminUser
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog >> allBlogPosts [
|
||||
^ posts
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog >> allBlogPostsFromCategory: aCategory [
|
||||
^ posts select: [ :p | p category = aCategory ]
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog >> allCategories [
|
||||
^ (self allBlogPosts collect: [ :p | p category ]) asSet
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog >> allVisibleBlogPosts [
|
||||
^ posts select: [ :p | p isVisible ]
|
||||
]
|
||||
|
||||
{ #category : 'reading' }
|
||||
TBBlog >> allVisibleBlogPostsFromCategory: aCategory [
|
||||
^ posts select: [ :p | p category = aCategory and: [ p isVisible ] ]
|
||||
]
|
||||
|
||||
{ #category : 'as yet unclassified' }
|
||||
TBBlog >> createAdministrator [
|
||||
^ TBAdministrator
|
||||
login: self class defaultAdminLogin
|
||||
password: self class defaultAdminPassword
|
||||
]
|
||||
|
||||
{ #category : 'initialization' }
|
||||
TBBlog >> initialize [
|
||||
super initialize.
|
||||
posts := OrderedCollection new.
|
||||
adminUser := self createAdministrator
|
||||
]
|
||||
|
||||
{ #category : 'deleting' }
|
||||
TBBlog >> removeAllPosts [
|
||||
posts := OrderedCollection new.
|
||||
self save
|
||||
]
|
||||
|
||||
{ #category : 'initialization' }
|
||||
TBBlog >> size [
|
||||
^ posts size
|
||||
]
|
||||
|
||||
{ #category : 'writing' }
|
||||
TBBlog >> writeBlogPost: aPost [
|
||||
"Add the blog post in database."
|
||||
self allBlogPosts add: aPost.
|
||||
self save
|
||||
]
|
||||
121
pharo-mooc/tiny-blog/src/TinyBlog/TBPost.class.st
Normal file
121
pharo-mooc/tiny-blog/src/TinyBlog/TBPost.class.st
Normal file
@@ -0,0 +1,121 @@
|
||||
Class {
|
||||
#name : 'TBPost',
|
||||
#superclass : 'Object',
|
||||
#instVars : [
|
||||
'title',
|
||||
'text',
|
||||
'date',
|
||||
'category',
|
||||
'visible'
|
||||
],
|
||||
#category : 'TinyBlog',
|
||||
#package : 'TinyBlog'
|
||||
}
|
||||
|
||||
{ #category : 'instance creation' }
|
||||
TBPost class >> title: aTitle text: aText [
|
||||
^ self new
|
||||
title: aTitle;
|
||||
text: aText;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : 'instance creation' }
|
||||
TBPost class >> title: aTitle text: aText category: aCategory [
|
||||
^ (self title: aTitle text: aText)
|
||||
category: aCategory;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : 'as yet unclassified' }
|
||||
TBPost class >> unclassifiedTag [
|
||||
^ 'Unclassified'
|
||||
]
|
||||
|
||||
{ #category : 'action' }
|
||||
TBPost >> beVisible [
|
||||
self visible: true
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> category [
|
||||
|
||||
^ category
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> category: anObject [
|
||||
|
||||
category := anObject
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> date [
|
||||
|
||||
^ date
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> date: anObject [
|
||||
|
||||
date := anObject
|
||||
]
|
||||
|
||||
{ #category : 'initialization' }
|
||||
TBPost >> initialize [
|
||||
super initialize.
|
||||
self category: self class unclassifiedTag.
|
||||
self date: Date today.
|
||||
self notVisible.
|
||||
]
|
||||
|
||||
{ #category : 'testing' }
|
||||
TBPost >> isUnclassified [
|
||||
^ self category = self class unclassifiedTag
|
||||
]
|
||||
|
||||
{ #category : 'testing' }
|
||||
TBPost >> isVisible [
|
||||
^ self visible
|
||||
]
|
||||
|
||||
{ #category : 'action' }
|
||||
TBPost >> notVisible [
|
||||
self visible: false
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> text [
|
||||
|
||||
^ text
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> text: anObject [
|
||||
|
||||
text := anObject
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> title [
|
||||
|
||||
^ title
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> title: anObject [
|
||||
|
||||
title := anObject
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> visible [
|
||||
|
||||
^ visible
|
||||
]
|
||||
|
||||
{ #category : 'accessing' }
|
||||
TBPost >> visible: anObject [
|
||||
|
||||
visible := anObject
|
||||
]
|
||||
1
pharo-mooc/tiny-blog/src/TinyBlog/package.st
Normal file
1
pharo-mooc/tiny-blog/src/TinyBlog/package.st
Normal file
@@ -0,0 +1 @@
|
||||
Package { #name : 'TinyBlog' }
|
||||
Reference in New Issue
Block a user