I added a mechanism SqueakSource to send email notifications whenever a new version is uploaded.
http://www.squeaksource.com/ss/SqueakSource-mtf.1025.mcz
Features: - Configurable recipient and reply-to address, both per project and global default - Can be enabled or disabled per-project, or for global default - Three types of commit emails, only configurable globally: - Summary only: Sends URL and version summary information. Requires no server-side mcz decoding, so is instantaneous. Importing SqueakSource-mtf.1025.mcz to localhost took 2 seconds under this setting. - Summary + full snapshot: additionally puts a text representation of the version in the body of the email. With my experiment, this setting took the longest, since a full snapshot is pretty big. Importing SqueakSource-mtf.1025.mcz to localhost took 9 seconds under this setting. - Summary + patch: finds the latest ancestor in the project and puts a patch in the body of the email. A sample patch email is forwarded at the end of this email. I was surprised to find this was faster than the above method; it does 2-3 times more parsing/processing as above, but writes much less data. Importing SqueakSource-mtf.1025.mcz to localhost took 5 seconds under this setting. - Two new textual representations for generic Monticello packages: MCTextWriter, and MCDiffyTextWriter. They are write-only formats intended for human, rather than computer, consumption. Currently it is grafted on top of the chunk format writer, but I can do better. Next version will feature a much better textual format. These two writer classes have no dependencies on SqueakSource, and could be added directly to Monticello.
Installation Instructions:
These instructions are for merging these features into an existing SqueakSource installation. For new installations, see http://wiki.squeak.org/squeak/5766 (slightly out of date; I will update it soon)
I tested these instructions on a 3.8.1 image running SqueakSource-al.1024: 1. Merge in SqueakSource-mtf.1025. It is nearly all new classes, and has no core changes, so it probably won't have conflicts. 2. log in as superuser 3. Go to global edit settings 4. Set the 5 options: - SMTP Server - Generated email: sender address - Default email recipients: (blank by default) - Default Reply-To Address: defaults to squeak-dev list - Show in commit notifications: (choose detail level) - Default subscriptions: (check the checkbox if you want to enable commit emails)
This sets the defaults for all projects. If any per-project settings are non-nil, they will ignore the defaults. So, you must do this right after installation and before any existing project settings have been changed.
Here is a generated commit email (Summary + Patch) for SqueakSource-mtf.1025:
----- Forwarded message from no-reply@localhost -----
To: tapplek@gmail.com From: no-reply@localhost Date: Tue, 21 Aug 2007 16:05:04 -0700 (MST) Subject: SqueakSource: SqueakSource-mtf.1025.mcz
A new version of SqueakSource was added to project SqueakSource: http://localhost:8888/ss/SqueakSource-mtf.1025.mcz
==================== Summary ====================
Name: SqueakSource-mtf.1025 Author: mtf Time: 21 August 2007, 4:03:16 pm UUID: e7fe67cd-2502-7f4e-81ce-7681f86bf1d8 Ancestors: SqueakSource-al.1024
- add model for sending commit emails to SSProject - add model for default values of above to SSRepository - add two classes for writing text representations of generic Monticello packages: MCTextWriter and MCDiffyTextWriter - add three classes for writing text representations SqueakSource versions and packages: SSBasicTextWriter, SSTextWriter, and SSDiffyTextWriter - add email sending capabilities via SSEmailSubscription
=============== Diff against SqueakSource-al.1024 ===============
Item was added: + ----- Method: SSProject>>subscriptionsDo: (in category 'accessing-subscriptions') ----- + subscriptionsDo: aBlock + ^ self subscriptions do: aBlock!
Item was added: + ----- Method: SSSubscription>>versionAdded:to: (in category 'as yet unclassified') ----- + versionAdded: aVersion to: aProject + ^ self!
Item was added: + SSBasicTextWriter subclass: #SSTextWriter + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'! + + !SSTextWriter commentStamp: '<historical>' prior: 0! + In addition to writing summaries, my instances also dump a full snapshot of the uploaded version. + + For speed notes of this class, see the class comment of SSDiffyTextWriter!
Item was added: + ----- Method: MCTextWriter>>writeVersion: (in category 'writing') ----- + writeVersion: aVersion + self writeVersionInfo: aVersion info. + self writeSnapshot: aVersion snapshot. + !
Item was added: + ----- Method: SSEMailSubscription>>writeHeaders (in category 'as yet unclassified') ----- + writeHeaders + sender ifNotEmpty: [ stream nextPutAll: 'From: '; nextPutAll: sender]. + project emailRecipients ifNotEmpty: [stream cr; nextPutAll: 'To: ']. + project emailRecipients + do: [:email | stream nextPutAll: email address] + separatedBy: [stream nextPutAll: ', ']. + project replyTo ifNotEmpty: [stream + cr; nextPutAll: 'Reply-To: '; nextPutAll: project replyTo]. + stream cr; + nextPutAll: 'Subject: '; + nextPutAll: project title; nextPutAll: ': '; + nextPutAll: version fileName; cr; cr!
Item was added: + ----- Method: SSDiffyTextWriter>>writePatchHeader: (in category 'as yet unclassified') ----- + writePatchHeader: info + self textWriter writePatchHeader: info!
Item was added: + ----- Method: SSRepository>>defaultEmailRecipients (in category 'accessing-settings') ----- + defaultEmailRecipients + ^self properties + at: #defaultEmailRecipients + ifAbsent: [OrderedCollection new]!
Item was changed: ----- Method: SSProject>>addVersion:author: (in category 'accessing-versions') ----- addVersion: aString author: aUser | array version | array := aString asByteArray. version := SSVersion array: array author: aUser. SSRepository storage saveMonticello: array of: version to: self. + versions at: version fileName put: version. + self versionAdded: version. + ^ version + ! - ^versions at: version fileName put: version. - !
Item was added: + ----- Method: SSRepository>>defaultEmailRecipients: (in category 'accessing-settings') ----- + defaultEmailRecipients: aCollection + ^self properties + at: #defaultEmailRecipients + put: aCollection!
Item was added: + SSModel subclass: #SSEmailAddress + instanceVariableNames: 'address' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Model'!
Item was added: + ----- Method: SSRepository>>commitWriterClass (in category 'accessing-settings') ----- + commitWriterClass + ^self properties + at: #commitWriterClass + ifAbsent: [SSBasicTextWriter]!
Item was added: + ----- Method: SSEMailSubscription>>writerClass (in category 'as yet unclassified') ----- + writerClass + ^ SSRepository current commitWriterClass!
Item was added: + ----- Method: SSRepository>>emailSender: (in category 'accessing-settings') ----- + emailSender: aString + ^self properties + at: #emailSender + put: aString!
Item was added: + ----- Method: SSProject>>replyTo (in category 'accessing') ----- + replyTo + "Answer the value of replyTo" + + ^ replyTo ifNil: [SSRepository current defaultReplyTo]!
Item was added: + ----- Method: SSTextWriter>>writeSnapshot: (in category 'as yet unclassified') ----- + writeSnapshot: snapshot + self textWriter writeSnapshot: snapshot!
Item was added: + ----- Method: SSEmailAddress>>address: (in category 'accessing') ----- + address: anObject + "Set the value of address" + + address _ anObject!
Item was added: + ----- Method: SSDiffyTextWriter class>>description (in category 'as yet unclassified') ----- + description + ^ 'Summary + changes'!
Item was added: + ----- Method: SSEMailSubscription class>>description (in category 'as yet unclassified') ----- + description + ^ 'Recieve commit notifications by email'!
Item was added: + MCStWriter subclass: #MCTextWriter + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'! + + !MCTextWriter commentStamp: '<historical>' prior: 0! + A basic writer for SqueakSource versions and projects. Does no decoding of .mcz files!
Item was added: + ----- Method: SSTextWriter class>>description (in category 'as yet unclassified') ----- + description + ^ 'Summary + full snapshot'!
Item was changed: ----- Method: SSProject>>metaobject (in category 'metamodel') ----- metaobject | metaobject | metaobject := MWMetaobject for: self. metaobject textAttribute: #id do: [ :attribute | attribute label: 'Name'; maxLength: 20; addRequiredRule; addValidationRule: [ :value | value allSatisfy: [ :char | char isLetter or: [ char isDigit ] ] ] errorString: 'name should only contain letters and digits'; addValidationRule: [ :value | SSRepository current isUniqueProjectId: value ] errorString: 'name is not unique' ]. metaobject textAttribute: #title do: [ :attribute | attribute label: 'Title'; maxLength: 50; addRequiredRule ]. metaobject textAttribute: #description do: [ :attribute | attribute label: 'Description'; multiLine: true ]. metaobject multipleRelationshipAttribute: #tags do: [ :attribute | attribute label: 'Tags'; relationshipTo: [ self repository tags ] formatWith: [ :each | each name ]; "nilItemString: 'none yet';" yourself ]. metaobject singleRelationshipAttribute: #license do: [ :attribute | attribute label: 'License'; relationshipTo: self licenses formatWith: [ :each | each isNil ifTrue: [ 'None' ] ifFalse: [ each first ifNil: [ 'None' ] ] ] ]. metaobject booleanAttribute: #canBless do: [:attribute | attribute label: 'Enable Blessings'. ].
metaobject singleRelationshipAttribute: #globalRight do: [ :attribute | attribute label: 'Global'; relationshipTo: SSAccessPolicy globalRights formatWith: [ :symbol | symbol asCapitalizedPhrase ] ]. metaobject multipleRelationshipAttribute: #admins do: [ :attribute | attribute label: 'Administrators'; addValidationRule: [ :admins | | currentUser | currentUser := SSSession currentSession user. currentUser isSuperUser or: [ admins anySatisfy: [ :each | each isGroup ifTrue: [ each hasMember: currentUser ] ifFalse: [ each = currentUser ] ] ] ] errorString: 'You can''t remove yourself from the list of administrators'; hide ]. metaobject multipleRelationshipAttribute: #developers do: [ :attribute | attribute label: 'Developers'; hide ]. metaobject multipleRelationshipAttribute: #guests do: [ :attribute | attribute label: 'Guests'; hide ]. + + metaobject multipleAttribute: #emailRecipients do: [ :attribute | + attribute + label: 'Send emails to'; + baseClass: SSEmailAddress]. + + metaobject textAttribute: #replyTo do: [ :attribute | + attribute + label: 'Reply-To Address'; + maxLength: 50]. + + metaobject multipleRelationshipAttribute: #subscriptions do: [ :attribute | + attribute + label: 'Subscriptions'; + relationshipTo: [SSSubscription allSubclasses]]. + ^metaobject!
Item was added: + ----- Method: MCTextWriter>>writeVersionInfo: (in category 'writing') ----- + writeVersionInfo: aVersionInfo + stream + nextPutAll: '==================== Summary ===================='; cr; cr; + nextPutAll: aVersionInfo summary; cr; cr!
Item was added: + ----- Method: SSRepository class>>defaultEmailSender (in category 'private') ----- + defaultEmailSender + ^'no-reply@', self hostName!
Item was changed: SSModel subclass: #SSProject + instanceVariableNames: 'id title description creator accessPolicy versions dateCreated feeds wiki blessings configs tags cache license emailAddresses replyTo subscriptions emailRecipients' - instanceVariableNames: 'id title description creator accessPolicy versions dateCreated feeds wiki blessings configs tags cache license' classVariableNames: '' poolDictionaries: '' category: 'SqueakSource-Model'!
Item was added: + ----- Method: SSEMailSubscription>>versionAdded:to: (in category 'as yet unclassified') ----- + versionAdded: aVersion to: aProject + sender := SSRepository current emailSender. + stream := String new writeStream. + project := aProject. + version := aVersion. + self writeHeaders. + (self writerClass on: stream) writeVersion: aVersion for: aProject. + self sendMail.!
Item was added: + ----- Method: SSEmailAddress>>metaobject (in category 'accessing') ----- + metaobject + | metaobject | + metaobject _ MWMetaobject for: self. + + metaobject textAttribute: #address do: [ :attribute | + attribute + maxLength: 50; + addRequiredRule]. + + ^metaobject!
Item was added: + ----- Method: MCTextWriter>>writeMethodPreamble: (in category 'writing') ----- + writeMethodPreamble: definition + stream + nextPutAll: '----- Method: '; + nextPutAll: definition fullClassName; + nextPutAll: '>>'; + nextPutAll: definition selector; + nextPutAll: ' (in category '; + nextPutAll: definition category asString printString; + nextPutAll: ') -----'; cr!
Item was added: + ----- Method: SSEmailAddress>>address (in category 'accessing') ----- + address + "Answer the value of address" + + ^ address!
Item was added: + SSTextWriter subclass: #SSDiffyTextWriter + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'! + + !SSDiffyTextWriter commentStamp: '<historical>' prior: 0! + Rather than a full snapshot dump, my instances write a patch from the latest ancestor stored in the project. If no ancestors are in the project, they fall back onto doing a full snapshot. + + Even though this class does much more decoding of .mcz files than SSTextWriter, it is usually a lot faster because a diff is much less information to write than a full snapshot. As a rough metric, a diff of SqueakSource-mtf.1025 (the package where this class was introduced) took 7 seconds to write (on my computer), while a full snapshot took 33 seconds to write. On the other hand, a version summary was instantaneous, as it requires no decoding of .mcz files!
Item was added: + ----- Method: SSRepository>>defaultSubscriptions: (in category 'accessing-settings') ----- + defaultSubscriptions: aCollection + ^self properties + at: #defaultSubscriptions + put: aCollection!
Item was added: + ----- Method: MCDiffyTextWriter>>writePatch: (in category 'writing') ----- + writePatch: aPatch + aPatch operations do: + [:ea | + ea isRemoval ifTrue: [self writeRemoval: ea]. + ea isAddition ifTrue: [self writeAddition: ea]. + ea isModification ifTrue: [self writeModification: ea]. + stream cr.].!
Item was added: + MCWriter subclass: #SSBasicTextWriter + instanceVariableNames: 'textWriter' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'! + + !SSBasicTextWriter commentStamp: '<historical>' prior: 0! + My instances write version summaries for SqueakSource versions and projects. Does no decoding of .mcz files. Subclasses extend the functionality. + + For speed notes about this class, see the class comment of SSDiffyTextWriter!
Item was added: + ----- Method: SSDiffyTextWriter>>textWriterClass (in category 'as yet unclassified') ----- + textWriterClass + ^ MCDiffyTextWriter!
Item was added: + ----- Method: SSEMailSubscription>>writeSummary (in category 'as yet unclassified') ----- + writeSummary + | author | + author := version authorString ifEmpty: [version guessedAuthor]. + author + ifNil: [ stream + nextPutAll: 'A new version of '; + nextPutAll: version package; + nextPutAll: ' was added to project '; + nextPutAll: project title; nextPut: $:; cr] + ifNotNil: [ stream + nextPutAll: author; + nextPutAll: ' uploaded a new version of '; + nextPutAll: version package; + nextPutAll: ' to project '; + nextPutAll: project title; nextPut: $:; cr]. + stream nextPutAll: (version url: project); cr; cr.!
Item was added: + ----- Method: MCTextWriter>>chunkContents: (in category 'writing') ----- + chunkContents: aBlock + stream nextChunkPut: (String streamContents: aBlock); cr!
Item was added: + ----- Method: SSDiffyTextWriter>>writePatch: (in category 'as yet unclassified') ----- + writePatch: patch + self textWriter writePatch: patch!
Item was added: + ----- Method: TextDiffBuilder>>stringForAttributes: (in category '*SqueakSource-Notifications') ----- + stringForAttributes: type + "Private. + Answer the String that prefixes text of the given type." + + ^type caseOf: { + [#insert] -> [ '+ ' ]. + [#remove] -> [ '- ']. + } otherwise: [ ' ' ]. + !
Item was added: + ----- Method: SSDiffyTextWriter>>writeVersion:for: (in category 'as yet unclassified') ----- + writeVersion: aSSVersion for: aProject + | reader ancestor patch | + reader := aSSVersion reader: aProject. + ancestor := (aSSVersion versionInfo latestAncestorIn: aProject) + ifNil: [reader info latestAncestorIn: aProject]. + self writeSummary: aSSVersion for: aProject. + self writeVersionInfo: aSSVersion versionInfo. + ancestor + ifNil: [self writeSnapshot: reader snapshot.] + ifNotNil: [ + patch := reader snapshot patchRelativeToBase: + (ancestor reader: aProject) snapshot. + self writePatchHeader: ancestor versionInfo. + self writePatch: patch.]!
Item was added: + ----- Method: SSEMailSubscription>>sendMail (in category 'as yet unclassified') ----- + sendMail + project emailRecipients ifEmpty: [^ self]. + SeasidePlatformSupport + deliverMailFrom: sender + to: (project emailRecipients collect: [:email | email address]) + text: stream contents. + "Workspace new contents: stream contents; openLabel: sender."!
Item was added: + ----- Method: SSTextWriter>>writeVersion:for: (in category 'as yet unclassified') ----- + writeVersion: aSSVersion for: aProject + | reader | + reader := aSSVersion reader: aProject. + self writeSummary: aSSVersion for: aProject. + self writeVersionInfo: aSSVersion versionInfo. + self writeSnapshot: reader snapshot.!
Item was added: + ----- Method: MCAncestry>>latestAncestorIn: (in category '*SqueakSource') ----- + latestAncestorIn: aProject + | ancestor | + self ancestors ifEmpty: [^ nil]. + self allAncestors do: [:anAncestry | + ancestor _ aProject versionAt: anAncestry name,'.mcz'. + ancestor ifNotNil: [^ ancestor]]. + ^ nil!
Item was added: + ----- Method: SSEMailSubscription>>writeDiffAgainst: (in category 'as yet unclassified') ----- + writeDiffAgainst: ancestor + stream nextPutAll: 'Diff against '; + nextPutAll: ancestor fileName; cr. + self writeVersion + !
Item was added: + MCTestCase subclass: #MCTextWriterTest + instanceVariableNames: 'version' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'!
Item was added: + ----- Method: SSProject>>subscriptions: (in category 'accessing-subscriptions') ----- + subscriptions: aCollection + "Private. Set the subscriptions" + subscriptions := aCollection!
Item was added: + ----- Method: SSProject>>emailRecipients (in category 'accessing') ----- + emailRecipients + "Answer the value of emailAddresses" + + ^ emailRecipients ifNil: [SSRepository current defaultEmailRecipients]!
Item was added: + ----- Method: SSRepository>>defaultSubscriptions (in category 'accessing-settings') ----- + defaultSubscriptions + ^self properties + at: #defaultSubscriptions + ifAbsent: [OrderedCollection new]!
Item was added: + ----- Method: SSProject>>replyTo: (in category 'accessing') ----- + replyTo: anObject + "Set the value of replyTo" + + replyTo _ anObject!
Item was added: + ----- Method: SSBasicTextWriter>>textWriter (in category 'as yet unclassified') ----- + textWriter + ^ textWriter ifNil: [textWriter := self textWriterClass on: stream]!
Item was added: + ----- Method: MCDiffyTextWriter>>writeModification: (in category 'writing') ----- + writeModification: aModification + stream nextPutAll: 'Item was changed:'; cr. + self writePatchFrom: aModification obsoletion to: aModification definition!
Item was added: + SSModel subclass: #SSSubscription + instanceVariableNames: 'stream project version' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'! + + !SSSubscription commentStamp: '<historical>' prior: 0! + My insances represent a notification that is sent externally (an email message, for instance) in response to an event (like a commit)!
Item was changed: ----- Method: SSRepository class>>defaultRootUrl (in category 'private') ----- defaultRootUrl + ^'http://', self hostName, ':', self defaultPort asString, '/'! - | hostName | - hostName := NetNameResolver nameForAddress: (NetNameResolver localHostAddress) timeout: 5. - hostName ifNil: [hostName := NetNameResolver localAddressString]. - ^'http://', hostName, ':', self defaultPort asString, '/'!
Item was added: + ----- Method: SSRepository>>commitWriterClass: (in category 'accessing-settings') ----- + commitWriterClass: aClass + ^self properties + at: #commitWriterClass + put: aClass!
Item was added: + ----- Method: MCDiffyTextWriter>>writePatchFrom:to: (in category 'writing') ----- + writePatchFrom: src to: dst + "src and dst are allowed to bi nil to represent a non-existent source or destination state" + stream nextPutAll: (TextDiffBuilder + from: (src ifNotNil: [self visitInFork: src] ifNil: ['']) + to: (dst ifNotNil: [self visitInFork: dst] ifNil: ['']) + ) buildTextPatch!
Item was changed: SystemOrganization addCategory: #'SqueakSource-Model'! SystemOrganization addCategory: #'SqueakSource-View'! SystemOrganization addCategory: #'SqueakSource-Server'! SystemOrganization addCategory: #'SqueakSource-Tests'! + SystemOrganization addCategory: #'SqueakSource-Notification'!
Item was added: + ----- Method: SSBasicTextWriter>>writeVersionInfo: (in category 'as yet unclassified') ----- + writeVersionInfo: info + self textWriter writeVersionInfo: info!
Item was added: + ----- Method: MCDiffyTextWriter>>writeRemoval: (in category 'writing') ----- + writeRemoval: aRemoval + stream nextPutAll: 'Item was removed:'; cr. + self writePatchFrom: aRemoval definition to: nil!
Item was added: + ----- Method: MCTextWriter>>visitMethodDefinition: (in category 'visiting') ----- + visitMethodDefinition: definition + self writeMethodPreamble: definition. + self writeMethodSource: definition. + stream cr!
Item was changed: ----- Method: SSProject>>initialize (in category 'initialization') ----- initialize super initialize. id := title := description := String new. accessPolicy := SSAccessPolicy new. versions := Dictionary new. dateCreated := Date today. tags := SortedCollection new. + cache := IdentityDictionary new.! - cache := IdentityDictionary new!
Item was added: + ----- Method: SSRepository class>>hostName (in category 'private') ----- + hostName + ^ (NetNameResolver nameForAddress: (NetNameResolver localHostAddress) timeout: 5) + ifNil: [NetNameResolver localAddressString]!
Item was added: + MCTextWriter subclass: #MCDiffyTextWriter + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'!
Item was added: + ----- Method: SSBasicTextWriter class>>description (in category 'as yet unclassified') ----- + description + ^ 'Summary only'!
Item was added: + ----- Method: SSProject>>versionAdded: (in category 'accessing-subscriptions') ----- + versionAdded: aVersion + ^ self subscriptionsDo: [:each | each new versionAdded: aVersion to: self]!
Item was added: + ----- Method: MCDiffyTextWriter>>writePatchHeader: (in category 'writing') ----- + writePatchHeader: info + stream + nextPutAll: '=============== Diff against '; + nextPutAll: info name; + nextPutAll: ' ==============='; cr; cr!
Item was added: + ----- Method: SSEMailSubscription>>writeVersion (in category 'as yet unclassified') ----- + writeVersion + | ancestor aVersion| + ancestor := version lastAncestorIn: project. + aVersion := (version reader: project) version. + ancestor + ifNotNil: [(MCDiffyTextWriter on: stream) writeVersion: + (aVersion asDiffAgainst: (ancestor reader: project) version)] + ifNil: [(MCTextWriter on: stream) writeVersion: aVersion] + !
Item was added: + ----- Method: SSSubscription class>>renderOn: (in category 'as yet unclassified') ----- + renderOn: html + html text: self description!
Item was added: + SSSubscription subclass: #SSEMailSubscription + instanceVariableNames: 'sender' + classVariableNames: '' + poolDictionaries: '' + category: 'SqueakSource-Notification'!
Item was added: + ----- Method: TextDiffBuilder>>printTextPatchSequence:on: (in category '*SqueakSource-Notifications') ----- + printTextPatchSequence: seq on: aStream + seq do: [:assoc | aStream + nextPutAll: (self stringForAttributes: assoc key); + nextPutAll: assoc value; cr]!
Item was changed: ----- Method: SSRepository>>metaobject (in category 'accessing') ----- metaobject | metaobject | metaobject _ MWMetaobject for: self.
metaobject textAttribute: #rootUrl do: [ :attribute | attribute label: 'Root URL'; addRequiredRule]. metaobject integerAttribute: #port do: [ :attribute | attribute label: 'Port'; addValidationRule: [ :value | value notNil ] errorString: 'invalid number'; addValidationRule: [ :value | value between: 1 and: 65535 ] errorString: 'invalid port'; addRequiredRule]. metaobject textAttribute: #smtpServer do: [ :attribute | attribute label: 'SMTP server'; addRequiredRule]. metaobject textAttribute: #superUserEmail do: [ :attribute | attribute label: 'Admin email'; addRequiredRule]. + metaobject textAttribute: #emailSender do: [ :attribute | + attribute label: 'Generated email'; + addRequiredRule]. + metaobject multipleAttribute: #defaultEmailRecipients do: [ :attribute | + attribute label: 'Default email recipients'; baseClass: SSEmailAddress]. + metaobject textAttribute: #defaultReplyTo do: [ :attribute | + attribute label: 'Default Reply-To Address'; maxLength: 50]. + metaobject singleRelationshipAttribute: #commitWriterClass do: [ :attribute | + attribute label: 'Show in commit notifications'; + relationshipTo: [SSBasicTextWriter withAllSubclasses]; + formatWith: [ :class | class description]]. + metaobject multipleRelationshipAttribute: #defaultSubscriptions do: [ :attribute | + attribute label: 'Default subscriptions'; relationshipTo: [SSSubscription allSubclasses]]. metaobject booleanAttribute: #allowRegisterProject do: [ :attribute | attribute label: 'Everyone can register projects']. metaobject booleanAttribute: #allProjectsVisible do: [ :attribute | attribute label: 'Everyone can see all projects' ]. metaobject booleanAttribute: #allowCreateTag do: [ :attribute | attribute label: 'Everyone can create tags']. metaobject integerAttribute: #batchSize do: [ :attribute | attribute label: 'Batch size for table reports'; addValidationRule: [ :value | value notNil ] errorString: 'invalid number'; addValidationRule: [ :value | value > 1 ] errorString: 'too small'; addRequiredRule]. metaobject colorAttribute: #styleColor do: [ :attribute | attribute label: 'Style color'; addValidationRule: [ :value | value ~= Color white ] errorString: 'invalid color']. metaobject textAttribute: #introText do: [ :attribute | attribute label: 'Override home text'; multiLine: true]. metaobject textAttribute: #timezone do: [ :attribute | attribute label: 'Timezone'; addRequiredRule ]. metaobject textAttribute: #googleAnalyticsAccount do: [ :attribute | attribute label: 'Google Analytics Account' ]. ^metaobject!
Item was added: + ----- Method: SSBasicTextWriter>>textWriterClass (in category 'as yet unclassified') ----- + textWriterClass + ^ MCTextWriter!
Item was changed: ----- Method: SSProjectEditor>>renderMainOn: (in category 'rendering') ----- renderMainOn: html + self isSubForm ifTrue: [^ super renderMainOn: html]. self renderIntroductionTextOn: html. html layoutTable: [ self renderValidationErrorsOn: html. self renderFieldsOn: html. self renderMemberListForAttribute: (self metaobject attributeOf: #admins) on: html. self renderMemberListForAttribute: (self metaobject attributeOf: #developers) on: html. self renderMemberListForAttribute: (self metaobject attributeOf: #guests) on: html. html tableRowWith: [ html space ] span: 2. html tableRowWith: [ self renderButtonsOn: html ] span: 2 ]!
Item was added: + ----- Method: SSRepository>>defaultReplyTo: (in category 'accessing-settings') ----- + defaultReplyTo: aString + ^self properties + at: #defaultReplyTo + put: aString!
Item was added: + ----- Method: SSBasicTextWriter>>writeVersion:for: (in category 'as yet unclassified') ----- + writeVersion: aSSVersion for: aProject + self writeSummary: aSSVersion for: aProject. + self writeVersionInfo: aSSVersion versionInfo.!
Item was added: + ----- Method: MCTextWriterTest>>testWriteSnapshot (in category 'as yet unclassified') ----- + testWriteSnapshot + Workspace new contents: (String streamContents: [:aStream | (MCTextWriter on: aStream) writeSnapshot: self mockSnapshot]); openLabel: 'pizza'. + !
Item was added: + ----- Method: SSRepository>>emailSender (in category 'accessing-settings') ----- + emailSender + ^self properties + at: #emailSender + ifAbsent: [self class defaultEmailSender]!
Item was added: + ----- Method: MCTextWriter>>visitInFork: (in category 'visiting') ----- + visitInFork: aDefinition + ^ String streamContents: [ :forkedStream | + aDefinition accept: (self class on: forkedStream)]!
Item was added: + ----- Method: MCTextWriter>>writeSnapshot: (in category 'writing') ----- + writeSnapshot: aSnapshot + stream nextPutAll: '==================== Snapshot ===================='; cr; cr. + super writeSnapshot: aSnapshot + !
Item was added: + ----- Method: SSBasicTextWriter>>writeSummary:for: (in category 'as yet unclassified') ----- + writeSummary: aSSVersion for: aProject + | author | + author := aSSVersion authorString ifEmpty: [aSSVersion guessedAuthor]. + author + ifNil: [ stream + nextPutAll: 'A new version of '; + nextPutAll: aSSVersion package; + nextPutAll: ' was added to project '; + nextPutAll: aProject title; nextPut: $:; cr] + ifNotNil: [ stream + nextPutAll: author; + nextPutAll: ' uploaded a new version of '; + nextPutAll: aSSVersion package; + nextPutAll: ' to project '; + nextPutAll: aProject title; nextPut: $:; cr]. + stream nextPutAll: (aSSVersion url: aProject); cr; cr.!
Item was added: + ----- Method: SSRepository>>defaultReplyTo (in category 'accessing-settings') ----- + defaultReplyTo + ^self properties + at: #defaultReplyTo + ifAbsent: ['squeak-dev@lists.squeakfountation.org']!
Item was added: + ----- Method: MCDiffyTextWriter>>writeVersion: (in category 'writing') ----- + writeVersion: aVersion + self writeVersionInfo: aVersion info. + self writePatchHeader: aVersion baseInfo. + self writePatch: aVersion patch.!
Item was added: + ----- Method: TextDiffBuilder>>buildTextPatch (in category '*SqueakSource-Notifications') ----- + buildTextPatch + ^String streamContents:[:stream| + self printTextPatchSequence: self buildPatchSequence on: stream. + ]!
Item was added: + ----- Method: SSProject>>emailRecipients: (in category 'accessing') ----- + emailRecipients: anObject + "Set the value of emailAddresses" + + emailRecipients _ anObject!
Item was added: + ----- Method: MCDiffyTextWriter>>writeAddition: (in category 'writing') ----- + writeAddition: anAddition + stream nextPutAll: 'Item was added:'; cr. + self writePatchFrom: nil to: anAddition definition!
Item was added: + ----- Method: SSProject>>subscriptions (in category 'accessing-subscriptions') ----- + subscriptions + "Answers the instances of SSSubscription who are interested in changes to this project" + ^ subscriptions ifNil: [self repository defaultSubscriptions]!
Item was added: + ----- Method: MCTextWriter>>writeDefinitions: (in category 'writing') ----- + writeDefinitions: aCollection + (MCDependencySorter sortItems: aCollection) + do: [:ea | ea accept: self. stream cr] + displayingProgress: 'Writing definitions...'.!
----- End forwarded message -----
this is cool. Stef
On 22 août 07, at 01:34, Matthew Fulmer wrote:
I added a mechanism SqueakSource to send email notifications whenever a new version is uploaded.
http://www.squeaksource.com/ss/SqueakSource-mtf.1025.mcz
Features:
- Configurable recipient and reply-to address, both per project and global default
- Can be enabled or disabled per-project, or for global default
- Three types of commit emails, only configurable globally:
- Summary only: Sends URL and version summary information. Requires no server-side mcz decoding, so is instantaneous. Importing SqueakSource-mtf.1025.mcz to localhost took 2 seconds under this setting.
- Summary + full snapshot: additionally puts a text representation of the version in the body of the email. With my experiment, this setting took the longest, since a full snapshot is pretty big. Importing SqueakSource-mtf.1025.mcz to localhost took 9 seconds under this setting.
- Summary + patch: finds the latest ancestor in the project and puts a patch in the body of the email. A sample patch email is forwarded at the end of this email. I was surprised to find this was faster than the above method; it does 2-3 times more parsing/processing as above, but writes much less data. Importing SqueakSource-mtf.1025.mcz to localhost took 5 seconds under this setting.
- Two new textual representations for generic Monticello packages: MCTextWriter, and MCDiffyTextWriter. They are write-only formats intended for human, rather than computer, consumption. Currently it is grafted on top of the chunk format writer, but I can do better. Next version will feature a much better textual format. These two writer classes have no dependencies on SqueakSource, and could be added directly to Monticello.
Installation Instructions:
These instructions are for merging these features into an existing SqueakSource installation. For new installations, see http://wiki.squeak.org/squeak/5766 (slightly out of date; I will update it soon)
I tested these instructions on a 3.8.1 image running SqueakSource-al.1024:
- Merge in SqueakSource-mtf.1025. It is nearly all new classes, and has no core changes, so it probably won't have conflicts.
- log in as superuser
- Go to global edit settings
- Set the 5 options:
- SMTP Server
- Generated email: sender address
- Default email recipients: (blank by default)
- Default Reply-To Address: defaults to squeak-dev list
- Show in commit notifications: (choose detail level)
- Default subscriptions: (check the checkbox if you want to enable commit emails)
This sets the defaults for all projects. If any per-project settings are non-nil, they will ignore the defaults. So, you must do this right after installation and before any existing project settings have been changed.
Here is a generated commit email (Summary + Patch) for SqueakSource-mtf.1025:
----- Forwarded message from no-reply@localhost -----
To: tapplek@gmail.com From: no-reply@localhost Date: Tue, 21 Aug 2007 16:05:04 -0700 (MST) Subject: SqueakSource: SqueakSource-mtf.1025.mcz
A new version of SqueakSource was added to project SqueakSource: http://localhost:8888/ss/SqueakSource-mtf.1025.mcz
==================== Summary ====================
Name: SqueakSource-mtf.1025 Author: mtf Time: 21 August 2007, 4:03:16 pm UUID: e7fe67cd-2502-7f4e-81ce-7681f86bf1d8 Ancestors: SqueakSource-al.1024
- add model for sending commit emails to SSProject
- add model for default values of above to SSRepository
- add two classes for writing text representations of generic
Monticello packages: MCTextWriter and MCDiffyTextWriter
- add three classes for writing text representations SqueakSource
versions and packages: SSBasicTextWriter, SSTextWriter, and SSDiffyTextWriter
- add email sending capabilities via SSEmailSubscription
=============== Diff against SqueakSource-al.1024 ===============
Item was added:
- ----- Method: SSProject>>subscriptionsDo: (in category 'accessing-
subscriptions') -----
- subscriptionsDo: aBlock
- ^ self subscriptions do: aBlock!
Item was added:
- ----- Method: SSSubscription>>versionAdded:to: (in category 'as
yet unclassified') -----
- versionAdded: aVersion to: aProject
- ^ self!
Item was added:
- SSBasicTextWriter subclass: #SSTextWriter
- instanceVariableNames: ''
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
- !SSTextWriter commentStamp: '<historical>' prior: 0!
- In addition to writing summaries, my instances also dump a full
snapshot of the uploaded version.
- For speed notes of this class, see the class comment of
SSDiffyTextWriter!
Item was added:
- ----- Method: MCTextWriter>>writeVersion: (in category 'writing')
- writeVersion: aVersion
- self writeVersionInfo: aVersion info.
- self writeSnapshot: aVersion snapshot.
- !
Item was added:
- ----- Method: SSEMailSubscription>>writeHeaders (in category 'as
yet unclassified') -----
- writeHeaders
- sender ifNotEmpty: [ stream nextPutAll: 'From: '; nextPutAll:
sender].
- project emailRecipients ifNotEmpty: [stream cr; nextPutAll: 'To:
'].
- project emailRecipients
do: [:email | stream nextPutAll: email address]
separatedBy: [stream nextPutAll: ', '].
- project replyTo ifNotEmpty: [stream
cr; nextPutAll: 'Reply-To: '; nextPutAll: project replyTo].
- stream cr;
nextPutAll: 'Subject: ';
nextPutAll: project title; nextPutAll: ': ';
nextPutAll: version fileName; cr; cr!
Item was added:
- ----- Method: SSDiffyTextWriter>>writePatchHeader: (in category
'as yet unclassified') -----
- writePatchHeader: info
- self textWriter writePatchHeader: info!
Item was added:
- ----- Method: SSRepository>>defaultEmailRecipients (in category
'accessing-settings') -----
- defaultEmailRecipients
- ^self properties
at: #defaultEmailRecipients
ifAbsent: [OrderedCollection new]!
Item was changed: ----- Method: SSProject>>addVersion:author: (in category 'accessing-versions') ----- addVersion: aString author: aUser | array version | array := aString asByteArray. version := SSVersion array: array author: aUser. SSRepository storage saveMonticello: array of: version to: self.
- versions at: version fileName put: version.
- self versionAdded: version.
- ^ version
- !
- ^versions at: version fileName put: version.
!
Item was added:
- ----- Method: SSRepository>>defaultEmailRecipients: (in category
'accessing-settings') -----
- defaultEmailRecipients: aCollection
- ^self properties
at: #defaultEmailRecipients
put: aCollection!
Item was added:
- SSModel subclass: #SSEmailAddress
- instanceVariableNames: 'address'
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Model'!
Item was added:
- ----- Method: SSRepository>>commitWriterClass (in category
'accessing-settings') -----
- commitWriterClass
- ^self properties
at: #commitWriterClass
ifAbsent: [SSBasicTextWriter]!
Item was added:
- ----- Method: SSEMailSubscription>>writerClass (in category 'as
yet unclassified') -----
- writerClass
- ^ SSRepository current commitWriterClass!
Item was added:
- ----- Method: SSRepository>>emailSender: (in category 'accessing-
settings') -----
- emailSender: aString
- ^self properties
at: #emailSender
put: aString!
Item was added:
- ----- Method: SSProject>>replyTo (in category 'accessing') -----
- replyTo
- "Answer the value of replyTo"
- ^ replyTo ifNil: [SSRepository current defaultReplyTo]!
Item was added:
- ----- Method: SSTextWriter>>writeSnapshot: (in category 'as yet
unclassified') -----
- writeSnapshot: snapshot
- self textWriter writeSnapshot: snapshot!
Item was added:
- ----- Method: SSEmailAddress>>address: (in category 'accessing')
- address: anObject
- "Set the value of address"
- address _ anObject!
Item was added:
- ----- Method: SSDiffyTextWriter class>>description (in category
'as yet unclassified') -----
- description
- ^ 'Summary + changes'!
Item was added:
- ----- Method: SSEMailSubscription class>>description (in category
'as yet unclassified') -----
- description
- ^ 'Recieve commit notifications by email'!
Item was added:
- MCStWriter subclass: #MCTextWriter
- instanceVariableNames: ''
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
- !MCTextWriter commentStamp: '<historical>' prior: 0!
- A basic writer for SqueakSource versions and projects. Does no
decoding of .mcz files!
Item was added:
- ----- Method: SSTextWriter class>>description (in category 'as
yet unclassified') -----
- description
- ^ 'Summary + full snapshot'!
Item was changed: ----- Method: SSProject>>metaobject (in category 'metamodel') ----- metaobject | metaobject | metaobject := MWMetaobject for: self. metaobject textAttribute: #id do: [ :attribute | attribute label: 'Name'; maxLength: 20; addRequiredRule; addValidationRule: [ :value | value allSatisfy: [ :char | char isLetter or: [ char isDigit ] ] ] errorString: 'name should only contain letters and digits'; addValidationRule: [ :value | SSRepository current isUniqueProjectId: value ] errorString: 'name is not unique' ]. metaobject textAttribute: #title do: [ :attribute | attribute label: 'Title'; maxLength: 50; addRequiredRule ]. metaobject textAttribute: #description do: [ :attribute | attribute label: 'Description'; multiLine: true ]. metaobject multipleRelationshipAttribute: #tags do: [ :attribute | attribute label: 'Tags'; relationshipTo: [ self repository tags ] formatWith: [ :each | each name ]; "nilItemString: 'none yet';" yourself ]. metaobject singleRelationshipAttribute: #license do: [ :attribute | attribute label: 'License'; relationshipTo: self licenses formatWith: [ :each | each isNil ifTrue: [ 'None' ] ifFalse: [ each first ifNil: [ 'None' ] ] ] ]. metaobject booleanAttribute: #canBless do: [:attribute | attribute label: 'Enable Blessings'. ].
metaobject singleRelationshipAttribute: #globalRight do: [ :attribute | attribute label: 'Global'; relationshipTo: SSAccessPolicy globalRights formatWith: [ :symbol | symbol asCapitalizedPhrase ] ]. metaobject multipleRelationshipAttribute: #admins do: [ :attribute | attribute label: 'Administrators'; addValidationRule: [ :admins | | currentUser | currentUser := SSSession currentSession user. currentUser isSuperUser or: [ admins anySatisfy: [ :each | each isGroup ifTrue: [ each hasMember: currentUser ] ifFalse: [ each = currentUser ] ] ] ] errorString: 'You can''t remove yourself from the list of administrators'; hide ]. metaobject multipleRelationshipAttribute: #developers do: [ :attribute | attribute label: 'Developers'; hide ]. metaobject multipleRelationshipAttribute: #guests do: [ :attribute | attribute label: 'Guests'; hide ].
- metaobject multipleAttribute: #emailRecipients do: [ :attribute |
attribute
label: 'Send emails to';
baseClass: SSEmailAddress].
- metaobject textAttribute: #replyTo do: [ :attribute |
attribute
label: 'Reply-To Address';
maxLength: 50].
- metaobject multipleRelationshipAttribute: #subscriptions do:
[ :attribute |
attribute
label: 'Subscriptions';
relationshipTo: [SSSubscription allSubclasses]].
- ^metaobject!
Item was added:
- ----- Method: MCTextWriter>>writeVersionInfo: (in category
'writing') -----
- writeVersionInfo: aVersionInfo
- stream
nextPutAll: '==================== Summary
===================='; cr; cr;
nextPutAll: aVersionInfo summary; cr; cr!
Item was added:
- ----- Method: SSRepository class>>defaultEmailSender (in category
'private') -----
- defaultEmailSender
- ^'no-reply@', self hostName!
Item was changed: SSModel subclass: #SSProject
- instanceVariableNames: 'id title description creator
accessPolicy versions dateCreated feeds wiki blessings configs tags cache license emailAddresses replyTo subscriptions emailRecipients'
- instanceVariableNames: 'id title description creator
accessPolicy versions dateCreated feeds wiki blessings configs tags cache license' classVariableNames: '' poolDictionaries: '' category: 'SqueakSource-Model'!
Item was added:
- ----- Method: SSEMailSubscription>>versionAdded:to: (in category
'as yet unclassified') -----
- versionAdded: aVersion to: aProject
- sender := SSRepository current emailSender.
- stream := String new writeStream.
- project := aProject.
- version := aVersion.
- self writeHeaders.
- (self writerClass on: stream) writeVersion: aVersion for: aProject.
- self sendMail.!
Item was added:
- ----- Method: SSEmailAddress>>metaobject (in category
'accessing') -----
- metaobject
- | metaobject |
- metaobject _ MWMetaobject for: self.
- metaobject textAttribute: #address do: [ :attribute |
attribute
maxLength: 50;
addRequiredRule].
- ^metaobject!
Item was added:
- ----- Method: MCTextWriter>>writeMethodPreamble: (in category
'writing') -----
- writeMethodPreamble: definition
- stream
nextPutAll: '----- Method: ';
nextPutAll: definition fullClassName;
nextPutAll: '>>';
nextPutAll: definition selector;
nextPutAll: ' (in category ';
nextPutAll: definition category asString printString;
nextPutAll: ') -----'; cr!
Item was added:
- ----- Method: SSEmailAddress>>address (in category 'accessing')
- address
- "Answer the value of address"
- ^ address!
Item was added:
- SSTextWriter subclass: #SSDiffyTextWriter
- instanceVariableNames: ''
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
- !SSDiffyTextWriter commentStamp: '<historical>' prior: 0!
- Rather than a full snapshot dump, my instances write a patch from
the latest ancestor stored in the project. If no ancestors are in the project, they fall back onto doing a full snapshot.
- Even though this class does much more decoding of .mcz files than
SSTextWriter, it is usually a lot faster because a diff is much less information to write than a full snapshot. As a rough metric, a diff of SqueakSource-mtf.1025 (the package where this class was introduced) took 7 seconds to write (on my computer), while a full snapshot took 33 seconds to write. On the other hand, a version summary was instantaneous, as it requires no decoding of .mcz files!
Item was added:
- ----- Method: SSRepository>>defaultSubscriptions: (in category
'accessing-settings') -----
- defaultSubscriptions: aCollection
- ^self properties
at: #defaultSubscriptions
put: aCollection!
Item was added:
- ----- Method: MCDiffyTextWriter>>writePatch: (in category
'writing') -----
- writePatch: aPatch
- aPatch operations do:
[:ea |
ea isRemoval ifTrue: [self writeRemoval: ea].
ea isAddition ifTrue: [self writeAddition: ea].
ea isModification ifTrue: [self writeModification: ea].
stream cr.].!
Item was added:
- MCWriter subclass: #SSBasicTextWriter
- instanceVariableNames: 'textWriter'
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
- !SSBasicTextWriter commentStamp: '<historical>' prior: 0!
- My instances write version summaries for SqueakSource versions
and projects. Does no decoding of .mcz files. Subclasses extend the functionality.
- For speed notes about this class, see the class comment of
SSDiffyTextWriter!
Item was added:
- ----- Method: SSDiffyTextWriter>>textWriterClass (in category 'as
yet unclassified') -----
- textWriterClass
- ^ MCDiffyTextWriter!
Item was added:
- ----- Method: SSEMailSubscription>>writeSummary (in category 'as
yet unclassified') -----
- writeSummary
- | author |
- author := version authorString ifEmpty: [version guessedAuthor].
- author
ifNil: [ stream
nextPutAll: 'A new version of ';
nextPutAll: version package;
nextPutAll: ' was added to project ';
nextPutAll: project title; nextPut: $:; cr]
ifNotNil: [ stream
nextPutAll: author;
nextPutAll: ' uploaded a new version of ';
nextPutAll: version package;
nextPutAll: ' to project ';
nextPutAll: project title; nextPut: $:; cr].
- stream nextPutAll: (version url: project); cr; cr.!
Item was added:
- ----- Method: MCTextWriter>>chunkContents: (in category
'writing') -----
- chunkContents: aBlock
- stream nextChunkPut: (String streamContents: aBlock); cr!
Item was added:
- ----- Method: SSDiffyTextWriter>>writePatch: (in category 'as yet
unclassified') -----
- writePatch: patch
- self textWriter writePatch: patch!
Item was added:
- ----- Method: TextDiffBuilder>>stringForAttributes: (in category
'*SqueakSource-Notifications') -----
- stringForAttributes: type
- "Private.
- Answer the String that prefixes text of the given type."
- ^type caseOf: {
[#insert] -> [ '+ ' ].
[#remove] -> [ '- '].
- } otherwise: [ ' ' ].
- !
Item was added:
- ----- Method: SSDiffyTextWriter>>writeVersion:for: (in category
'as yet unclassified') -----
- writeVersion: aSSVersion for: aProject
- | reader ancestor patch |
- reader := aSSVersion reader: aProject.
- ancestor := (aSSVersion versionInfo latestAncestorIn: aProject)
ifNil: [reader info latestAncestorIn: aProject].
- self writeSummary: aSSVersion for: aProject.
- self writeVersionInfo: aSSVersion versionInfo.
- ancestor
ifNil: [self writeSnapshot: reader snapshot.]
ifNotNil: [
patch := reader snapshot patchRelativeToBase:
(ancestor reader: aProject) snapshot.
self writePatchHeader: ancestor versionInfo.
self writePatch: patch.]!
Item was added:
- ----- Method: SSEMailSubscription>>sendMail (in category 'as yet
unclassified') -----
- sendMail
- project emailRecipients ifEmpty: [^ self].
- SeasidePlatformSupport
deliverMailFrom: sender
to: (project emailRecipients collect: [:email | email address])
text: stream contents.
- "Workspace new contents: stream contents; openLabel: sender."!
Item was added:
- ----- Method: SSTextWriter>>writeVersion:for: (in category 'as
yet unclassified') -----
- writeVersion: aSSVersion for: aProject
- | reader |
- reader := aSSVersion reader: aProject.
- self writeSummary: aSSVersion for: aProject.
- self writeVersionInfo: aSSVersion versionInfo.
- self writeSnapshot: reader snapshot.!
Item was added:
- ----- Method: MCAncestry>>latestAncestorIn: (in category
'*SqueakSource') -----
- latestAncestorIn: aProject
- | ancestor |
- self ancestors ifEmpty: [^ nil].
- self allAncestors do: [:anAncestry |
ancestor _ aProject versionAt: anAncestry name,'.mcz'.
ancestor ifNotNil: [^ ancestor]].
- ^ nil!
Item was added:
- ----- Method: SSEMailSubscription>>writeDiffAgainst: (in category
'as yet unclassified') -----
- writeDiffAgainst: ancestor
- stream nextPutAll: 'Diff against ';
nextPutAll: ancestor fileName; cr.
- self writeVersion
!
Item was added:
- MCTestCase subclass: #MCTextWriterTest
- instanceVariableNames: 'version'
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
Item was added:
- ----- Method: SSProject>>subscriptions: (in category 'accessing-
subscriptions') -----
- subscriptions: aCollection
- "Private. Set the subscriptions"
- subscriptions := aCollection!
Item was added:
- ----- Method: SSProject>>emailRecipients (in category
'accessing') -----
- emailRecipients
- "Answer the value of emailAddresses"
- ^ emailRecipients ifNil: [SSRepository current
defaultEmailRecipients]!
Item was added:
- ----- Method: SSRepository>>defaultSubscriptions (in category
'accessing-settings') -----
- defaultSubscriptions
- ^self properties
at: #defaultSubscriptions
ifAbsent: [OrderedCollection new]!
Item was added:
- ----- Method: SSProject>>replyTo: (in category 'accessing') -----
- replyTo: anObject
- "Set the value of replyTo"
- replyTo _ anObject!
Item was added:
- ----- Method: SSBasicTextWriter>>textWriter (in category 'as yet
unclassified') -----
- textWriter
- ^ textWriter ifNil: [textWriter := self textWriterClass on:
stream]!
Item was added:
- ----- Method: MCDiffyTextWriter>>writeModification: (in category
'writing') -----
- writeModification: aModification
- stream nextPutAll: 'Item was changed:'; cr.
- self writePatchFrom: aModification obsoletion to: aModification
definition!
Item was added:
- SSModel subclass: #SSSubscription
- instanceVariableNames: 'stream project version'
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
- !SSSubscription commentStamp: '<historical>' prior: 0!
- My insances represent a notification that is sent externally (an
email message, for instance) in response to an event (like a commit)!
Item was changed: ----- Method: SSRepository class>>defaultRootUrl (in category 'private') ----- defaultRootUrl
- ^'http://', self hostName, ':', self defaultPort asString, '/'!
- | hostName |
- hostName := NetNameResolver nameForAddress: (NetNameResolver
localHostAddress) timeout: 5.
- hostName ifNil: [hostName := NetNameResolver localAddressString].
- ^'http://', hostName, ':', self defaultPort asString, '/'!
Item was added:
- ----- Method: SSRepository>>commitWriterClass: (in category
'accessing-settings') -----
- commitWriterClass: aClass
- ^self properties
at: #commitWriterClass
put: aClass!
Item was added:
- ----- Method: MCDiffyTextWriter>>writePatchFrom:to: (in category
'writing') -----
- writePatchFrom: src to: dst
- "src and dst are allowed to bi nil to represent a non-existent
source or destination state"
- stream nextPutAll: (TextDiffBuilder
from: (src ifNotNil: [self visitInFork: src] ifNil: [''])
to: (dst ifNotNil: [self visitInFork: dst] ifNil: [''])
- ) buildTextPatch!
Item was changed: SystemOrganization addCategory: #'SqueakSource-Model'! SystemOrganization addCategory: #'SqueakSource-View'! SystemOrganization addCategory: #'SqueakSource-Server'! SystemOrganization addCategory: #'SqueakSource-Tests'!
- SystemOrganization addCategory: #'SqueakSource-Notification'!
Item was added:
- ----- Method: SSBasicTextWriter>>writeVersionInfo: (in category
'as yet unclassified') -----
- writeVersionInfo: info
- self textWriter writeVersionInfo: info!
Item was added:
- ----- Method: MCDiffyTextWriter>>writeRemoval: (in category
'writing') -----
- writeRemoval: aRemoval
- stream nextPutAll: 'Item was removed:'; cr.
- self writePatchFrom: aRemoval definition to: nil!
Item was added:
- ----- Method: MCTextWriter>>visitMethodDefinition: (in category
'visiting') -----
- visitMethodDefinition: definition
- self writeMethodPreamble: definition.
- self writeMethodSource: definition.
- stream cr!
Item was changed: ----- Method: SSProject>>initialize (in category 'initialization') ----- initialize super initialize. id := title := description := String new. accessPolicy := SSAccessPolicy new. versions := Dictionary new. dateCreated := Date today. tags := SortedCollection new.
- cache := IdentityDictionary new.!
- cache := IdentityDictionary new!
Item was added:
- ----- Method: SSRepository class>>hostName (in category
'private') -----
- hostName
- ^ (NetNameResolver nameForAddress: (NetNameResolver
localHostAddress) timeout: 5)
ifNil: [NetNameResolver localAddressString]!
Item was added:
- MCTextWriter subclass: #MCDiffyTextWriter
- instanceVariableNames: ''
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
Item was added:
- ----- Method: SSBasicTextWriter class>>description (in category
'as yet unclassified') -----
- description
- ^ 'Summary only'!
Item was added:
- ----- Method: SSProject>>versionAdded: (in category 'accessing-
subscriptions') -----
- versionAdded: aVersion
- ^ self subscriptionsDo: [:each | each new versionAdded: aVersion
to: self]!
Item was added:
- ----- Method: MCDiffyTextWriter>>writePatchHeader: (in category
'writing') -----
- writePatchHeader: info
- stream
nextPutAll: '=============== Diff against ';
nextPutAll: info name;
nextPutAll: ' ==============='; cr; cr!
Item was added:
- ----- Method: SSEMailSubscription>>writeVersion (in category 'as
yet unclassified') -----
- writeVersion
- | ancestor aVersion|
- ancestor := version lastAncestorIn: project.
- aVersion := (version reader: project) version.
- ancestor
ifNotNil: [(MCDiffyTextWriter on: stream) writeVersion:
(aVersion asDiffAgainst: (ancestor reader: project) version)]
ifNil: [(MCTextWriter on: stream) writeVersion: aVersion]
!
Item was added:
- ----- Method: SSSubscription class>>renderOn: (in category 'as
yet unclassified') -----
- renderOn: html
- html text: self description!
Item was added:
- SSSubscription subclass: #SSEMailSubscription
- instanceVariableNames: 'sender'
- classVariableNames: ''
- poolDictionaries: ''
- category: 'SqueakSource-Notification'!
Item was added:
- ----- Method: TextDiffBuilder>>printTextPatchSequence:on: (in
category '*SqueakSource-Notifications') -----
- printTextPatchSequence: seq on: aStream
- seq do: [:assoc | aStream
nextPutAll: (self stringForAttributes: assoc key);
nextPutAll: assoc value; cr]!
Item was changed:
----- Method: SSRepository>>metaobject (in category 'accessing')
metaobject | metaobject | metaobject _ MWMetaobject for: self.
metaobject textAttribute: #rootUrl do: [ :attribute | attribute label: 'Root URL'; addRequiredRule]. metaobject integerAttribute: #port do: [ :attribute | attribute label: 'Port'; addValidationRule: [ :value | value notNil ] errorString: 'invalid number'; addValidationRule: [ :value | value between: 1 and: 65535 ] errorString: 'invalid port'; addRequiredRule]. metaobject textAttribute: #smtpServer do: [ :attribute | attribute label: 'SMTP server'; addRequiredRule]. metaobject textAttribute: #superUserEmail do: [ :attribute | attribute label: 'Admin email'; addRequiredRule].
- metaobject textAttribute: #emailSender do: [ :attribute |
attribute label: 'Generated email';
addRequiredRule].
- metaobject multipleAttribute: #defaultEmailRecipients do:
[ :attribute |
attribute label: 'Default email recipients'; baseClass:
SSEmailAddress].
- metaobject textAttribute: #defaultReplyTo do: [ :attribute |
attribute label: 'Default Reply-To Address'; maxLength: 50].
- metaobject singleRelationshipAttribute: #commitWriterClass do:
[ :attribute |
attribute label: 'Show in commit notifications';
relationshipTo: [SSBasicTextWriter withAllSubclasses];
formatWith: [ :class | class description]].
- metaobject multipleRelationshipAttribute: #defaultSubscriptions
do: [ :attribute |
attribute label: 'Default subscriptions'; relationshipTo:
[SSSubscription allSubclasses]]. metaobject booleanAttribute: #allowRegisterProject do: [ :attribute | attribute label: 'Everyone can register projects']. metaobject booleanAttribute: #allProjectsVisible do: [ :attribute | attribute label: 'Everyone can see all projects' ]. metaobject booleanAttribute: #allowCreateTag do: [ :attribute | attribute label: 'Everyone can create tags']. metaobject integerAttribute: #batchSize do: [ :attribute | attribute label: 'Batch size for table reports'; addValidationRule: [ :value | value notNil ] errorString: 'invalid number'; addValidationRule: [ :value | value > 1 ] errorString: 'too small'; addRequiredRule]. metaobject colorAttribute: #styleColor do: [ :attribute | attribute label: 'Style color'; addValidationRule: [ :value | value ~= Color white ] errorString: 'invalid color']. metaobject textAttribute: #introText do: [ :attribute | attribute label: 'Override home text'; multiLine: true]. metaobject textAttribute: #timezone do: [ :attribute | attribute label: 'Timezone'; addRequiredRule ]. metaobject textAttribute: #googleAnalyticsAccount do: [ :attribute | attribute label: 'Google Analytics Account' ]. ^metaobject!
Item was added:
- ----- Method: SSBasicTextWriter>>textWriterClass (in category 'as
yet unclassified') -----
- textWriterClass
- ^ MCTextWriter!
Item was changed: ----- Method: SSProjectEditor>>renderMainOn: (in category 'rendering') ----- renderMainOn: html
- self isSubForm ifTrue: [^ super renderMainOn: html]. self renderIntroductionTextOn: html. html layoutTable: [ self renderValidationErrorsOn: html. self renderFieldsOn: html. self renderMemberListForAttribute: (self metaobject
attributeOf: #admins) on: html. self renderMemberListForAttribute: (self metaobject attributeOf: #developers) on: html. self renderMemberListForAttribute: (self metaobject attributeOf: #guests) on: html. html tableRowWith: [ html space ] span: 2. html tableRowWith: [ self renderButtonsOn: html ] span: 2 ]!
Item was added:
- ----- Method: SSRepository>>defaultReplyTo: (in category
'accessing-settings') -----
- defaultReplyTo: aString
- ^self properties
at: #defaultReplyTo
put: aString!
Item was added:
- ----- Method: SSBasicTextWriter>>writeVersion:for: (in category
'as yet unclassified') -----
- writeVersion: aSSVersion for: aProject
- self writeSummary: aSSVersion for: aProject.
- self writeVersionInfo: aSSVersion versionInfo.!
Item was added:
- ----- Method: MCTextWriterTest>>testWriteSnapshot (in category
'as yet unclassified') -----
- testWriteSnapshot
- Workspace new contents: (String streamContents: [:aStream |
(MCTextWriter on: aStream) writeSnapshot: self mockSnapshot]); openLabel: 'pizza'.
- !
Item was added:
- ----- Method: SSRepository>>emailSender (in category 'accessing-
settings') -----
- emailSender
- ^self properties
at: #emailSender
ifAbsent: [self class defaultEmailSender]!
Item was added:
- ----- Method: MCTextWriter>>visitInFork: (in category 'visiting')
- visitInFork: aDefinition
- ^ String streamContents: [ :forkedStream |
aDefinition accept: (self class on: forkedStream)]!
Item was added:
- ----- Method: MCTextWriter>>writeSnapshot: (in category
'writing') -----
- writeSnapshot: aSnapshot
- stream nextPutAll: '==================== Snapshot
===================='; cr; cr.
- super writeSnapshot: aSnapshot
- !
Item was added:
- ----- Method: SSBasicTextWriter>>writeSummary:for: (in category
'as yet unclassified') -----
- writeSummary: aSSVersion for: aProject
- | author |
- author := aSSVersion authorString ifEmpty: [aSSVersion
guessedAuthor].
- author
ifNil: [ stream
nextPutAll: 'A new version of ';
nextPutAll: aSSVersion package;
nextPutAll: ' was added to project ';
nextPutAll: aProject title; nextPut: $:; cr]
ifNotNil: [ stream
nextPutAll: author;
nextPutAll: ' uploaded a new version of ';
nextPutAll: aSSVersion package;
nextPutAll: ' to project ';
nextPutAll: aProject title; nextPut: $:; cr].
- stream nextPutAll: (aSSVersion url: aProject); cr; cr.!
Item was added:
- ----- Method: SSRepository>>defaultReplyTo (in category
'accessing-settings') -----
- defaultReplyTo
- ^self properties
at: #defaultReplyTo
ifAbsent: ['squeak-dev@lists.squeakfountation.org']!
Item was added:
- ----- Method: MCDiffyTextWriter>>writeVersion: (in category
'writing') -----
- writeVersion: aVersion
- self writeVersionInfo: aVersion info.
- self writePatchHeader: aVersion baseInfo.
- self writePatch: aVersion patch.!
Item was added:
- ----- Method: TextDiffBuilder>>buildTextPatch (in category
'*SqueakSource-Notifications') -----
- buildTextPatch
- ^String streamContents:[:stream|
self printTextPatchSequence: self buildPatchSequence on: stream.
- ]!
Item was added:
- ----- Method: SSProject>>emailRecipients: (in category
'accessing') -----
- emailRecipients: anObject
- "Set the value of emailAddresses"
- emailRecipients _ anObject!
Item was added:
- ----- Method: MCDiffyTextWriter>>writeAddition: (in category
'writing') -----
- writeAddition: anAddition
- stream nextPutAll: 'Item was added:'; cr.
- self writePatchFrom: nil to: anAddition definition!
Item was added:
- ----- Method: SSProject>>subscriptions (in category 'accessing-
subscriptions') -----
- subscriptions
- "Answers the instances of SSSubscription who are interested in
changes to this project"
- ^ subscriptions ifNil: [self repository defaultSubscriptions]!
Item was added:
- ----- Method: MCTextWriter>>writeDefinitions: (in category
'writing') -----
- writeDefinitions: aCollection
- (MCDependencySorter sortItems: aCollection)
do: [:ea | ea accept: self. stream cr]
displayingProgress: 'Writing definitions...'.!
----- End forwarded message -----
-- Matthew Fulmer -- http://mtfulmer.wordpress.com/ Help improve Squeak Documentation: http://wiki.squeak.org/squeak/808
squeak-dev@lists.squeakfoundation.org