Commits (3)
......@@ -11,7 +11,7 @@ namespace pEp.DPE
internal class DistributedPolicyEngine : IDistributedPolicyEngine
{
private readonly static string DPE_FOLDER = Path.Combine(Globals.PEPUserFolder, "DPE");
public readonly static string DPE_BACKUP_LOCATION = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, "temp");
public readonly static string DPE_TEMP_LOCATION = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, "temp");
private const string PATCH_EXTENSION = ".patch";
private const string PATCH_MESSAGE_SUBJECT = "Configuration changes";
public const string DPE_MESSAGE_CLASS = "IPM.Note.DPE";
......
namespace pEp.UI.Models
{
public class ConfigFile
{
public string FileName { get; }
public string TempFileName { get; }
public string Diff { get; }
public ConfigFile(string fileName, string tempFileName, string diff)
{
this.FileName = fileName;
this.TempFileName = tempFileName;
this.Diff = diff;
}
}
}
......@@ -97,7 +97,6 @@ namespace pEp.UI.Models
{
if (dialogResult == System.Windows.Forms.DialogResult.OK)
{
patchDialog.Patch.CreationDate = DateTime.UtcNow;
Globals.ThisAddIn.DistributedPolicyEngine.Suggest(patchDialog.Patch, me);
}
}
......
......@@ -9,6 +9,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
......@@ -18,17 +19,36 @@ namespace pEp.UI.ViewModels
{
#region Fields
private bool _IsCommitMessageValid = false;
private bool _IsDiffValid = false;
private bool _IsValid = false;
private RelayCommand _LoadFromFileCommand = null;
private Tuple<string, string, string> _SelectedFile = null;
private FlowDocument _VisibleDiff = null;
private RelayCommand _AddOrEditFileCommand = null;
private bool _IsCommitMessageValid = false;
private bool _IsDiffValid = false;
private bool _IsValid = false;
private ConfigFile _SelectedFile = null;
private FlowDocument _VisibleDiff = null;
private const string FILE_A_PREFIX = "---a";
private const string FILE_B_PREFIX = "+++b";
#endregion
#region Properties
/// <summary>
/// Gets the command to load the diff from file.
/// </summary>
public RelayCommand AddOrEditFileCommand
{
get
{
if (this._AddOrEditFileCommand == null)
{
this._AddOrEditFileCommand = new RelayCommand(this.AddOrEditFile);
}
return this._AddOrEditFileCommand;
}
}
/// <summary>
/// Command to cancel the dialog.
/// </summary>
......@@ -131,26 +151,10 @@ namespace pEp.UI.ViewModels
}
}
/// <summary>
/// Gets the command to load the diff from file.
/// </summary>
public RelayCommand LoadFromFileCommand
{
get
{
if (this._LoadFromFileCommand == null)
{
this._LoadFromFileCommand = new RelayCommand(this.AddFile);
}
return this._LoadFromFileCommand;
}
}
/// <summary>
/// Gets the collection of config files that are being modified.
/// </summary>
public ObservableCollection<Tuple<string, string, string>> ConfigFiles { get; } = new ObservableCollection<Tuple<string, string, string>>();
public ObservableCollection<ConfigFile> ConfigFiles { get; } = new ObservableCollection<ConfigFile>();
/// <summary>
/// The command to accept the patch dialog.
......@@ -170,7 +174,7 @@ namespace pEp.UI.ViewModels
/// <summary>
/// Gets the currently selected file.
/// </summary>
public Tuple<string, string, string> SelectedFile
public ConfigFile SelectedFile
{
get => this._SelectedFile;
set
......@@ -240,22 +244,44 @@ namespace pEp.UI.ViewModels
/// Adds a new file to the list of modified config files.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private void AddFile(object parameter)
private void AddOrEditFile(object parameter)
{
// Create the dialog to select the key file
OpenFileDialog openFileDialog = new OpenFileDialog
string fileName = parameter as string;
if (string.IsNullOrEmpty(fileName))
{
CheckFileExists = true,
CheckPathExists = true,
Filter = "All files|*.*",
Multiselect = false
};
// Create the dialog to select the key file
OpenFileDialog openFileDialog = new OpenFileDialog
{
CheckFileExists = true,
CheckPathExists = true,
Filter = "All files|*.*",
Multiselect = false
};
// Import the key file and set the key as default
if (openFileDialog.ShowDialog() == true)
{
fileName = openFileDialog.FileName;
}
}
// Import the key file and set the key as default
if (openFileDialog.ShowDialog() == true)
if (!string.IsNullOrEmpty(fileName))
{
this.EditFileAndCreateDiff(openFileDialog.FileName);
this.EditFileAndCreateDiff(fileName);
}
this.ValidatePatch();
}
/// <summary>
/// Closes this window.
/// </summary>
/// <param name="dialogResult">The dialog result.</param>
private new void Close(bool? dialogResult)
{
// Delete temporary directory and close
this.DeleteTempDirectory();
base.Close(dialogResult);
}
/// <summary>
......@@ -267,16 +293,34 @@ namespace pEp.UI.ViewModels
// The patch was created right now
this.Dialog.Patch.CreationDate = DateTime.UtcNow;
// Concatenate all diffs from the selected files
this.Dialog.Patch.Diff = this.UnifyDiffs();
// Get a common root URI of all diff files
this.Dialog.Patch.Uri = this.GetRootUri();
// Concatenate all diffs from the selected files
this.Dialog.Patch.Diff = this.UnifyDiffs();
// Close with result True.
this.Close(true);
}
/// <summary>
/// Deletes the temporary directory.
/// </summary>
private void DeleteTempDirectory()
{
try
{
if (Directory.Exists(DistributedPolicyEngine.DPE_TEMP_LOCATION))
{
Directory.Delete(DistributedPolicyEngine.DPE_TEMP_LOCATION, true);
}
}
catch (Exception ex)
{
Log.Error("DeleteTempDirectory: Error deleting temporary directory. " + ex);
}
}
/// <summary>
/// Creates a diff from two files.
/// </summary>
......@@ -323,8 +367,7 @@ namespace pEp.UI.ViewModels
* => The modified part.
* => Three unchanged lines (if available) after the modified part.
*/
string diff = $"---a/{ fileNameB }\n+++b/{ fileNameB }\n";
string diff = null;
SideBySideDiffModel sideBySideDiffModel = SideBySideDiffBuilder.Diff(fileA, fileB);
List<DiffPiece> newLines = sideBySideDiffModel.NewText.Lines;
List<DiffPiece> oldLines = sideBySideDiffModel.OldText.Lines;
......@@ -349,7 +392,7 @@ namespace pEp.UI.ViewModels
}
else if (newLines[i].Type == ChangeType.Inserted)
{
block.Add("-" + oldLines[i].Text);
block.Add("+" + newLines[i].Text);
unchangedCount = 0;
}
else if (newLines[i].Type == ChangeType.Modified)
......@@ -373,6 +416,10 @@ namespace pEp.UI.ViewModels
{
block.Insert(0, " " + newLines[index].Text);
}
else
{
break;
}
}
index++;
newFileIndex = newLines[index].Position ?? 1;
......@@ -396,108 +443,13 @@ namespace pEp.UI.ViewModels
}
}
return diff.TrimEnd('\n');
}
if (string.IsNullOrEmpty(diff))
{
return null;
}
//private string CreateDiff(string fileNameA, string fileNameB)
//{
// if (!(File.Exists(fileNameA) && File.Exists(fileNameB)))
// {
// Log.ErrorAndFailInDebugMode("CreateDiff: Input file doesn't exist.");
// return null;
// }
// string fileA, fileB;
// try
// {
// fileA = File.ReadAllText(fileNameA);
// fileB = File.ReadAllText(fileNameB);
// }
// catch (Exception ex)
// {
// Log.Error("CreateDiff: Error reading input file. " + ex);
// return null;
// }
// string diff = $"---a/{ fileNameB }\n+++b/{ fileNameB }\n";
// DiffPaneModel diffPaneModel = InlineDiffBuilder.Diff(fileA, fileB);
// List<DiffPiece> lines = diffPaneModel.Lines;
// List<DiffPiece> block = new List<DiffPiece>();
// for (int i = 0; i < lines.Count; i++)
// {
// if (lines[i].Type == ChangeType.Deleted || lines[i].Type == ChangeType.Inserted)
// {
// if (block.Count == 0)
// {
// int index = i - 3;
// do
// {
// if (index < 0)
// {
// continue;
// }
// if (lines[index].Type == ChangeType.Unchanged)
// {
// block.Add(lines[index]);
// }
// } while (++index < i);
// }
// block.Add(lines[i]);
// }
// else if (block.Count > 0)
// {
// block.Add(lines[i]);
// int index = 0;
// while ((++i < lines.Count) &&
// (++index < 3))
// {
// if (lines[i].Type == ChangeType.Unchanged)
// {
// block.Add(lines[i]);
// }
// else
// {
// break;
// }
// }
// if ((index == 3) || (i == lines.Count))
// {
// diff += $"@@ -{ block.First().Position },{ block.Last().Position } +{1},{1} @@\n";
// foreach (var diffPiece in block)
// {
// switch (diffPiece.Type)
// {
// case ChangeType.Unchanged:
// diff += $" { diffPiece.Text }\n";
// break;
// case ChangeType.Deleted:
// diff += $"-{ diffPiece.Text }\n";
// break;
// case ChangeType.Inserted:
// diff += $"+{ diffPiece.Text }\n";
// break;
// case ChangeType.Imaginary:
// case ChangeType.Modified:
// default:
// Log.ErrorAndFailInDebugMode("CreateDiff: Untreated diff piece.");
// break;
// }
// }
// block = new List<DiffPiece>();
// }
// }
// }
// return diff.TrimEnd('\n');
//}
return diff.Insert(0, $"{ PatchDialogViewModel.FILE_A_PREFIX }/{ fileNameB }\n{ PatchDialogViewModel.FILE_B_PREFIX }/{ fileNameB }\n").TrimEnd('\n');
}
/// <summary>
/// Opens the given file to edit and creates a diff once the editor is closed.
......@@ -508,38 +460,52 @@ namespace pEp.UI.ViewModels
try
{
// Get the temp file that will actually be edited
string tempFileName;
if ((this.ConfigFiles.First(cf => cf.Item1.Equals(fileName)) is Tuple<string, string, string> configFile) &&
(string.IsNullOrEmpty(configFile.Item2) == false))
string tempFileName, originalFileName;
if ((this.ConfigFiles.FirstOrDefault(cf => cf.FileName.Equals(fileName)) is ConfigFile configFile) &&
(string.IsNullOrEmpty(configFile.TempFileName) == false))
{
tempFileName = configFile.Item2;
tempFileName = configFile.TempFileName;
originalFileName = tempFileName + ".orig";
File.Copy(tempFileName, fileName, true);
}
else
{
tempFileName = Path.Combine(DistributedPolicyEngine.DPE_BACKUP_LOCATION, Path.GetFileName(fileName) + ".orig");
Directory.CreateDirectory(DistributedPolicyEngine.DPE_BACKUP_LOCATION);
File.Copy(fileName, tempFileName);
tempFileName = Path.Combine(DistributedPolicyEngine.DPE_TEMP_LOCATION, Path.GetFileName(fileName));
originalFileName = tempFileName + ".orig";
Directory.CreateDirectory(DistributedPolicyEngine.DPE_TEMP_LOCATION);
File.Copy(fileName, tempFileName, true);
File.Copy(fileName, originalFileName, true);
}
// Open temp file in VS Code for the user to edit
Process openCodeProcess = new Process
// Open temp file in Notepad for the user to edit
using (Process modifyFileProcess = Process.Start(new ProcessStartInfo
{
StartInfo = new ProcessStartInfo
{
FileName = "code",
Arguments = tempFileName,
UseShellExecute = true,
CreateNoWindow = true
}
};
openCodeProcess.Start();
openCodeProcess.WaitForExit();
FileName = "Notepad.exe",
Arguments = fileName,
UseShellExecute = false,
CreateNoWindow = true
}))
{
modifyFileProcess.WaitForExit();
}
// If changes were made, add or update file, otherwise show notification
if (this.CreateDiff(originalFileName, fileName) is string diff)
{
this.ConfigFiles.Remove(this.ConfigFiles.Where(f => (f.FileName?.Equals(fileName) == true) && f.TempFileName?.Equals(tempFileName) == true).FirstOrDefault());
this.ConfigFiles.Add(new ConfigFile(fileName, tempFileName, diff));
if (this.ConfigFiles.Remove(this.ConfigFiles.Where(f => (f.Item1?.Equals(fileName) == true) && f.Item2?.Equals(tempFileName) == true).First()))
// Select the created diff in the UI
this.SelectedFile = this.ConfigFiles.Last();
}
else
{
string diff = this.CreateDiff(fileName, tempFileName);
this.ConfigFiles.Add(new Tuple<string, string, string>(fileName, tempFileName, diff));
MessageBox.Show("No changes were made. Ignoring selected file.");
}
// Always update the respective files
File.Copy(fileName, tempFileName, true);
File.Copy(originalFileName, fileName, true);
}
catch (Exception ex)
{
......@@ -599,14 +565,14 @@ namespace pEp.UI.ViewModels
/// <returns>The root URI</returns>
private string GetRootUri()
{
string rootUri = this.ConfigFiles.First()?.Item1;
string rootUri = this.ConfigFiles.First()?.FileName;
foreach (var modifiedFile in this.ConfigFiles)
foreach (ConfigFile modifiedFile in this.ConfigFiles)
{
for (int i = 0; i < modifiedFile.Item1.Length; i++)
for (int i = 0; i < modifiedFile.FileName.Length; i++)
{
if ((rootUri.Length <= i) ||
(modifiedFile.Item1[i] == rootUri[i]))
(modifiedFile.FileName[i] == rootUri[i]))
{
continue;
}
......@@ -636,11 +602,16 @@ namespace pEp.UI.ViewModels
{
string diff = null;
foreach (var modifiedFile in this.ConfigFiles)
// Concatenate diffs
foreach (ConfigFile modifiedFile in this.ConfigFiles)
{
diff += modifiedFile.Item3 + "\n";
diff += modifiedFile.Diff + "\n";
}
// Remove root parts of URI
diff = diff.Replace(PatchDialogViewModel.FILE_A_PREFIX + "/" + this.Dialog.Patch.Uri, PatchDialogViewModel.FILE_A_PREFIX + "/");
diff = diff.Replace(PatchDialogViewModel.FILE_B_PREFIX + "/" + this.Dialog.Patch.Uri, PatchDialogViewModel.FILE_B_PREFIX + "/");
return diff.TrimEnd('\n');
}
......@@ -649,7 +620,7 @@ namespace pEp.UI.ViewModels
/// </summary>
private void UpdateVisibleDiff()
{
this.VisibleDiff = this.FormatDiff(this.SelectedFile?.Item3);
this.VisibleDiff = this.FormatDiff(this.SelectedFile?.Diff);
}
/// <summary>
......
......@@ -48,7 +48,9 @@
ItemsSource="{Binding ConfigFiles}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Item1}" />
<ContentControl MouseDoubleClick="ListBoxItem_MouseDoubleClick">
<Label Content="{Binding FileName}" />
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
......@@ -57,7 +59,7 @@
<Button Content="Add file"
Margin="5"
Style="{StaticResource StyleButtonGray}"
Command="{Binding LoadFromFileCommand}"/>
Command="{Binding AddOrEditFileCommand}"/>
<Button Content="Remove file"
Margin="5"
Style="{StaticResource StyleButtonGray}"
......
using System.Windows.Controls;
using pEp.UI.Models;
using pEp.UI.ViewModels;
using System.Windows.Controls;
using System.Windows.Input;
namespace pEp.UI.Views
{
......@@ -7,9 +10,21 @@ namespace pEp.UI.Views
/// </summary>
public partial class PatchDialogView : UserControl
{
/// <summary>
/// Primary constructor.
/// </summary>
public PatchDialogView()
{
InitializeComponent();
}
/// <summary>
/// Event handler for when a list box item is double-clicked.
/// </summary>
private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Edit the file that is being double-clicked
(this.DataContext as PatchDialogViewModel)?.AddOrEditFileCommand?.Execute(((sender as ContentControl)?.DataContext as ConfigFile)?.FileName);
}
}
}
......@@ -431,6 +431,7 @@
<Compile Include="UI\FormRegionDPE.Designer.cs">
<DependentUpon>FormRegionDPE.cs</DependentUpon>
</Compile>
<Compile Include="UI\Models\ConfigFile.cs" />
<Compile Include="UI\Models\GroupWizard.cs" />
<Compile Include="UI\Models\CustomMessageBox.cs" />
<Compile Include="UI\Models\InputMessageBox.cs" />
......