Commits (5)
...@@ -28,14 +28,15 @@ namespace pEp.DPE ...@@ -28,14 +28,15 @@ namespace pEp.DPE
if (context.Request.HttpMethod != "POST") if (context.Request.HttpMethod != "POST")
{ {
Log.Warning("Process request: Ignoring request of type " + context.Request.HttpMethod); Log.Warning("Process request: Ignoring request of type " + context.Request.HttpMethod);
return;
} }
string request = null; string request = null;
try try
{ {
using (var stream = context.Request.InputStream) using (Stream stream = context.Request.InputStream)
using (var sr = new StreamReader(stream)) using (StreamReader sr = new StreamReader(stream))
{ {
request = sr.ReadToEnd(); request = sr.ReadToEnd();
} }
...@@ -43,6 +44,8 @@ namespace pEp.DPE ...@@ -43,6 +44,8 @@ namespace pEp.DPE
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("ProcessRequest: Error getting request. " + ex.ToString()); Log.Error("ProcessRequest: Error getting request. " + ex.ToString());
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.Response.Close();
return; return;
} }
......
...@@ -10,8 +10,8 @@ namespace pEp.DPE ...@@ -10,8 +10,8 @@ namespace pEp.DPE
{ {
internal class DistributedPolicyEngine : IDistributedPolicyEngine internal class DistributedPolicyEngine : IDistributedPolicyEngine
{ {
private readonly static string DPE_FOLDER = Path.Combine(Globals.PEPUserFolder, "DPE"); private static readonly string DPE_FOLDER = Path.Combine(Globals.PEPUserFolder, "DPE");
public readonly static string DPE_TEMP_LOCATION = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, "temp"); public static readonly string DPE_TEMP_LOCATION = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, "temp");
private const string PATCH_EXTENSION = ".patch"; private const string PATCH_EXTENSION = ".patch";
private const string PATCH_MESSAGE_SUBJECT = "Configuration changes"; private const string PATCH_MESSAGE_SUBJECT = "Configuration changes";
public const string DPE_MESSAGE_CLASS = "IPM.Note.DPE"; public const string DPE_MESSAGE_CLASS = "IPM.Note.DPE";
...@@ -127,7 +127,6 @@ namespace pEp.DPE ...@@ -127,7 +127,6 @@ namespace pEp.DPE
public void Reject(Patch patch, PEPIdentity me) public void Reject(Patch patch, PEPIdentity me)
{ {
DPEWebClient.RejectPatch(patch, me); DPEWebClient.RejectPatch(patch, me);
this.DeletePatch(patch);
AdapterExtensions.ShowNotification("Patch rejected", patch.CommitMessage); AdapterExtensions.ShowNotification("Patch rejected", patch.CommitMessage);
} }
...@@ -137,9 +136,9 @@ namespace pEp.DPE ...@@ -137,9 +136,9 @@ namespace pEp.DPE
/// <param name="patchEvents">The patch events to subscribe to.</param> /// <param name="patchEvents">The patch events to subscribe to.</param>
public void Subscribe(PatchEvents patchEvents) public void Subscribe(PatchEvents patchEvents)
{ {
patchEvents.PatchAccepted += PatchEvents_PatchAccepted; patchEvents.PatchAccepted += this.PatchEvents_PatchAccepted;
patchEvents.PatchRejected += PatchEvents_PatchRejected; patchEvents.PatchRejected += this.PatchEvents_PatchRejected;
patchEvents.PatchSuggested += PatchEvents_PatchSuggested; patchEvents.PatchSuggested += this.PatchEvents_PatchSuggested;
} }
/// <summary> /// <summary>
...@@ -161,7 +160,6 @@ namespace pEp.DPE ...@@ -161,7 +160,6 @@ namespace pEp.DPE
public void Support(Patch patch, PEPIdentity me) public void Support(Patch patch, PEPIdentity me)
{ {
DPEWebClient.SupportPatch(patch, me); DPEWebClient.SupportPatch(patch, me);
this.DeletePatch(patch);
AdapterExtensions.ShowNotification("Patch supported", patch.CommitMessage); AdapterExtensions.ShowNotification("Patch supported", patch.CommitMessage);
} }
...@@ -171,67 +169,9 @@ namespace pEp.DPE ...@@ -171,67 +169,9 @@ namespace pEp.DPE
/// <param name="patchEvents">The patch events to unsubscribe from.</param> /// <param name="patchEvents">The patch events to unsubscribe from.</param>
public void Unsubscribe(PatchEvents patchEvents) public void Unsubscribe(PatchEvents patchEvents)
{ {
patchEvents.PatchAccepted -= PatchEvents_PatchAccepted; patchEvents.PatchAccepted -= this.PatchEvents_PatchAccepted;
patchEvents.PatchRejected -= PatchEvents_PatchRejected; patchEvents.PatchRejected -= this.PatchEvents_PatchRejected;
patchEvents.PatchSuggested -= PatchEvents_PatchSuggested; patchEvents.PatchSuggested -= this.PatchEvents_PatchSuggested;
}
/// <summary>
/// Deletes the patch from the disk.
/// </summary>
/// <param name="patch">The patch to delete.</param>
/// <returns>True if the patch was deleted or not found, otherwise false.</returns>
private bool DeletePatch(Patch patch)
{
try
{
string fileName = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, patch.Id + DistributedPolicyEngine.PATCH_EXTENSION);
if (File.Exists(fileName))
{
File.Delete(fileName);
}
return true;
}
catch (Exception ex)
{
Log.Error("DeletePatch: Error occured. " + ex.ToString());
}
return false;
}
#endregion
#region Static methods
/// <summary>
/// Gets the patches that are currently saved to disk.
/// </summary>
/// <returns>The list of open patches or an empty list if none was found.</returns>
internal static List<Tuple<Patch, bool>> GetOpenPatches()
{
List<Tuple<Patch, bool>> patches = new List<Tuple<Patch, bool>>();
try
{
foreach (string file in Directory.GetFiles(DistributedPolicyEngine.DPE_FOLDER, "*" + DistributedPolicyEngine.PATCH_EXTENSION))
{
string xml = File.ReadAllText(file);
if ((Patch.Deserialize(xml) is Patch patch) &&
(patch != null))
{
patches.Add(new Tuple<Patch, bool>(patch, false));
}
}
}
catch (Exception ex)
{
Log.Error("GetPatches: Error getting patches. " + ex.ToString());
}
return patches;
} }
#endregion #endregion
......
...@@ -17,16 +17,18 @@ namespace pEp ...@@ -17,16 +17,18 @@ namespace pEp
/// </summary> /// </summary>
internal static class MailItemExtensions internal static class MailItemExtensions
{ {
public const string USER_PROPERTY_KEY_ORIG_ENTRY_ID = "origEntryID"; public const string USER_PROPERTY_KEY_DPE_PATCH_STATUS = "patchStatus";
public const string USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE = "patchEditDate";
public const string USER_PROPERTY_KEY_INSPECTOR_CLOSED = "inspectorClosed"; public const string USER_PROPERTY_KEY_INSPECTOR_CLOSED = "inspectorClosed";
public const string USER_PROPERTY_KEY_IS_INCOMING = "isIncoming"; public const string USER_PROPERTY_KEY_IS_INCOMING = "isIncoming";
public const string USER_PROPERTY_KEY_IS_MIRROR = "isMirror"; public const string USER_PROPERTY_KEY_IS_MIRROR = "isMirror";
public const string USER_PROPERTY_KEY_ORIG_ENTRY_ID = "origEntryID";
public const string USER_PROPERTY_KEY_PROCESSING_STATE = "processingState"; public const string USER_PROPERTY_KEY_PROCESSING_STATE = "processingState";
public const string USER_PROPERTY_KEY_REPLY_ACTION = "replyAction"; public const string USER_PROPERTY_KEY_REPLY_ACTION = "replyAction";
public const string UNKNOWN_SENDER = "unknown"; public const string UNKNOWN_SENDER = "unknown";
private static readonly object mutexCopiedItemsList = new object(); private static readonly object mutexCopiedItemsList = new object();
private static List<string> copiedItemsList = new List<string>(); private static readonly List<string> copiedItemsList = new List<string>();
/// <summary> /// <summary>
/// Enumeration defining the standard, setable pEp properties of an extended MailItem. /// Enumeration defining the standard, setable pEp properties of an extended MailItem.
...@@ -1190,7 +1192,6 @@ namespace pEp ...@@ -1190,7 +1192,6 @@ namespace pEp
/// <returns>True if it is an attached mail, otherwise false.</returns> /// <returns>True if it is an attached mail, otherwise false.</returns>
public static bool GetIsAttachedMail(this Outlook.MailItem omi, out string messageId) public static bool GetIsAttachedMail(this Outlook.MailItem omi, out string messageId)
{ {
messageId = null;
HeaderList headers = omi.GetParsedTransportMessageHeaders(); HeaderList headers = omi.GetParsedTransportMessageHeaders();
try try
......
...@@ -1614,7 +1614,6 @@ namespace pEp ...@@ -1614,7 +1614,6 @@ namespace pEp
// TODO: Check whether this method should be part of the engine? // TODO: Check whether this method should be part of the engine?
int unencryptedCount = 0; int unencryptedCount = 0;
pEpRating rating = pEpRating.pEpRatingUndefined; pEpRating rating = pEpRating.pEpRatingUndefined;
List<PEPIdentity> forceUnencryptedList = new List<PEPIdentity>();
PEPIdentity[] recipients; PEPIdentity[] recipients;
PEPMessage workingMessage; PEPMessage workingMessage;
......
using pEp.DPE; using pEp.DPE;
using pEp.UI.Models;
using pEp.UI.ViewModels; using pEp.UI.ViewModels;
using System; using System;
using System.ComponentModel;
using Outlook = Microsoft.Office.Interop.Outlook; using Outlook = Microsoft.Office.Interop.Outlook;
namespace pEp namespace pEp
{ {
partial class FormRegionDPE internal partial class FormRegionDPE
{ {
#region Form Region Factory #region Form Region Factory
...@@ -33,10 +33,41 @@ namespace pEp ...@@ -33,10 +33,41 @@ namespace pEp
try try
{ {
omi = this.OutlookItem as Outlook.MailItem; omi = this.OutlookItem as Outlook.MailItem;
string xml = omi.HTMLBody; string xml = omi.HTMLBody;
Patch patch = Patch.Deserialize(xml); Patch patch = Patch.Deserialize(xml);
PEPIdentity.GetFromIdentity(omi, out PEPIdentity submitter); FormControlPatchViewModel.PatchStatus patchStatus = FormControlPatchViewModel.PatchStatus.Open;
this.FormControlPatchView.DataContext = new FormControlPatchViewModel(patch, submitter); DateTime? editDate = null;
// Get patch status if available
if ((omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_STATUS, FormControlPatchViewModel.PatchStatus.Open) is string patchStatusString) &&
Enum.TryParse(patchStatusString, out FormControlPatchViewModel.PatchStatus status))
{
patchStatus = status;
}
// Get last edit date if available
if ((omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE) is string editDateString) &&
DateTime.TryParse(editDateString, out DateTime savedEditDate))
{
editDate = savedEditDate;
}
// Get the patch submitter
if (PEPIdentity.GetFromIdentity(omi, out PEPIdentity submitter) == Globals.ReturnStatus.Success)
{
this.FormControlPatchView.DataContext = new FormControlPatchViewModel(patch, submitter, patchStatus, editDate);
// Subscribe to property changed event
if (this.FormControlPatchView.DataContext is FormControlPatchViewModel formControlPatchViewModel)
{
formControlPatchViewModel.PropertyChanged += this.FormRegionDPE_PropertyChanged;
}
}
else
{
throw new Exception("FormRegionDPE_FormRegionShowing: Error getting patch submitter.");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -48,11 +79,55 @@ namespace pEp ...@@ -48,11 +79,55 @@ namespace pEp
} }
} }
/// <summary>
/// Event handler for when a property in the associated view model changes.
/// </summary>
private void FormRegionDPE_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Set patch status and edit date as user properties if being changed
if ((e.PropertyName == nameof(FormControlPatchViewModel.Status)) ||
(e.PropertyName == nameof(FormControlPatchViewModel.EditDate)))
{
Outlook.MailItem omi = null;
try
{
omi = this.OutlookItem as Outlook.MailItem;
FormControlPatchViewModel.PatchStatus patchStatus = (sender as FormControlPatchViewModel).Status;
DateTime? editDate = (sender as FormControlPatchViewModel).EditDate;
if (patchStatus != FormControlPatchViewModel.PatchStatus.Open)
{
omi.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_STATUS, Enum.GetName(typeof(FormControlPatchViewModel.PatchStatus), patchStatus));
}
if (editDate != null)
{
omi.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE, editDate?.ToString("f"));
}
omi.Save();
}
catch (Exception ex)
{
Log.Error("FormRegionDPE_PropertyChanged: Error setting user property. " + ex);
}
finally
{
omi = null;
}
}
}
// Occurs when the form region is closed. // Occurs when the form region is closed.
// Use this.OutlookItem to get a reference to the current Outlook item. // Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region. // Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegionDPE_FormRegionClosed(object sender, EventArgs e) private void FormRegionDPE_FormRegionClosed(object sender, EventArgs e)
{ {
// Unsubscribe from property changed event
if (this.FormControlPatchView.DataContext is FormControlPatchViewModel formControlPatchViewModel)
{
formControlPatchViewModel.PropertyChanged -= this.FormRegionDPE_PropertyChanged;
}
} }
} }
} }
using pEp.DPE; using pEp.DPE;
using System;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media;
namespace pEp.UI.ViewModels namespace pEp.UI.ViewModels
{ {
internal class FormControlPatchViewModel : ViewModelBase internal class FormControlPatchViewModel : ViewModelBase
{ {
public enum PatchStatus
{
Open,
Accepted,
Supported,
Rejected
}
#region Fields #region Fields
private Patch patch; private readonly Patch patch;
private PatchStatus _Status = PatchStatus.Open;
private DateTime? _EditDate = null;
private string _Explanation = null;
#endregion #endregion
...@@ -48,12 +60,17 @@ namespace pEp.UI.ViewModels ...@@ -48,12 +60,17 @@ namespace pEp.UI.ViewModels
/// <summary> /// <summary>
/// Gets the diff of this patch as formatted flow document. /// Gets the diff of this patch as formatted flow document.
/// </summary> /// </summary>
public FlowDocument DisplayDiff { get; } public FlowDocument DisplayDiff => PatchDialogViewModel.FormatDiff(this.Diff);
/// <summary>
/// Gets or sets the last date the patch was edited.
/// </summary>
public DateTime? EditDate { get => this._EditDate; set => this.SetProperty(ref this._EditDate, value); }
/// <summary> /// <summary>
/// The explanation shown in the UI regarding this patch. /// The explanation shown in the UI regarding this patch.
/// </summary> /// </summary>
public string Explanation { get; } public string Explanation { get => this._Explanation; set => this.SetProperty(ref this._Explanation, value); }
/// <summary> /// <summary>
/// Gets whether the OK button is visible. /// Gets whether the OK button is visible.
...@@ -68,7 +85,7 @@ namespace pEp.UI.ViewModels ...@@ -68,7 +85,7 @@ namespace pEp.UI.ViewModels
/// <summary> /// <summary>
/// The command to accept the patch dialog. /// The command to accept the patch dialog.
/// </summary> /// </summary>
public RelayCommand OKButtonCommand => new RelayCommand(this.SupportPatch); public RelayCommand OKButtonCommand => new RelayCommand(this.SupportPatch, p => (this.Status == PatchStatus.Open));
/// <summary> /// <summary>
/// Gets the OK button text. /// Gets the OK button text.
...@@ -78,7 +95,7 @@ namespace pEp.UI.ViewModels ...@@ -78,7 +95,7 @@ namespace pEp.UI.ViewModels
/// <summary> /// <summary>
/// The command to reject the dialog. /// The command to reject the dialog.
/// </summary> /// </summary>
public RelayCommand RejectButtonCommand => new RelayCommand(this.RejectPatch); public RelayCommand RejectButtonCommand => new RelayCommand(this.RejectPatch, p => (this.Status == PatchStatus.Open));
/// <summary> /// <summary>
/// Gets the Reject button text. /// Gets the Reject button text.
...@@ -90,6 +107,11 @@ namespace pEp.UI.ViewModels ...@@ -90,6 +107,11 @@ namespace pEp.UI.ViewModels
/// </summary> /// </summary>
public PEPIdentity Submitter { get; } public PEPIdentity Submitter { get; }
/// <summary>
/// Gets or sets the status of this patch.
/// </summary>
public PatchStatus Status { get => this._Status; set => this.SetProperty(ref this._Status, value); }
/// <summary> /// <summary>
/// Gets or sets the tag of the patch. /// Gets or sets the tag of the patch.
/// </summary> /// </summary>
...@@ -125,14 +147,17 @@ namespace pEp.UI.ViewModels ...@@ -125,14 +147,17 @@ namespace pEp.UI.ViewModels
/// </summary> /// </summary>
/// <param name="patch">The patch to create the form region with.</param> /// <param name="patch">The patch to create the form region with.</param>
/// <param name="submitter">The submitter of the patch.</param> /// <param name="submitter">The submitter of the patch.</param>
public FormControlPatchViewModel(Patch patch, PEPIdentity submitter) /// <param name="status">The status of the patch.</param>
/// <param name="editDate">The last time this patch was edited (accepted/supported/rejected).</param>
public FormControlPatchViewModel(Patch patch, PEPIdentity submitter, PatchStatus status = PatchStatus.Open, DateTime? editDate = null)
{ {
this.EditDate = editDate;
this.patch = patch; this.patch = patch;
this.Submitter = submitter; this.Submitter = submitter;
this.Explanation = "New configuration changes pending approval"; this.Status = status;
this.IsRejectButtonVisible = true; this.IsRejectButtonVisible = true;
this.OKButtonText = "Support"; this.OKButtonText = "Support";
this.DisplayDiff = this.FormatDiff(); this.SetExplanation();
} }
#endregion #endregion
...@@ -140,58 +165,44 @@ namespace pEp.UI.ViewModels ...@@ -140,58 +165,44 @@ namespace pEp.UI.ViewModels
#region Methods #region Methods
/// <summary> /// <summary>
/// Formats the diff displaying colors of changes. /// Rejects this patch.
/// </summary> /// </summary>
/// <returns>The formatted diff or null if no diff is available.</returns> /// <param name="parameter">The command parameter.</param>
private FlowDocument FormatDiff() private void RejectPatch(object parameter)
{
if (string.IsNullOrEmpty(this.Diff))
{ {
return null; Globals.ThisAddIn.DistributedPolicyEngine.Reject(this.patch, new PEPIdentity());
this.Status = PatchStatus.Rejected;
this.EditDate = DateTime.UtcNow;
this.SetExplanation();
} }
FlowDocument document = new FlowDocument /// <summary>
{ /// Sets the explanation text.
FontFamily = new FontFamily("Courier New"), /// </summary>
FontSize = 12.0, private void SetExplanation()
Background = Brushes.White
};
string[] lines = this.Diff?.Replace("\r\n", "\n")?.Split('\n') ?? new string[] { };
foreach (string line in lines)
{
Paragraph paragraph = new Paragraph(new Run(line))
{
Margin = new System.Windows.Thickness(1),
LineHeight = 14.0
};
if (line.StartsWith("-"))
{
paragraph.Background = Globals.ResourceDict["BrushRedBackground"] as Brush;
}
else if (line.StartsWith("+"))
{ {
paragraph.Background = Globals.ResourceDict["BrushGreenBackground"] as Brush; string editDate = "<n/a>";
} if (this.EditDate != null)
else if (line.StartsWith("@@"))
{ {
paragraph.Background = Brushes.LightGray; editDate = ((DateTime)this.EditDate).ToLocalTime().ToString("F");
} }
document.Blocks.Add(paragraph); switch (this.Status)
}
return document;
}
/// <summary>
/// Rejects this patch.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private void RejectPatch(object parameter)
{ {
Globals.ThisAddIn.DistributedPolicyEngine.Reject(this.patch, new PEPIdentity()); case PatchStatus.Accepted:
this.Explanation = $"Accepted on { editDate }";
break;
case PatchStatus.Supported:
this.Explanation = $"Supported on { editDate }";
break;
case PatchStatus.Rejected:
this.Explanation = $"Rejected on { editDate }";
break;
case PatchStatus.Open:
default:
this.Explanation = "New configuration changes pending approval";
break;
}
} }
/// <summary> /// <summary>
...@@ -201,6 +212,9 @@ namespace pEp.UI.ViewModels ...@@ -201,6 +212,9 @@ namespace pEp.UI.ViewModels
private void SupportPatch(object parameter) private void SupportPatch(object parameter)
{ {
Globals.ThisAddIn.DistributedPolicyEngine.Support(this.patch, new PEPIdentity()); Globals.ThisAddIn.DistributedPolicyEngine.Support(this.patch, new PEPIdentity());
this.Status = PatchStatus.Supported;
this.EditDate = DateTime.UtcNow;
this.SetExplanation();
} }
#endregion #endregion
......
...@@ -31,7 +31,7 @@ namespace pEp.UI.ViewModels ...@@ -31,7 +31,7 @@ namespace pEp.UI.ViewModels
MessageGroups = 5 MessageGroups = 5
} }
private PEPSettings pEpSettings; private readonly PEPSettings pEpSettings;
private int _AccountSettingsListSelectedIndex = -1; private int _AccountSettingsListSelectedIndex = -1;
private RelayCommand _CommandButtonAddGroup = null; private RelayCommand _CommandButtonAddGroup = null;
...@@ -45,9 +45,6 @@ namespace pEp.UI.ViewModels ...@@ -45,9 +45,6 @@ namespace pEp.UI.ViewModels
private RelayCommand _CommandButtonRefreshLogs = null; private RelayCommand _CommandButtonRefreshLogs = null;
private RelayCommand _CommandButtonResetPEPStore = null; private RelayCommand _CommandButtonResetPEPStore = null;
private RelayCommand _CommandButtonResetAllOwnKeys = null; private RelayCommand _CommandButtonResetAllOwnKeys = null;
private RelayCommand<IList<object>> _CommandOpenPatch = null;
private RelayCommand<IList<object>> _CommandRejectPatch = null;
private RelayCommand<IList<object>> _CommandSupportPatch = null;
private bool _IsAdvancedEnabled = false; private bool _IsAdvancedEnabled = false;
private string _LogEngine = null; private string _LogEngine = null;
private string _LogOutlook = null; private string _LogOutlook = null;
...@@ -78,12 +75,6 @@ namespace pEp.UI.ViewModels ...@@ -78,12 +75,6 @@ namespace pEp.UI.ViewModels
this.AccountSettingsList.Add(new AccountViewModel(accountSettings, this.CalculateDependentProperties)); this.AccountSettingsList.Add(new AccountViewModel(accountSettings, this.CalculateDependentProperties));
}); });
// Add patches
DistributedPolicyEngine.GetOpenPatches()?.ForEach((tuple) =>
{
this.Patches.Add(new PatchViewModel(tuple.Item1, tuple.Item2));
});
// Calculate dependent properties // Calculate dependent properties
this.calcDepPropIsEnabled = true; this.calcDepPropIsEnabled = true;
this.CalculateDependentProperties(); this.CalculateDependentProperties();
...@@ -305,55 +296,6 @@ namespace pEp.UI.ViewModels ...@@ -305,55 +296,6 @@ namespace pEp.UI.ViewModels
} }
} }
/// <summary>
/// Gets the command to open a patch.
/// </summary>
public RelayCommand<IList<object>> CommandOpenPatch
{
get
{
if (this._CommandOpenPatch == null)
{
this._CommandOpenPatch = new RelayCommand<IList<object>>(p => (p?.First() as PatchViewModel)?.OpenPatchDialog(PatchDialog.PatchAction.SupportOrRejectPatch));
}
return this._CommandOpenPatch;
}
}
/// <summary>
/// Gets the command to reject a patch.
/// </summary>
public RelayCommand<IList<object>> CommandRejectPatch
{
get
{
if (this._CommandRejectPatch == null)
{
this._CommandRejectPatch = new RelayCommand<IList<object>>(p => this.RejectPatch(p?.First() as PatchViewModel));
}
return this._CommandRejectPatch;
}
}
/// <summary>
/// Gets the command to support a patch.
/// </summary>
public RelayCommand<IList<object>> CommandSupportPatch
{
get
{
if (this._CommandSupportPatch == null)
{
this._CommandSupportPatch = new RelayCommand<IList<object>>(p => this.SupportPatch(p?.First() as PatchViewModel));
}
return this._CommandSupportPatch;
}
}
/// <summary> /// <summary>
/// Gets the list of projects that are used in pEp for Outlook and that /// Gets the list of projects that are used in pEp for Outlook and that
/// are being credited. /// are being credited.
...@@ -1113,37 +1055,6 @@ namespace pEp.UI.ViewModels ...@@ -1113,37 +1055,6 @@ namespace pEp.UI.ViewModels
} while (retry); } while (retry);
} }
/// <summary>
/// Opens the Patch dialog.
/// </summary>
/// <param name="patchViewModel">The patch view model that </param>
/// <param name="patchAction">The patch action to execute.</param>
/// <returns>The dialog result.</returns>
public System.Windows.Forms.DialogResult OpenPatchDialog(PatchViewModel patchViewModel, PatchDialog.PatchAction patchAction)
{
if (patchViewModel == null)
{
Log.ErrorAndFailInDebugMode("OpenPatchDialog: patch view model is null.");
}
System.Windows.Forms.DialogResult dialogResult = patchViewModel?.OpenPatchDialog(patchAction) ?? System.Windows.Forms.DialogResult.Cancel;
if (dialogResult != System.Windows.Forms.DialogResult.Cancel)
{
this.Patches.Clear();
// Add patches
DistributedPolicyEngine.GetOpenPatches()?.ForEach((tuple) =>
{
this.Patches.Add(new PatchViewModel(tuple.Item1, tuple.Item2));
});
this.OnPropertyChanged(nameof(this.Patches));
}
return dialogResult;
}
/// <summary> /// <summary>
/// Resets the logs. /// Resets the logs.
/// </summary> /// </summary>
...@@ -1179,43 +1090,6 @@ namespace pEp.UI.ViewModels ...@@ -1179,43 +1090,6 @@ namespace pEp.UI.ViewModels
} }
} }
/// <summary>
/// Removes the given patch from the list of patches.
/// </summary>
/// <param name="patchViewModel">The patch view model to remove.</param>
private void RemovePatch(PatchViewModel patchViewModel)
{
if (patchViewModel == null)
{
Log.ErrorAndFailInDebugMode("RemovePatch: patch view model is null.");
}
if (!this.Patches.Remove(patchViewModel))
{
Log.Error("RemovePatch: Could not remove patch.");
}
}
/// <summary>
/// Rejects the patch.
/// </summary>
/// <param name="patchViewModel">The patch's view model.</param>
private void RejectPatch(PatchViewModel patchViewModel)
{
patchViewModel?.RejectPatch();
this.RemovePatch(patchViewModel);
}
/// <summary>
/// Supports the patch.
/// </summary>
/// <param name="patchViewModel">The patch's view model.</param>
private void SupportPatch(PatchViewModel patchViewModel)
{
patchViewModel?.SupportPatch();
this.RemovePatch(patchViewModel);
}
#endregion #endregion
} }
} }
...@@ -10,6 +10,7 @@ using System.Diagnostics; ...@@ -10,6 +10,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media; using System.Windows.Media;
...@@ -372,40 +373,30 @@ namespace pEp.UI.ViewModels ...@@ -372,40 +373,30 @@ namespace pEp.UI.ViewModels
List<DiffPiece> newLines = sideBySideDiffModel.NewText.Lines; List<DiffPiece> newLines = sideBySideDiffModel.NewText.Lines;
List<DiffPiece> oldLines = sideBySideDiffModel.OldText.Lines; List<DiffPiece> oldLines = sideBySideDiffModel.OldText.Lines;
List<string> block = new List<string>(); List<string> block = new List<string>();
int unchangedCount = 0;
bool newBlock = true; bool newBlock = true;
int oldFileIndex = 0, newFileIndex = 0; int oldFileIndex = 0, newFileIndex = 0;
for (int i = 0; i < newLines.Count; i++) for (int i = 0; i < newLines.Count; i++)
{ {
// Add lines as necessary // Add lines as necessary
if ((newLines[i].Type == ChangeType.Unchanged) && if (newLines[i].Type == ChangeType.Imaginary)
(block.Count > 0))
{
block.Add(" " + newLines[i].Text);
unchangedCount++;
}
else if (newLines[i].Type == ChangeType.Imaginary)
{ {
block.Add("-" + oldLines[i].Text); block.Add("-" + oldLines[i].Text);
unchangedCount = 0;
} }
else if (newLines[i].Type == ChangeType.Inserted) else if (newLines[i].Type == ChangeType.Inserted)
{ {
block.Add("+" + newLines[i].Text); block.Add("+" + newLines[i].Text);
unchangedCount = 0;
} }
else if (newLines[i].Type == ChangeType.Modified) else if (newLines[i].Type == ChangeType.Modified)
{ {
block.Add("-" + oldLines[i].Text); block.Add("-" + oldLines[i].Text);
block.Add("+" + newLines[i].Text); block.Add("+" + newLines[i].Text);
unchangedCount = 0;
} }
// If a block is being constructed, add additional parts as necessary // If a block is being constructed, add additional parts as necessary
if (block.Count > 0) if (block.Count > 0)
{ {
// If we have a new block, add the three preceding unchanged lines // First, add the three preceding unchanged lines (if available)
if (newBlock) if (newBlock)
{ {
newBlock = false; newBlock = false;
...@@ -425,14 +416,32 @@ namespace pEp.UI.ViewModels ...@@ -425,14 +416,32 @@ namespace pEp.UI.ViewModels
newFileIndex = newLines[index].Position ?? 1; newFileIndex = newLines[index].Position ?? 1;
oldFileIndex = oldLines[index].Position ?? 1; oldFileIndex = oldLines[index].Position ?? 1;
} }
else if ((unchangedCount > 2) || (i == newLines.Count - 1))
// Then, add the three following lines (if available)
int counter = 0;
while ((++i < newLines.Count) &&
(newLines[i].Type == ChangeType.Unchanged))
{
if (counter++ < 3)
{
block.Add(" " + newLines[i].Text);
}
else
{
break;
}
}
// Close block
if ((counter > 3) ||
(i >= newLines.Count))
{ {
// Once a block is complete, add descriptor at the beginning and add lines to diff // Once a block is complete, add descriptor at the beginning and add lines to diff
int oldFileLineCount = block.Count(s => !s.StartsWith("+")); int oldFileLineCount = block.Count(s => !s.StartsWith("+"));
int newFileLineCount = block.Count(s => !s.StartsWith("-")); int newFileLineCount = block.Count(s => !s.StartsWith("-"));
diff += $"@@ -{ oldFileIndex },{ oldFileLineCount } +{ newFileIndex },{ newFileLineCount } @@\n"; diff += $"@@ -{ oldFileIndex },{ oldFileLineCount } +{ newFileIndex },{ newFileLineCount } @@\n";
foreach (var line in block) foreach (string line in block)
{ {
diff += line + '\n'; diff += line + '\n';
} }
...@@ -440,6 +449,8 @@ namespace pEp.UI.ViewModels ...@@ -440,6 +449,8 @@ namespace pEp.UI.ViewModels
block = new List<string>(); block = new List<string>();
newBlock = true; newBlock = true;
} }
i--;
} }
} }
...@@ -512,53 +523,6 @@ namespace pEp.UI.ViewModels ...@@ -512,53 +523,6 @@ namespace pEp.UI.ViewModels
Log.Error($"EditFileAndCreateDiff: Error reading file content from { fileName }: { ex }"); Log.Error($"EditFileAndCreateDiff: Error reading file content from { fileName }: { ex }");
} }
} }
/// <summary>
/// Formats the diff displaying colors of changes.
/// </summary>
/// <returns>The formatted diff or null if no diff is available.</returns>
private FlowDocument FormatDiff(string diff)
{
if (string.IsNullOrEmpty(diff))
{
return null;
}
FlowDocument document = new FlowDocument
{
FontFamily = new FontFamily("Courier New"),
FontSize = 12.0,
Background = Brushes.White,
};
string[] lines = diff?.Replace("\r\n", "\n")?.Split('\n') ?? new string[] { };
foreach (string line in lines)
{
Paragraph paragraph = new Paragraph(new Run(line))
{
Margin = new System.Windows.Thickness(1),
LineHeight = 14.0
};
if (line.StartsWith("-"))
{
paragraph.Background = Globals.ResourceDict["BrushRedBackground"] as Brush;
}
else if (line.StartsWith("+"))
{
paragraph.Background = Globals.ResourceDict["BrushGreenBackground"] as Brush;
}
else if (line.StartsWith("@@"))
{
paragraph.Background = Brushes.LightGray;
}
document.Blocks.Add(paragraph);
}
return document;
}
/// <summary> /// <summary>
/// Gets a common root URI from all selected files in the dialog. /// Gets a common root URI from all selected files in the dialog.
/// </summary> /// </summary>
...@@ -620,7 +584,7 @@ namespace pEp.UI.ViewModels ...@@ -620,7 +584,7 @@ namespace pEp.UI.ViewModels
/// </summary> /// </summary>
private void UpdateVisibleDiff() private void UpdateVisibleDiff()
{ {
this.VisibleDiff = this.FormatDiff(this.SelectedFile?.Diff); this.VisibleDiff = PatchDialogViewModel.FormatDiff(this.SelectedFile?.Diff);
} }
/// <summary> /// <summary>
...@@ -635,5 +599,73 @@ namespace pEp.UI.ViewModels ...@@ -635,5 +599,73 @@ namespace pEp.UI.ViewModels
} }
#endregion #endregion
#region Static methods
/// <summary>
/// Formats the diff displaying colors of changes.
/// </summary>
/// <returns>The formatted diff or null if no diff is available.</returns>
public static FlowDocument FormatDiff(string diff)
{
if (string.IsNullOrEmpty(diff))
{
return null;
}
// Initialize the document with zero width to set the real width later
FlowDocument document = new FlowDocument
{
Background = Brushes.White,
PageWidth = 0
};
string[] lines = diff.Replace("\r\n", "\n")?.Split('\n') ?? new string[] { };
foreach (string line in lines)
{
Paragraph paragraph = new Paragraph()
{
Margin = new Thickness(1),
};
TextBlock textBlock = new TextBlock()
{
Text = line,
FontFamily = new FontFamily("Courier New"),
LineHeight = 14.0,
FontSize = 12.0,
TextWrapping = TextWrapping.NoWrap
};
// Set the document width to the biggest textblock width plus some margin
// to account for padding inside the scroll viewer
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
if (textBlock.DesiredSize.Width > document.PageWidth)
{
document.PageWidth = textBlock.DesiredSize.Width + 50;
}
paragraph.Inlines.Add(textBlock);
// Color the added and removed lines accordingly
if (line.StartsWith("-"))
{
paragraph.Background = Globals.ResourceDict["BrushRedBackground"] as Brush;
}
else if (line.StartsWith("+"))
{
paragraph.Background = Globals.ResourceDict["BrushGreenBackground"] as Brush;
}
else if (line.StartsWith("@@"))
{
paragraph.Background = Brushes.LightGray;
}
document.Blocks.Add(paragraph);
}
return document;
}
#endregion
} }
} }
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
<FlowDocumentScrollViewer Grid.Column="1" <FlowDocumentScrollViewer Grid.Column="1"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
Grid.Row="3" Grid.Row="3"
HorizontalScrollBarVisibility="Auto"
BorderBrush="Black" BorderBrush="Black"
BorderThickness="1" BorderThickness="1"
Margin="5,10,10,10" Margin="5,10,10,10"
......
using System; using System.Windows.Controls;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace pEp.UI.Views namespace pEp.UI.Views
{ {
......
...@@ -976,52 +976,7 @@ ...@@ -976,52 +976,7 @@
FontSize="16" /> FontSize="16" />
<Separator /> <Separator />
<!--Patch management--> <!--New patch button-->
<ListBox Name="patchManagementListBox"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Patches}"
MinHeight="300"
Margin="10,5" >
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Open"
Command="{Binding DataContext.CommandOpenPatch}"
CommandParameter="{Binding Path=SelectedItems}"/>
<MenuItem Header="Support"
Command="{Binding DataContext.CommandSupportPatch}"
CommandParameter="{Binding Path=SelectedItems}"/>
<MenuItem Header="Reject"
Command="{Binding DataContext.CommandRejectPatch}"
CommandParameter="{Binding Path=SelectedItems}"/>
</ContextMenu>
</ListBox.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl MouseDoubleClick="ContentControl_MouseDoubleClick">
<Grid Background="{Binding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding CommitMessage}"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"
Foreground="{Binding Foreground}"
Margin="10"/>
<TextBlock Grid.Column="1"
Text="{Binding LastUpdate}"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"
Foreground="{Binding Foreground}"
Margin="10"/>
</Grid>
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="New patch" <Button Content="New patch"
Style="{StaticResource StyleButtonGray}" Style="{StaticResource StyleButtonGray}"
Margin="10,5,5,5" Margin="10,5,5,5"
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
BorderBrush="Black" BorderBrush="Black"
BorderThickness="1" BorderThickness="1"
Margin="5,10,10,10" Margin="5,10,10,10"
HorizontalScrollBarVisibility="Auto"
Document="{Binding VisibleDiff}"/> Document="{Binding VisibleDiff}"/>
<ui:TextBoxWithPlaceholder Grid.Column="0" <ui:TextBoxWithPlaceholder Grid.Column="0"
Grid.ColumnSpan="3" Grid.ColumnSpan="3"
......