Commits (3)
...@@ -18,8 +18,8 @@ namespace pEp.DPE ...@@ -18,8 +18,8 @@ namespace pEp.DPE
/// </summary> /// </summary>
/// <param name="patch">The patch to reject.</param> /// <param name="patch">The patch to reject.</param>
/// <param name="me">The own identity that rejects the patch.</param> /// <param name="me">The own identity that rejects the patch.</param>
/// <exception cref="ArgumentNullException" /> /// <exception cref="ArgumentNullException">The post URI is null.</exception>
/// <exception cref="HttpRequestException" /> /// <exception cref="HttpRequestException">The HTTP request failed.</exception>
/// <returns>The Http response.</returns> /// <returns>The Http response.</returns>
public static async Task<HttpResponseMessage> RejectPatch(Patch patch, PEPIdentity me) public static async Task<HttpResponseMessage> RejectPatch(Patch patch, PEPIdentity me)
{ {
......
...@@ -145,7 +145,7 @@ namespace pEp.DPE ...@@ -145,7 +145,7 @@ namespace pEp.DPE
{ {
Outlook.Folder inbox = null; Outlook.Folder inbox = null;
Outlook.Items items = null; Outlook.Items items = null;
try try
{ {
inbox = Globals.ThisAddIn.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder; inbox = Globals.ThisAddIn.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder;
...@@ -168,7 +168,7 @@ namespace pEp.DPE ...@@ -168,7 +168,7 @@ namespace pEp.DPE
// Now find the one with the respective patch id // Now find the one with the respective patch id
string filter = string.Format("[{0}] = '{1}'", MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_ID, patch.Id); string filter = string.Format("[{0}] = '{1}'", MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_ID, patch.Id);
items = items.Restrict(filter); items = items.Restrict(filter);
if (items.Count > 1) if (items.Count > 1)
{ {
Log.Error("GetPatchMailItem: More than one item found with patch id " + patch.Id); Log.Error("GetPatchMailItem: More than one item found with patch id " + patch.Id);
...@@ -206,42 +206,32 @@ namespace pEp.DPE ...@@ -206,42 +206,32 @@ namespace pEp.DPE
/// <param name="patch">The patch to post.</param> /// <param name="patch">The patch to post.</param>
/// <param name="me">The own identity.</param> /// <param name="me">The own identity.</param>
/// <param name="postAction">The POST action to perform.</param> /// <param name="postAction">The POST action to perform.</param>
/// <returns></returns> /// <exception cref="HttpRequestException">The HTTP request failed.</exception>
private async Task<Exception> PostAsync(Patch patch, PEPIdentity me, PostAction postAction) private async Task PostAsync(Patch patch, PEPIdentity me, PostAction postAction)
{ {
// Determine the correct method to execute // Determine the correct method to execute
Task<HttpResponseMessage> postTask = null; HttpResponseMessage httpResponseMessage = null;
switch (postAction) switch (postAction)
{ {
case PostAction.Reject: case PostAction.Reject:
postTask = DPEWebClient.RejectPatch(patch, me); httpResponseMessage = await DPEWebClient.RejectPatch(patch, me);
break; break;
case PostAction.Suggest: case PostAction.Suggest:
postTask = DPEWebClient.SuggestPatch(patch, me); httpResponseMessage = await DPEWebClient.SuggestPatch(patch, me);
break; break;
case PostAction.Support: case PostAction.Support:
postTask = DPEWebClient.SupportPatch(patch, me); httpResponseMessage = await DPEWebClient.SupportPatch(patch, me);
break; break;
default: default:
Log.ErrorAndFailInDebugMode("PostAsync: Unknown post action."); Log.ErrorAndFailInDebugMode("PostAsync: Unknown post action.");
break; break;
} }
// Execute the task and return status if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
return await postTask.ContinueWith((task) =>
{ {
if (task.Exception != null) Log.Error("PostAsync: Status code is " + Enum.GetName(typeof(HttpStatusCode), httpResponseMessage.StatusCode));
{ throw new HttpRequestException(Enum.GetName(typeof(HttpStatusCode), httpResponseMessage.StatusCode));
Log.Error("ExecuteAsync: Error executing POST of type " + Enum.GetName(typeof(PostAction), postAction)); }
return task.Exception;
}
else
{
return (task.Result.StatusCode != HttpStatusCode.OK) ?
new Exception(Enum.GetName(typeof(HttpStatusCode), task.Result.StatusCode)) :
null;
}
});
} }
/// <summary> /// <summary>
...@@ -249,15 +239,27 @@ namespace pEp.DPE ...@@ -249,15 +239,27 @@ namespace pEp.DPE
/// </summary> /// </summary>
/// <param name="patch">The patch to reject.</param> /// <param name="patch">The patch to reject.</param>
/// <param name="me">The own identity that rejects the patch.</param> /// <param name="me">The own identity that rejects the patch.</param>
/// <exception cref="HttpRequestException">The HTTP request failed.</exception>
/// <exception cref="UpdatePatchMailItemException">The update of the mail item failed.</exception>
public async Task Reject(Patch patch, PEPIdentity me) public async Task Reject(Patch patch, PEPIdentity me)
{ {
if (await this.PostAsync(patch, me, PostAction.Reject) is Exception ex) try
{
await this.PostAsync(patch, me, PostAction.Reject);
}
catch (HttpRequestException ex)
{ {
throw ex; throw ex;
} }
this.UpdatePatchMailItem(patch, PatchStatus.Rejected); if (this.UpdatePatchMailItem(patch, PatchStatus.Rejected))
AdapterExtensions.ShowNotification("Patch rejected", patch.CommitMessage); {
AdapterExtensions.ShowNotification("Patch rejected", patch.CommitMessage);
}
else
{
throw new UpdatePatchMailItemException("Error updating patch mail item.");
}
} }
/// <summary> /// <summary>
...@@ -281,6 +283,11 @@ namespace pEp.DPE ...@@ -281,6 +283,11 @@ namespace pEp.DPE
omi.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE, DateTime.UtcNow); omi.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE, DateTime.UtcNow);
omi.Save(); omi.Save();
} }
else
{
Log.Error("UpdatePatchMailItem: patch mail item not found.");
return false;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -307,9 +314,14 @@ namespace pEp.DPE ...@@ -307,9 +314,14 @@ namespace pEp.DPE
/// </summary> /// </summary>
/// <param name="patch">The patch to suggest.</param> /// <param name="patch">The patch to suggest.</param>
/// <param name="me">The own identity that suggests the patch.</param> /// <param name="me">The own identity that suggests the patch.</param>
/// <exception cref="HttpRequestException">The HTTP request failed.</exception>
public async Task Suggest(Patch patch, PEPIdentity me) public async Task Suggest(Patch patch, PEPIdentity me)
{ {
if (await this.PostAsync(patch, me, PostAction.Suggest) is Exception ex) try
{
await this.PostAsync(patch, me, PostAction.Suggest);
}
catch (HttpRequestException ex)
{ {
throw ex; throw ex;
} }
...@@ -322,15 +334,27 @@ namespace pEp.DPE ...@@ -322,15 +334,27 @@ namespace pEp.DPE
/// </summary> /// </summary>
/// <param name="patch">The patch to support.</param> /// <param name="patch">The patch to support.</param>
/// <param name="me">The own identity that supports the patch.</param> /// <param name="me">The own identity that supports the patch.</param>
/// <exception cref="HttpRequestException">The HTTP request failed.</exception>
/// <exception cref="UpdatePatchMailItemException">The update of the mail item failed.</exception>
public async Task Support(Patch patch, PEPIdentity me) public async Task Support(Patch patch, PEPIdentity me)
{ {
if (await this.PostAsync(patch, me, PostAction.Support) is Exception ex) try
{
await this.PostAsync(patch, me, PostAction.Support);
}
catch (HttpRequestException ex)
{ {
throw ex; throw ex;
} }
this.UpdatePatchMailItem(patch, PatchStatus.Supported); if (this.UpdatePatchMailItem(patch, PatchStatus.Supported))
AdapterExtensions.ShowNotification("Patch supported", patch.CommitMessage); {
AdapterExtensions.ShowNotification("Patch supported", patch.CommitMessage);
}
else
{
throw new UpdatePatchMailItemException("Error updating patch mail item.");
}
} }
/// <summary> /// <summary>
...@@ -345,5 +369,15 @@ namespace pEp.DPE ...@@ -345,5 +369,15 @@ namespace pEp.DPE
} }
#endregion #endregion
/// <summary>
/// Custom exception for when the update of the patch mail item fails.
/// </summary>
public class UpdatePatchMailItemException : Exception
{
public UpdatePatchMailItemException(string message) : base(message)
{
}
}
} }
} }
\ No newline at end of file
using pEp.DPE; using pEp.DPE;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace pEp.UI.Models namespace pEp.UI.Models
{ {
...@@ -71,8 +72,7 @@ namespace pEp.UI.Models ...@@ -71,8 +72,7 @@ namespace pEp.UI.Models
/// <param name="patchAction">The action to perform with the patch.</param> /// <param name="patchAction">The action to perform with the patch.</param>
/// <param name="patch">The patch to manage.</param> /// <param name="patch">The patch to manage.</param>
/// <param name="isEditable">Whether the patch can be edited.</param> /// <param name="isEditable">Whether the patch can be edited.</param>
/// <returns>The dialog result.</returns> public static async Task ShowDialog(PatchDialog.PatchAction patchAction, Patch patch, bool isEditable = false)
public static System.Windows.Forms.DialogResult ShowDialog(PatchDialog.PatchAction patchAction, Patch patch, bool isEditable = false)
{ {
// If needed, make sure we have a valid own identity // If needed, make sure we have a valid own identity
PEPIdentity me = null; PEPIdentity me = null;
...@@ -82,43 +82,40 @@ namespace pEp.UI.Models ...@@ -82,43 +82,40 @@ namespace pEp.UI.Models
true) != Globals.ReturnStatus.Success)) true) != Globals.ReturnStatus.Success))
{ {
Log.Error("ShowDialog: Error getting own identity."); Log.Error("ShowDialog: Error getting own identity.");
return System.Windows.Forms.DialogResult.Cancel; return;
} }
// Open dialog and retrieve result // Open dialog and retrieve result
PatchDialog patchDialog = new PatchDialog(patchAction, patch, isEditable); PatchDialog patchDialog = new PatchDialog(patchAction, patch, isEditable);
System.Windows.Forms.DialogResult dialogResult = new DialogHost(patchDialog).ShowDialog(); if (new DialogHost(patchDialog).ShowDialog() == System.Windows.Forms.DialogResult.OK)
// Perform the action the user selected
switch (patchAction)
{ {
case PatchDialog.PatchAction.NewPatch: // Perform the action the user selected
case PatchDialog.PatchAction.EditPatch: if (patchAction == PatchDialog.PatchAction.NewPatch)
{
try
{ {
if (dialogResult == System.Windows.Forms.DialogResult.OK) await Globals.ThisAddIn.DistributedPolicyEngine.Suggest(patchDialog.Patch, me);
{
Globals.ThisAddIn.DistributedPolicyEngine.Suggest(patchDialog.Patch, me);
}
} }
break; catch (Exception ex)
case PatchDialog.PatchAction.SupportOrRejectPatch:
{ {
if (dialogResult == System.Windows.Forms.DialogResult.OK) Log.Error("ShowDialog: Error occured. " + ex);
{
Globals.ThisAddIn.DistributedPolicyEngine.Support(patchDialog.Patch, me); while (ex.InnerException != null)
}
else if (dialogResult == System.Windows.Forms.DialogResult.No)
{ {
Globals.ThisAddIn.DistributedPolicyEngine.Reject(patchDialog.Patch, me); ex = ex.InnerException;
} }
CustomMessageBox.ShowDialog(ex.Message, "Error suggesting patch", "OK");
return;
} }
break;
case PatchDialog.PatchAction.ShowPatch:
default:
break;
}
return dialogResult; AdapterExtensions.ShowNotification("Patch successfully suggested", patch.CommitMessage);
}
else
{
Log.ErrorAndFailInDebugMode("ShowDialog: Unsupported patch action " + Enum.GetName(typeof(PatchDialog.PatchAction), patchAction));
}
}
} }
} }
} }
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
using pEp.UI.Models; using pEp.UI.Models;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
namespace pEp.UI.ViewModels namespace pEp.UI.ViewModels
...@@ -18,6 +18,7 @@ namespace pEp.UI.ViewModels ...@@ -18,6 +18,7 @@ namespace pEp.UI.ViewModels
private FlowDocument _DisplayDiff = null; private FlowDocument _DisplayDiff = null;
private DateTime? _EditDate = null; private DateTime? _EditDate = null;
private string _Explanation = null; private string _Explanation = null;
private bool _IsLoading = false;
private DistributedPolicyEngine.PatchStatus _Status = DistributedPolicyEngine.PatchStatus.Open; private DistributedPolicyEngine.PatchStatus _Status = DistributedPolicyEngine.PatchStatus.Open;
#endregion #endregion
...@@ -75,6 +76,11 @@ namespace pEp.UI.ViewModels ...@@ -75,6 +76,11 @@ namespace pEp.UI.ViewModels
/// </summary> /// </summary>
public string Explanation { get => this._Explanation; set => this.SetProperty(ref this._Explanation, value); } public string Explanation { get => this._Explanation; set => this.SetProperty(ref this._Explanation, value); }
/// <summary>
/// Gets or sets whether the screen is loading.
/// </summary>
public bool IsLoading { get => this._IsLoading; set => this.SetProperty(ref this._IsLoading, value); }
/// <summary> /// <summary>
/// Gets whether the OK button is visible. /// Gets whether the OK button is visible.
/// </summary> /// </summary>
...@@ -167,27 +173,55 @@ namespace pEp.UI.ViewModels ...@@ -167,27 +173,55 @@ namespace pEp.UI.ViewModels
#region Methods #region Methods
/// <summary> /// <summary>
/// Rejects this patch. /// Executes a patch task (support/reject) and shows an error message if necessary.
/// </summary> /// </summary>
/// <param name="parameter">The command parameter.</param> /// <param name="patchTask">The task to execute.</param>
private async void RejectPatch(object parameter) /// <returns>True if the patch task was executed successfully, otherwise false.</returns>
private async Task<bool> ExecutePatchTask(Task patchTask)
{ {
try try
{ {
await Globals.ThisAddIn.DistributedPolicyEngine.Reject(this.patch, new PEPIdentity()); // Block UI and show loading cursor
this.IsLoading = true;
Mouse.OverrideCursor = Cursors.Wait;
// Execute the task
await patchTask;
// Unblock UI again
this.IsLoading = false;
Mouse.OverrideCursor = null;
} }
catch (Exception ex) catch (Exception ex)
{ {
this.IsLoading = false;
Mouse.OverrideCursor = null;
Log.Error("Error occured. " + ex);
// Get the innermost exception message and show message box
while (ex.InnerException != null) while (ex.InnerException != null)
{ {
ex = ex.InnerException; ex = ex.InnerException;
} }
CustomMessageBox.ShowDialog(ex.Message, "Error", "OK"); CustomMessageBox.ShowDialog(ex.Message, "Error", "OK");
return; return false;
} }
this.UpdateView(DistributedPolicyEngine.PatchStatus.Rejected); return true;
}
/// <summary>
/// Rejects this patch.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private async void RejectPatch(object parameter)
{
if (await this.ExecutePatchTask(Globals.ThisAddIn.DistributedPolicyEngine.Reject(this.patch, new PEPIdentity())))
{
this.UpdateView(DistributedPolicyEngine.PatchStatus.Rejected);
}
} }
/// <summary> /// <summary>
...@@ -225,24 +259,10 @@ namespace pEp.UI.ViewModels ...@@ -225,24 +259,10 @@ namespace pEp.UI.ViewModels
/// <param name="parameter">The command parameter.</param> /// <param name="parameter">The command parameter.</param>
private async void SupportPatch(object parameter) private async void SupportPatch(object parameter)
{ {
try if (await this.ExecutePatchTask(Globals.ThisAddIn.DistributedPolicyEngine.Support(this.patch, new PEPIdentity())))
{
await Globals.ThisAddIn.DistributedPolicyEngine.Support(this.patch, new PEPIdentity());
}
catch (Exception ex)
{ {
Log.Error("SupportPatch: Error occured. " + ex); this.UpdateView(DistributedPolicyEngine.PatchStatus.Supported);
while (ex.InnerException != null)
{
ex = ex.InnerException;
}
CustomMessageBox.ShowDialog(ex.Message, "Error", "OK");
return;
} }
this.UpdateView(DistributedPolicyEngine.PatchStatus.Supported);
} }
/// <summary> /// <summary>
......
...@@ -214,7 +214,7 @@ namespace pEp.UI.ViewModels ...@@ -214,7 +214,7 @@ namespace pEp.UI.ViewModels
{ {
if (this._CommandButtonNewPatch == null) if (this._CommandButtonNewPatch == null)
{ {
this._CommandButtonNewPatch = new RelayCommand(p => PatchDialog.ShowDialog(PatchDialog.PatchAction.NewPatch, new Patch(), true)); this._CommandButtonNewPatch = new RelayCommand(async p => await PatchDialog.ShowDialog(PatchDialog.PatchAction.NewPatch, new Patch(), true));
} }
return this._CommandButtonNewPatch; return this._CommandButtonNewPatch;
...@@ -624,11 +624,6 @@ namespace pEp.UI.ViewModels ...@@ -624,11 +624,6 @@ namespace pEp.UI.ViewModels
} }
} }
/// <summary>
/// Gets the collection of existing patches.
/// </summary>
public ObservableCollection<PatchViewModel> Patches { get; } = new ObservableCollection<PatchViewModel>();
/// <summary> /// <summary>
/// Gets or sets the pEp copyright used in the about information. /// Gets or sets the pEp copyright used in the about information.
/// </summary> /// </summary>
......
using pEp.DPE;
using pEp.UI.Models;
using System;
using System.Linq;
using System.Windows.Media;
namespace pEp.UI.ViewModels
{
internal class PatchViewModel
{
private Patch patch;
private PEPIdentity _Me = null;
/// <summary>
/// Gets the background color for this patch.
/// </summary>
public Brush Background => this.IsEditable ? Brushes.White : Brushes.WhiteSmoke;
/// <summary>
/// Gets the foreground color for this patch.
/// </summary>
public Brush Foreground => this.IsEditable ? Brushes.Black : Brushes.Gray;
/// <summary>
/// Gets the commit message of this patch.
/// </summary>
public string CommitMessage => this.patch?.CommitMessage;
/// <summary>
/// Gets whether the patch is editable.
/// </summary>
public bool IsEditable { get; }
/// <summary>
/// Gets a string stating the patch's last update.
/// </summary>
public string LastUpdate => this.GetLastUpdateString();
/// <summary>
/// Gets the own identity to use to support/reject patches.
/// </summary>
public PEPIdentity Me
{
get
{
if ((this._Me == null) &&
(PEPIdentity.GetOwnIdentity(Globals.ThisAddIn.Settings.AccountSettingsList.First(),
out PEPIdentity me,
true) == Globals.ReturnStatus.Success))
{
this._Me = me;
}
return this._Me;
}
}
/// <summary>
/// Primar constructor.
/// </summary>
/// <param name="patch">The patch that this view model is based on.</param>
public PatchViewModel(Patch patch, bool isEditable)
{
this.patch = patch;
this.IsEditable = isEditable;
}
/// <summary>
/// Gets a string referencing the patch's last update.
/// </summary>
/// <returns>A string describing the last update.</returns>
private string GetLastUpdateString()
{
TimeSpan elapsedTime = DateTime.UtcNow - (this.patch?.CreationDate ?? DateTime.UtcNow);
if (elapsedTime.Days > 0)
{
return (elapsedTime.Days == 1) ? "1 day ago" : $"{elapsedTime.Days} days ago";
}
else if (elapsedTime.Hours > 0)
{
return (elapsedTime.Hours == 1) ? "1 hour ago" : $"{elapsedTime.Hours} hours ago";
}
else if (elapsedTime.Minutes > 0)
{
return (elapsedTime.Minutes == 1) ? "1 minute ago" : $"{elapsedTime.Minutes} minutes ago";
}
else
{
return "just now";
}
}
/// <summary>
/// Opens the patch dialog.
/// </summary>
/// <returns>The dialog result.</returns>
public System.Windows.Forms.DialogResult OpenPatchDialog(PatchDialog.PatchAction patchAction)
{
return PatchDialog.ShowDialog(patchAction, this.patch, this.IsEditable);
}
/// <summary>
/// Supports the patch.
/// </summary>
public void SupportPatch()
{
Globals.ThisAddIn.DistributedPolicyEngine.Support(this.patch, this.Me);
}
/// <summary>
/// Rejects the patch.
/// </summary>
public void RejectPatch()
{
Globals.ThisAddIn.DistributedPolicyEngine.Reject(this.patch, this.Me);
}
}
}
...@@ -41,6 +41,16 @@ ...@@ -41,6 +41,16 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Rectangle Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.RowSpan="8"
Grid.ZIndex="100"
Fill="White"
Opacity="0.6"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}" />
<Label Grid.Column="0" <Label Grid.Column="0"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
Grid.Row="0" Grid.Row="0"
......
...@@ -29,17 +29,5 @@ namespace pEp.UI.Views ...@@ -29,17 +29,5 @@ namespace pEp.UI.Views
Process.Start(e.Uri.AbsoluteUri); Process.Start(e.Uri.AbsoluteUri);
e.Handled = true; e.Handled = true;
} }
/// <summary>
/// Event handler for when a patch is double-clicked.
/// </summary>
private void ContentControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if ((sender as ContentControl)?.DataContext is PatchViewModel patchViewModel)
{
PatchDialog.PatchAction action = patchViewModel.IsEditable ? PatchDialog.PatchAction.EditPatch : PatchDialog.PatchAction.SupportOrRejectPatch;
patchViewModel.OpenPatchDialog(action);
}
}
} }
} }
...@@ -448,7 +448,6 @@ ...@@ -448,7 +448,6 @@
<Compile Include="UI\ViewModels\InputMessageBoxViewModel.cs" /> <Compile Include="UI\ViewModels\InputMessageBoxViewModel.cs" />
<Compile Include="UI\ViewModels\MessageBoxBaseViewModel.cs" /> <Compile Include="UI\ViewModels\MessageBoxBaseViewModel.cs" />
<Compile Include="UI\ViewModels\PatchDialogViewModel.cs" /> <Compile Include="UI\ViewModels\PatchDialogViewModel.cs" />
<Compile Include="UI\ViewModels\PatchViewModel.cs" />
<Compile Include="UI\Views\CustomMessageBoxView.xaml.cs"> <Compile Include="UI\Views\CustomMessageBoxView.xaml.cs">
<DependentUpon>CustomMessageBoxView.xaml</DependentUpon> <DependentUpon>CustomMessageBoxView.xaml</DependentUpon>
</Compile> </Compile>
......