diff --git a/src/Brea/BreaMember.class.st b/src/Brea/BreaMember.class.st new file mode 100644 index 0000000..07e8c98 --- /dev/null +++ b/src/Brea/BreaMember.class.st @@ -0,0 +1,489 @@ +" +I model a member of a Brea site, usually a human. +" +Class { + #name : #BreaMember, + #superclass : #Object, + #instVars : [ + 'givenName', + 'familyName', + 'picture', + 'country', + 'tags', + 'email', + 'password', + 'webPresence', + 'organizations' + ], + #category : #Brea +} + +{ #category : #converting } +BreaMember >> asStonModified [ + "asSton is generated a core dumped now. This renaming is trying to solve that. Maybe + in the offical release it will be solved" + ^ STON toStringPretty: self +] + +{ #category : #accessing } +BreaMember >> country [ + ^ country +] + +{ #category : #accessing } +BreaMember >> country: anObject [ + country := anObject +] + +{ #category : #public } +BreaMember >> countryTemplate [ + ^ '{{#country}} + + Country + {{.}} + + {{/country}}' asMustacheTemplate value: self +] + +{ #category : #helpers } +BreaMember >> createTestUser [ + ^ self class new + givenName: 'Test'; + familyName: 'User'; + country: 'Neverland'; + memberOf: 'HackBo' withWebsite: 'http://hackbo.co/'; + memberOf: 'mutabiT' withWebsite: 'http://mutabit.com/'; + website: 'http://test.user'; + twitter: '@offrayLC'; + email: 'iam@test.user'; + tags: 'just, a lot, of words, separated, by commas'. +] + +{ #category : #accessing } +BreaMember >> email [ + ^ email +] + +{ #category : #accessing } +BreaMember >> email: anEmailAddress [ + email := (SHA1 new hashMessage: anEmailAddress) hex +] + +{ #category : #accessing } +BreaMember >> facebook [ + ^ self webPresence facebook. +] + +{ #category : #accessing } +BreaMember >> facebook: aProfileName [ + aProfileName = '' + ifTrue: [ self webPresence facebook: nil ] + ifFalse: [ self webPresence facebook: aProfileName ] +] + +{ #category : #public } +BreaMember >> facebookTemplate [ + ^ '{{#facebook}} + + + Facebook + + + {{.}} + + {{/facebook}}' asMustacheTemplate value: self +] + +{ #category : #accessing } +BreaMember >> familyName [ + ^ familyName +] + +{ #category : #accessing } +BreaMember >> familyName: anObject [ + familyName := anObject +] + +{ #category : #helpers } +BreaMember >> fullName [ + ^ self givenName asLowercase, '-', self familyName asLowercase +] + +{ #category : #accessing } +BreaMember >> getGenericProfilePicture [ + "Other considered avatars where: + 'https://upload.wikimedia.org/wikipedia/commons/1/1e/Default-avatar.jpg'" + self picture: 'https://www.jamf.com/jamf-nation/img/default-avatars/generic-user.png'. + ^ picture +] + +{ #category : #accessing } +BreaMember >> givenName [ + ^ givenName +] + +{ #category : #accessing } +BreaMember >> givenName: anObject [ + givenName := anObject +] + +{ #category : #public } +BreaMember >> head [ + ^ '', self headMeta, self headTitle, self headStyles,'' +] + +{ #category : #utility } +BreaMember >> headMeta [ + ^ ' + + + ' +] + +{ #category : #utility } +BreaMember >> headStyles [ + ^ ' + + + + + + ' +] + +{ #category : #utility } +BreaMember >> headTitle [ + ^ self headTitled: 'GIG: Network' +] + +{ #category : #utility } +BreaMember >> headTitled: aString [ + ^ '', aString ,'' +] + +{ #category : #public } +BreaMember >> html [ + "I show the member profile as HTML" + ^ self head, self htmlOutput +] + +{ #category : #public } +BreaMember >> htmlInput [ + "I capture data in a HTML Form and use it to create a new BreaMember" + ^ ' + + + + + + + + Add Member | GIG Network + + + + + + +
+
+ +
+
+
+
+
+

Add new member

+
+
+

+ Fill out the form. + The fields preceded by [*] are obligatory. +

+
+
+ + + Letters and spaces only +
+
+ + + Letters and spaces only +

+
+ + +
+
+ + + Letters and spaces only +

+
+ + + Letters and spaces only +

+
+ + +
+
+ + +

+
+ + +
+
+ + +

+
+ + +
+

+ +

+
+
+
+
+ +
+
+ + + +' +] + +{ #category : #public } +BreaMember >> htmlOutput [ + + ^ self htmlOutputTemplate asMustacheTemplate value: self +] + +{ #category : #public } +BreaMember >> htmlOutputTemplate [ + "I show the member profile as HTML" + ^ '
+
+
+

{{givenName}} {{familyName}}

+
+
+ +
+
+ + + + + + ', + self countryTemplate, + self organizationsTemplate, + self websiteTemplate, + self twitterTemplate, + self facebookTemplate, + self tagsTemplate, + ' + +
Name{{givenName}} {{familyName}}
+
+
+ Read more +
+
+
+ ' +] + +{ #category : #accessing } +BreaMember >> instagram [ + ^ self webPresence instagram. +] + +{ #category : #accessing } +BreaMember >> instagram: aProfileName [ + self webPresence instagram: aProfileName +] + +{ #category : #accessing } +BreaMember >> memberOf: anOrgName [ + self organizations add: + (BreaOrganization new name: anOrgName) +] + +{ #category : #accessing } +BreaMember >> memberOf: anOrgName withWebsite: aUrl [ + self organizations add: + (BreaOrganization new + name: anOrgName; + website: aUrl) +] + +{ #category : #accessing } +BreaMember >> organizations [ + ^ organizations ifNil: [ organizations := OrderedCollection new ] +] + +{ #category : #accessing } +BreaMember >> organizations: anOrgListOrName [ + anOrgListOrName splitOn: ',' do: [ :each | self memberOf: each ] +] + +{ #category : #public } +BreaMember >> organizationsTemplate [ + ^ ' + + Organization(s) + + {{#organizations}} + {{name}} + {{/organizations}} + + ' asMustacheTemplate value: self +] + +{ #category : #accessing } +BreaMember >> password [ + ^ password +] + +{ #category : #accessing } +BreaMember >> password: anObject [ + password := anObject +] + +{ #category : #accessing } +BreaMember >> picture [ + ^ picture ifNil: [ ^ self getGenericProfilePicture ] +] + +{ #category : #accessing } +BreaMember >> picture: anImageFilePath [ + picture := anImageFilePath +] + +{ #category : #helpers } +BreaMember >> renderTestUserAsHtml [ + ^ self class new createTestUser html +] + +{ #category : #'server handling' } +BreaMember >> storeInto: aFileDirectory [ + | folder file | + folder := (aFileDirectory asFileReference / self fullName) ensureCreateDirectory. + file := (folder / 'info.ston') ensureCreateFile. + file writeStreamDo: [:stream | + (STON writer on: stream) + newLine: String crlf; + prettyPrint: true; + nextPut: self] +] + +{ #category : #accessing } +BreaMember >> tags [ + ^ tags +] + +{ #category : #accessing } +BreaMember >> tags: anObject [ + tags := anObject +] + +{ #category : #public } +BreaMember >> tagsTemplate [ + ^ '{{#tags}} + + + Tags
+ {{.}} + + + {{/tags}}' asMustacheTemplate value: self +] + +{ #category : #accessing } +BreaMember >> twitter [ + ^ self webPresence twitter. +] + +{ #category : #accessing } +BreaMember >> twitter: aProfileName [ + aProfileName = '' + ifTrue: [ self webPresence twitter: nil ] + ifFalse: [ self webPresence twitter: aProfileName ] +] + +{ #category : #public } +BreaMember >> twitterTemplate [ + ^ '{{#twitter}} + + + Twitter + + + {{.}} + + {{/twitter}}' asMustacheTemplate value: self +] + +{ #category : #accessing } +BreaMember >> webPresence [ + ^ webPresence ifNil: [ webPresence := BreaWebPresence new ] +] + +{ #category : #accessing } +BreaMember >> webPresence: anObject [ + webPresence := anObject +] + +{ #category : #accessing } +BreaMember >> website [ + ^ self webPresence website. +] + +{ #category : #accessing } +BreaMember >> website: anUrl [ + self webPresence website: anUrl +] + +{ #category : #public } +BreaMember >> websiteTemplate [ + ^ '{{#website}} + + Website + + {{.}} + + {{/website}}' asMustacheTemplate value: self +] diff --git a/src/Brea/BreaMemberTest.class.st b/src/Brea/BreaMemberTest.class.st new file mode 100644 index 0000000..162a3c5 --- /dev/null +++ b/src/Brea/BreaMemberTest.class.st @@ -0,0 +1,8 @@ +" +A BreaMemberTest is a test class for testing the behavior of BreaMember +" +Class { + #name : #BreaMemberTest, + #superclass : #TestCase, + #category : #'Brea-Tests' +} diff --git a/src/Brea/BreaOrganization.class.st b/src/Brea/BreaOrganization.class.st new file mode 100644 index 0000000..7142be6 --- /dev/null +++ b/src/Brea/BreaOrganization.class.st @@ -0,0 +1,32 @@ +" +I store the places a BreaMember is affiliated to. +" +Class { + #name : #BreaOrganization, + #superclass : #Object, + #instVars : [ + 'name', + 'website' + ], + #category : #Brea +} + +{ #category : #accessing } +BreaOrganization >> name [ + ^ name +] + +{ #category : #accessing } +BreaOrganization >> name: anObject [ + name := anObject +] + +{ #category : #accessing } +BreaOrganization >> website [ + ^ website +] + +{ #category : #accessing } +BreaOrganization >> website: anObject [ + website := anObject +] diff --git a/src/Brea/BreaWebPresence.class.st b/src/Brea/BreaWebPresence.class.st new file mode 100644 index 0000000..815a62c --- /dev/null +++ b/src/Brea/BreaWebPresence.class.st @@ -0,0 +1,54 @@ +" +I store the common forms of web presence a BreaMember can have online. +" +Class { + #name : #BreaWebPresence, + #superclass : #Object, + #instVars : [ + 'website', + 'twitter', + 'facebook', + 'instagram' + ], + #category : #Brea +} + +{ #category : #accessing } +BreaWebPresence >> facebook [ + ^ facebook +] + +{ #category : #accessing } +BreaWebPresence >> facebook: anObject [ + facebook := anObject +] + +{ #category : #accessing } +BreaWebPresence >> instagram [ + ^ instagram +] + +{ #category : #accessing } +BreaWebPresence >> instagram: anObject [ + instagram := anObject +] + +{ #category : #accessing } +BreaWebPresence >> twitter [ + ^ twitter +] + +{ #category : #accessing } +BreaWebPresence >> twitter: anObject [ + twitter := anObject +] + +{ #category : #accessing } +BreaWebPresence >> website [ + ^ website +] + +{ #category : #accessing } +BreaWebPresence >> website: anObject [ + website := anObject +] diff --git a/src/Brea/BreaWebsite.class.st b/src/Brea/BreaWebsite.class.st new file mode 100644 index 0000000..e89753f --- /dev/null +++ b/src/Brea/BreaWebsite.class.st @@ -0,0 +1,301 @@ +" +I model the fossil repository where public data is stored for the +building of this web site. +" +Class { + #name : #BreaWebsite, + #superclass : #Object, + #instVars : [ + 'fossilRepo', + 'server', + 'template', + 'title', + 'widgets' + ], + #category : #Brea +} + +{ #category : #utility } +BreaWebsite class >> availableTemplates [ + self templates keys. +] + +{ #category : #utility } +BreaWebsite class >> demoFolder [ + ^ FileLocator temp asFileReference / 'BreaDemo'. +] + +{ #category : #example } +BreaWebsite class >> example [ + "I run an example mockup of a website using Brea. + After runing me, go to: + - http://localhost:8080/demo + - http://localhost:8080/members/test + - http://localhost:8080/members/add " + self new + local: FileLocator temp asFileReference / 'BreaDemo'; + template: 'portafolio'; + downloadTemplate; + modifyTemplate; + start +] + +{ #category : #example } +BreaWebsite class >> exampleDashboard [ + "I run an example mockup of a website using Brea. + After runing me, go to: http://localhost:8080/demo " + self new + local: FileLocator temp asFileReference / 'BreaDemo'; + template: 'dashboard'; + downloadTemplate; + modifyTemplate; + start +] + +{ #category : #'server handling' } +BreaWebsite class >> stopAll [ + "I stop the server" + Teapot stopAll +] + +{ #category : #utility } +BreaWebsite class >> templates [ + "I provide the supported MDL templates taken from: https://getmdl.io/templates/" + ^ Dictionary new + at: 'portafolio' put: 'https://code.getmdl.io/1.3.0/mdl-template-portfolio.zip'; + at: 'dashboard' put: 'https://code.getmdl.io/1.3.0/mdl-template-dashboard.zip'; + yourself. +] + +{ #category : #utility } +BreaWebsite >> defaultTemplate [ + self template + ifNil: [ + self template: 'portafolio' ]. + ^ self template. +] + +{ #category : #utility } +BreaWebsite >> downloadDefaultTemplate [ + self downloadTemplateNamed: self defaultTemplate Into: self local. +] + +{ #category : #utility } +BreaWebsite >> downloadDefaultTemplateInto: aDirectory [ + | remoteUrl templatesFile | + aDirectory ensureDeleteAll. + aDirectory ensureCreateDirectory. + remoteUrl := self class templates at: self template. + GrafoscopioUtils + downloadingFrom: remoteUrl + withMessage: 'Downloading templates' + into: FileLocator temp asFileReference. + templatesFile := FileLocator temp asFileReference / (remoteUrl splitOn: '/') last. + ZipArchive new + readFrom: templatesFile; + extractAllTo: aDirectory +] + +{ #category : #utility } +BreaWebsite >> downloadTemplate [ + self downloadTemplateNamed: self template Into: self local asFileReference. +] + +{ #category : #utility } +BreaWebsite >> downloadTemplateNamed: aName Into: aDirectory [ + "aName: String aDirectory: aFileLocation" + | remoteUrl templatesFile | + aDirectory ensureDeleteAll. + aDirectory ensureCreateDirectory. + remoteUrl := self class templates at: aName. + GrafoscopioUtils + downloadingFrom: remoteUrl + withMessage: 'Downloading templates' + into: FileLocator temp asFileReference. + templatesFile := FileLocator temp asFileReference / (remoteUrl splitOn: '/') last. + ZipArchive new + readFrom: templatesFile; + extractAllTo: aDirectory +] + +{ #category : #accessing } +BreaWebsite >> fossilRepo [ + ^ fossilRepo ifNil: [ fossilRepo := FossilRepo new ] +] + +{ #category : #accessing } +BreaWebsite >> fossilRepo: aFossilRepo [ + fossilRepo := aFossilRepo +] + +{ #category : #utility } +BreaWebsite >> local [ + ^ self fossilRepo local +] + +{ #category : #'server handling' } +BreaWebsite >> local: aFilePath [ + "I define the local storage of the Fossil repository. + For the moment aFilePath must be an absolute " + | localSite | + localSite := aFilePath asFileReference. + localSite exists + ifFalse: [ localSite ensureCreateDirectory]. + self fossilRepo local: aFilePath. +] + +{ #category : #'server handling' } +BreaWebsite >> local: aFilePath remote: anUrl [ + "I define the local and remote storages of the Fossil repository" + self remote: anUrl. + self local: aFilePath +] + +{ #category : #utility } +BreaWebsite >> modifyDashboardTemplate [ + "I replace default templates with versioned files, that contains Mustache tags used + for examples." + "This part should be factorized and integrated into a single method tha modifies any + template, instead of repeated the code of modifyPortafolioTemplate." + | remoteRepoUrl files | + remoteRepoUrl := 'http://mutabit.com/repos.fossil/brea/templates/portafolio'. + files := #('index.html' 'styles.css'). + "files do: [ :file | + GrafoscopioUtils + downloadingFrom: remoteRepoUrl, 'doc/tip/', file + withMessage: 'Replacing ', file + into: self local ]" +] + +{ #category : #utility } +BreaWebsite >> modifyPortafolioTemplate [ + "I replace default templates with versioned files, that contains Mustache tags used + for examples." + | remoteRepoUrl files | + remoteRepoUrl := 'http://mutabit.com/repos.fossil/gig/'. + files := #('index.html' 'styles.css'). + files do: [ :file | + GrafoscopioUtils + downloadingFrom: remoteRepoUrl, 'doc/tip/', file + withMessage: 'Replacing ', file + into: self local ] +] + +{ #category : #utility } +BreaWebsite >> modifyTemplate [ + "I replace default templates with versioned files, that contains Mustache tags used + for examples." + self template = 'portafolio' ifTrue: [ self modifyPortafolioTemplate ]. + self template = 'dashboard' ifTrue: [ self modifyDashboardTemplate ] +] + +{ #category : #'input processing' } +BreaWebsite >> processNewMember: request [ + | member badRequest | + badRequest := [ ^ ZnResponse badRequest: request ]. + (request hasEntity + and: [ request contentType matches: ZnMimeType applicationFormUrlEncoded ]) + ifFalse: [ badRequest ]. + member := BreaMember new + givenName: (request at: #givenName); + familyName: (request at: #familyName); + email: (request at: #email); + country: (request at: #country); + organizations: (request at: #organizations); + picture: (request at: #picture); + website: (request at: #website); + twitter: (request at: #twitter); + facebook: (request at: #facebook); + tags: (request at: #tags). + self store: member. + ^ 'New member stored!' +] + +{ #category : #'server handling' } +BreaWebsite >> remote: anUrl [ + "I define the remote storage of the Fossil repository" + self remote: anUrl. +] + +{ #category : #'server handling' } +BreaWebsite >> routes [ + "I define how the website behaves accordingly to particular routes." + self server + serveStatic: 'demo' from: (self local); + GET: 'members/test' -> [ :req | BreaMember new renderTestUserAsHtml ]; + GET: 'members/add' -> [ :req | BreaMember new htmlInput ]; + POST: 'members/summit' -> [ :req | self processNewMember: req ] +] + +{ #category : #accessing } +BreaWebsite >> server [ + ^ server ifNil: [ self setup ] +] + +{ #category : #accessing } +BreaWebsite >> server: anObject [ + server := anObject +] + +{ #category : #accessing } +BreaWebsite >> setup [ + ^ server := Teapot + configure: + {(#port -> 8080). + (#debugMode -> true)} +] + +{ #category : #'server handling' } +BreaWebsite >> start [ + "I define the config and start the server" + self routes. + self server start +] + +{ #category : #'server handling' } +BreaWebsite >> storageFor: anObject [ + "I define the places where local storage is done for several types of objects" + anObject class = BreaMember + ifTrue: [ ^ self local asFileReference / 'members' ]. + ^ self +] + +{ #category : #'server handling' } +BreaWebsite >> store: anObject [ + "I store different kind of objects in the website repository. + For the moment I will only store BreaMembers, but as long as new + objects will emerge, I will specialize other ways of storage." + anObject storeInto: (self storageFor: anObject) +] + +{ #category : #accessing } +BreaWebsite >> template [ + ^ template +] + +{ #category : #accessing } +BreaWebsite >> template: aTemplateName [ + "I define the default template to be used for the Brea website. + Available options are at self class templates." + template := aTemplateName +] + +{ #category : #accessing } +BreaWebsite >> title [ + ^ title +] + +{ #category : #accessing } +BreaWebsite >> title: anObject [ + title := anObject +] + +{ #category : #accessing } +BreaWebsite >> widgets [ + ^ widgets ifNil: [ widgets := OrderedCollection new ] +] + +{ #category : #accessing } +BreaWebsite >> widgets: anOrderedCollection [ + widgets := anOrderedCollection +] diff --git a/src/Brea/BreaWidget.class.st b/src/Brea/BreaWidget.class.st new file mode 100644 index 0000000..02043f2 --- /dev/null +++ b/src/Brea/BreaWidget.class.st @@ -0,0 +1,80 @@ +" +I model a widget to be put, generally in a HTML view of a website. +" +Class { + #name : #BreaWidget, + #superclass : #Object, + #instVars : [ + 'source', + 'views' + ], + #category : #Brea +} + +{ #category : #accessing } +BreaWidget >> exportViewsTo: aFolder [ + self shouldBeImplemented +] + +{ #category : #'data visualization' } +BreaWidget >> piechartFor: dataArray titled: aString colored: aPalette [ +"I create a pie chart for a subset of the data stored in a dataArray, which is an Association +of keys and values. +aselector should be a message undestood by the dataArray." +| b lb | +b := RTPieBuilder new. +b interaction popup. +b objects: dataArray. +b slice: #value. +b labeled. +"And add the legend" +lb := RTLegendBuilder new. +lb view: b view. +lb addText: aString. +lb build. +^ b +] + +{ #category : #'data visualization' } +BreaWidget >> piechartFor: dataArray using: aSelector titled: aString colored: aPalette [ +"I create a pie chart for a subset of the data stored in a dataArray, which is an Association +of keys and values. +aselector should be a message undestood by the dataArray." +| b lb | +b := RTPieBuilder new. +b interaction popup. +b objects: dataArray. +b slice: aSelector. +b labeled. +"And add the legend" +lb := RTLegendBuilder new. +lb view: b view. +lb addText: 'Asitentes a talleres'. +lb build. +^ b +] + +{ #category : #accessing } +BreaWidget >> source [ + ^ source +] + +{ #category : #accessing } +BreaWidget >> source: aUrl [ + source := aUrl +] + +{ #category : #accessing } +BreaWidget >> sourceAsJSON [ + ^ NeoJSONReader fromString: self source asUrl retrieveContents +] + +{ #category : #accessing } +BreaWidget >> views [ + ^ views ifNil: [ views := Dictionary new ] +] + +{ #category : #accessing } +BreaWidget >> views: anObject [ + views := anObject +] diff --git a/src/Brea/package.st b/src/Brea/package.st new file mode 100644 index 0000000..91625ae --- /dev/null +++ b/src/Brea/package.st @@ -0,0 +1 @@ +Package { #name : #Brea }