main
cs 224 lines 7.76 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.WixBA
4 {
5 using System;
6 using System.ComponentModel;
7 using System.Windows.Input;
8 using WixToolset.BootstrapperApplicationApi;
9
10 /// <summary>
11 /// The states of the update view model.
12 /// </summary>
13 public enum UpdateState
14 {
15 Unknown,
16 Initializing,
17 Checking,
18 Current,
19 Available,
20 Failed,
21 }
22
23 /// <summary>
24 /// The model of the update view.
25 /// </summary>
26 public class UpdateViewModel : PropertyNotifyBase
27 {
28 private RootViewModel root;
29 private UpdateState state;
30 private ICommand updateCommand;
31 private string updateVersion;
32 private string updateChanges;
33
34
35 public UpdateViewModel(RootViewModel root)
36 {
37 this.root = root;
38 WixBA.Model.Bootstrapper.DetectUpdateBegin += this.DetectUpdateBegin;
39 WixBA.Model.Bootstrapper.DetectUpdate += this.DetectUpdate;
40 WixBA.Model.Bootstrapper.DetectUpdateComplete += this.DetectUpdateComplete;
41 WixBA.Model.Bootstrapper.DetectComplete += this.DetectComplete;
42
43 this.root.PropertyChanged += new PropertyChangedEventHandler(this.RootPropertyChanged);
44
45 this.State = UpdateState.Initializing;
46 }
47
48 void RootPropertyChanged(object sender, PropertyChangedEventArgs e)
49 {
50 if ("InstallState" == e.PropertyName)
51 {
52 base.OnPropertyChanged("CanUpdate");
53 }
54 }
55
56 public bool CheckingEnabled
57 {
58 get { return this.State == UpdateState.Initializing || this.State == UpdateState.Checking; }
59 }
60
61 public bool CanUpdate
62 {
63 get
64 {
65 switch(this.root.InstallState)
66 {
67 case InstallationState.Waiting:
68 case InstallationState.Applied:
69 case InstallationState.Failed:
70 return this.IsUpdateAvailable;
71 default:
72 return false;
73 }
74 }
75 }
76
77 public ICommand UpdateCommand
78 {
79 get
80 {
81 if (this.updateCommand == null)
82 {
83 this.updateCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.UpdateReplace), param => this.CanUpdate);
84 }
85
86 return this.updateCommand;
87 }
88 }
89
90 public bool IsUpdateAvailable
91 {
92 get { return this.State == UpdateState.Available; }
93 }
94
95 /// <summary>
96 /// Gets and sets the state of the update view model.
97 /// </summary>
98 public UpdateState State
99 {
100 get
101 {
102 return this.state;
103 }
104
105 set
106 {
107 if (this.state != value)
108 {
109 this.state = value;
110 base.OnPropertyChanged("State");
111 base.OnPropertyChanged("CanUpdate");
112 base.OnPropertyChanged("CheckingEnabled");
113 base.OnPropertyChanged("IsUpdateAvailable");
114 }
115 }
116 }
117 /// <summary>
118 /// The version of an available update.
119 /// </summary>
120 public string UpdateVersion
121 {
122 get
123 {
124 return updateVersion;
125 }
126 set
127 {
128 if (this.updateVersion != value)
129 {
130 this.updateVersion = value;
131 base.OnPropertyChanged("UpdateVersion");
132 }
133 }
134 }
135
136 /// <summary>
137 /// The changes in the available update.
138 /// </summary>
139 public string UpdateChanges
140 {
141 get
142 {
143 return updateChanges;
144 }
145 set
146 {
147 if (this.updateChanges != value)
148 {
149 this.updateChanges = value;
150 base.OnPropertyChanged("UpdateChanges");
151 }
152 }
153 }
154
155 private void DetectUpdateBegin(object sender, DetectUpdateBeginEventArgs e)
156 {
157 // Don't check for updates if:
158 // the first check failed (no retry)
159 // if we are being run as an uninstall
160 // if we are not under a full UI.
161 if ((UpdateState.Failed != this.State) && (LaunchAction.Uninstall != WixBA.Model.Command.Action) && (Display.Full == WixBA.Model.Command.Display))
162 {
163 this.State = UpdateState.Checking;
164 e.Skip = false;
165 }
166 }
167
168 private void DetectUpdate(object sender, DetectUpdateEventArgs e)
169 {
170 // The list of updates is sorted in descending version, so the first callback should be the largest update available.
171 // This update should be either larger than ours (so we are out of date), the same as ours (so we are current)
172 // or smaller than ours (we have a private build). If we really wanted to, we could leave the e.StopProcessingUpdates alone and
173 // enumerate all of the updates.
174 WixBA.Model.Engine.Log(LogLevel.Verbose, String.Format("Potential update v{0} from '{1}'; current version: v{2}", e.Version, e.UpdateLocation, WixBA.Model.Version));
175 if (WixBA.Model.Engine.CompareVersions(e.Version, WixBA.Model.Version) > 0)
176 {
177 var updatePackageId = Guid.NewGuid().ToString("N");
178
179 WixBA.Model.Engine.SetUpdate(null, e.UpdateLocation, e.Size, UpdateHashType.None, null, updatePackageId);
180
181 if (!WixBA.Model.BAManifest.Bundle.Packages.ContainsKey(updatePackageId))
182 {
183 WixBA.Model.BAManifest.Bundle.AddUpdateBundleAsPackage(updatePackageId);
184 }
185
186 this.UpdateVersion = String.Concat("v", e.Version.ToString());
187 string changesFormat = @"<body style='overflow: auto;'>{0}</body>";
188 this.UpdateChanges = String.Format(changesFormat, e.Content);
189 this.State = UpdateState.Available;
190 }
191 else
192 {
193 this.State = UpdateState.Current;
194 }
195 e.StopProcessingUpdates = true;
196 }
197
198 private void DetectUpdateComplete(object sender, DetectUpdateCompleteEventArgs e)
199 {
200 // Failed to process an update, allow the existing bundle to still install.
201 if ((UpdateState.Failed != this.State) && !Hresult.Succeeded(e.Status))
202 {
203 this.State = UpdateState.Failed;
204 WixBA.Model.Engine.Log(LogLevel.Verbose, String.Format("Failed to locate an update, status of 0x{0:X8}, updates disabled.", e.Status));
205 e.IgnoreError = true;
206 }
207 // If we are uninstalling, we don't want to check or show an update
208 // If we are checking, then the feed didn't find any valid enclosures
209 // If we are initializing, we're either uninstalling or not a full UI
210 else if ((LaunchAction.Uninstall == WixBA.Model.Command.Action) || (UpdateState.Initializing == this.State) || (UpdateState.Checking == this.State))
211 {
212 this.State = UpdateState.Unknown;
213 }
214 }
215
216 private void DetectComplete(object sender, DetectCompleteEventArgs e)
217 {
218 if (this.State == UpdateState.Initializing || this.State == UpdateState.Checking)
219 {
220 this.State = UpdateState.Unknown;
221 }
222 }
223 }
224 }