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
+
+
+
+
+
+
+
+
+
+
+'
+]
+
+{ #category : #public }
+BreaMember >> htmlOutput [
+
+ ^ self htmlOutputTemplate asMustacheTemplate value: self
+]
+
+{ #category : #public }
+BreaMember >> htmlOutputTemplate [
+ "I show the member profile as HTML"
+ ^ '
+
+
+
{{givenName}} {{familyName}}
+
+
+
+
+
+
+ Name |
+ {{givenName}} {{familyName}} |
+
',
+ self countryTemplate,
+ self organizationsTemplate,
+ self websiteTemplate,
+ self twitterTemplate,
+ self facebookTemplate,
+ self tagsTemplate,
+ '
+
+
+
+
+
+
+ '
+]
+
+{ #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 }