diff --git a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs index 6fd3d6ae87e..8858121b6c7 100644 --- a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs @@ -27,6 +27,8 @@ public class HomeWorkspaceModel : WorkspaceModel { #region Class Data Members and Properties + private string thumbnail; + private Uri graphDocumentationURL; private readonly DynamoScheduler scheduler; private PulseMaker pulseMaker; private readonly bool verboseLogging; @@ -89,6 +91,46 @@ public class HomeWorkspaceModel : WorkspaceModel [JsonIgnore] public long EvaluationCount { get; private set; } + /// + /// Link to documentation page for this workspace + /// + public Uri GraphDocumentationURL + { + get { return graphDocumentationURL; } + set + { + if (graphDocumentationURL == value) + return; + + graphDocumentationURL = value; + RaisePropertyChanged(nameof(GraphDocumentationURL)); + } + } + + + /// + /// Workspace thumbnail as Base64 string. + /// Returns null if provide value is not Base64 encoded. + /// + public string Thumbnail + { + get { return thumbnail; } + set + { + try + { + // if value is not a valid Base64 string this will throw, and we return null. + byte[] data = Convert.FromBase64String(value); + thumbnail = value; + RaisePropertyChanged(nameof(Thumbnail)); + } + catch + { + return; + } + } + } + /// /// In near future, the file loading mechanism will be completely moved /// into WorkspaceModel, that's the time we removed this property setter below. diff --git a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs index 950a1d444d7..3416cc9e2bf 100644 --- a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs +++ b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs @@ -641,15 +641,30 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist } else { - ws = new HomeWorkspaceModel(guid, engine, scheduler, factory, + var homeWorkspace = new HomeWorkspaceModel(guid, engine, scheduler, factory, loadedTraceData, nodes, notes, annotations, Enumerable.Empty(), elementResolver, info, verboseLogging, isTestMode); + + if (obj.TryGetValue(nameof(HomeWorkspaceModel.Thumbnail), StringComparison.OrdinalIgnoreCase, out JToken thumbnail)) + homeWorkspace.Thumbnail = thumbnail.ToString(); + + if (obj.TryGetValue(nameof(HomeWorkspaceModel.GraphDocumentationURL), StringComparison.OrdinalIgnoreCase, out JToken helpLink)) + { + if (Uri.TryCreate(helpLink.ToString(), UriKind.Absolute, out Uri uri)) + homeWorkspace.GraphDocumentationURL = uri; + } + + ws = homeWorkspace; } ws.NodeLibraryDependencies = nodeLibraryDependencies.ToList(); ws.ExtensionData = GetExtensionData(serializer, obj); + if (obj.TryGetValue(nameof(WorkspaceModel.Author), StringComparison.OrdinalIgnoreCase, out JToken author)) + ws.Author = author.ToString(); + + return ws; } @@ -771,6 +786,22 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s writer.WritePropertyName(WorkspaceReadConverter.EXTENSION_WORKSPACE_DATA); serializer.Serialize(writer, ws.ExtensionData); + + if (!isCustomNode && ws is HomeWorkspaceModel hws) + { + // Thumbnail + writer.WritePropertyName(nameof(HomeWorkspaceModel.Thumbnail)); + writer.WriteValue(hws.Thumbnail); + + // GraphDocumentaionLink + writer.WritePropertyName(nameof(HomeWorkspaceModel.GraphDocumentationURL)); + writer.WriteValue(hws.GraphDocumentationURL); + } + + // Graph Author + writer.WritePropertyName(nameof(WorkspaceModel.Author)); + writer.WriteValue(ws.Author); + if (engine != null) { // Bindings diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index b68336d6953..040b982c9da 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -696,7 +696,7 @@ internal List NodeLibraryDependencies } private Dictionary nodePackageDictionary = new Dictionary(); - + /// /// An author of the workspace @@ -707,7 +707,7 @@ public string Author set { author = value; - RaisePropertyChanged("Author"); + RaisePropertyChanged(nameof(Author)); } } diff --git a/test/DynamoCoreTests/Graph/Workspaces/WorkspaceModelTests.cs b/test/DynamoCoreTests/Graph/Workspaces/WorkspaceModelTests.cs index 4974f2a7e98..a4e998de46f 100644 --- a/test/DynamoCoreTests/Graph/Workspaces/WorkspaceModelTests.cs +++ b/test/DynamoCoreTests/Graph/Workspaces/WorkspaceModelTests.cs @@ -284,5 +284,40 @@ public void UpdateModelValueInvalidParameters() Assert.AreEqual(Resources.ModelNotFoundError, ex3.Message); } + + [Test] + public void CanStoreBase64EncodedImageInThumbnailProperty() + { + // Arrange + var imagePath = Path.Combine(TestDirectory, @"DynamoCoreTests\Graph\Workspaces\thumbnailTestImage.png"); + Assert.That(File.Exists(imagePath)); + + // Act + byte[] imageArray = System.IO.File.ReadAllBytes(imagePath); + string base64ImageRepresentation = Convert.ToBase64String(imageArray); + if (!(this.CurrentDynamoModel.CurrentWorkspace is HomeWorkspaceModel hws)) + throw new Exception("current workspace is not a HomeWorkspaceModel"); + + hws.Thumbnail = base64ImageRepresentation; + + // Assert + Assert.NotNull(base64ImageRepresentation); + Assert.AreEqual(hws.Thumbnail, base64ImageRepresentation); + } + + [Test] + public void WillNotStoreInvalidBase64StringInThumbnailProperty() + { + // Arrange + var invalidImagePath = "GenericString"; + + // Act + if (!(this.CurrentDynamoModel.CurrentWorkspace is HomeWorkspaceModel hws)) + throw new Exception("current workspace is not a HomeWorkspaceModel"); + hws.Thumbnail = invalidImagePath; + + // Assert + Assert.IsNull(hws.Thumbnail); + } } } diff --git a/test/DynamoCoreTests/Graph/Workspaces/thumbnailTestImage.png b/test/DynamoCoreTests/Graph/Workspaces/thumbnailTestImage.png new file mode 100644 index 00000000000..6e4f005b3b7 Binary files /dev/null and b/test/DynamoCoreTests/Graph/Workspaces/thumbnailTestImage.png differ diff --git a/test/core/dummy_node/2080_JSONTESTCRASH undo_redo.dyn b/test/core/dummy_node/2080_JSONTESTCRASH undo_redo.dyn index d015940da0a..fe13685ae1c 100644 --- a/test/core/dummy_node/2080_JSONTESTCRASH undo_redo.dyn +++ b/test/core/dummy_node/2080_JSONTESTCRASH undo_redo.dyn @@ -53,13 +53,16 @@ "Connectors": [], "Dependencies": [], "NodeLibraryDependencies": [], + "Thumbnail": "", + "GraphDocumentationURL": null, + "Author": "None provided", "Bindings": [], "View": { "Dynamo": { "ScaleFactor": 1.0, "HasRunWithoutCrash": true, "IsVisibleInDynamoLibrary": true, - "Version": "2.3.0.5184", + "Version": "2.11.0.4415", "RunType": "Automatic", "RunPeriod": "1000" }, @@ -77,21 +80,21 @@ }, "NodeViews": [ { - "ShowGeometry": true, - "Name": "Code Block", "Id": "158f8f25ddb746c88323ae262e87611e", "IsSetAsInput": false, "IsSetAsOutput": false, + "Name": "Code Block", + "ShowGeometry": true, "Excluded": false, "X": 179.8, "Y": 160.39999999999992 }, { - "ShowGeometry": true, - "Name": "Code Block", "Id": "6cbd0760736f4235a03b38fb359e9957", "IsSetAsInput": false, "IsSetAsOutput": false, + "Name": "Code Block", + "ShowGeometry": true, "Excluded": false, "X": 76.8, "Y": 162.4