message: migrating interactions

diff:
@@ -7,12 +7,15 @@ Class {
#instVars : [
'message'
],
- #category : 'PharoLauncher-Core-Commands'
+ #category : #'PharoLauncher-Core-Commands'
}
{ #category : #action }
PhLUIConfirmation >> confirm [
- ^ UIManager default confirm: message
+
+ ^ self application newConfirm
+ label: message;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #accessing }

diff:
@@ -7,32 +7,51 @@ Class {
#instVars : [
'message'
],
- #category : 'PharoLauncher-Core-Commands'
+ #category : #'PharoLauncher-Core-Commands'
}
{ #category : #action }
PhLUIInformation >> abort [
- UIManager default abort: message
+
+ self application newInform
+ title: 'Error';
+ label: message;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #action }
PhLUIInformation >> alert [
- UIManager default alert: message
+
+ self application newInform
+ title: 'Alert';
+ label: message;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #action }
PhLUIInformation >> deny [
- UIManager default deny: message
+
+ self application newInform
+ title: 'Access Denied';
+ label: message;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #action }
PhLUIInformation >> denyWithTitle: titleString [
- UIManager default deny: message title: titleString
+
+ self application newInform
+ title: titleString;
+ label: message;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #action }
PhLUIInformation >> inform [
- UIManager inform: message
+
+ self application newInform
+ label: message;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #accessing }

diff:
@@ -14,23 +14,33 @@ Class {
{ #category : #action }
PhLUIRequest >> chooseFrom: aListOfChoices [
- ^ UIManager default
- chooseFrom: aListOfChoices
- lines: #()
- title: title
+
+ ^ self application newSelect
+ title: title;
+ items: aListOfChoices;
+ openModalWithSpecParent: self application topWindow
]
{ #category : #accessing }
PhLUIRequest >> initialAnswer: aString [
+
initialAnswer := aString
]
{ #category : #action }
PhLUIRequest >> request [
| answer |
- [ answer := UIManager default request: title initialAnswer: initialAnswer ]
- doWhileTrue: [ answer isNotNil and: [ validationBlock isNotNil and: [ (validationBlock value: answer) not ] ] ].
- "answer is nil when the user press cancels. That's a possible answer we have to accept."
+
+ [
+ answer := self application newRequest
+ title: title;
+ text: initialAnswer;
+ openModalWithSpecParent: self application topWindow.
+ ] doWhileTrue: [
+ answer isNotNil
+ and: [ validationBlock isNotNil
+ and: [ (validationBlock value: answer) not ] ] ].
+
^ answer
]

diff:
Class {
#name : #PhLUserInteraction,
#superclass : #Object,
- #category : 'PharoLauncher-Core-Commands'
+ #category : #'PharoLauncher-Core-Commands'
}
+
+{ #category : #accessing }
+PhLUserInteraction >> application [
+
+ ^ PharoLauncherApplication default
+]

diff:
@@ -11,7 +11,7 @@ PhLGtkConfiguration >> configureGeneral: anApplication [
toolbar.compact {
font-size: 0.6em }
toolbar.compact toolbutton > button {
- padding: 2px;
- margin: 2px }
+ padding: 1px;
+ margin: 1px }
'
]

diff:
@@ -27,10 +27,13 @@ Class {
{ #category : #specs }
PhLImageCreationPresenter class >> defaultSpec [
+
^ SpBoxLayout newVertical
+ borderWidth: 5;
spacing: 5;
add: 'Image name:' expand: false;
add: (SpBoxLayout newHorizontal
+ spacing: 5;
add: #imageName;
add: #imageNameCheck expand: false;
yourself)
@@ -39,12 +42,13 @@ PhLImageCreationPresenter class >> defaultSpec [
add: #descriptionText height: 60;
add: 'Initialization script:' expand: false;
add: (SpBoxLayout newHorizontal
+ spacing: 5;
add: #initScript;
add: #editInitScriptButton expand: false;
yourself)
expand: false;
add: #initScriptText height: 100;
- add: #createButton expand: false;
+ addLast: #createButton expand: false;
yourself
]
@@ -125,6 +129,7 @@ PhLImageCreationPresenter >> initialize [
{ #category : #initialization }
PhLImageCreationPresenter >> initializePresenters [
+
imageName := self newTextInput.
imageNameCheck := self newImage.
initScriptText := self newText
@@ -150,13 +155,16 @@ PhLImageCreationPresenter >> initializePresenters [
[ createImageCommand execute.
self close. ]
on: PhLNameNotAvailableError
- do: [ PhLUIInformation new
+ do: [
+ self newInformation
message: 'An image with the same name already exists! Please choose a new one.';
alert ] ] ]
]
{ #category : #initialization }
PhLImageCreationPresenter >> modelChanged [
+'OK' crTrace.
+self template crTrace.
self template ifNil: [ ^ self ].
self template isTemplate
ifTrue: [ self enable.

diff:
@@ -17,7 +17,8 @@ Class {
'location',
'vm',
'templateUrl',
- 'shouldRunInitializationScript'
+ 'shouldRunInitializationScript',
+ 'updating'
],
#category : #'PharoLauncher-Spec2'
}
@@ -25,23 +26,7 @@ Class {
{ #category : #specs }
PhLImageDetailPresenter class >> defaultSpec [
- ^ SpBoxLayout newTopToBottom
- add: #imageLabel expand: false;
- add: (SpGridLayout new
- beRowNotHomogeneous;
- beColumnNotHomogeneous;
- column: 1 withConstraints: [ :c | c beNotExpand; vAlignStart ];
- column: 2 withConstraints: [ :c | c beExpand ];
- add: 'Description' at: 1@1; add: #description at: 2@1;
- add: 'Origin template' at: 1@2; add: #template at: 2@2;
- add: #templateUrl at: 2@3;
- add: 'Initialization script' at: 1@4; add: (SpBoxLayout newLeftToRight
- add: #initializationScript expand: false;
- add: #shouldRunInitializationScript expand: false;
- yourself) at: 2@4;
- add: 'Location' at: 1@5; add: #location at: 2@5;
- yourself);
- yourself
+ ^ nil
]
{ #category : #specs }
@@ -59,10 +44,9 @@ PhLImageDetailPresenter class >> labelWidth [
{ #category : #initialization }
PhLImageDetailPresenter >> connectPresenters [
- description
- whenTextChangedDo: [ :text | PhLImageDescriptionUpdater image: self image description: text ].
- shouldRunInitializationScript
- whenChangedDo: [ :state | self image shouldRunInitializationScript: state ].
+
+ description whenTextChangedDo: [ :text | self updateImageDescription: text ].
+ shouldRunInitializationScript whenChangedDo: [ :state | self updateImageRunScript: state ].
]
{ #category : #accessing }
@@ -80,8 +64,16 @@ PhLImageDetailPresenter >> imageLabel [
^ imageLabel
]
+{ #category : #initialization }
+PhLImageDetailPresenter >> initialize [
+
+ updating := false.
+ super initialize.
+]
+
{ #category : #initialization }
PhLImageDetailPresenter >> initializePresenters [
+
imageLabel := self newLabel.
description := self newText.
template := self newLabel.
@@ -92,6 +84,23 @@ PhLImageDetailPresenter >> initializePresenters [
disable.
location := self newLabel.
vm := self newTextInput.
+
+ self layout: (SpGridLayout new
+ beRowNotHomogeneous;
+ beColumnNotHomogeneous;
+ "column: 1 withConstraints: [ :c | c beNotExpand ];"
+ column: 2 withConstraints: [ :c | c beExpand ];
+ add: imageLabel at: 1@1 span: 2@1;
+ add: 'Description' at: 1@2; add: description at: 2@2;
+ add: 'Origin template' at: 1@3; add: template at: 2@3;
+ add: templateUrl at: 2@4;
+ add: 'Initialization script' at: 1@5; add: (SpBoxLayout newLeftToRight
+ add: initializationScript expand: false;
+ add: shouldRunInitializationScript expand: false;
+ yourself)
+ at: 2@5;
+ add: 'Location' at: 1@6; add: location at: 2@6;
+ yourself)
]
{ #category : #accessing }
@@ -102,14 +111,18 @@ PhLImageDetailPresenter >> location [
{ #category : #updating }
PhLImageDetailPresenter >> modelChanged [
| image |
+
+ updating := true.
+
image := self image ifNil: [ PhLNullImage new ].
- imageLabel label: image name asText allBold, ' image, last edited ', image lastModification asString.
+ imageLabel label: image name, ' image, last edited ', image lastModification asString.
description text: image description.
- template label: image originTemplateName.
+ template label: (image originTemplateName ifEmpty: [ '(noname)' ]).
templateUrl label: image originTemplateUrl.
image initializationScript
- ifNil: [ initializationScript label: '<none>'.
+ ifNil: [
+ initializationScript label: '<none>'.
shouldRunInitializationScript disable. ]
ifNotNil: [ :script |
initializationScript label: script basename.
@@ -117,7 +130,9 @@ PhLImageDetailPresenter >> modelChanged [
enable;
state: image shouldRunInitializationScript. ].
location label: image location.
- vm text: 'N/A'"self image vm".
+ vm text: 'N/A'. "self image vm."
+
+ updating := false
]
{ #category : #accessing }
@@ -130,6 +145,24 @@ PhLImageDetailPresenter >> templateUrl [
^ templateUrl
]
+{ #category : #initialization }
+PhLImageDetailPresenter >> updateImageDescription: text [
+
+ updating ifTrue: [ ^ self ].
+
+ PhLImageDescriptionUpdater
+ image: self image
+ description: text
+]
+
+{ #category : #initialization }
+PhLImageDetailPresenter >> updateImageRunScript: state [
+
+ updating ifTrue: [ ^ self ].
+
+ self image shouldRunInitializationScript: state
+]
+
{ #category : #accessing }
PhLImageDetailPresenter >> vm [
^ vm

diff:
@@ -106,7 +106,8 @@ PhLImagesPresenter >> actionFilter [
{ #category : #'api - actions' }
PhLImagesPresenter >> actionLaunch [
| cmd |
- cmd := self toolbarGroup commandOrGroupNamed: 'Launch'.
+
+ cmd := PhLLaunchImageCommand forContext: self.
cmd execute
]
@@ -131,6 +132,7 @@ PhLImagesPresenter >> buildImageFilter [
PhLImagesPresenter >> buildImageTable [
^ self newTable
+ activateOnDoubleClick;
items: self imageRepository images;
sortingBlock: #lastModification descending;
whenActivatedDo: [ self actionLaunch ];
@@ -240,7 +242,7 @@ PhLImagesPresenter >> initializePresenters [
add: toolbar expand: false;
add: imageFilter expand: false;
add: (SpPanedLayout newTopToBottom
- positionOfSlider: 60 percent;
+ positionOfSlider: 55 percent;
add: imageTable;
add: descriptionPanel;
yourself);
@@ -251,6 +253,7 @@ PhLImagesPresenter >> initializePresenters [
{ #category : #initialization }
PhLImagesPresenter >> initializeWindow: aWindowPresenter [
+
aWindowPresenter
title: 'Pharo Launcher';
initialExtent: 700 @ 550;

diff:
@@ -133,6 +133,7 @@ PhLLaunchConfigurationPresenter >> imageArguments [
{ #category : #initialization }
PhLLaunchConfigurationPresenter >> initializePresenters [
+
configurationTable := (self instantiate: SpEditableListPresenter)
addItemBlock: [ self newConfiguration ];
label: 'Launch configurations';
@@ -156,24 +157,27 @@ PhLLaunchConfigurationPresenter >> initializePresenters [
"Run in a separate process to do not block UI. Can take some seconds."
[ self initializeVmListContent ] fork.
vmArgumentList := (self instantiate: SpEditableListPresenter)
- addItemBlock: [ self application newRequest
+ addItemBlock: [
+ self newRequest
title: 'New VM argument?';
request ];
yourself.
imageArgumentList := (self instantiate: SpEditableListPresenter)
- addItemBlock: [ self application newRequest
+ addItemBlock: [
+ self newRequest
title: 'New image argument?';
request ];
yourself.
applyChangesButton := self newButton
label: 'Apply';
- icon: (self iconNamed: #scriptManager);
+ icon: (self application iconNamed: #scriptManager);
action: [ self saveSelectedConfiguration ];
yourself.
saveAndSelectsButton := self newButton
label: 'Save & select';
- icon: (self iconNamed: #smallSave);
- action: [ self saveSelectedConfiguration.
+ icon: (self application iconNamed: #smallSave);
+ action: [
+ self saveSelectedConfiguration.
self delete ];
yourself
]
@@ -195,11 +199,12 @@ PhLLaunchConfigurationPresenter >> initializeWindow: aWindowPresenter [
{ #category : #action }
PhLLaunchConfigurationPresenter >> newConfiguration [
| newName configuration |
+
newName := self application newRequest
title: 'New Launch configuration name?';
validateAnswer: [ :answer |
self image launchConfigurations noneSatisfy: [ :each | each name = answer ] ];
- request.
+ openModalWithSpec.
newName ifNil: [ ^ nil ].
configuration := (PhLLaunchConfiguration withImage: self image)

diff:
@@ -17,12 +17,11 @@ Class {
{ #category : #example }
PhLScriptPresenter class >> defaultSpec [
+
^ SpPanedLayout newHorizontal
- position: 30 percent;
- add: #scriptTable
- withConstraints: [ :constraints |
- constraints
- spec: #withoutOrderingBar";
+ positionOfSlider: 30 percent;
+ add: #scriptTable withConstraints: [ :constraints |
+ constraints spec: #withoutOrderingBar ";
width: 200" ];
add: (SpBoxLayout newVertical
add: #fileEditor;
@@ -92,10 +91,12 @@ PhLScriptPresenter >> initializeWindow: aWindowPresenter [
{ #category : #action }
PhLScriptPresenter >> newScript [
| newName |
- newName := self application newRequest
+
+ newName := self newRequest
title: 'New script name?';
validateAnswer: [ :answer | (self scriptDirectory / answer) exists not ];
request.
+
^ self newScriptNamed: newName
]

diff:
@@ -36,14 +36,15 @@ PhLTemplatesPresenter class >> buildContextualMenuGroup [
{ #category : #specs }
PhLTemplatesPresenter class >> defaultSpec [
+
^ SpPanedLayout newHorizontal
add: (SpPanedLayout newHorizontal
add: (SpBoxLayout newVertical
- add: '1. Choose a template category:' height: self labelHeight;
+ add: '1. Choose a template category:' expand: false;
add: #categoryTable;
yourself);
add: (SpBoxLayout newVertical
- add: '2. Choose a template:' height: self labelHeight;
+ add: '2. Choose a template:' expand: false;
add: #templateTable;
yourself);
yourself);
@@ -67,6 +68,46 @@ PhLTemplatesPresenter class >> example [
yourself
]
+{ #category : #initialization }
+PhLTemplatesPresenter >> buildCategoryTable [
+
+ ^ self newTable
+ hideColumnHeaders;
+ items: self templateRepository roots;
+ addColumn: ((SpImageTableColumn evaluated: #icon) width: 30);
+ addColumn: (SpStringTableColumn evaluated: #name);
+ yourself
+]
+
+{ #category : #initialization }
+PhLTemplatesPresenter >> buildDescriptionPanel [
+ | panel |
+
+ panel := self instantiate: PhLImageCreationPresenter on: nil.
+ panel disable.
+
+ ^ panel
+]
+
+{ #category : #initialization }
+PhLTemplatesPresenter >> buildTemplateTable [
+
+ ^ self newTreeTable
+ hideColumnHeaders;
+ children: #children;
+ addColumn: (SpStringTableColumn evaluated: #name);
+ contextMenuFromCommandsGroup: [ self rootCommandsGroup / 'Context Menu' ];
+ beMultipleSelection; "bulk delete templates for example"
+ "whenBuiltDo: [ :tree | tree widget alternateRowsColor ];"
+ whenRootsChangedDo: [ :roots |
+ templateTable lazilyComputeChildren: (roots anySatisfy: #shouldComputeChildrenLazily) ];
+ whenSelectionChangedDo: [ :selection |
+ selection isEmpty
+ ifTrue: [ descriptionPanel disable ]
+ ifFalse: [ descriptionPanel enable ] ]
+ yourself
+]
+
{ #category : #initialization }
PhLTemplatesPresenter >> connectPresenters [
@@ -124,28 +165,10 @@ PhLTemplatesPresenter >> initializationScript [
{ #category : #initialization }
PhLTemplatesPresenter >> initializePresenters [
- categoryTable := self newTable
- items: self templateRepository roots;
- addColumn: ((SpImageTableColumn evaluated: #icon) width: 30);
- addColumn: (SpStringTableColumn evaluated: #name);
- yourself.
- templateTable := self newTreeTable
- children: #children;
- addColumn: (SpStringTableColumn evaluated: #name);
- contextMenuFromCommandsGroup: [ self rootCommandsGroup / 'Context Menu' ];
- beMultipleSelection; "bulk delete templates for example"
- "whenBuiltDo: [ :tree | tree widget alternateRowsColor ];"
- whenRootsChangedDo: [ :roots |
- templateTable lazilyComputeChildren: (roots anySatisfy: #shouldComputeChildrenLazily) ];
- whenSelectionChangedDo: [ :selection |
- selection isEmpty
- ifTrue: [ descriptionPanel disable ]
- ifFalse: [ descriptionPanel enable ] ]
- yourself.
- descriptionPanel := self
- instantiate: PhLImageCreationPresenter
- on: nil.
- descriptionPanel disable.
+
+ categoryTable := self buildCategoryTable.
+ templateTable := self buildTemplateTable.
+ descriptionPanel := self buildDescriptionPanel
]
{ #category : #initialization }

diff:
@@ -6,8 +6,6 @@ PharoLauncherApplication open
Class {
#name : #PharoLauncherApplication,
#superclass : #SpApplication,
- #traits : 'TPhLInteractionTrait',
- #classTraits : 'TPhLInteractionTrait classTrait',
#instVars : [
'imageRepository',
'templateRepository',

original_message: migrating interactions


message: using correct model (transmitting just the first selected, not all the array)

change_type: MODIFY

diff:
@@ -163,8 +163,7 @@ PhLImageCreationPresenter >> initializePresenters [
{ #category : #initialization }
PhLImageCreationPresenter >> modelChanged [
-'OK' crTrace.
-self template crTrace.
+
self template ifNil: [ ^ self ].
self template isTemplate
ifTrue: [ self enable.
@@ -210,7 +209,5 @@ PhLImageCreationPresenter >> showNameOk [
PhLImageCreationPresenter >> template [
"Multiple selection is allowed in templates pane => we get a list of templates"
- ^ self model isEmptyOrNil
- ifTrue: [ nil ]
- ifFalse: [ self model first ]
+ ^ self model
]

diff:
@@ -102,9 +102,7 @@ PhLTemplatesPresenter >> buildTemplateTable [
whenRootsChangedDo: [ :roots |
templateTable lazilyComputeChildren: (roots anySatisfy: #shouldComputeChildrenLazily) ];
whenSelectionChangedDo: [ :selection |
- selection isEmpty
- ifTrue: [ descriptionPanel disable ]
- ifFalse: [ descriptionPanel enable ] ]
+ descriptionPanel enabled: selection isEmpty not ]
yourself
]
@@ -118,7 +116,13 @@ PhLTemplatesPresenter >> connectPresenters [
group
ifNil: [ #() ]
ifNotNil: [ group children ] ].
- templateTable transmitTo: descriptionPanel.
+
+ templateTable
+ transmitTo: descriptionPanel
+ transform: [ :selectionArray |
+ selectionArray
+ ifNotEmpty: [ selectionArray last ]
+ ifEmpty: [ nil ] ].
]
{ #category : #accessing }

license: MIT License

repo: pharo-project/pharo-launcher


message: updtae subprocess version

diff:
@@ -15,7 +15,7 @@ BaselineOfPharoLauncher >> baseline: spec [
baseline: #Spec2
with: [ spec repository: 'github://pharo-spec/Spec:spec-pharo-launcher/src' ];
baseline: #OSSubprocess
- with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.2' ];
+ with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.3' ];
baseline: #OSWinSubprocess
with: [ spec repository: 'github://pharo-contributions/OSWinSubprocess:v1' ];
baseline: #XMLParser

original_message: updtae subprocess version


message:
quick and dirty "fix" for usages of UIManager
for now this is using `PharoLauncherApplication default` access which is also ugly, but the whole application is bad designed (with bad separation of concerns), so for now it will have to work :/

diff:
@@ -16,15 +16,8 @@ Class {
{ #category : #accessing }
PhLAbstractTemplateGroup >> children [
- ^ [ self templatesAndGroups ]
- on: PhLDownloadError
- do: [ :error | | children |
- PhlRetryPresenter new
- explanation: error longDescription;
- retryBlock: [ children := self children ];
- cancelBlock: [ children := #() ];
- openModalWithSpec.
- children ]
+
+ ^ self templatesAndGroups
]
{ #category : #accessing }

diff:
@@ -44,14 +44,14 @@ PhLDownloadManager >> basicDownload: url toFile: destinationFile [
PhLDownloadManager >> displayProgressDuring: workBlock [
"Inform user about download progression"
- UIManager default
- informUserDuring: [ :bar |
+ PharoLauncherApplication default newProgressDialog
+ informUserDuring: [ :dialog |
workBlock
on: HTTPProgress
do: [ :progress |
- bar label: progress printString.
+ dialog label: progress printString.
progress isEmpty
- ifFalse: [ bar current: progress percentage ].
+ ifFalse: [ dialog progressPercent: progress percentage ].
progress resume ] ]
]

diff:
@@ -17,10 +17,10 @@ PhLOpenNewImageWindowCommand >> asSpecCommand [
{ #category : #executing }
PhLOpenNewImageWindowCommand >> execute [
+
^ (PhLTemplatesPresenter
newApplication: self application
model: PhLTemplateGroupRepository default)
- selectCategory: 3; "official category"
openWithSpec;
yourself
]

diff:
@@ -106,12 +106,12 @@ PhLVirtualMachineManager class >> isPreSpur: anImageFormatVersion [
{ #category : #unzipping }
PhLVirtualMachineManager class >> pharoUnzip: aZipFileReference to: outputDirectory [
- UIManager default informUserDuring:
- [ :bar |
+
+ PharoLauncherApplication default newProgressDialog
+ informUserDuring: [ :bar |
ZipArchive new
readFrom: aZipFileReference;
extractAllTo: outputDirectory informing: bar overwrite: true ]
-
]
{ #category : #private }
@@ -536,7 +536,8 @@ PhLVirtualMachineManager >> virtualMachines [
{ #category : #querying }
PhLVirtualMachineManager >> vm [
| vm |
- UIManager default
+
+ PharoLauncherApplication default newProgressDialog
informUserDuring: [ :bar |
bar label: 'Determining Image version'.
vm := self availableVirtualMachines
@@ -554,7 +555,7 @@ PhLVirtualMachineManager >> vm [
bar
label: 'Running the image';
current: 100.
- vm ]. ].
+ vm ] ].
^ vm executablePath
]

diff:
@@ -123,6 +123,7 @@ PhLImageCreationPresenter >> initializationScripts [
{ #category : #initialization }
PhLImageCreationPresenter >> initialize [
+
super initialize.
reservedImagesNamesCache := self application imageRepository imageNames collect: #asLowercase
]
@@ -193,15 +194,17 @@ PhLImageCreationPresenter >> refreshInitializationScriptList [
{ #category : #private }
PhLImageCreationPresenter >> showNameError [
+
imageNameCheck
- image: (self iconNamed: #smallError);
+ image: (self application iconNamed: #smallCancel);
help: 'An image with the same name already exists!'
]
{ #category : #private }
PhLImageCreationPresenter >> showNameOk [
+
imageNameCheck
- image: (self iconNamed: #smallOk);
+ image: (self application iconNamed: #smallOk);
help: 'Image name is valid!'
]

diff:
@@ -256,7 +256,7 @@ PhLImagesPresenter >> initializeWindow: aWindowPresenter [
aWindowPresenter
title: 'Pharo Launcher';
- initialExtent: 700 @ 550;
+ initialExtent: 850@550;
centered;
whenClosedDo: [ self imageRepository unsubscribe: self ].
self rootCommandsGroup installShortcutsIn: aWindowPresenter.

diff:
@@ -38,7 +38,9 @@ PhLTemplatesPresenter class >> buildContextualMenuGroup [
PhLTemplatesPresenter class >> defaultSpec [
^ SpPanedLayout newHorizontal
+ positionOfSlider: 60 percent;
add: (SpPanedLayout newHorizontal
+ positionOfSlider: 40 percent;
add: (SpBoxLayout newVertical
add: '1. Choose a template category:' expand: false;
add: #categoryTable;
@@ -94,7 +96,7 @@ PhLTemplatesPresenter >> buildTemplateTable [
^ self newTreeTable
hideColumnHeaders;
- children: #children;
+ children: [ :aTemplateGroup | self childrenOf: aTemplateGroup ];
addColumn: (SpStringTableColumn evaluated: #name);
contextMenuFromCommandsGroup: [ self rootCommandsGroup / 'Context Menu' ];
beMultipleSelection; "bulk delete templates for example"
@@ -106,6 +108,21 @@ PhLTemplatesPresenter >> buildTemplateTable [
yourself
]
+{ #category : #private }
+PhLTemplatesPresenter >> childrenOf: aTemplateGroup [
+
+ ^ [ aTemplateGroup children ]
+ on: PhLDownloadError
+ do: [ :error |
+ | children |
+ (PhlRetryPresenter newApplication: self application)
+ explanation: error longDescription;
+ retryBlock: [ children := aTemplateGroup children ];
+ cancelBlock: [ children := #() ];
+ openModalWithSpec.
+ children ]
+]
+
{ #category : #initialization }
PhLTemplatesPresenter >> connectPresenters [
@@ -167,6 +184,14 @@ PhLTemplatesPresenter >> initializationScript [
^ descriptionPanel initializationScript
]
+{ #category : #initialization }
+PhLTemplatesPresenter >> initialize [
+
+ super initialize.
+ self selectCategory: 3
+
+]
+
{ #category : #initialization }
PhLTemplatesPresenter >> initializePresenters [
@@ -223,5 +248,6 @@ PhLTemplatesPresenter >> singleTemplate [
{ #category : #'api - accessing' }
PhLTemplatesPresenter >> templateRepository [
+
^ self model
]

diff:
@@ -303,6 +303,14 @@ PharoLauncherApplication >> startFull [
using: SpWorldPresenter
]
+{ #category : #api }
+PharoLauncherApplication >> syntaxHighlightTheme [
+
+ ^ self
+ propertyAt: #syntaxHighlightTheme
+ ifAbsent: [ nil ]
+]
+
{ #category : #accessing }
PharoLauncherApplication >> templateRepository [
^ templateRepository

diff:
@@ -41,13 +41,13 @@ PhlRetryPresenter >> explanation: aString [
{ #category : #initialization }
PhlRetryPresenter >> initializeDialogWindow: aWindowPresenter [
+
aWindowPresenter
title: 'Retry?';
initialExtent: 400 @ 120;
addButton: 'Retry' do: [ :presenter | presenter close. retryBlock value. ];
addButton: 'Cancel' do: [ :presenter | presenter beCancel; close. cancelBlock value. ];
- centered.
-
+ centeredRelativeToTopWindow
]
{ #category : #initialization }

diff:
@@ -11,11 +11,12 @@ Class {
{ #category : #'ui requests' }
YesNoDialogWithTickBoxWindow class >> confirm: description label: title [
- ^ (UIManager default modalMorph openModal: (
- (self newWithTheme: Smalltalk ui theme)
- textFont: Smalltalk ui theme textFont;
+
+ ^ (PharoLauncherApplication default newConfirm
title: title;
- text: description)) answer
+ message: description;
+ openModalWithSpecParent: PharoLauncherApplication default topWindow)
+ isOk
]
{ #category : #accessing }


message: setting parent window so it gets relative to it

diff:
@@ -45,6 +45,7 @@ PhLDownloadManager >> displayProgressDuring: workBlock [
"Inform user about download progression"
PharoLauncherApplication default newProgressDialog
+ parentWindow: PharoLauncherApplication default topWindow;
informUserDuring: [ :dialog |
workBlock
on: HTTPProgress

diff:
@@ -108,6 +108,7 @@ PhLVirtualMachineManager class >> isPreSpur: anImageFormatVersion [
PhLVirtualMachineManager class >> pharoUnzip: aZipFileReference to: outputDirectory [
PharoLauncherApplication default newProgressDialog
+ parentWindow: PharoLauncherApplication default topWindow;
informUserDuring: [ :bar |
ZipArchive new
readFrom: aZipFileReference;
@@ -538,12 +539,13 @@ PhLVirtualMachineManager >> vm [
| vm |
PharoLauncherApplication default newProgressDialog
+ parentWindow: PharoLauncherApplication default topWindow;
informUserDuring: [ :bar |
bar label: 'Determining Image version'.
vm := self availableVirtualMachines
at: self vmId
- ifAbsent:
- [ bar
+ ifAbsent: [
+ bar
label: 'Fetching VM to run Pharo ', self imageVersion , ' images';
current: 25.
self fetchVm.
@@ -556,6 +558,7 @@ PhLVirtualMachineManager >> vm [
label: 'Running the image';
current: 100.
vm ] ].
+
^ vm executablePath
]


message: there is no 1.3 but 1.3.0

diff:
@@ -15,7 +15,7 @@ BaselineOfPharoLauncher >> baseline: spec [
baseline: #Spec2
with: [ spec repository: 'github://pharo-spec/Spec:spec-pharo-launcher/src' ];
baseline: #OSSubprocess
- with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.3' ];
+ with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.3.0' ];
baseline: #OSWinSubprocess
with: [ spec repository: 'github://pharo-contributions/OSWinSubprocess:v1' ];
baseline: #XMLParser

original_message: there is no 1.3 but 1.3.0


message: do not check updates everytime

diff:
@@ -34,7 +34,9 @@ PhLStartupManager class >> resetStartUpClasses [
]
{ #category : #'system startup' }
-PhLStartupManager class >> startUp [
+PhLStartupManager class >> startUp: isANewSession [
+
+ isANewSession ifFalse: [ ^ self ].
self startUpClasses do: [ :c | c ifNotNil: [ c launcherStartUp ] ]
]

original_message: do not check updates everytime


message: start moving out of direct access to UI

diff:
@@ -4,7 +4,7 @@ I represent an error as can be signaled by any code of Pharo Launcher.
Class {
#name : #PhLError,
#superclass : #Error,
- #category : 'PharoLauncher-Core-Exceptions'
+ #category : #'PharoLauncher-Core-Exceptions'
}
{ #category : #accessing }
@@ -19,8 +19,6 @@ PhLError >> name [
{ #category : #ui }
PhLError >> uiAlert [
- UIManager default
- alert: self longDescription
- title: self name
- configure: [ :morph | (morph findDeeplyA: TextMorph) unlock ]
+
+ PharoLauncherApplication default showError: self
]

diff:
@@ -360,12 +360,11 @@ PhLImage >> launchWithConfiguration: aPhLLaunchConfiguration [
on: PhLImageVersionDeterminationError
do: [ :error | error uiAlert ] ]
on: PhLArchitectureMismatchWarning
- do: [ :warning | | |
- (PhLLaunchImageCommand warnOnArchMismatch and: [ architectureWarningEnabled ])
- ifTrue: [
+ do: [ :warning |
+ architectureWarningEnabled ifTrue: [
architectureWarningEnabled := false. "only raise one warning"
- continue := YesNoDialogWithTickBoxWindow confirm: warning longDescription label: warning name. ].
- continue ifTrue: [ warning resume ]. ]
+ continue := self showWarning: warning ].
+ continue ifTrue: [ warning resume ] ]
]
{ #category : #accessing }
@@ -468,6 +467,21 @@ PhLImage >> showNativeFolder [
PhLFileBrowser openOn: file
]
+{ #category : #'private ui' }
+PhLImage >> showWarning: warning [
+ | continue |
+
+ continue := true.
+ (PhLLaunchImageCommand warnOnArchMismatch)
+ ifTrue: [
+ continue := (PharoLauncherApplication default newConfirm
+ title: warning name ;
+ message: warning longDescription;
+ openModalWithSpecParent: PharoLauncherApplication default topWindow) ].
+
+ ^ continue
+]
+
{ #category : #accessing }
PhLImage >> sourcesFile [
^ self file parent files

change_type: DELETE

diff:
-"
-Yes - No Dialog window with a tick box used to disable warnings by example.
-
-self confirm: 'Is this widget fine?' label: 'Title'
-"
-Class {
- #name : #YesNoDialogWithTickBoxWindow,
- #superclass : #QuestionWithoutCancelDialogWindow,
- #category : #'PharoLauncher-Spec2'
-}
-
-{ #category : #'ui requests' }
-YesNoDialogWithTickBoxWindow class >> confirm: description label: title [
-
- ^ (PharoLauncherApplication default newConfirm
- title: title;
- message: description;
- openModalWithSpecParent: PharoLauncherApplication default topWindow)
- isOk
-]
-
-{ #category : #accessing }
-YesNoDialogWithTickBoxWindow >> checkBoxBallonText [
- ^ PhLLaunchImageCommand archMismatchSettingDescription
-]
-
-{ #category : #accessing }
-YesNoDialogWithTickBoxWindow >> checkBoxLabel [
- ^ PhLLaunchImageCommand archMismatchSettingLabel
-]
-
-{ #category : #accessing }
-YesNoDialogWithTickBoxWindow >> checkBoxSelector [
- ^ #warnOnArchMismatch
-]
-
-{ #category : #accessing }
-YesNoDialogWithTickBoxWindow >> checkBoxSetterSelector [
- ^ #warnOnArchMismatch:
-]
-
-{ #category : #'instance creation' }
-YesNoDialogWithTickBoxWindow >> newMainPanel [
- | panel |
- panel := super newMainPanel.
- self newTickBoxRow ifNotNil: [:m | panel addMorphBack: m ].
- ^ panel
-]
-
-{ #category : #'instance creation' }
-YesNoDialogWithTickBoxWindow >> newTickBoxRow [
- "Answer a new tick box row."
-
- | row checkBox |
- checkBox := CheckboxMorph
- on: PhLLaunchImageCommand
- selected: self checkBoxSelector
- changeSelected: self checkBoxSetterSelector.
- checkBox
- label: self checkBoxLabel;
- vResizing: #shrinkWrap;
- hResizing: #shrinkWrap;
- setBalloonText: self checkBoxBallonText.
- row := Morph new
- color: Color transparent;
- changeTableLayout;
- cellInset: 8;
- listDirection: #rightToLeft;
- listCentering: #bottomRight;
- hResizing: #spaceFill;
- vResizing: #shrinkWrap.
- row addMorphBack: checkBox.
- ^ row
-]

original_message: start moving out of direct access to UI


message: adding ui methods

diff:
@@ -7,6 +7,12 @@ Class {
#category : #'PharoLauncher-Core-Commands'
}
+{ #category : #accessing }
+PhLTemplateCommand >> application [
+
+ ^ self context application
+]
+
{ #category : #accessing }
PhLTemplateCommand >> templatesPresenter [
^ self context

diff:
@@ -107,12 +107,14 @@ PhLVirtualMachineManager class >> isPreSpur: anImageFormatVersion [
{ #category : #unzipping }
PhLVirtualMachineManager class >> pharoUnzip: aZipFileReference to: outputDirectory [
- PharoLauncherApplication default newProgress
- parentWindow: PharoLauncherApplication default topWindow;
- informUserDuring: [ :bar |
+ PharoLauncherApplication default
+ pushProgress: ('Unzipping {1}' format: { aZipFileReference basename })
+ with: [ :bar |
ZipArchive new
readFrom: aZipFileReference;
- extractAllTo: outputDirectory informing: bar overwrite: true ]
+ extractAllTo: outputDirectory
+ informing: bar
+ overwrite: true ]
]
{ #category : #private }
@@ -538,9 +540,9 @@ PhLVirtualMachineManager >> virtualMachines [
PhLVirtualMachineManager >> vm [
| vm |
- PharoLauncherApplication default newProgress
- parentWindow: PharoLauncherApplication default topWindow;
- informUserDuring: [ :bar |
+ PharoLauncherApplication default
+ pushProgress: 'UNNAMED'
+ with: [ :bar |
bar label: 'Determining Image version'.
vm := self availableVirtualMachines
at: self vmId

diff:
@@ -229,6 +229,7 @@ PharoLauncherApplication >> imageRepositoryChanged [
{ #category : #announcements }
PharoLauncherApplication >> imageRepositoryChanged: newImage [
+
self imageRepositoryChanged.
imagesPresenter ifNotNil: [ imagesPresenter selectImage: newImage ].
]
@@ -264,6 +265,15 @@ PharoLauncherApplication >> openAsWorld: aBoolean [
openAsWorld := aBoolean
]
+{ #category : #ui }
+PharoLauncherApplication >> pushProgress: aString with: aBlock [
+
+ self newProgress
+ parentWindow: self topWindow;
+ title: aString;
+ informUserDuring: aBlock
+]
+
{ #category : #initialization }
PharoLauncherApplication >> resetTemplateRepository [
templateRepository := TemplateRepository
@@ -282,6 +292,15 @@ PharoLauncherApplication >> scriptsDirectory: aFileReference [
scriptsDirectory := aFileReference
]
+{ #category : #ui }
+PharoLauncherApplication >> showError: anError [
+
+ self newInform
+ title: anError name;
+ label: anError longDescription;
+ openModalWithSpecParent: self topWindow
+]
+
{ #category : #running }
PharoLauncherApplication >> start [

original_message: adding ui methods


message: remove non used method (wrongly added)

diff:
@@ -322,14 +322,6 @@ PharoLauncherApplication >> startFull [
using: SpWorldPresenter
]
-{ #category : #api }
-PharoLauncherApplication >> syntaxHighlightTheme [
-
- ^ self
- propertyAt: #syntaxHighlightTheme
- ifAbsent: [ nil ]
-]
-
{ #category : #accessing }
PharoLauncherApplication >> templateRepository [
^ templateRepository

original_message: remove non used method (wrongly added)


message: fix startup as world mode

diff:
@@ -319,7 +319,7 @@ PharoLauncherApplication >> startFull [
self
open: imagesPresenter
- withSpecLayout: imagesPresenter defaultSpec
+ withSpecLayout: imagesPresenter layout
using: SpWorldPresenter
]

original_message: fix startup as world mode


message: adapt baseline (and remove spec2 dependency, since is going to be already there)

diff:
@@ -9,42 +9,82 @@ BaselineOfPharoLauncher >> baseline: spec [
<baseline>
spec for: #'common' do: [
+
+ self ston: spec.
+ self osSubprocess: spec.
+ self osWinSubprocess: spec.
+ self xmlParser: spec.
+
+ spec
+ package: 'PharoLauncher-80Compatibility';
+ package: 'PharoLauncher-Pharo9ToRemove-FileLocator';
+ package: 'PharoLauncher-80Compatibility';
+ package: 'PharoLauncher-Core' with: [
+ spec requires: #(
+ 'XMLParser'
+ 'OSSubprocess'
+ 'OSWinSubprocess'
+ 'Ston'
+ 'PharoLauncher-80Compatibility'
+ 'PharoLauncher-Pharo9ToRemove-FileLocator'). ];
+ package: 'PharoLauncher-Spec2' with: [
+ spec requires: #('PharoLauncher-Core') ];
+ package: 'PharoLauncher-GToolkit' with: [
+ spec requires: #('PharoLauncher-Core') ];
+ package: 'PharoLauncher-Squeak' with: [
+ spec requires: #('PharoLauncher-Core') ];
+ package: 'PharoLauncher-Tests-Core' with: [
+ spec requires: #('PharoLauncher-Core' ) ];
+ package: 'PharoLauncher-Tests-Commands' with: [
+ spec requires: #('PharoLauncher-Spec2' ). ];
+ package: 'PharoLauncher-Tests-SpecUI' with: [
+ spec requires: #('PharoLauncher-Core' 'PharoLauncher-Spec2'). ];
+ package: 'PharoLauncher-Tests-Download' with: [
+ spec requires: #('PharoLauncher-Tests-Core' ). ];
+ package: 'PharoLauncher-Tests-Functional' with: [
+ spec requires: #('PharoLauncher-Core' 'PharoLauncher-Tests-Download'). ].
+ spec
+ group: 'Default' with: #(
+ 'PharoLauncher-Tests-Core'
+ 'PharoLauncher-Tests-Download'
+ 'PharoLauncher-Core'
+ 'PharoLauncher-Spec2'
+ 'PharoLauncher-Tests-SpecUI'
+ 'PharoLauncher-Tests-Functional'
+ 'PharoLauncher-GToolkit'
+ 'PharoLauncher-Squeak') ]
+]
+
+{ #category : #'external projects' }
+BaselineOfPharoLauncher >> osSubprocess: spec [
+
spec
- baseline: #Ston
- with: [ spec repository: 'github://svenvc/ston:master/repository' ];
- baseline: #Spec2
- with: [ spec repository: 'github://pharo-spec/Spec:spec-pharo-launcher/src' ];
baseline: #OSSubprocess
- with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.3.0' ];
+ with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.3.0' ]
+]
+
+{ #category : #'external projects' }
+BaselineOfPharoLauncher >> osWinSubprocess: spec [
+
+ spec
baseline: #OSWinSubprocess
- with: [ spec repository: 'github://pharo-contributions/OSWinSubprocess:v1' ];
+ with: [ spec repository: 'github://pharo-contributions/OSWinSubprocess:v1' ]
+]
+
+{ #category : #'external projects' }
+BaselineOfPharoLauncher >> ston: spec [
+
+ spec
+ baseline: #Ston
+ with: [ spec repository: 'github://svenvc/ston:master/repository' ]
+]
+
+{ #category : #'external projects' }
+BaselineOfPharoLauncher >> xmlParser: spec [
+
+ spec
baseline: #XMLParser
with: [ spec
repository: 'github://pharo-contributions/XML-XMLParser:3.5.x';
- loads: #('Core') ].
- spec
- package: #'PharoLauncher-80Compatibility';
- package: #'PharoLauncher-Pharo9ToRemove-FileLocator';
- package: #'PharoLauncher-80Compatibility';
- package: #'PharoLauncher-Core' with: [
- spec requires: #(#'XMLParser' #'OSSubprocess' #'OSWinSubprocess' #'Ston' #'PharoLauncher-80Compatibility' #'PharoLauncher-Pharo9ToRemove-FileLocator'). ];
- package: #'PharoLauncher-Spec2' with: [
- spec requires: #(#'PharoLauncher-Core' #Spec2). ];
- package: #'PharoLauncher-GToolkit' with: [
- spec requires: #(#'PharoLauncher-Core'). ];
- package: #'PharoLauncher-Squeak' with: [
- spec requires: #(#'PharoLauncher-Core'). ];
-
- package: #'PharoLauncher-Tests-Core' with: [
- spec requires: #(#'PharoLauncher-Core' ). ];
- package: #'PharoLauncher-Tests-Commands' with: [
- spec requires: #(#'PharoLauncher-Spec2' ). ];
- package: #'PharoLauncher-Tests-SpecUI' with: [
- spec requires: #(#'PharoLauncher-Core' #'PharoLauncher-Spec2'). ];
- package: #'PharoLauncher-Tests-Download' with: [
- spec requires: #(#'PharoLauncher-Tests-Core' ). ];
- package: #'PharoLauncher-Tests-Functional' with: [
- spec requires: #(#'PharoLauncher-Core' #'PharoLauncher-Tests-Download'). ].
- spec
- group: 'Default' with: #(#'PharoLauncher-Tests-Core' #'PharoLauncher-Tests-Download' #'PharoLauncher-Core' #'PharoLauncher-Spec2' #'PharoLauncher-Tests-SpecUI' #'PharoLauncher-Tests-Functional' #'PharoLauncher-GToolkit' #'PharoLauncher-Squeak'). ].
+ loads: #('Core') ]
]


message: remove a code I put to test

diff:
@@ -258,11 +258,6 @@ PhLVirtualMachineManager >> compatibleVmUrls [
{ #category : #querying }
PhLVirtualMachineManager >> defaultVirtualMachines [
^ {
- (PhLVirtualMachine id: '90-ARMv7-headless')
- beHeadless;
- beLatest;
- beARMv7;
- yourself.
(PhLVirtualMachine id: '90-x64-headless')
beHeadless;
beLatest;

original_message: remove a code I put to test


message: ensure topWindow answers a window (if there is no "active" top window, then the last opened)

diff:
@@ -237,7 +237,11 @@ PharoLauncherApplication >> imageRepositoryChanged: newImage [
{ #category : #initialization }
PharoLauncherApplication >> initialize [
+
super initialize.
+ "I want to register windows in order to be able to know which one is the last opened
+ This can be use to push progress bars (they stack)."
+ windows := OrderedCollection new.
openAsWorld := false.
imageRepository := PhLDirectoryBasedImageRepository default.
self templatesClearedOnStartup
@@ -337,3 +341,15 @@ PharoLauncherApplication >> templateRepository: aPhLTemplateGroupRepository [
PharoLauncherApplication >> templatesClearedOnStartup [
^ self class templatesClearedOnStartup
]
+
+{ #category : #ui }
+PharoLauncherApplication >> topWindow [
+ "The window that will accept a modal window related to it.
+ This is usually the top window, but in case of stacking dialogs, some times this is lost.
+ In that case, I want still choose one window"
+
+ super topWindow ifNotNil: [ :topWindow | ^ topWindow ].
+ ^ self windows
+ ifNotEmpty: [ :aCollection | aCollection last ]
+ ifEmpty: [ nil ]
+]


message: giving more information on the download dialogs, but also force a max length of the title

diff:
@@ -36,17 +36,19 @@ PhLDownloadManager >> basicDownload: url toFile: destinationFile [
| tmpFile |
tmpFile := destinationFile , UUIDGenerator next asString.
- self displayProgressDuring: [ (self newHTTPClientForUrl: url) downloadTo: tmpFile ].
+ self
+ displayProgress: ('Downloading {1}...' format: { url })
+ during: [ (self newHTTPClientForUrl: url) downloadTo: tmpFile ].
destinationFile ensureDelete.
tmpFile renameTo: destinationFile basename.
]
{ #category : #private }
-PhLDownloadManager >> displayProgressDuring: workBlock [
+PhLDownloadManager >> displayProgress: aTitle during: workBlock [
"Inform user about download progression"
PharoLauncherApplication default
- pushProgress: 'Downloading...'
+ pushProgress: aTitle
with: [ :bar |
workBlock
on: HTTPProgress
@@ -57,6 +59,15 @@ PhLDownloadManager >> displayProgressDuring: workBlock [
progress resume ] ]
]
+{ #category : #private }
+PhLDownloadManager >> displayProgressDuring: workBlock [
+ "Inform user about download progression"
+
+ self
+ displayProgress: 'Downloading...'
+ during: workBlock
+]
+
{ #category : #action }
PhLDownloadManager >> download: url toFile: destinationFile [
[ self basicDownload: url toFile: destinationFile ]
@@ -84,7 +95,9 @@ PhLDownloadManager >> downloadToPositionableStream: url mimeType: mimeType [
beOneShot;
accept: mimeType;
enforceAcceptContentType: true.
- self displayProgressDuring: [ content := client get ].
+ self
+ displayProgress: ('Downloading {1}...' format: { url })
+ during: [ content := client get ].
^ content readStream
]

diff:
@@ -275,7 +275,7 @@ PharoLauncherApplication >> pushProgress: aString with: aBlock [
self newProgress
parentWindow: self topWindow;
- title: aString;
+ title: (aString contractTo: 60);
informUserDuring: aBlock
]


message: using new asWorldWindow selector

diff:
@@ -321,10 +321,7 @@ PharoLauncherApplication >> start [
{ #category : #running }
PharoLauncherApplication >> startFull [
- self
- open: imagesPresenter
- withSpecLayout: imagesPresenter layout
- using: SpWorldPresenter
+ imagesPresenter asWorldWindow open
]
{ #category : #accessing }

original_message: using new asWorldWindow selector


message: add a "pharoVersionString" to format properly versions (9.0 instead 90)

diff:
@@ -425,6 +425,12 @@ PhLImage >> pharoVersionFromFile [
ifFalse: [ '' ]
]
+{ #category : #accessing }
+PhLImage >> pharoVersionString [
+
+ ^ (self pharoVersion asNumber / 10.0) asFloat asString
+]
+
{ #category : #printing }
PhLImage >> printOn: aStream [
aStream

diff:
@@ -151,7 +151,7 @@ PhLImagesPresenter >> buildImageTable [
addColumn: ((SpStringTableColumn title: 'Architecture' evaluated: #architecture)
width: 80;
yourself);
- addColumn: ((SpStringTableColumn title: 'Pharo Version' evaluated: #pharoVersion)
+ addColumn: ((SpStringTableColumn title: 'Pharo Version' evaluated: #pharoVersionString)
width: 80;
yourself);
addColumn: ((SpStringTableColumn


message: using new api for progress bars

diff:
@@ -283,10 +283,7 @@ PharoLauncherApplication >> openAsWorld: aBoolean [
{ #category : #ui }
PharoLauncherApplication >> pushProgress: aString with: aBlock [
- self newProgress
- parentWindow: self topWindow;
- title: (aString contractTo: 60);
- informUserDuring: aBlock
+ self newProgress pushJob: (SpJob newTitle: aString block: aBlock)
]
{ #category : #initialization }

original_message: using new api for progress bars


message:
Remove unnecessary complex code and test to now simply #openInOSFileBrowser in more recent versions of Pharo
Fix

diff:
-"
-I'm a simple visitor in charge of opening an operating system browser on the provided path string.
-"
-Class {
- #name : #PhLFileBrowser,
- #superclass : #OSPlatformVisitor,
- #instVars : [
- 'process',
- 'vmPath',
- 'launchInALoginShell',
- 'imageFile',
- 'usePharoSettings',
- 'path'
- ],
- #category : #'PharoLauncher-Core-Model'
-}
-
-{ #category : #'instance creation' }
-PhLFileBrowser class >> openOn: aFileReferenceOrPathString [
-
- ^ self new
- path: aFileReferenceOrPathString;
- open.
-]
-
-{ #category : #action }
-PhLFileBrowser >> open [
- self visit
-]
-
-{ #category : #accessing }
-PhLFileBrowser >> path: aFileReferenceOrPathString [
- "Opens an OS-specific file and directory browser on ==aFileReference==. If aFileReference is a file, opens the browser on its containing directory instead."
- | ref |
- aFileReferenceOrPathString isNil ifTrue: [ ^ nil ].
- ref := aFileReferenceOrPathString asFileReference.
- ref exists ifFalse: [ ^ nil ].
-
- ref := ref isFile ifTrue: [ ref parent ] ifFalse: [ ref ].
- (ref isNil or: [ ref exists not ])
- ifTrue: [ ^ nil ].
-
- path := ref fullName.
-]
-
-{ #category : #visiting }
-PhLFileBrowser >> visitMacOS: aPlatform [
- ^ PhLProcessWrapper new
- shellCommand;
- addArgument: ('open "{1}"' format: {path});
- runUnwatch
-]
-
-{ #category : #visiting }
-PhLFileBrowser >> visitUnix: aPlatform [
- ^ PhLProcessWrapper new
- shellCommand;
- addArgument: ('xdg-open "{1}"' format: {path});
- runUnwatch
-]
-
-{ #category : #visiting }
-PhLFileBrowser >> visitWindows: aPlatform [
- ^ aPlatform privShellExplore: path
-]

diff:
@@ -479,7 +479,7 @@ PhLImage >> shouldRunInitializationScript: aBoolean [
{ #category : #printing }
PhLImage >> showNativeFolder [
- PhLFileBrowser openOn: file
+ file asFileReference openInOSFileBrowser
]
{ #category : #'private ui' }

diff:
@@ -356,7 +356,7 @@ PhLVirtualMachine >> removeFromSystem [
{ #category : #actions }
PhLVirtualMachine >> showInFolder [
- PhLFileBrowser openOn: self vmStore / self id
+ (self vmStore / self id) asFileReference openInOSFileBrowser
]
{ #category : #'ston persistence' }

diff:
@@ -9,30 +9,6 @@ WinPlatform >> hostArchitecture [
^ processorArch
]
-{ #category : #'*PharoLauncher-Core' }
-WinPlatform >> privShellExecute: lpOperation file: lpFile parameters: lpParameters directory: lpDirectory show: nShowCmd [
-
- ^self ffiCall: #(
- FFIConstantHandle ShellExecuteA(
- 0,
- char* lpOperation,
- char* lpFile,
- char* lpParameters,
- char* lpDirectory,
- int nShowCmd)) module: #shell32
-
-
-]
-
-{ #category : #'*PharoLauncher-Core' }
-WinPlatform >> privShellExplore: folder [
- "
- self privShellExplore: 'c:\'
- "
-
- ^self privShellExecute: 'explore' file: folder parameters: '' directory: '' show: 5 "SW_SHOW"
-]
-
{ #category : #'*PharoLauncher-Core' }
WinPlatform >> uname [
^ PhLVmProvider osWindows

diff:
-Class {
- #name : #PhLFileBrowserTest,
- #superclass : #TestCase,
- #category : #'PharoLauncher-Tests-Core'
-}
-
-{ #category : #tests }
-PhLFileBrowserTest >> testCanOpenAFileBrowserOnImageFolder [
- | path |
- path := Smalltalk image imageDirectory fullName.
- PhLFileBrowser openOn: path.
-]

original_message:
Remove unnecessary complex code and test to now simply #openInOSFileBrowser in more recent versions of Pharo (#569)

Fix #568


message:
Use "Show" when no image is downloaded yet gives error
Fix

diff:
@@ -4,7 +4,7 @@ Open your OS's file browser on the selected image directory.
Class {
#name : #PhLShowImageInFolderCommand,
#superclass : #PhLImageCommand,
- #category : 'PharoLauncher-Core-Commands'
+ #category : #'PharoLauncher-Core-Commands'
}
{ #category : #converting }
@@ -18,6 +18,8 @@ PhLShowImageInFolderCommand >> asSpecCommand [
{ #category : #executing }
PhLShowImageInFolderCommand >> execute [
+
+ self imagesPresenter hasSingleImageSelected ifFalse: [ ^self ].
self imagesPresenter singleImage showNativeFolder
]

original_message:
Use "Show" when no image is downloaded yet gives error (#571)

Fix #570


change_type: ADD

diff: Binary files /dev/null and b/examples/images/javascript-sensors-viewer-stream-test.png differ

diff: Binary files /dev/null and b/examples/images/sensor-graph.png differ

diff: Binary files /dev/null and b/examples/images/sensors-viewer-stream-test.png differ

license: Apache License 2.0

repo: astarte-platform/astarte

original_message:
Add images for javascript examples

Add screenshots of sample usage for javascript examples.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -110,7 +110,7 @@ Astarte, by default, is extremely easy to configure assuming your chosen SSO is
The main purpose of Astarte's design, however, is to keep things simple for everyone. Putting up a full-fledged SSO dedicated to Astarte is beyond the scope of this documentation, and we favor the use case where an existing SSO infrastructure is integrated with Astarte, rather than built ad-hoc.
-For simple use cases and instant satisfaction, it is strongly advised to use a simpler solution, such as a dedicated OAuth server. Almost all popular languages and frameworks provide great projects which can spin up an OAuth2 server + user management in a matter of hours, from [Elixir/Phoenix](https://github.com/mustafaturan/shield) to [Java/Spring](https://github.com/spring-cloud-samples/authserver) to [Go](https://github.com/RichardKnop/go-oauth2-server).
+For simple use cases and instant satisfaction, it is strongly advised to use a simpler solution, such as a dedicated OAuth server. Almost all popular languages and frameworks provide great projects which can spin up an OAuth2 server + user management in a matter of hours, from [Elixir/Phoenix](https://github.com/mbuhot/shield) to [Java/Spring](https://github.com/spring-cloud-samples/authserver) to [Go](https://github.com/RichardKnop/go-oauth2-server).
[Astarte's Enterprise Distribution](https://astarte.cloud/enterprise) includes other add-ons, such as automation and configuration for popular SSOs.

diff:
@@ -28,7 +28,7 @@ is then used for obtaining information about which Transports the Device can use
and for obtaining Credentials for its assigned Transports.
The ability to request Credentials of a Device can be inhibited with [AppEngine
-API](/api/#/device/updateDeviceStatus) or using
+API](https://docs.astarte-platform.org/1.0/api/#/device/updateDeviceStatus) or using
[`astartectl`](https://github.com/astarte-platform/astartectl) with this command:
```bash

original_message:
Fix broken links in docs

Use up-to-date links to replace broken links in docs that resulted in
404 pages.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff: Binary files /dev/null and b/doc/images/astarte-dashboard-devices.png differ

diff: Binary files /dev/null and b/doc/images/astarte-dashboard-interface-editor.png differ

diff: Binary files /dev/null and b/doc/images/astarte-dashboard-interfaces.png differ

diff: Binary files /dev/null and b/doc/images/astarte-dashboard-realm-overview.png differ

diff: Binary files /dev/null and b/doc/images/astarte-dashboard-trigger-editor.png differ

diff: Binary files /dev/null and b/doc/images/astarte-dashboard-triggers.png differ

original_message:
Add pictures of the Astarte Dashboard

Add screenshot images of various pages of the Astarte Dashboard.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -203,7 +203,7 @@ $ astartectl appengine devices get-samples <your device id> org.astarte-platform
If you get a meaningful value, congratulations - you have a working Astarte installation with your first `datastream` coming in!
-Moreover, Astarte's Docker Compose also installs [Astarte Dashboard](https://github.com/astarte-platform/astarte-dashboard), from which you can manage your Realms and install Triggers, Interfaces and more from a Web UI. It is accessible by default at `http://localhost:4040/` - remember that if you are not exposing Astarte from `localhost`, you have to change Realm Management API's URL in Dashboard's configuration file, to be found in `compose/astarte-dashboard/config.json` in Astarte's repository. You can generate a token for Astarte Dashboard, as usual, through `astartectl utils gen-jwt realm-management -k test_private.pem`. Grant a longer expiration by using the `-e` parameter to avoid being logged out too quickly.
+Moreover, Astarte's Docker Compose also installs [Astarte Dashboard](https://github.com/astarte-platform/astarte-dashboard), from which you can manage your Realms and install Triggers, Interfaces and more from a Web UI. It is accessible by default at `http://localhost:4040/` - remember that if you are not exposing Astarte from `localhost`, you have to change Realm Management API's URL in Dashboard's configuration file, to be found in `compose/astarte-dashboard/config.json` in Astarte's repository. You can generate a token for Astarte Dashboard, as usual, through `astartectl utils gen-jwt all-realm-apis -k test_private.pem`. By default, `astartectl` will generate a token valid for 8 hours, but you can set a specific expiration by using the `-e <seconds>` parameter.
From here on, you can use all of Astarte's APIs and features from your own installation. You can add devices, experiment with interfaces, or develop your own applications on top of Astarte's triggers or AppEngine's APIs. And have a lot of fun!

original_message:
Fix command in docs to generate JWT for Dashboard

Fix Astarte in 5 minutes guide to suggest the right command to generate
a token for the Dashboard with sufficient claims.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -20,7 +20,7 @@ AppEngine is Astarte's main API endpoint for end users. AppEngine exposes a REST
## Realm Management
-Realm Management is an *administrator-like* API for configuring a Realm. It is used for managing Interfaces, Triggers, Devices and more.
+Realm Management is an *administrator-like* API for configuring a Realm. It is used for managing Interfaces and Triggers.
## Housekeeping

diff:
@@ -13,8 +13,8 @@ When interacting with Astarte as a User, you have several options to choose from
* [astartectl](https://github.com/astarte-platform/astartectl): `astartectl` is the main command-line tool to interact with Astarte clusters, which packs
in a number of subcommands to interact with Astarte API sets. It is a swiss army knife to perform daily operations on Astarte Clusters, and it abstracts
most Astarte API interactions in a user-friendly way.
-* Astarte Dashboard: Astarte provides a built-in UI that can be used for managing Interfaces, Devices and Triggers. If your cluster has the dashboard enabled, it can
- be usually found at `dashboard.<base Astarte URL>`. It is meant to be a graphical, user-friendly tool to perform daily operations in the Cluster.
+* [Astarte Dashboard](015-astarte_dashboard.html): Astarte provides a built-in UI that can be used for managing Interfaces, Devices and Triggers.
+ It is meant to be a graphical, user-friendly tool to perform daily operations on Realms.
* Astarte API Clients: API Clients are provided for a variety of languages. These clients abstract API interaction with language-friendly paradigms, and provide
API automations for several operations. Currently, the main API client available is [astarte-go](https://github.com/astarte-platform/astarte-go).
* Astarte APIs: The base APIs are the lower level interaction layer. They are accessible, in standard installations, at `api.<base Astarte URL>/<apiset>`, and are

diff:
+# Astarte Dashboard
+
+Astarte provides a built-in UI that can be used for managing Interfaces, Devices, and Triggers. The
+Dashboard simplifies the development phases of applications that make use of Astarte, as well as
+troubleshooting activities.
+
+You can browse the source code of the Dashboard software on its
+[GitHub repository](https://github.com/astarte-platform/astarte-dashboard).
+
+## Introduction
+
+The Astarte Dashboard is a Single Page Application that provides users with an overview of their
+Realm and a user-friendy way of managing it on any web browser. The Dashboard is designed to be a
+quick and easy way to give you immediate feedback on your work and as a quick and intuitive way to
+configure your realm. It is not designed to be operated by end-users, rather by infrastructure
+maintainers, owners who need information on the system status, and those working on projects based
+on Astarte. It is shipped by default with the Astarte standard distribution.
+
+The Dashboard is a graphical client for Astarte APIs; it shares similar features with the CLI client
+[astartectl](https://github.com/astarte-platform/astartectl), the command-line utility to manage
+Astarte.
+
+The Dashboard helps you manage:
+
+- [Triggers](015-astarte_dashboard.html#triggers)
+- [Interfaces](015-astarte_dashboard.html#interfaces)
+- [Devices](015-astarte_dashboard.html#devices)
+- [Groups](015-astarte_dashboard.html#groups)
+- [Realm Settings](015-astarte_dashboard.html#realm-settings)
+
+In case your Astarte distribution comes packaged with the
+[Flow framework](https://docs.astarte-platform.org/flow/snapshot), the Dashboard is probably
+configured to manage Flow resources as well.
+
+- [Blocks](015-astarte_dashboard.html#blocks)
+- [Pipelines](015-astarte_dashboard.html#pipelines)
+- [Flows](015-astarte_dashboard.html#flows)
+
+Please note that the Flow framework is not available for use in a docker-compose environment since
+it relies on Kubernetes APIs to operate.
+
+### How to access it
+
+Depending on how you are using Astarte, here is where you can find the Dashboard:
+
+- Docker-compose: if you are using a local instance of Astarte via `docker-compose`, you will find
+ it by pointing your browser to the default address `http://localhost:4040/`. To login, fill in the
+ name of your realm and a valid JWT token: if you possess the realm private key, as it is the case
+ if you followed the [Astarte in 5 minutes](010-astarte_in_5_minutes.html) guide, you can generate
+ the token with the command `astartectl utils gen-jwt all-realm-apis -k <private_key>`.
+- Astarte Cloud: if you are using our managed Astarte option, you can hop onto our
+ [Console](https://console.astarte.cloud/realms), find or create your realm and click the Dashboard
+ button.
+- Kubernetes cluster: in this case, if the Dashboard is enabled, it can be usually found at
+ `dashboard.<base Astarte URL>`. Refer to your system administrator for more details.
+
+## Main overview
+
+Upon successful login, the main screen is the home page that provides an overview of the realm
+status and resources.
+
+![Astarte Dashboard with a realm overview](assets/astarte-dashboard-realm-overview.png)
+
+### API Status
+
+The API Status gives you general information about the status of services:
+
+- **Realm Management** is an _administrator-like_ API for configuring a Realm. It is used for
+ managing Interfaces and Triggers.
+
+- **AppEngine** is Astarte's main API endpoint for end-users. AppEngine exposes a RESTful API to
+ retrieve and send data from/to devices, according to their interfaces. Every direct device
+ interaction can be done from here. It also exposes Channels, a WebSocket-based solution for
+ listening to device events in real-time with the same mechanism and semantics used by Triggers.
+
+- **Pairing** takes care of Device Authentication and Authorization. It interacts with Astarte's CA
+ and orchestrates the way devices connect and interact with Transports. It also handles Device
+ Registration. Agent, Device and Pairing interaction is described in detail
+ [here](050-pairing_mechanism.html).
+
+- **Flow** is the API endpoint for Astarte Flow, used for managing Blocks, Pipelines, and Flows.
+
+Possible statuses are:
+
+1. This service is operating normally.
+2. This service appears offline.
+
+A general status on API health is also present in the app's sidebar, thus always providing a
+feedback regardless of which page you are currently visiting.
+
+### Realm resources
+
+Within the main overview, a brief summary is available as well for the existing resources of the
+realm: registered and connected devices, installed interfaces, and installed triggers.
+
+More detailed overviews of each resource are available in the dedicated Dashboard sections,
+accessible via the navigation links in the Dashboard's side menu.
+
+## Interfaces
+
+[Interfaces](030-manage_interfaces.html) are a core concept of Astarte which defines how data is
+exchanged between Astarte and its peers.
+
+You can navigate to this section thanks to the side menu of the Dashboard. A list of all installed
+interfaces is displayed, together with their major versions.
+
+![Astarte Dashboard, interface list](assets/astarte-dashboard-interfaces.png)
+
+### Installing interfaces
+
+From the Interface list, clicking on the **Install a new interface** button will load up the
+**Interface Editor**, an interactive tool that you can use to configure your interfaces.
+
+The Interface Editor provides you with two ways to define your interfaces: on the left panel, a
+graphical frontend, while on the right panel you may input a JSON definition to achieve the same
+result. Each panel updates automatically whenever the other is changed.
+
+![Astarte Dashboard, Interface Editor](assets/astarte-dashboard-interface-editor.png)
+
+While defining a new interface, the Interface Editor will help you in supplying the right options
+and filling in mandatory entries such as:
+
+- Name: an arbitrary name, formatted in reversed DNS casing.
+- Major and Minor versions: based on [Semantic Versioning](https://semver.org/).
+- Type: indicates whether data is streamed continuously (`datastream`) or is stateful and persistent
+ (`properties`).
+- Ownership: the write-only allowed actor. All the other actors are read-only.
+- Mappings: a list of endpoints that represent the data structure, following REST controller
+ semantics.
+
+You can learn more about Interface definitions in their
+[documentation's section](030-interface.html).
+
+Note that when creating interface drafts, or for testing purposes in general, it is recommended to
+use `0` as the major version: to prevent data loss, Astarte allows only interfaces where
+`major_version` equals `0` to be deleted.
+
+### Managing interfaces
+
+From the Interface list, you can select an interface to load and view its details in the Interface
+Editor. Clicking on the name of the interface will select its latest revision; clicking on a
+specific major of the interface will select the latest revision for that major. Note that interface
+revisions follow the [Semantic Versioning](https://semver.org/) convention.
+
+Once the Interface Editor is loaded you can review, update or delete the definition of the
+interface.
+
+Note that to prevent data loss, Astarte allows only interfaces where `major_version` equals `0` to
+be deleted.
+
+For similar reasons, when updating the definition of an interface, the Interface Editor will not
+allow you to change core properties on a `minor` version update. If you need to apply substantial
+changes, you can define and install a new `major` version for the interface.
+
+## Triggers
+
+[Triggers](060-using_triggers.html) in Astarte are the go-to mechanism for generating push events.
+
+You can navigate to this section thanks to the side menu of the Dashboard. A list of all installed
+triggers is displayed.
+
+![Astarte Dashboard, trigger list](assets/astarte-dashboard-triggers.png)
+
+### Installing triggers
+
+From the Trigger list, clicking on the **Install a new trigger** button will load up the **Trigger
+Editor**, an interactive tool that you can use to configure your triggers. It works in a very
+similar fashion to Interface Editor and shares the same User Interface.
+
+The Trigger Editor provides you with two ways to define your triggers: on the left panel, a
+graphical frontend, while on the right panel you may input a JSON definition to achieve the same
+result. Each panel updates automatically whenever the other is changed.
+
+The graphical tool dynamically loads installed Interfaces in the Realm and eases trigger creation by
+providing not only linting and validation, but also dynamic resolution of Interface names.
+
+![Astarte Dashboard, Trigger Editor](assets/astarte-dashboard-trigger-editor.png)
+
+You can learn more about Trigger definitions in their [documentation's section](060-triggers.html).
+
+Note that due to how triggers work, you should install the trigger before a device connects. Doing
+otherwise will cause the trigger to kick in at a later time, and as such no events will be streamed
+for a while.
+
+### Managing triggers
+
+From the Trigger list, you can select a trigger to load and view its details in the Trigger Editor.
+
+Once the Trigger Editor is loaded you can review the definition of the trigger.
+
+You can also delete the trigger instance by clicking on the **Delete trigger** button.
+
+## Devices
+
+Devices are Astarte's main entities for exchanging data.
+
+You can navigate to this section thanks to the side menu of the Dashboard. A list of all registered
+devices is displayed.
+
+![Astarte Dashboard, device list](assets/astarte-dashboard-devices.png)
+
+Each device in the list is displayed together with info regarding its _status_ and the _last
+connection event_. The status is represented by a grey dot if the device never connected to Astarte,
+a green dot if it is currently connected, a red dot if it is currently disconnected. The last
+connection event reports, if available, the date of the last connection or disconnection.
+
+A filter section is present on the side of the list to aid the search for specific devices,
+filtering the list by device ID, name, connection status, or configured metadata.
+
+### Registering a device
+
+From the Device list, new devices may be registered by clicking on the **Register a new device**
+button which will take you to the registration page.
+
+Here you can proceed with the registration of the device by:
+
+- providing a device ID: either by generating a random ID or by specifying a `Name` and
+ `Namespace UUID` to generate the ID in a deterministic fashion.
+- optionally declaring the initial _introspection_ of the device: this is an indication of the list
+ of interfaces that the device will use to exchange data.
+
+You can learn more about Devices and the registration process in their
+[documentation's section](035-register_device.html).
+
+### Device status and details
+
+From the Device list, you can select a device to navigate to its dedicated page.
+
+Here you can review and manage different info about your device.
+
+#### Device Info
+
+- **Device Info**: displays info such as the device ID and the device Name alias, if set. It reports
+ whether the device is currently connected or disconnected, or if it was never connected. From this
+ section you can also momentarily **Inhibit credentials** for the device, preventing it to obtain
+ access to Astarte; or you can directly **Wipe credential secret** of the device, a permanent
+ action which will require to register the device again to have a new Credential Secret.
+- **Aliases**: where you can manage custom aliases for the device. Note that setting a `name` alias
+ will provide a name for the device.
+- **Metadata**: a dedicated section to attach arbitrary info to the device, in a key-value form.
+- **Groups**: where you can review and manage the Groups the device belongs to.
+- **Interfaces**: a list of all currently and previously used interfaces. Clicking on an interface's
+ name will load a dedicated page to review data exchanged by the device through that interface.
+- **Stats**: a rundown on exchanged data via different interfaces. Here you can review, in both
+ visual and numeric form, the quantity of bytes and messages the device is exchanging over each
+ interface. This way you can always know at a glance which interfaces are the busiest and how
+ chatty your device is.
+- **Status Events**: here is info collected by Astarte regarding the IP addresses involved in the
+ connections of the device to Astarte, the dates of first registration and credentials request, and
+ the dates of last connection and disconnection, if available.
+- **Live Events**: a section that reports live events regarding the device. It makes use of
+ [Astarte Channels](052-using_channels.html) and displays the connections, exchanged data, and
+ errors of the device, as they happen in real-time.
+
+## Groups
+
+[Groups](065-managing-groups.html) are logical collections of devices to ease the management and
+querying of devices.
+
+You can navigate to this section thanks to the side menu of the Dashboard. A list of all existing
+groups is displayed, together with the number of total and connected devices for each group.
+
+### Creating a group
+
+From the Group list, clicking on the **Create a new group** button will load a dedicated page to
+setup the new group.
+
+You are required to specify a name for the group and to select at least one device that will belong
+to it. Indeed, note that a group must contain at least one device to exist.
+
+To confirm the creation of the new group, click the **Create group** button.
+
+### Managing groups
+
+From the Group list, you can select a group to view its configuration on a dedicated page. Here you
+can review the list of devices that belong to it.
+
+To remove a device from a group, click on the Delete icon next to it.
+
+To add a device to a group, you can first navigate to the device's page and then add it to a group
+from there.
+
+To delete a group, remove all devices that belong to it and the group will automatically cease to
+exist.
+
+## Blocks
+
+[Blocks](https://docs.astarte-platform.org/flow/snapshot/0003-blocks.html) are computation units
+that can be chained together to define a logical computation topology.
+
+You can navigate to this section thanks to the side menu of the Dashboard. Here is a list of all
+existing blocks, both custom and native ones: the former ones are those defined by you, the latter
+ones are those provided by Astarte and are displayed with a `native` label.
+
+### Creating blocks
+
+From the Block list, clicking on the **Create** button will load a dedicated page to define a new
+block.
+
+Here you can define the block by specifying:
+
+- A `name` for the block.
+- A `type`, between `Producer`, `Consumer` or `Producer & Consumer`, depending on how the block
+ should treat messages and connect to other blocks.
+- A `schema`, reporting the JSON Schema definition of how a configuration should look like for the
+ block.
+- A `source`, containing the implementation of the block, written in the
+ [DSL format](https://docs.astarte-platform.org/flow/snapshot/0010-defining-a-pipeline.html) for
+ Pipelines.
+
+Confirm the creation of the block by clicking on the **Create new block** button.
+
+To learn more about block definition you can read their
+[documentation's section](https://docs.astarte-platform.org/flow/snapshot/0003-blocks.html).
+
+### Managing blocks
+
+From the Block list, you can select a block to view its definition in a dedicated page.
+
+Here you can review the details of the block such as the block type, the schema and, if it is a
+custom block, its source.
+
+To delete a block, click on the **Delete block** button. Note that you cannot delete `native` blocks
+provided by Astarte.
+
+## Pipelines
+
+A [Pipeline](https://docs.astarte-platform.org/flow/snapshot/0004-pipelines.html) is a computation
+blueprint (therefore a description) built as a chain of blocks.
+
+You can navigate to this section thanks to the side menu of the Dashboard. Here is a list of all
+existing pipelines from which you can review, manage and
+[instantiate](015-astarte_dashboard.html#instantiating-flows) them.
+
+### Creating pipelines
+
+From the Pipeline list, clicking on the **Create** button will load up the **Pipeline Editor** page,
+an interactive tool that you can use to configure your pipeline.
+
+The Pipeline Editor is composed of two parts:
+
+- A sidebar listing all available Blocks, grouped by type
+- A space where you can drag & drop blocks, connecting them to effectively design a pipeline
+
+You can read more about the Pipeline Editor on the
+[dedicated documentation](https://docs.astarte-platform.org/flow/snapshot/0020-pipeline-editor.html).
+
+Once you have designed your pipeline, you can review and specify its:
+
+- `name`
+- `source`, containing the implementation of the pipeline, written in the
+ [DSL format](https://docs.astarte-platform.org/flow/snapshot/0010-defining-a-pipeline.html) for
+ Pipelines.
+- `schema`, reporting the JSON Schema definition of how a configuration should look like for the
+ pipeline.
+- `description`, explaining the scope and supposed usage of the pipeline.
+
+Then hit the **Create new pipeline** button to confirm the definition of the pipeline.
+
+### Managing pipelines
+
+From the Pipeline list, you can select a pipeline to view its definition in a dedicated page.
+
+Here you can review the details of the pipeline such as the pipeline description, its schema, and
+its source.
+
+To delete a pipeline, click on the **Delete pipeline** button.
+
+## Flows
+
+[Flows](https://docs.astarte-platform.org/flow/snapshot/0005-flows.html) are specific instances of a
+pipeline, created providing concrete values to the parametric configuration of a pipeline.
+
+You can navigate to this section thanks to the side menu of the Dashboard. Here is a list of all
+existing flows. Each flow reports its current status and the pipeline it originated from.
+
+To review the details of a Flow, click on its name to load up the dedicated page.
+
+To delete a flow, click on the Delete icon next to it.
+
+### Instantiating flows
+
+From the Pipeline list, clicking on the **Instantiate** button of a Pipeline will load the Flow
+configuration page; from there you can supply a name and a configuration for the flow before hitting
+the **Instantiate Flow** button to confirm.
+
+## Realm Settings
+
+You can navigate to this section thanks to the side menu of the Dashboard. Here you can review and
+update settings for your realm.
+
+You can update the public key of the realm, which is useful if you mean to use a new private key to
+generate auth tokens. Please note that it is a permanent action and Astarte will prevent
+interactions that use auth tokens generated with the previous key.

original_message:
Add a documentation page for the Astarte Dashboard

Add a page in the User Documentation to document the usage of the
Astarte Dashboard.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


message:
Add Readme files for javascript examples
Add README.md files as documentation on supposed usage of javascript
examples:
javascript-sensors-viewer
sensor-graph
sensors-viewer
A README.md file is added as well on the root folder for examples.

diff:
+# Examples
+
+This is a collection of examples that shows how to interact with Astarte in common use cases.
+
+## JavaScript examples
+
+- [javascript-sensors-viewer](javascript-sensors-viewer): sample web page in vanilla JavaScript to
+ show available sensors of a device and their data.
+- [sensors-viewer](sensors-viewer): sample web page in React to show available sensors of a device
+ and their data.
+- [sensor-graph](sensor-graph): sample web page in React to plot data coming from a sensor of a
+ device.

diff:
+# Sensors Viewer example
+
+This is a sample web page made in vanilla JavaScript that you can use to show available sensors of a
+device and their data.
+
+You can follow the walkthrough below if you wish to try it out: you will first simulate a device
+that streams data from its sensors to Astarte, then use this web page to retrieve an overview of the
+device sensors from Astarte.
+
+## Prerequisites
+
+For this example you should:
+
+1. have an Astarte instance up and running with a realm you have access to
+2. install the [example interfaces](../../standard-interfaces/) in the realm
+3. register a device that will use those interfaces in the realm; be sure to note down its device ID
+ and Credentials Secret
+4. modify the `configuration` variable in `index.html` and specify: the `endpoint` of the Astarte
+ instance, the `realm` you chose, a valid JWT `token` that can be used to communicate with
+ AppEngine, the `device` ID your registered.
+
+## Docker walkthrough
+
+You can use Astarte's `stream-qt5-test` to emulate an Astarte device and generate a `datastream`:
+
+```sh
+docker run --net="host" -e "DEVICE_ID=<device_id>" -e "PAIRING_HOST=<pairing_host>" -e "REALM=<realm>" -e "AGENT_KEY=<agent_key>" -e "IGNORE_SSL_ERRORS=true" astarte/astarte-stream-qt5-test:0.11.4
+```
+
+where `device_id` is the device you registered, `pairing_host` is the Astarte endpoint for the
+Pairing APIs, `realm` is the name of your realm, `agent_key` is the valid JWT token of an Agent.
+
+If all went well, `stream-qt5-test` should now be streaming data to Astarte.
+
+Going back to the web page of this example, you can input the device ID to request an overview of
+the device sensors and their data.
+
+![Sensors Viewer example, requesting the overview of a device sensors and their data](../images/javascript-sensors-viewer-stream-test.png)

diff:
+# Sensor Graph example
+
+This is a sample web page made in React that you can use to plot data coming from a sensor of a
+device.
+
+You can follow the walkthrough below if you wish to try it out: you will first simulate a device
+that streams data from its sensors to Astarte, then use this web page to plot the data.
+
+## Prerequisites
+
+For this example you should:
+
+1. have an Astarte instance up and running with a realm you have access to
+2. install the [example interfaces](../../standard-interfaces/) in the realm
+3. register a device that will use those interfaces in the realm; note down its device ID and
+ Credentials Secret
+4. have [NodeJS](https://nodejs.org/) installed on your machine and serve this web page with
+ ```sh
+ npm install
+ npm run start
+ ```
+
+If all went well, you should see a form where you can supply:
+
+- the base URL of the Astarte instance
+- the name of your realm
+- a valid JWT access token for the realm that can be used to communicate with AppEngine
+
+## Docker walkthrough
+
+You can use Astarte's `stream-qt5-test` to emulate an Astarte device and generate a `datastream`:
+
+```sh
+docker run --net="host" -e "DEVICE_ID=<device_id>" -e "PAIRING_HOST=<pairing_host>" -e "REALM=<realm>" -e "AGENT_KEY=<agent_key>" -e "IGNORE_SSL_ERRORS=true" astarte/astarte-stream-qt5-test:0.11.4
+```
+
+where `device_id` is the device you registered, `pairing_host` is the Astarte endpoint for the
+Pairing APIs, `realm` is the name of your realm, `agent_key` is the valid JWT token of an Agent.
+
+If all went well, `stream-qt5-test` should now be streaming data to Astarte.
+
+Going back to the web page of this example, you can input the device ID to request a plot of the
+device data.
+
+![Sensor Graph example, requesting the plot of a device sensor](../images/sensor-graph.png)

diff:
+# Sensors Viewer example
+
+This is a sample web page made in React that you can use to show available sensors of a device and
+their data.
+
+You can follow the walkthrough below if you wish to try it out: you will first simulate a device
+that streams data from its sensors to Astarte, then use this web page to retrieve an overview of the
+device sensors from Astarte.
+
+## Prerequisites
+
+For this example you should:
+
+1. have an Astarte instance up and running with a realm you have access to
+2. install the [example interfaces](../../standard-interfaces/) in the realm
+3. register a device that will use those interfaces in the realm; note down its device ID and
+ Credentials Secret
+4. have [NodeJS](https://nodejs.org/) installed on your machine and serve this web page with
+ ```sh
+ npm install
+ npm run start
+ ```
+
+If all went well, you should see a form where you can supply:
+
+- the base URL of the Astarte instance
+- the name of your realm
+- a valid JWT access token for the realm that can be used to communicate with AppEngine
+
+## Docker walkthrough
+
+You can use Astarte's `stream-qt5-test` to emulate an Astarte device and generate a `datastream`:
+
+```sh
+docker run --net="host" -e "DEVICE_ID=<device_id>" -e "PAIRING_HOST=<pairing_host>" -e "REALM=<realm>" -e "AGENT_KEY=<agent_key>" -e "IGNORE_SSL_ERRORS=true" astarte/astarte-stream-qt5-test:0.11.4
+```
+
+where `device_id` is the device you registered, `pairing_host` is the Astarte endpoint for the
+Pairing APIs, `realm` is the name of your realm, `agent_key` is the valid JWT token of an Agent.
+
+If all went well, `stream-qt5-test` should now be streaming data to Astarte.
+
+Going back to the web page of this example, you can input the device ID to request an overview of
+the device sensors and their data.
+
+![Sensors Viewer example, requesting the overview of a device sensors and their data](../images/sensors-viewer-stream-test.png)

original_message:
Add Readme files for javascript examples

Add README.md files as documentation on supposed usage of javascript
examples:
- javascript-sensors-viewer
- sensor-graph
- sensors-viewer
A README.md file is added as well on the root folder for examples.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -96,7 +96,7 @@ Also, we will need a JWT token to authenticate against Housekeeping. `generate-c
Use `astartectl` to create a new Realm:
```sh
-$ astartectl housekeeping realms create test --housekeeping-url http://localhost:4001/ -p test_public.pem -k compose/astarte-keys/housekeeping_private.pem
+$ astartectl housekeeping realms create test --housekeeping-url http://localhost:4001/ --realm-public-key test_public.pem -k compose/astarte-keys/housekeeping_private.pem
```
This creates a `test` realm, which should be ready to be used almost immediately. To ensure your realm is available and ready, check if it exists in Astarte by issuing:

original_message:
Fix astartectl command to create a realm

Fix Astarte in 5 minutes guide where the astartectl command to create a
realm used a not existent flag.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -125,7 +125,7 @@ We will also test Astarte's push capabilities with a trigger. This will send a P
Due to how triggers work, it is fundamental to install the trigger before a device connects. Doing otherwise will cause the trigger to kick in at a later time, and as such no events will be streamed for a while.
-Replace `http://example.com` with your target URL in the command below, you can use a Postbin service like [Mailgun Postbin](http://bin.mailgun.net) to generate a URL and see the POST requests. The resulting trigger would be:
+Replace `$TRIGGER_TARGET_URL` with your target URL in the example below, you can use a Postbin service like [Mailgun Postbin](http://bin.mailgun.net) to generate a URL and see the POST requests. The resulting trigger would be:
```json
{

original_message:
Fix typo in docs for Astarte in 5 minutes

Fix typo in Astarte in 5 minutes guide where there was a reference to a
non existent URL.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


message:
Add Readme files for javascript examples
Add README.md files as documentation on supposed usage of javascript
examples:
sensor-channels
sensor-samplingrate

diff:
@@ -10,3 +10,7 @@ This is a collection of examples that shows how to interact with Astarte in comm
and their data.
- [sensor-graph](sensor-graph): sample web page in React to plot data coming from a sensor of a
device.
+- [sensor-channels](sensor-channels): sample web page in React to show live data streamed in real
+ time from a device sensors.
+- [sensor-samplingrate](sensor-samplingrate): sample web page in React to show sensors of a device
+ and configure them

diff:
+# Sensor Channels example
+
+This is a sample web page made in React that leverages Astarte Channels and shows live data of a
+device sensors streamed in real time.
+
+You can follow the walkthrough below if you wish to try it out: you will simulate a device that streams data from its sensors to Astarte and see this data flowing in real time.
+
+## Prerequisites
+
+For this example you should:
+
+1. have an Astarte instance up and running with a realm you have access to
+2. install the [example interfaces](../../standard-interfaces/) in the realm
+3. register a device that will use those interfaces in the realm; be sure to note down its device ID
+ and Credentials Secret
+4. have [NodeJS](https://nodejs.org/) installed on your machine and serve this web page with
+ ```sh
+ npm install
+ npm run start
+ ```
+
+If all went well, you should see a form where you can supply:
+
+- the base URL of the Astarte instance
+- the name of your realm
+- a valid JWT access token for the realm that can be used to communicate with AppEngine and Channels
+
+## Docker walkthrough
+
+You can use Astarte's `stream-qt5-test` to emulate an Astarte device and generate a `datastream`:
+
+```sh
+docker run --net="host" -e "DEVICE_ID=<device_id>" -e "PAIRING_HOST=<pairing_host>" -e "REALM=<realm>" -e "AGENT_KEY=<agent_key>" -e "IGNORE_SSL_ERRORS=true" astarte/astarte-stream-qt5-test:1.0.0-beta.1
+```
+
+where `device_id` is the device you registered, `pairing_host` is the Astarte endpoint for the
+Pairing APIs, `realm` is the name of your realm, `agent_key` is the valid JWT token of an Agent.
+
+If all went well, `stream-qt5-test` should now be streaming data to Astarte.
+
+Going back to the web page of this example, you can input the device ID to begin watching the
+sensors data being streamed in real time from the device.
+
+![Sensor Channels example, showing real time sensors data](../images/sensor-channels.png)

original_message:
Add Readme files for javascript examples

Add README.md files as documentation on supposed usage of javascript
examples:
- sensor-channels
- sensor-samplingrate

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -203,6 +203,9 @@ yaml file will be saved, the Operator will take over ensuring the reconciliation
instance to the requested version.
### Caveats
+
+#### CFSSL leftover persistent volume
+
Astarte v0.11 employs a persistent volume to store CA certificate and private key, while upgrading
to v1.0 involves a change in how device certificates are stored as, behind the scenes, these data
are held as a Kubernetes TLS secret.
@@ -213,3 +216,9 @@ claim are left within the cluster even if not used anymore.
If you followed the procedure described [here](#migrate-ca-certificate-and-key) you are free to
remove the CFSSL persistent volume and claim without the need for your devices to request new
credentials.
+
+#### AppEngine `/socket` route removal
+
+The `/socket` endpoint exposed by AppEngine to interact with Astarte Channels, which was already
+deprecated in Astarte v0.11, has been removed. You must use the new route `/v1/socket`
+instead.

original_message:
Add note on /socket endpoint deprecation

Add caveat for 0.11 -> 1.0 migration docs to advise users to use the new
Astarte Channels endpoint.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
+{
+ "interface_name": "org.astarte-platform.genericsensors.Geolocation",
+ "version_major": 1,
+ "version_minor": 0,
+ "type": "datastream",
+ "aggregation": "object",
+ "ownership": "device",
+ "description": "Generic Geolocation sampled data.",
+ "doc": "Geolocation allows geolocation sensors to stream location data, such as GPS data. It is usually used in combination with AvailableSensors, which makes API client aware of what sensors are present on devices and what measurement systems are used. sensor_id represents a unique identifier for an individual sensor, and should match sensor_id in AvailableSensors when used in combination.",
+ "mappings": [
+ {
+ "endpoint": "/%{sensor_id}/latitude",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled latitude value."
+ },
+ {
+ "endpoint": "/%{sensor_id}/longitude",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled longitude value."
+ },
+ {
+ "endpoint": "/%{sensor_id}/altitude",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled altitude value."
+ },
+ {
+ "endpoint": "/%{sensor_id}/accuracy",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled accuracy of the latitude and longitude properties."
+ },
+ {
+ "endpoint": "/%{sensor_id}/altitudeAccuracy",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled accuracy of the altitude property."
+ },
+ {
+ "endpoint": "/%{sensor_id}/heading",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled value representing the direction towards which the device is facing."
+ },
+ {
+ "endpoint": "/%{sensor_id}/speed",
+ "type": "double",
+ "explicit_timestamp": true,
+ "description": "Sampled value representing the velocity of the device."
+ }
+ ]
+}

original_message:
Add Geolocation standard interface

Add org.astarte-platform.genericsensors.Geolocation interface definition
to the standard-interfaces folder.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -14,3 +14,5 @@ This is a collection of examples that shows how to interact with Astarte in comm
time from a device sensors.
- [sensor-samplingrate](sensor-samplingrate): sample web page in React to show sensors of a device
and configure them
+- [sensor-geolocation](sensor-geolocation): sample web page in React to show positional data coming
+ from GPS sensors on a map.

diff:
+# Sensor Geolocation example
+
+This is a sample web page made in React that you can use to show positional data coming from GPS sensors of a device.
+
+You can follow the walkthrough below if you wish to try it out: you will first simulate a device
+that streams data from its GPS sensor to Astarte, then use this web page to show the sensor's location on a map.
+
+## Prerequisites
+
+For this example you should:
+
+1. have an Astarte instance up and running with a realm you have access to
+2. install the [example interfaces](../../standard-interfaces/) in the realm
+3. register a device that will use those interfaces in the realm; note down its device ID and
+ Credentials Secret
+4. have [NodeJS](https://nodejs.org/) installed on your machine and serve this web page with
+ ```sh
+ npm install
+ npm run start
+ ```
+
+If all went well, you should see a form where you can supply:
+
+- the base URL of the Astarte instance
+- the name of your realm
+- a valid JWT access token for the realm that can be used to communicate with AppEngine
+
+## Elixir walkthrough
+
+A device SDK, such as the
+[Elixir SDK](https://github.com/astarte-platform/astarte-device-sdk-elixir), can be used to simulate
+the device and:
+
+- advertise a GPS sensor to Astarte, via the `AvailableSensors` interface
+- stream some data from the GPS sensor to Astarte, via the `Geolocation` interface
+
+You can download a local copy of the Elixir SDK on your machine, install
+[Elixir](https://elixir-lang.org/), and launch an interactive session with the SDK with:
+
+```sh
+iex -S mix
+```
+
+Let's first load some useful variables in the interactive session; replace right members of the
+assignments as needed:
+
+```elixir
+device_id = "DEVICE_ID"
+credentials_secret = "DEVICE_CREDENTIALS_SECRET"
+realm = "REALM_NAME"
+pairing_url = "PAIRING_URL"
+interfaces_folder = "/path/to/interfaces"
+```
+
+Note that `interfaces_folder` is the folder on your machine where you have the JSON definitions for
+the [example interfaces](../../standard-interfaces/).
+
+We can pass these options to the SDK to create a virtual Device and have it connected to the realm:
+
+```elixir
+opts = [pairing_url: pairing_url, realm: realm, device_id: device_id, credentials_secret: credentials_secret, interface_provider: interfaces_folder, ignore_ssl_errors: true]
+{:ok, pid} = Astarte.Device.start_link(opts)
+```
+
+The virtual Device should now be connected. Let's use it to advertise a GPS sensor that will stream data adherent to W3C specs:
+
+```elixir
+Astarte.Device.set_property(pid, "org.astarte-platform.genericsensors.AvailableSensors", "/gps1/name", "Car location")
+Astarte.Device.set_property(pid, "org.astarte-platform.genericsensors.AvailableSensors", "/gps1/unit", "W3C")
+```
+
+You can now stream a sample GPS measurement:
+
+```elixir
+Astarte.Device.send_datastream(pid, "org.astarte-platform.genericsensors.Geolocation", "/gps1", %{ latitude: 45.463, longitude: 9.188 })
+```
+
+Going back to the web page of this example, you can input the device ID to retrieve the device's location and display it on map:
+
+![Sensor Geolocation example, displaying a device's location on a map](../images/sensor-geolocation.png)

original_message:
Add Readme file for the sensor-geolocation example

Add README.md file as documentation on supposed usage of the
sensor-geolocation example.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


diff:
@@ -55,7 +55,7 @@ Supported token claims are:
* `a_aea`: Defines the regular expressions for AppEngine API
* `a_rma`: Defines the regular expressions for Realm Management API
-* `a_hka`: Defines the regular expressions for Housekeeping API
+* `a_ha`: Defines the regular expressions for Housekeeping API
* `a_pa`: Defines the regular expressions for Pairing API
* `a_ch`: Defines the regular expressions for Channels

original_message:
Fix typo a_hka for Housekeeping token claims in docs

Rename a_hka token claims to a_ha.

Signed-off-by: Davide Briani <davide.briani@ispirata.com>


message: Support CREATE2 opcode in Constantinople

diff:
@@ -70,7 +70,8 @@ enum evmc_call_kind
EVMC_CALL = 0, /**< Request CALL. */
EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. The value param ignored. */
EVMC_CALLCODE = 2, /**< Request CALLCODE. */
- EVMC_CREATE = 3 /**< Request CREATE. Semantic of some params changes. */
+ EVMC_CREATE = 3, /**< Request CREATE. Semantic of some params changes. */
+ EVMC_CREATE2 = 4 /**< Request CREATE2. Semantic of some params changes. */
};
/** The flags for ::evmc_message. */

repo: ethereum/evmc


message: CREATE2 support in instructions lib

diff:
@@ -166,6 +166,7 @@ enum evmc_opcode
OP_CALLCODE = 0xf2,
OP_RETURN = 0xf3,
OP_DELEGATECALL = 0xf4,
+ OP_CREATE2 = 0xf5,
OP_STATICCALL = 0xfa,

diff:
@@ -251,7 +251,7 @@ static const char* constantinople_names[256] = {
/* 0xf2 */ "CALLCODE",
/* 0xf3 */ "RETURN",
/* 0xf4 */ "DELEGATECALL",
- /* 0xf5 */ NULL,
+ /* 0xf5 */ "CREATE2",
/* 0xf6 */ NULL,
/* 0xf7 */ NULL,
/* 0xf8 */ NULL,

diff:
@@ -88,6 +88,22 @@ TEST(instructions, byzantium_hard_fork)
EXPECT_EQ(sdn[OP_STATICCALL], nullptr);
}
+TEST(instructions, constantinople_hard_fork)
+{
+ const auto c = evmc_get_instruction_metrics_table(EVMC_CONSTANTINOPLE);
+ const auto b = evmc_get_instruction_metrics_table(EVMC_BYZANTIUM);
+ const auto cn = evmc_get_instruction_names_table(EVMC_CONSTANTINOPLE);
+ const auto bn = evmc_get_instruction_names_table(EVMC_BYZANTIUM);
+
+ EXPECT_EQ(c[OP_CREATE2].gas_cost, 32000);
+ EXPECT_EQ(c[OP_CREATE2].num_stack_arguments, 4);
+ EXPECT_EQ(c[OP_CREATE2].num_stack_returned_items, 1);
+ EXPECT_EQ(b[OP_CREATE2].gas_cost, -1);
+ EXPECT_EQ(cn[OP_CREATE2], std::string{"CREATE2"});
+ EXPECT_EQ(bn[OP_CREATE2], nullptr);
+}
+
+
TEST(instructions, name_gas_cost_equivalence)
{
for (auto rev = EVMC_FRONTIER; rev <= EVMC_LATEST_REVISION;

original_message: CREATE2 support in instructions lib


message: Add CREATE2 salt to evmc_message

diff:
@@ -40,7 +40,7 @@ extern "C" {
enum
{
/** The EVMC ABI version number of the interface declared in this file. */
- EVMC_ABI_VERSION = 1
+ EVMC_ABI_VERSION = 2
};
/**
@@ -68,10 +68,11 @@ struct evmc_address
enum evmc_call_kind
{
EVMC_CALL = 0, /**< Request CALL. */
- EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. The value param ignored. */
+ EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. Valid since Homestead.
+ The value param ignored. */
EVMC_CALLCODE = 2, /**< Request CALLCODE. */
- EVMC_CREATE = 3, /**< Request CREATE. Semantic of some params changes. */
- EVMC_CREATE2 = 4 /**< Request CREATE2. Semantic of some params changes. */
+ EVMC_CREATE = 3, /**< Request CREATE. */
+ EVMC_CREATE2 = 4 /**< Request CREATE2. Valid since Constantinople.*/
};
/** The flags for ::evmc_message. */
@@ -117,6 +118,13 @@ struct evmc_message
*/
struct evmc_uint256be code_hash;
+ /**
+ * The optional value used in new contract address construction.
+ *
+ * Ignored unless kind is EVMC_CREATE2.
+ */
+ struct evmc_uint256be create2_salt;
+
/** The amount of gas for message execution. */
int64_t gas;

diff:
@@ -12,7 +12,6 @@ static_assert(sizeof(evmc_uint256be) == 32, "evmc_uint256be is too big");
static_assert(sizeof(evmc_address) == 20, "evmc_address is too big");
static_assert(sizeof(evmc_result) <= 64, "evmc_result does not fit cache line");
static_assert(sizeof(evmc_instance) <= 64, "evmc_instance does not fit cache line");
-static_assert(sizeof(evmc_message) <= 18 * 8, "evmc_message not optimally packed");
static_assert(offsetof(evmc_message, code_hash) % 8 == 0, "evmc_message.code_hash not aligned");
// Check enums match int size.

original_message: Add CREATE2 salt to evmc_message


message: Support for EXTCODEHASH

diff:
@@ -16,13 +16,6 @@ struct evmc_uint256be balance(struct evmc_context* context, const struct evmc_ad
return ret;
}
-struct evmc_address address(struct evmc_context* context)
-{
- (void)context;
- struct evmc_address ret = {.bytes = {1, 2, 3, 4}};
- return ret;
-}
-
static void print_address(const struct evmc_address* address)
{
int i = 0;
@@ -84,6 +77,17 @@ static size_t get_code_size(struct evmc_context* context, const struct evmc_addr
return 0;
}
+static void get_code_hash(struct evmc_uint256be* result,
+ struct evmc_context* context,
+ const struct evmc_address* address)
+{
+ (void)result;
+ (void)context;
+ printf("EVM-C: CODEHASH @");
+ print_address(address);
+ printf("\n");
+}
+
static size_t copy_code(struct evmc_context* context,
const struct evmc_address* address,
size_t code_offset,
@@ -155,8 +159,8 @@ static void evm_log(struct evmc_context* context,
}
static const struct evmc_context_fn_table ctx_fn_table = {
- account_exists, get_storage, set_storage, get_balance, get_code_size, copy_code,
- selfdestruct, call, get_tx_context, get_block_hash, evm_log,
+ account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash,
+ copy_code, selfdestruct, call, get_tx_context, get_block_hash, evm_log,
};
/// Example how the API is supposed to be used.

diff:
@@ -40,7 +40,7 @@ extern "C" {
enum
{
/** The EVMC ABI version number of the interface declared in this file. */
- EVMC_ABI_VERSION = 3
+ EVMC_ABI_VERSION = 4
};
/**
@@ -491,6 +491,18 @@ typedef void (*evmc_get_balance_fn)(struct evmc_uint256be* result,
typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context,
const struct evmc_address* address);
+/**
+ * Get code size callback function.
+ *
+ * This callback function is used by an EVM to get the keccak256 hash of the code stored
+ * in the account at the given address. For accounts not having a code, this
+ * function returns keccak256 hash of empty data. For accounts not existing in the state,
+ * this function returns 0.
+ */
+typedef void (*evmc_get_code_hash_fn)(struct evmc_uint256be* result,
+ struct evmc_context* context,
+ const struct evmc_address* address);
+
/**
* Copy code callback function.
*
@@ -591,6 +603,9 @@ struct evmc_context_fn_table
/** Get code size callback function. */
evmc_get_code_size_fn get_code_size;
+ /** Get code hash callback function. */
+ evmc_get_code_hash_fn get_code_hash;
+
/** Copy code callback function. */
evmc_copy_code_fn copy_code;

diff:
@@ -85,7 +85,7 @@ static struct evmc_instruction_metrics constantinople_metrics[256] = {
/* EXTCODECOPY = 0x3c */ {700, 4, 0},
/* RETURNDATASIZE = 0x3d */ {BASE, 0, 1},
/* RETURNDATACOPY = 0x3e */ {VERYLOW, 3, 0},
- /* = 0x3f */ {UNDEFINED, 0, 0},
+ /* EXTCODEHASH = 0x3f */ {400, 1, 1},
/* BLOCKHASH = 0x40 */ {20, 1, 1},
/* COINBASE = 0x41 */ {BASE, 0, 1},
/* TIMESTAMP = 0x42 */ {BASE, 0, 1},

original_message: Support for EXTCODEHASH