Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Windows
pEp for Outlook
Compare Revisions
e5f5de6b29a1657d5b5ce75566f3b592724dc581...1510fb85c5ce8474d8f3dfe9234c4ce124ba22dc
Commits (7)
Move the patch button to backstage
· 82177e2a
Thomas
authored
Sep 28, 2021
82177e2a
Show animation while calling API
· 2a11d8b6
Thomas
authored
Sep 29, 2021
2a11d8b6
Add initialization overlay to hide form region during loading
· d0c20197
Thomas
authored
Sep 30, 2021
d0c20197
Update the patch model
· 3c22c064
Thomas
authored
Sep 30, 2021
3c22c064
Unify patch view models
· 6f78066a
Thomas
authored
Sep 30, 2021
6f78066a
Rename patch view model
· 3bfe6a7d
Thomas
authored
Sep 30, 2021
3bfe6a7d
Add ApplyTo identities and catch errors
· 1510fb85
Thomas
authored
Sep 30, 2021
1510fb85
Hide whitespace changes
Inline
Side-by-side
DPE/DPEWebClient.cs
View file @
1510fb85
...
@@ -20,7 +20,7 @@ namespace pEp.DPE
...
@@ -20,7 +20,7 @@ namespace pEp.DPE
/// <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">The post URI is null.</exception>
/// <exception cref="ArgumentNullException">The post URI is null.</exception>
/// <exception cref="HttpRequestException">The HTTP request failed.</exception>
/// <exception cref="HttpRequestException">The HTTP request failed.</exception>
/// <returns>The H
ttp
response.</returns>
/// <returns>The H
TTP
response.</returns>
public
static
async
Task
<
HttpResponseMessage
>
RejectPatch
(
Patch
patch
,
PEPIdentity
me
)
public
static
async
Task
<
HttpResponseMessage
>
RejectPatch
(
Patch
patch
,
PEPIdentity
me
)
{
{
// POST http://localhost:port/pEpDPE/patches/patch_id/reject
// POST http://localhost:port/pEpDPE/patches/patch_id/reject
...
@@ -46,9 +46,9 @@ namespace pEp.DPE
...
@@ -46,9 +46,9 @@ 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="ArgumentNullException"
/
>
/// <exception cref="ArgumentNullException"
>The post URI is null.</exception
>
/// <exception cref="HttpRequestException"
/
>
/// <exception cref="HttpRequestException"
>The HTTP request failed.</exception
>
/// <returns>The H
ttp
response.</returns>
/// <returns>The H
TTP
response.</returns>
public
static
async
Task
<
HttpResponseMessage
>
SuggestPatch
(
Patch
patch
,
PEPIdentity
me
)
public
static
async
Task
<
HttpResponseMessage
>
SuggestPatch
(
Patch
patch
,
PEPIdentity
me
)
{
{
// POST http://localhost:port/pEpDPE/patches/
// POST http://localhost:port/pEpDPE/patches/
...
@@ -74,9 +74,9 @@ namespace pEp.DPE
...
@@ -74,9 +74,9 @@ 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="ArgumentNullException"
/
>
/// <exception cref="ArgumentNullException"
>The post URI is null.</exception
>
/// <exception cref="HttpRequestException"
/
>
/// <exception cref="HttpRequestException"
>The HTTP request failed.</exception
>
/// <returns>The H
ttp
response.</returns>
/// <returns>The H
TTP
response.</returns>
public
static
async
Task
<
HttpResponseMessage
>
SupportPatch
(
Patch
patch
,
PEPIdentity
me
)
public
static
async
Task
<
HttpResponseMessage
>
SupportPatch
(
Patch
patch
,
PEPIdentity
me
)
{
{
// POST http://localhost:port/pEpDPE/patches/patch_id/support
// POST http://localhost:port/pEpDPE/patches/patch_id/support
...
...
DPE/DistributedPolicyEngine.cs
View file @
1510fb85
...
@@ -156,8 +156,15 @@ namespace pEp.DPE
...
@@ -156,8 +156,15 @@ namespace pEp.DPE
Log
.
Verbose
(
"GetPatchMailItem: "
+
items
.
Count
+
" items found in "
+
inbox
.
FolderPath
);
Log
.
Verbose
(
"GetPatchMailItem: "
+
items
.
Count
+
" items found in "
+
inbox
.
FolderPath
);
// First filter out older items
// First filter out older items
items
=
items
.
Restrict
(
"[ReceivedTime] >= '"
+
patch
.
CreationDate
.
ToString
(
"g"
)
+
"'"
);
try
Log
.
Verbose
(
"GetPatchMailItem: Items since receival of patch: "
+
items
?.
Count
??
"0"
);
{
items
=
items
.
Restrict
(
"[ReceivedTime] >= '"
+
DateTime
.
FromFileTimeUtc
(
patch
.
CreationDate
).
ToString
(
"g"
)
+
"'"
);
Log
.
Verbose
(
"GetPatchMailItem: Items since receival of patch: "
+
items
?.
Count
??
"0"
);
}
catch
(
Exception
ex
)
{
Log
.
Error
(
"GetPatchMailItem: Error restricting items. "
+
ex
);
}
Outlook
.
UserDefinedProperties
up
=
inbox
.
UserDefinedProperties
;
Outlook
.
UserDefinedProperties
up
=
inbox
.
UserDefinedProperties
;
if
(
up
.
Find
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_ID
)
==
null
)
if
(
up
.
Find
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_ID
)
==
null
)
...
@@ -211,20 +218,27 @@ namespace pEp.DPE
...
@@ -211,20 +218,27 @@ namespace pEp.DPE
{
{
// Determine the correct method to execute
// Determine the correct method to execute
HttpResponseMessage
httpResponseMessage
=
null
;
HttpResponseMessage
httpResponseMessage
=
null
;
switch
(
postAction
)
try
{
{
case
PostAction
.
Reject
:
switch
(
postAction
)
httpResponseMessage
=
await
DPEWebClient
.
RejectPatch
(
patch
,
me
);
{
break
;
case
PostAction
.
Reject
:
case
PostAction
.
Suggest
:
httpResponseMessage
=
await
DPEWebClient
.
RejectPatch
(
patch
,
me
);
httpResponseMessage
=
await
DPEWebClient
.
SuggestPatch
(
patch
,
me
);
break
;
break
;
case
PostAction
.
Suggest
:
case
PostAction
.
Support
:
httpResponseMessage
=
await
DPEWebClient
.
SuggestPatch
(
patch
,
me
);
httpResponseMessage
=
await
DPEWebClient
.
SupportPatch
(
patch
,
me
);
break
;
break
;
case
PostAction
.
Support
:
default
:
httpResponseMessage
=
await
DPEWebClient
.
SupportPatch
(
patch
,
me
);
Log
.
ErrorAndFailInDebugMode
(
"PostAsync: Unknown post action."
);
break
;
break
;
default
:
Log
.
ErrorAndFailInDebugMode
(
"PostAsync: Unknown post action."
);
break
;
}
}
catch
(
HttpRequestException
ex
)
{
throw
ex
;
}
}
if
(
httpResponseMessage
.
StatusCode
!=
HttpStatusCode
.
OK
)
if
(
httpResponseMessage
.
StatusCode
!=
HttpStatusCode
.
OK
)
...
@@ -280,7 +294,7 @@ namespace pEp.DPE
...
@@ -280,7 +294,7 @@ namespace pEp.DPE
if
(
omi
!=
null
)
if
(
omi
!=
null
)
{
{
omi
.
SetUserProperty
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_STATUS
,
(
int
)
patchStatus
);
omi
.
SetUserProperty
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_STATUS
,
(
int
)
patchStatus
);
omi
.
SetUserProperty
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE
,
DateTime
.
UtcNow
);
omi
.
SetUserProperty
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE
,
DateTime
.
UtcNow
.
ToFileTime
()
);
omi
.
Save
();
omi
.
Save
();
}
}
else
else
...
...
DPE/Patch.cs
View file @
1510fb85
using
pEp.Extensions
;
using
pEp.Extensions
;
using
System
;
using
System
;
using
System.Collections.Generic
;
namespace
pEp.DPE
namespace
pEp.DPE
{
{
public
class
Patch
public
class
Patch
{
{
public
static
readonly
string
REG_FILE_SCHEME
=
"microsoft.windows.registry
:
"
;
public
static
readonly
string
REG_FILE_SCHEME
=
"
file:///
microsoft.windows.registry"
;
public
enum
RejectReason
public
enum
RejectReason
{
{
None
,
None
,
...
@@ -16,48 +17,21 @@ namespace pEp.DPE
...
@@ -16,48 +17,21 @@ namespace pEp.DPE
RejectedByUser
RejectedByUser
}
}
public
string
Id
{
get
;
set
;
}
=
Guid
.
NewGuid
().
ToString
();
internal
List
<
PEPIdentity
>
ApplyTo
{
get
;
}
=
new
List
<
PEPIdentity
>();
public
string
Uri
{
get
;
set
;
}
public
string
CommitMessage
{
get
;
set
;
}
public
string
Diff
{
get
;
set
;
}
public
long
CreationDate
{
get
;
set
;
}
=
-
1
;
public
string
CommitMessage
{
get
;
set
;
}
public
string
Diff
{
get
;
set
;
}
public
string
Tag
{
get
;
set
;
}
public
string
Id
{
get
;
set
;
}
=
Guid
.
NewGuid
().
ToString
();
public
DateTime
CreationDate
{
get
;
set
;
}
=
DateTime
.
UtcNow
;
public
Uri
RootPath
{
get
;
set
;
}
public
string
Tag
{
get
;
set
;
}
/// <summary>
/// <summary>
/// Primary constructor.
Needed for de/serialization.
/// Primary constructor.
/// </summary>
/// </summary>
public
Patch
()
public
Patch
()
{
{
}
}
/// <summary>
/// Constructor for a new patch.
/// </summary>
/// <param name="uri">The location to apply the patch to.</param>
/// <param name="diff">The diff to apply.</param>
/// <param name="commitMessage">The commit message for this patch.</param>
/// <param name="tag">The tag for this patch (optional).</param>
public
Patch
(
string
uri
,
string
diff
,
string
commitMessage
,
string
tag
=
null
)
{
this
.
CommitMessage
=
commitMessage
;
this
.
Diff
=
diff
;
this
.
Tag
=
tag
;
this
.
Uri
=
uri
;
}
/// <summary>
/// Constructor for an existing patch
/// </summary>
/// <param name="uri">The location to apply the patch to.</param>
/// <param name="diff">The diff to apply.</param>
/// <param name="commitMessage">The commit message for this patch.</param>
/// <param name="creationDate">The date where this patch was created.</param>
/// <param name="tag">The tag for this patch (optional).</param>
public
Patch
(
string
uri
,
string
diff
,
string
commitMessage
,
DateTime
creationDate
,
string
tag
=
null
)
:
this
(
uri
,
diff
,
commitMessage
,
tag
)
{
this
.
CreationDate
=
creationDate
;
}
/// <summary>
/// <summary>
/// Deserializes a JSON string into a Patch object.
/// Deserializes a JSON string into a Patch object.
/// </summary>
/// </summary>
...
@@ -65,7 +39,16 @@ namespace pEp.DPE
...
@@ -65,7 +39,16 @@ namespace pEp.DPE
/// <returns>The Patch object or null if an error occured.</returns>
/// <returns>The Patch object or null if an error occured.</returns>
public
static
Patch
Deserialize
(
string
json
)
public
static
Patch
Deserialize
(
string
json
)
{
{
return
json
.
Deserialize
<
Patch
>();
try
{
return
json
.
Deserialize
<
Patch
>();
}
catch
(
Exception
ex
)
{
Log
.
Error
(
"Deserialize: Error occured. "
+
ex
);
}
return
null
;
}
}
}
}
}
}
UI/FormRegionDPE.cs
View file @
1510fb85
...
@@ -47,15 +47,23 @@ namespace pEp
...
@@ -47,15 +47,23 @@ namespace pEp
// Get last edit date if available
// Get last edit date if available
if
((
omi
.
GetUserProperty
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE
)
is
string
editDateString
)
&&
if
((
omi
.
GetUserProperty
(
MailItemExtensions
.
USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE
)
is
string
editDateString
)
&&
DateTime
.
TryParse
(
editDateString
,
out
DateTime
savedEditDate
))
long
.
TryParse
(
editDateString
,
out
long
fileTimeUtc
))
{
{
editDate
=
savedEditDate
;
try
{
editDate
=
DateTime
.
FromFileTimeUtc
(
fileTimeUtc
);
}
catch
(
Exception
ex
)
{
Log
.
Error
(
"FormRegionDPE_FormRegionShowing: Error getting file time. "
+
ex
);
editDate
=
null
;
}
}
}
// Get the patch submitter and set data context
// Get the patch submitter and set data context
if
(
PEPIdentity
.
GetFromIdentity
(
omi
,
out
PEPIdentity
submitter
)
==
Globals
.
ReturnStatus
.
Success
)
if
(
PEPIdentity
.
GetFromIdentity
(
omi
,
out
PEPIdentity
submitter
)
==
Globals
.
ReturnStatus
.
Success
)
{
{
this
.
FormControlPatchView
.
DataContext
=
new
FormControl
PatchViewModel
(
patch
,
submitter
,
patchStatus
,
editDate
);
this
.
FormControlPatchView
.
DataContext
=
new
PatchViewModel
(
patch
,
submitter
,
patchStatus
,
editDate
);
}
}
else
else
{
{
...
...
UI/Models/PatchDialog.cs
View file @
1510fb85
using
pEp.DPE
;
using
pEp.DPE
;
using
System
;
using
System.Linq
;
using
System.Threading.Tasks
;
namespace
pEp.UI.Models
namespace
pEp.UI.Models
{
{
...
@@ -18,11 +15,6 @@ namespace pEp.UI.Models
...
@@ -18,11 +15,6 @@ namespace pEp.UI.Models
SupportOrRejectPatch
SupportOrRejectPatch
}
}
/// <summary>
/// Gets whether the patch is editable.
/// </summary>
public
bool
IsEditable
{
get
;
}
=
false
;
/// <summary>
/// <summary>
/// Get the patch.
/// Get the patch.
/// </summary>
/// </summary>
...
@@ -38,12 +30,10 @@ namespace pEp.UI.Models
...
@@ -38,12 +30,10 @@ namespace pEp.UI.Models
/// </summary>
/// </summary>
/// <param name="patchAction">The action to perform with this patch.</param>
/// <param name="patchAction">The action to perform with this patch.</param>
/// <param name="patch">The patch to be used in this dialog.</param>
/// <param name="patch">The patch to be used in this dialog.</param>
/// <param name="isEditable">Whether the patch is editable.</param>
public
PatchDialog
(
PatchAction
patchAction
,
Patch
patch
=
null
)
:
base
(
Dialog
.
Type
.
Patch
,
null
)
public
PatchDialog
(
PatchAction
patchAction
,
Patch
patch
=
null
,
bool
isEditable
=
false
)
:
base
(
Dialog
.
Type
.
Patch
,
null
)
{
{
this
.
PatchActionType
=
patchAction
;
this
.
PatchActionType
=
patchAction
;
this
.
Patch
=
patch
;
this
.
Patch
=
patch
??
new
Patch
();
this
.
IsEditable
=
isEditable
;
// Set dialog title
// Set dialog title
switch
(
patchAction
)
switch
(
patchAction
)
...
@@ -69,53 +59,10 @@ namespace pEp.UI.Models
...
@@ -69,53 +59,10 @@ namespace pEp.UI.Models
/// <summary>
/// <summary>
/// Shows a patch dialog.
/// Shows a patch dialog.
/// </summary>
/// </summary>
/// <param name="patchAction">The action to perform with the patch.</param>
public
static
void
ShowNewPatchDialog
()
/// <param name="patch">The patch to manage.</param>
/// <param name="isEditable">Whether the patch can be edited.</param>
public
static
async
Task
ShowDialog
(
PatchDialog
.
PatchAction
patchAction
,
Patch
patch
,
bool
isEditable
=
false
)
{
{
// If needed, make sure we have a valid own identity
PatchDialog
patchDialog
=
new
PatchDialog
(
PatchAction
.
NewPatch
);
PEPIdentity
me
=
null
;
new
DialogHost
(
patchDialog
).
ShowDialog
();
if
((
patchAction
!=
PatchDialog
.
PatchAction
.
ShowPatch
)
&&
(
PEPIdentity
.
GetOwnIdentity
(
Globals
.
ThisAddIn
.
Settings
.
AccountSettingsList
.
First
(),
out
me
,
true
)
!=
Globals
.
ReturnStatus
.
Success
))
{
Log
.
Error
(
"ShowDialog: Error getting own identity."
);
return
;
}
// Open dialog and retrieve result
PatchDialog
patchDialog
=
new
PatchDialog
(
patchAction
,
patch
,
isEditable
);
if
(
new
DialogHost
(
patchDialog
).
ShowDialog
()
==
System
.
Windows
.
Forms
.
DialogResult
.
OK
)
{
// Perform the action the user selected
if
(
patchAction
==
PatchDialog
.
PatchAction
.
NewPatch
)
{
try
{
await
Globals
.
ThisAddIn
.
DistributedPolicyEngine
.
Suggest
(
patchDialog
.
Patch
,
me
);
}
catch
(
Exception
ex
)
{
Log
.
Error
(
"ShowDialog: Error occured. "
+
ex
);
while
(
ex
.
InnerException
!=
null
)
{
ex
=
ex
.
InnerException
;
}
CustomMessageBox
.
ShowDialog
(
ex
.
Message
,
"Error suggesting patch"
,
"OK"
);
return
;
}
AdapterExtensions
.
ShowNotification
(
"Patch successfully suggested"
,
patch
.
CommitMessage
);
}
else
{
Log
.
ErrorAndFailInDebugMode
(
"ShowDialog: Unsupported patch action "
+
Enum
.
GetName
(
typeof
(
PatchDialog
.
PatchAction
),
patchAction
));
}
}
}
}
}
}
}
}
UI/RibbonCustomizations.cs
View file @
1510fb85
...
@@ -1360,6 +1360,14 @@ namespace pEp
...
@@ -1360,6 +1360,14 @@ namespace pEp
return
(
Properties
.
Resources
.
ImageBackstageCompatibility
);
return
(
Properties
.
Resources
.
ImageBackstageCompatibility
);
}
}
/// <summary>
/// Callback to get the image for the ButtonDPE.
/// </summary>
public
System
.
Drawing
.
Bitmap
ButtonDPE_GetImage
(
Office
.
IRibbonControl
control
)
{
return
(
Properties
.
Resources
.
ImageLogoGreen
);
}
/// <summary>
/// <summary>
/// Callback to get the image for the ButtonCompatibility.
/// Callback to get the image for the ButtonCompatibility.
/// </summary>
/// </summary>
...
@@ -1408,6 +1416,30 @@ namespace pEp
...
@@ -1408,6 +1416,30 @@ namespace pEp
return
(
Properties
.
Resources
.
Ribbon_GroupCompatibilityLabel
);
return
(
Properties
.
Resources
.
Ribbon_GroupCompatibilityLabel
);
}
}
/// <summary>
/// Callback to get the label text for the GroupDPE.
/// </summary>
public
string
GroupDPE_GetLabel
(
Office
.
IRibbonControl
control
)
{
return
"Patch management"
;
}
/// <summary>
/// Callback to get the helper text for the GroupDPE.
/// </summary>
public
string
GroupDPE_GetHelperText
(
Office
.
IRibbonControl
control
)
{
return
"Manage patches and change the configuration"
;
}
/// <summary>
/// Callback to get the visibility of the GroupDPE.
/// </summary>
public
bool
GroupDPE_GetIsVisible
(
Office
.
IRibbonControl
control
)
{
return
true
;
}
/// <summary>
/// <summary>
/// Callback to get the visibility of the GroupPEPHome.
/// Callback to get the visibility of the GroupPEPHome.
/// </summary>
/// </summary>
...
@@ -1453,6 +1485,14 @@ namespace pEp
...
@@ -1453,6 +1485,14 @@ namespace pEp
return
(
Properties
.
Resources
.
Ribbon_ButtonAccountsLabel
);
return
(
Properties
.
Resources
.
Ribbon_ButtonAccountsLabel
);
}
}
/// <summary>
/// Callback to get the action for when the DPE button is clicked.
/// </summary>
public
void
ButtonDPE_Click
(
Office
.
IRibbonControl
control
)
{
PatchDialog
.
ShowNewPatchDialog
();
}
/// <summary>
/// <summary>
/// Callback to get the label text for the ButtonCompatibility.
/// Callback to get the label text for the ButtonCompatibility.
/// </summary>
/// </summary>
...
@@ -1461,6 +1501,14 @@ namespace pEp
...
@@ -1461,6 +1501,14 @@ namespace pEp
return
(
Properties
.
Resources
.
Ribbon_ButtonCompatibilityLabel
);
return
(
Properties
.
Resources
.
Ribbon_ButtonCompatibilityLabel
);
}
}
/// <summary>
/// Callback to get the label text for the ButtonDPE.
/// </summary>
public
string
ButtonDPE_GetLabel
(
Office
.
IRibbonControl
control
)
{
return
"Patch management"
;
}
/// <summary>
/// <summary>
/// Callback to get the label text for the LabelControlName.
/// Callback to get the label text for the LabelControlName.
/// </summary>
/// </summary>
...
...
UI/RibbonCustomizationsExplorer.xml
View file @
1510fb85
...
@@ -118,6 +118,19 @@
...
@@ -118,6 +118,19 @@
getLabel=
"ButtonCompatibility_GetLabel"
/>
getLabel=
"ButtonCompatibility_GetLabel"
/>
</primaryItem>
</primaryItem>
</group>
</group>
<!--Patch Management-->
<group
id=
"GroupDPE"
getLabel=
"GroupDPE_GetLabel"
getHelperText=
"GroupDPE_GetHelperText"
getVisible=
"GroupDPE_GetIsVisible"
>
<primaryItem>
<button
id=
"ButtonDPE"
isDefinitive=
"true"
onAction=
"ButtonDPE_Click"
getImage=
"ButtonDPE_GetImage"
getLabel=
"ButtonDPE_GetLabel"
/>
</primaryItem>
</group>
</firstColumn>
</firstColumn>
<secondColumn>
<secondColumn>
<!-- About -->
<!-- About -->
...
@@ -153,13 +166,13 @@
...
@@ -153,13 +166,13 @@
</tab>
</tab>
</backstage>
</backstage>
<contextMenus>
<contextMenus>
<!--The Contact context menu to reset trust-->
<!--The Contact context menu to reset trust-->
<contextMenu
idMso=
"ContextMenuContactItem"
>
<contextMenu
idMso=
"ContextMenuContactItem"
>
<button
id=
"ButtonContextMenuResetContactTrust"
<button
id=
"ButtonContextMenuResetContactTrust"
getImage=
"ButtonContextMenuContactResetTrust_GetImage"
getImage=
"ButtonContextMenuContactResetTrust_GetImage"
getLabel=
"ButtonContextMenuContactResetTrust_GetLabel"
getLabel=
"ButtonContextMenuContactResetTrust_GetLabel"
onAction=
"ButtonContextMenuContactResetTrust_Click"
/>
onAction=
"ButtonContextMenuContactResetTrust_Click"
/>
</contextMenu>
</contextMenu>
</contextMenus>
</contextMenus>
</customUI>
</customUI>
...
...
UI/RibbonCustomizationsExplorer2010.xml
View file @
1510fb85
...
@@ -60,6 +60,19 @@
...
@@ -60,6 +60,19 @@
getLabel=
"ButtonCompatibility_GetLabel"
/>
getLabel=
"ButtonCompatibility_GetLabel"
/>
</primaryItem>
</primaryItem>
</group>
</group>
<!--Patch Management-->
<group
id=
"GroupDPE"
getLabel=
"GroupDPE_GetLabel"
getHelperText=
"GroupDPE_GetHelperText"
getVisible=
"GroupDPE_GetIsVisible"
>
<primaryItem>
<button
id=
"ButtonDPE"
isDefinitive=
"true"
onAction=
"ButtonDPE_Click"
getImage=
"ButtonDPE_GetImage"
getLabel=
"ButtonDPE_GetLabel"
/>
</primaryItem>
</group>
</firstColumn>
</firstColumn>
<secondColumn>
<secondColumn>
<!-- About -->
<!-- About -->
...
...
UI/ViewModels/FormControlPatchViewModel.cs
deleted
100644 → 0
View file @
e5f5de6b
using
pEp.DPE
;
using
pEp.UI.Models
;
using
System
;
using
System.Threading.Tasks
;
using
System.Windows.Documents
;
using
System.Windows.Input
;
using
System.Windows.Media
;
namespace
pEp.UI.ViewModels
{
internal
class
FormControlPatchViewModel
:
ViewModelBase
{
#
region
Fields
private
readonly
Patch
patch
;
private
Brush
_Background
=
Brushes
.
White
;
private
FlowDocument
_DisplayDiff
=
null
;
private
DateTime
?
_EditDate
=
null
;
private
string
_Explanation
=
null
;
private
bool
_IsLoading
=
false
;
private
DistributedPolicyEngine
.
PatchStatus
_Status
=
DistributedPolicyEngine
.
PatchStatus
.
Open
;
#
endregion
#
region
Properties
/// <summary>
/// Gets or sets the background.
/// </summary>
public
Brush
Background
{
get
=>
this
.
_Background
;
set
=>
this
.
SetProperty
(
ref
this
.
_Background
,
value
);
}
/// <summary>
/// The commit message of the patch.
/// </summary>
public
string
CommitMessage
{
get
=>
this
.
patch
?.
CommitMessage
;
set
{
this
.
patch
.
CommitMessage
=
value
;
this
.
OnPropertyChanged
();
}
}
/// <summary>
/// Gets the creation date as string.
/// </summary>
public
string
CreationDateString
=>
this
.
patch
?.
CreationDate
.
ToString
(
"F"
);
/// <summary>
/// The diff of this patch.
/// </summary>
public
string
Diff
{
get
=>
this
.
patch
?.
Diff
;
set
{
this
.
patch
.
Diff
=
value
;
this
.
OnPropertyChanged
();
}
}
/// <summary>
/// Gets or sets the diff of this patch as formatted flow document.
/// </summary>
public
FlowDocument
DisplayDiff
{
get
=>
this
.
_DisplayDiff
;
set
=>
this
.
SetProperty
(
ref
this
.
_DisplayDiff
,
value
);
}
/// <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>
/// The explanation shown in the UI regarding this patch.
/// </summary>
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>
/// Gets whether the OK button is visible.
/// </summary>
public
bool
IsOKButtonVisible
{
get
;
}
=
true
;
/// <summary>
/// Gets whether the Reject button is visible.
/// </summary>
public
bool
IsRejectButtonVisible
{
get
;
}
=
false
;
/// <summary>
/// The command to accept the patch dialog.
/// </summary>
public
RelayCommand
OKButtonCommand
=>
new
RelayCommand
(
this
.
SupportPatch
,
p
=>
(
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
));
/// <summary>
/// Gets the OK button text.
/// </summary>
public
string
OKButtonText
{
get
;
}
=
Properties
.
Resources
.
Options_OKText
;
/// <summary>
/// The command to reject the dialog.
/// </summary>
public
RelayCommand
RejectButtonCommand
=>
new
RelayCommand
(
this
.
RejectPatch
,
p
=>
(
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
));
/// <summary>
/// Gets the Reject button text.
/// </summary>
public
string
RejectButtonText
{
get
;
}
=
Properties
.
Resources
.
SyncWizard_RejectButton
;
/// <summary>
/// Gets the submitter of this patch.
/// </summary>
public
PEPIdentity
Submitter
{
get
;
}
/// <summary>
/// Gets or sets the status of this patch.
/// </summary>
public
DistributedPolicyEngine
.
PatchStatus
Status
{
get
=>
this
.
_Status
;
set
=>
this
.
SetProperty
(
ref
this
.
_Status
,
value
);
}
/// <summary>
/// Gets or sets the tag of the patch.
/// </summary>
public
string
Tag
{
get
=>
this
.
patch
?.
Tag
;
set
{
this
.
patch
.
Tag
=
value
;
this
.
OnPropertyChanged
();
}
}
/// <summary>
/// Gets or sets the URI of the patch.
/// </summary>
public
string
Uri
{
get
=>
this
.
patch
?.
Uri
;
set
{
this
.
patch
.
Uri
=
value
;
this
.
OnPropertyChanged
();
}
}
#
endregion
#
region
Constructors
/// <summary>
/// Primary constructor.
/// </summary>
/// <param name="patch">The patch to create the form region with.</param>
/// <param name="submitter">The submitter of the patch.</param>
/// <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
,
DistributedPolicyEngine
.
PatchStatus
status
=
DistributedPolicyEngine
.
PatchStatus
.
Open
,
DateTime
?
editDate
=
null
)
{
this
.
EditDate
=
editDate
;
this
.
patch
=
patch
;
this
.
Submitter
=
submitter
;
this
.
IsRejectButtonVisible
=
true
;
this
.
OKButtonText
=
"Support"
;
this
.
UpdateView
(
status
);
}
#
endregion
#
region
Methods
/// <summary>
/// Executes a patch task (support/reject) and shows an error message if necessary.
/// </summary>
/// <param name="patchTask">The task to execute.</param>
/// <returns>True if the patch task was executed successfully, otherwise false.</returns>
private
async
Task
<
bool
>
ExecutePatchTask
(
Task
patchTask
)
{
try
{
// 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
)
{
this
.
IsLoading
=
false
;
Mouse
.
OverrideCursor
=
null
;
Log
.
Error
(
"Error occured. "
+
ex
);
// Get the innermost exception message and show message box
while
(
ex
.
InnerException
!=
null
)
{
ex
=
ex
.
InnerException
;
}
CustomMessageBox
.
ShowDialog
(
ex
.
Message
,
"Error"
,
"OK"
);
return
false
;
}
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>
/// Sets the explanation text.
/// </summary>
private
void
SetExplanation
()
{
string
editDate
=
"<n/a>"
;
if
(
this
.
EditDate
!=
null
)
{
editDate
=
((
DateTime
)
this
.
EditDate
).
ToLocalTime
().
ToString
(
"F"
);
}
switch
(
this
.
Status
)
{
case
DistributedPolicyEngine
.
PatchStatus
.
Accepted
:
this
.
Explanation
=
$"Accepted on
{
editDate
}
"
;
break
;
case
DistributedPolicyEngine
.
PatchStatus
.
Supported
:
this
.
Explanation
=
$"Supported on
{
editDate
}
"
;
break
;
case
DistributedPolicyEngine
.
PatchStatus
.
Rejected
:
this
.
Explanation
=
$"Rejected on
{
editDate
}
"
;
break
;
case
DistributedPolicyEngine
.
PatchStatus
.
Open
:
default
:
this
.
Explanation
=
"New configuration changes pending approval"
;
break
;
}
}
/// <summary>
/// Supports this patch.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private
async
void
SupportPatch
(
object
parameter
)
{
if
(
await
this
.
ExecutePatchTask
(
Globals
.
ThisAddIn
.
DistributedPolicyEngine
.
Support
(
this
.
patch
,
new
PEPIdentity
())))
{
this
.
UpdateView
(
DistributedPolicyEngine
.
PatchStatus
.
Supported
);
}
}
/// <summary>
/// Updates the view according to a new patch status.
/// </summary>
/// <param name="patchStatus">The new patch status.</param>
private
void
UpdateView
(
DistributedPolicyEngine
.
PatchStatus
patchStatus
)
{
this
.
Status
=
patchStatus
;
this
.
EditDate
=
DateTime
.
UtcNow
;
this
.
SetExplanation
();
this
.
DisplayDiff
=
PatchDialogViewModel
.
FormatDiff
(
this
.
Diff
,
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
);
this
.
Background
=
(
patchStatus
==
DistributedPolicyEngine
.
PatchStatus
.
Open
)
?
Brushes
.
White
:
Brushes
.
WhiteSmoke
;
}
#
endregion
}
}
UI/ViewModels/OptionsViewModel.cs
View file @
1510fb85
...
@@ -40,7 +40,6 @@ namespace pEp.UI.ViewModels
...
@@ -40,7 +40,6 @@ namespace pEp.UI.ViewModels
private
RelayCommand
_CommandButtonDeleteGroup
=
null
;
private
RelayCommand
_CommandButtonDeleteGroup
=
null
;
private
RelayCommand
_CommandButtonExportKeys
=
null
;
private
RelayCommand
_CommandButtonExportKeys
=
null
;
private
RelayCommand
_CommandButtonImportKeys
=
null
;
private
RelayCommand
_CommandButtonImportKeys
=
null
;
private
RelayCommand
_CommandButtonNewPatch
=
null
;
private
RelayCommand
_CommandButtonOK
=
null
;
private
RelayCommand
_CommandButtonOK
=
null
;
private
RelayCommand
_CommandButtonRefreshLogs
=
null
;
private
RelayCommand
_CommandButtonRefreshLogs
=
null
;
private
RelayCommand
_CommandButtonResetPEPStore
=
null
;
private
RelayCommand
_CommandButtonResetPEPStore
=
null
;
...
@@ -205,22 +204,6 @@ namespace pEp.UI.ViewModels
...
@@ -205,22 +204,6 @@ namespace pEp.UI.ViewModels
}
}
}
}
/// <summary>
/// Gets the command to execute when the New patch button is clicked.
/// </summary>
public
RelayCommand
CommandButtonNewPatch
{
get
{
if
(
this
.
_CommandButtonNewPatch
==
null
)
{
this
.
_CommandButtonNewPatch
=
new
RelayCommand
(
async
p
=>
await
PatchDialog
.
ShowDialog
(
PatchDialog
.
PatchAction
.
NewPatch
,
new
Patch
(),
true
));
}
return
this
.
_CommandButtonNewPatch
;
}
}
/// <summary>
/// <summary>
/// Gets the command to execute when the OK button is clicked.
/// Gets the command to execute when the OK button is clicked.
/// </summary>
/// </summary>
...
...
UI/ViewModels/Patch
Dialog
ViewModel.cs
→
UI/ViewModels/PatchViewModel.cs
View file @
1510fb85
...
@@ -9,23 +9,31 @@ using System.Collections.ObjectModel;
...
@@ -9,23 +9,31 @@ using System.Collections.ObjectModel;
using
System.Diagnostics
;
using
System.Diagnostics
;
using
System.IO
;
using
System.IO
;
using
System.Linq
;
using
System.Linq
;
using
System.Threading.Tasks
;
using
System.Windows
;
using
System.Windows
;
using
System.Windows.Controls
;
using
System.Windows.Controls
;
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
{
{
internal
class
Patch
Dialog
ViewModel
:
DialogHostViewModel
internal
class
PatchViewModel
:
DialogHostViewModel
{
{
#
region
Fields
#
region
Fields
private
RelayCommand
_AddOrEditFileCommand
=
null
;
private
RelayCommand
_AddOrEditFileCommand
=
null
;
private
bool
_IsCommitMessageValid
=
false
;
private
Brush
_Background
=
Brushes
.
White
;
private
bool
_IsDiffValid
=
false
;
private
DateTime
?
_EditDate
=
null
;
private
bool
_IsValid
=
false
;
private
string
_Explanation
=
null
;
private
ConfigFile
_SelectedFile
=
null
;
private
FlowDocument
_FormattedDiff
=
null
;
private
FlowDocument
_VisibleDiff
=
null
;
private
bool
_IsCommitMessageValid
=
false
;
private
bool
_IsDiffValid
=
false
;
private
bool
_IsLoading
=
false
;
private
bool
_IsValid
=
false
;
private
bool
_Loaded
=
false
;
private
ConfigFile
_SelectedFile
=
null
;
private
DistributedPolicyEngine
.
PatchStatus
_Status
=
DistributedPolicyEngine
.
PatchStatus
.
Open
;
private
const
string
FILE_A_PREFIX
=
"--- "
;
private
const
string
FILE_A_PREFIX
=
"--- "
;
private
const
string
FILE_B_PREFIX
=
"+++ "
;
private
const
string
FILE_B_PREFIX
=
"+++ "
;
...
@@ -51,14 +59,19 @@ namespace pEp.UI.ViewModels
...
@@ -51,14 +59,19 @@ namespace pEp.UI.ViewModels
}
}
/// <summary>
/// <summary>
///
Command to cancel the dialog
.
///
Gets the identities this patch will be applied to
.
/// </summary>
/// </summary>
public
RelayCommand
CancelButtonCommand
=>
new
RelayCommand
(
p
=>
this
.
Close
(
null
));
public
ObservableCollection
<
PEPIdentity
>
ApplyTo
=>
new
ObservableCollection
<
PEPIdentity
>(
this
.
Dialog
.
Patch
.
ApplyTo
);
/// <summary>
/// Gets or sets the background.
/// </summary>
public
Brush
Background
{
get
=>
this
.
_Background
;
set
=>
this
.
SetProperty
(
ref
this
.
_Background
,
value
);
}
/// <summary>
/// <summary>
///
Gets the Cancel button text
.
///
Command to cancel the dialog
.
/// </summary>
/// </summary>
public
string
CancelButton
Text
{
get
;
}
=
Properties
.
Resources
.
Options_CancelText
;
public
RelayCommand
CancelButton
Command
=>
new
RelayCommand
(
p
=>
this
.
Close
(
null
))
;
/// <summary>
/// <summary>
/// The commit message of the patch.
/// The commit message of the patch.
...
@@ -74,15 +87,47 @@ namespace pEp.UI.ViewModels
...
@@ -74,15 +87,47 @@ namespace pEp.UI.ViewModels
}
}
}
}
/// <summary>
/// Gets the collection of config files that are being modified.
/// </summary>
public
ObservableCollection
<
ConfigFile
>
ConfigFiles
{
get
;
}
=
new
ObservableCollection
<
ConfigFile
>();
/// <summary>
/// Gets the creation date as string.
/// </summary>
public
string
CreationDateString
{
get
{
long
?
fileTime
=
this
.
Dialog
?.
Patch
?.
CreationDate
;
return
(
fileTime
!=
null
)
?
DateTime
.
FromFileTimeUtc
((
long
)
fileTime
).
ToLocalTime
().
ToString
(
"F"
)
:
null
;
}
}
/// <summary>
/// The own identity that acts as sender of DPE calls.
/// </summary>
public
PEPIdentity
From
{
get
;
}
/// <summary>
/// <summary>
/// The dialog object.
/// The dialog object.
/// </summary>
/// </summary>
public
new
PatchDialog
Dialog
=>
base
.
Dialog
as
PatchDialog
;
public
new
PatchDialog
Dialog
=>
base
.
Dialog
as
PatchDialog
;
/// <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>
/// Gets the diff of the selected file as formatted flow document.
/// </summary>
public
FlowDocument
FormattedDiff
{
get
=>
this
.
_FormattedDiff
;
set
=>
this
.
SetProperty
(
ref
this
.
_FormattedDiff
,
value
);
}
/// <summary>
/// <summary>
/// Gets whether the Cancel button is visible.
/// Gets whether the Cancel button is visible.
...
@@ -92,80 +137,51 @@ namespace pEp.UI.ViewModels
...
@@ -92,80 +137,51 @@ namespace pEp.UI.ViewModels
/// <summary>
/// <summary>
/// Gets or sets whether the commit message is valid.
/// Gets or sets whether the commit message is valid.
/// </summary>
/// </summary>
public
bool
IsCommitMessageValid
public
bool
IsCommitMessageValid
{
get
=>
this
.
_IsCommitMessageValid
;
set
=>
this
.
SetProperty
(
ref
this
.
_IsCommitMessageValid
,
value
);
}
{
get
=>
this
.
_IsCommitMessageValid
;
set
{
if
(
value
!=
this
.
_IsCommitMessageValid
)
{
this
.
_IsCommitMessageValid
=
value
;
this
.
OnPropertyChanged
();
}
}
}
/// <summary>
/// <summary>
/// Gets or sets whether the diff is valid.
/// Gets or sets whether the diff is valid.
/// </summary>
/// </summary>
public
bool
IsDiffValid
public
bool
IsDiffValid
{
get
=>
this
.
_IsDiffValid
;
set
=>
this
.
SetProperty
(
ref
this
.
_IsDiffValid
,
value
);
}
{
get
=>
this
.
_IsDiffValid
;
set
{
if
(
value
!=
this
.
_IsDiffValid
)
{
this
.
_IsDiffValid
=
value
;
this
.
OnPropertyChanged
();
}
}
}
/// <summary>
/// <summary>
/// Gets whether the
patch in this dialog is editable
.
/// Gets
or sets
whether the
view is currently loading
.
/// </summary>
/// </summary>
public
bool
Is
Editable
=>
this
.
Dialog
.
IsEditable
;
public
bool
Is
Loading
{
get
=>
this
.
_IsLoading
;
set
=>
this
.
SetProperty
(
ref
this
.
_IsLoading
,
value
);
}
/// <summary>
/// <summary>
/// Gets whether th
e OK button is visible
.
/// Gets
or sets
whether th
is patch is valid
.
/// </summary>
/// </summary>
public
bool
Is
OKButtonVisible
{
get
;
}
=
t
rue
;
public
bool
Is
Valid
{
get
=>
this
.
_IsValid
;
set
=
>
t
his
.
SetProperty
(
ref
this
.
_IsValid
,
value
);
}
/// <summary>
/// <summary>
/// Gets whether the
Reject button is visible
.
/// Gets
or sets
whether the
screen is fully loaded
.
/// </summary>
/// </summary>
public
bool
IsRejectButtonVisible
{
get
;
}
=
false
;
public
bool
Loaded
{
get
=>
this
.
_Loaded
;
set
=>
this
.
SetProperty
(
ref
this
.
_Loaded
,
value
);
}
/// <summary>
/// <summary>
///
Gets or sets whether
th
is
patch
is valid
.
///
The command to accept
th
e
patch
dialog
.
/// </summary>
/// </summary>
public
bool
IsVali
d
public
RelayCommand
OKButtonComman
d
{
{
get
=>
this
.
_IsValid
;
get
set
{
{
if
(
value
!=
this
.
_IsValid
)
if
((
this
.
Dialog
.
PatchActionType
==
PatchDialog
.
PatchAction
.
NewPatch
)
||
(
this
.
Dialog
.
PatchActionType
==
PatchDialog
.
PatchAction
.
EditPatch
))
{
{
this
.
_IsValid
=
value
;
return
new
RelayCommand
(
this
.
CreateAndSuggestPatch
,
p
=>
this
.
IsValid
);
this
.
OnPropertyChanged
();
}
else
{
return
new
RelayCommand
(
this
.
SupportPatch
,
p
=>
(
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
));
}
}
}
}
}
}
/// <summary>
/// <summary>
/// Gets the collection of config files that are being modified.
/// The command to reject the dialog.
/// </summary>
public
ObservableCollection
<
ConfigFile
>
ConfigFiles
{
get
;
}
=
new
ObservableCollection
<
ConfigFile
>();
/// <summary>
/// The command to accept the patch dialog.
/// </summary>
public
RelayCommand
OKButtonCommand
=>
new
RelayCommand
(
this
.
CreateAndSuggestPatch
,
p
=>
this
.
IsValid
);
/// <summary>
/// Gets the OK button text.
/// </summary>
/// </summary>
public
string
OKButtonText
{
get
;
}
=
Properties
.
Resources
.
Options_OKText
;
public
RelayCommand
RejectButtonCommand
=>
new
RelayCommand
(
this
.
RejectPatch
,
p
=>
(
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
))
;
/// <summary>
/// <summary>
/// Gets the Remove files button command
/// Gets the Remove files button command
...
@@ -185,6 +201,24 @@ namespace pEp.UI.ViewModels
...
@@ -185,6 +201,24 @@ namespace pEp.UI.ViewModels
}
}
}
}
/// <summary>
/// Gets or sets the status of this patch.
/// </summary>
public
DistributedPolicyEngine
.
PatchStatus
Status
{
get
=>
this
.
_Status
;
set
=>
this
.
SetProperty
(
ref
this
.
_Status
,
value
);
}
/// <summary>
/// Gets or sets the root path of the patch.
/// </summary>
public
string
RootPath
{
get
=>
this
.
Dialog
.
Patch
?.
RootPath
?.
OriginalString
;
set
{
this
.
Dialog
.
Patch
.
RootPath
=
new
Uri
(
value
);
this
.
OnPropertyChanged
();
}
}
/// <summary>
/// <summary>
/// Gets or sets the tag of the patch.
/// Gets or sets the tag of the patch.
/// </summary>
/// </summary>
...
@@ -198,21 +232,16 @@ namespace pEp.UI.ViewModels
...
@@ -198,21 +232,16 @@ namespace pEp.UI.ViewModels
}
}
}
}
/// <summary>
/// Gets the diff of the selected file as formatted flow document.
/// </summary>
public
FlowDocument
VisibleDiff
{
get
=>
this
.
_VisibleDiff
;
set
=>
this
.
SetProperty
(
ref
this
.
_VisibleDiff
,
value
);
}
#
endregion
#
endregion
#
region
Constructors
#
region
Constructors
/// <summary>
/// <summary>
///
Primary c
onstructor.
///
C
onstructor
for a patch dialog
.
/// </summary>
/// </summary>
/// <param name="dialog">The dialog object.</param>
/// <param name="dialog">The dialog object.</param>
/// <param name="closeDialogAction">The action to close the dialog.</param>
/// <param name="closeDialogAction">The action to close the dialog.</param>
public
Patch
Dialog
ViewModel
(
PatchDialog
dialog
,
Action
closeDialogAction
)
:
base
(
dialog
,
closeDialogAction
)
public
PatchViewModel
(
PatchDialog
dialog
,
Action
closeDialogAction
)
:
base
(
dialog
,
closeDialogAction
)
{
{
switch
(
dialog
.
PatchActionType
)
switch
(
dialog
.
PatchActionType
)
{
{
...
@@ -224,8 +253,6 @@ namespace pEp.UI.ViewModels
...
@@ -224,8 +253,6 @@ namespace pEp.UI.ViewModels
case
PatchDialog
.
PatchAction
.
SupportOrRejectPatch
:
case
PatchDialog
.
PatchAction
.
SupportOrRejectPatch
:
{
{
this
.
Explanation
=
"Support or reject patch"
;
this
.
Explanation
=
"Support or reject patch"
;
this
.
IsRejectButtonVisible
=
true
;
this
.
OKButtonText
=
"Support"
;
}
}
break
;
break
;
case
PatchDialog
.
PatchAction
.
EditPatch
:
case
PatchDialog
.
PatchAction
.
EditPatch
:
...
@@ -234,9 +261,28 @@ namespace pEp.UI.ViewModels
...
@@ -234,9 +261,28 @@ namespace pEp.UI.ViewModels
break
;
break
;
}
}
this
.
From
=
new
PEPIdentity
();
this
.
ValidatePatch
();
this
.
ValidatePatch
();
}
}
/// <summary>
/// Constructor for the DPE form region.
/// </summary>
/// <param name="patch">The patch to create the form region with.</param>
/// <param name="submitter">The submitter of the patch.</param>
/// <param name="status">The status of the patch.</param>
/// <param name="editDate">The last time this patch was edited (accepted/supported/rejected).</param>
public
PatchViewModel
(
Patch
patch
,
PEPIdentity
submitter
,
DistributedPolicyEngine
.
PatchStatus
status
=
DistributedPolicyEngine
.
PatchStatus
.
Open
,
DateTime
?
editDate
=
null
)
:
this
(
new
PatchDialog
(
PatchDialog
.
PatchAction
.
ShowPatch
,
patch
),
null
)
{
this
.
EditDate
=
editDate
;
this
.
From
=
submitter
;
this
.
UpdateView
(
status
);
this
.
Loaded
=
true
;
}
#
endregion
#
endregion
#
region
Methods
#
region
Methods
...
@@ -289,36 +335,29 @@ namespace pEp.UI.ViewModels
...
@@ -289,36 +335,29 @@ namespace pEp.UI.ViewModels
/// Creates a patch from the view model and suggests it.
/// Creates a patch from the view model and suggests it.
/// </summary>
/// </summary>
/// <param name="parameter">The command parameter.</param>
/// <param name="parameter">The command parameter.</param>
private
void
CreateAndSuggestPatch
(
object
parameter
)
private
async
void
CreateAndSuggestPatch
(
object
parameter
)
{
{
// The patch was created right now
// The patch was created right now
this
.
Dialog
.
Patch
.
CreationDate
=
DateTime
.
UtcNow
;
this
.
Dialog
.
Patch
.
CreationDate
=
DateTime
.
UtcNow
.
ToFileTimeUtc
()
;
// Get a common root URI of all diff files
// Get a common root URI of all diff files
this
.
Dialog
.
Patch
.
Uri
=
this
.
GetRoot
Uri
();
this
.
Dialog
.
Patch
.
RootPath
=
this
.
GetRoot
Path
();
// Concatenate all diffs from the selected files
// Concatenate all diffs from the selected files
this
.
Dialog
.
Patch
.
Diff
=
this
.
UnifyDiffs
();
this
.
Dialog
.
Patch
.
Diff
=
this
.
UnifyDiffs
();
// Close with result True.
// Add Apply identities
this
.
Close
(
true
);
this
.
Dialog
.
Patch
.
ApplyTo
.
Clear
();
}
foreach
(
PEPIdentity
identity
in
this
.
ApplyTo
)
/// <summary>
/// Deletes the temporary directory.
/// </summary>
private
void
DeleteTempDirectory
()
{
try
{
{
if
(
Directory
.
Exists
(
DistributedPolicyEngine
.
DPE_TEMP_LOCATION
))
this
.
Dialog
.
Patch
.
ApplyTo
.
Add
(
identity
);
{
Directory
.
Delete
(
DistributedPolicyEngine
.
DPE_TEMP_LOCATION
,
true
);
}
}
}
catch
(
Exception
ex
)
// Send patch to DPE and close if successful
if
(
await
this
.
ExecutePatchTask
(
Globals
.
ThisAddIn
.
DistributedPolicyEngine
.
Suggest
(
this
.
Dialog
.
Patch
,
this
.
From
)))
{
{
Log
.
Error
(
"DeleteTempDirectory: Error deleting temporary directory. "
+
ex
);
AdapterExtensions
.
ShowNotification
(
"Patch successfully suggested"
,
this
.
Dialog
.
Patch
.
CommitMessage
);
this
.
Close
(
true
);
}
}
}
}
...
@@ -463,7 +502,76 @@ namespace pEp.UI.ViewModels
...
@@ -463,7 +502,76 @@ namespace pEp.UI.ViewModels
return
null
;
return
null
;
}
}
return
diff
.
Insert
(
0
,
$"
{
PatchDialogViewModel
.
FILE_A_PREFIX
}{
fileNameB
}
\n
{
PatchDialogViewModel
.
FILE_B_PREFIX
}{
fileNameB
}
\n"
).
TrimEnd
(
'\n'
);
return
diff
.
Insert
(
0
,
$"
{
PatchViewModel
.
FILE_A_PREFIX
}{
fileNameB
}
\n
{
PatchViewModel
.
FILE_B_PREFIX
}{
fileNameB
}
\n"
).
TrimEnd
(
'\n'
);
}
/// <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>
/// Executes a patch task (support/reject) and shows an error message if necessary.
/// </summary>
/// <param name="patchTask">The task to execute.</param>
/// <returns>True if the patch task was executed successfully, otherwise false.</returns>
private
async
Task
<
bool
>
ExecutePatchTask
(
Task
patchTask
)
{
string
errorMessage
=
null
;
try
{
// 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
)
{
Log
.
Error
(
"Error occured. "
+
ex
);
// Get the innermost exception message and show message box
while
(
ex
.
InnerException
!=
null
)
{
ex
=
ex
.
InnerException
;
}
errorMessage
=
ex
.
Message
;
}
finally
{
this
.
IsLoading
=
false
;
Mouse
.
OverrideCursor
=
null
;
}
if
(
string
.
IsNullOrEmpty
(
errorMessage
))
{
return
true
;
}
else
{
CustomMessageBox
.
ShowDialog
(
errorMessage
,
"Error"
,
"OK"
);
return
false
;
}
}
}
/// <summary>
/// <summary>
...
@@ -532,11 +640,12 @@ namespace pEp.UI.ViewModels
...
@@ -532,11 +640,12 @@ 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>
/// <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>
/// <returns>The root URI</returns>
/// <returns>The root URI</returns>
private
string
GetRoot
Uri
()
private
Uri
GetRoot
Path
()
{
{
string
firstFileName
=
this
.
ConfigFiles
.
First
()?.
FileName
;
string
firstFileName
=
this
.
ConfigFiles
.
First
()?.
FileName
;
if
(
string
.
IsNullOrEmpty
(
firstFileName
))
if
(
string
.
IsNullOrEmpty
(
firstFileName
))
...
@@ -545,16 +654,16 @@ namespace pEp.UI.ViewModels
...
@@ -545,16 +654,16 @@ namespace pEp.UI.ViewModels
return
null
;
return
null
;
}
}
string
root
Uri
=
Directory
.
GetParent
(
firstFileName
).
FullName
.
Replace
(
'\\'
,
'/'
);
string
root
Path
=
Directory
.
GetParent
(
firstFileName
).
FullName
.
Replace
(
'\\'
,
'/'
);
foreach
(
ConfigFile
modifiedFile
in
this
.
ConfigFiles
)
foreach
(
ConfigFile
modifiedFile
in
this
.
ConfigFiles
)
{
{
string
folderName
=
Directory
.
GetParent
(
modifiedFile
.
FileName
).
FullName
.
Replace
(
'\\'
,
'/'
);
string
folderName
=
Directory
.
GetParent
(
modifiedFile
.
FileName
).
FullName
.
Replace
(
'\\'
,
'/'
);
int
i
=
0
;
int
i
=
0
;
while
((
i
<
folderName
.
Length
)
&&
while
((
i
<
folderName
.
Length
)
&&
(
i
<
root
Uri
.
Length
))
(
i
<
root
Path
.
Length
))
{
{
if
(
folderName
[
i
]
!=
root
Uri
[
i
])
if
(
folderName
[
i
]
!=
root
Path
[
i
])
{
{
break
;
break
;
}
}
...
@@ -562,10 +671,23 @@ namespace pEp.UI.ViewModels
...
@@ -562,10 +671,23 @@ namespace pEp.UI.ViewModels
i
++;
i
++;
}
}
root
Uri
=
root
Uri
.
Substring
(
0
,
i
);
root
Path
=
root
Path
.
Substring
(
0
,
i
);
}
}
return
rootUri
;
return
new
Uri
(
rootPath
);
}
/// <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
.
Dialog
.
Patch
,
this
.
From
)))
{
this
.
EditDate
=
DateTime
.
UtcNow
;
this
.
UpdateView
(
DistributedPolicyEngine
.
PatchStatus
.
Rejected
);
}
}
}
/// <summary>
/// <summary>
...
@@ -577,6 +699,48 @@ namespace pEp.UI.ViewModels
...
@@ -577,6 +699,48 @@ namespace pEp.UI.ViewModels
this
.
ConfigFiles
.
Remove
(
this
.
SelectedFile
);
this
.
ConfigFiles
.
Remove
(
this
.
SelectedFile
);
}
}
/// <summary>
/// Sets the explanation text.
/// </summary>
private
void
SetExplanation
()
{
string
editDate
=
"<n/a>"
;
if
(
this
.
EditDate
!=
null
)
{
editDate
=
((
DateTime
)
this
.
EditDate
).
ToLocalTime
().
ToString
(
"F"
);
}
switch
(
this
.
Status
)
{
case
DistributedPolicyEngine
.
PatchStatus
.
Accepted
:
this
.
Explanation
=
$"Accepted on
{
editDate
}
"
;
break
;
case
DistributedPolicyEngine
.
PatchStatus
.
Supported
:
this
.
Explanation
=
$"Supported on
{
editDate
}
"
;
break
;
case
DistributedPolicyEngine
.
PatchStatus
.
Rejected
:
this
.
Explanation
=
$"Rejected on
{
editDate
}
"
;
break
;
case
DistributedPolicyEngine
.
PatchStatus
.
Open
:
default
:
this
.
Explanation
=
"New configuration changes pending approval"
;
break
;
}
}
/// <summary>
/// Supports this patch.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private
async
void
SupportPatch
(
object
parameter
)
{
if
(
await
this
.
ExecutePatchTask
(
Globals
.
ThisAddIn
.
DistributedPolicyEngine
.
Support
(
this
.
Dialog
.
Patch
,
this
.
From
)))
{
this
.
EditDate
=
DateTime
.
UtcNow
;
this
.
UpdateView
(
DistributedPolicyEngine
.
PatchStatus
.
Supported
);
}
}
/// <summary>
/// <summary>
/// Concatenates all diffs from the selected files into one string.
/// Concatenates all diffs from the selected files into one string.
/// </summary>
/// </summary>
...
@@ -592,18 +756,30 @@ namespace pEp.UI.ViewModels
...
@@ -592,18 +756,30 @@ namespace pEp.UI.ViewModels
}
}
// Remove root parts of URI
// Remove root parts of URI
diff
=
diff
.
Replace
(
Patch
Dialog
ViewModel
.
FILE_A_PREFIX
+
this
.
Dialog
.
Patch
.
Uri
+
"/"
,
Patch
Dialog
ViewModel
.
FILE_A_PREFIX
);
diff
=
diff
.
Replace
(
PatchViewModel
.
FILE_A_PREFIX
+
this
.
Dialog
.
Patch
.
RootPath
+
"/"
,
PatchViewModel
.
FILE_A_PREFIX
);
diff
=
diff
.
Replace
(
Patch
Dialog
ViewModel
.
FILE_B_PREFIX
+
this
.
Dialog
.
Patch
.
Uri
+
"/"
,
Patch
Dialog
ViewModel
.
FILE_B_PREFIX
);
diff
=
diff
.
Replace
(
PatchViewModel
.
FILE_B_PREFIX
+
this
.
Dialog
.
Patch
.
RootPath
+
"/"
,
PatchViewModel
.
FILE_B_PREFIX
);
return
diff
.
TrimEnd
(
'\n'
);
return
diff
.
TrimEnd
(
'\n'
);
}
}
/// <summary>
/// Updates the view according to a new patch status.
/// </summary>
/// <param name="patchStatus">The new patch status.</param>
private
void
UpdateView
(
DistributedPolicyEngine
.
PatchStatus
patchStatus
)
{
this
.
Status
=
patchStatus
;
this
.
SetExplanation
();
this
.
FormattedDiff
=
PatchViewModel
.
FormatDiff
(
this
.
Dialog
.
Patch
.
Diff
,
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
);
this
.
Background
=
(
patchStatus
==
DistributedPolicyEngine
.
PatchStatus
.
Open
)
?
Brushes
.
White
:
Brushes
.
WhiteSmoke
;
}
/// <summary>
/// <summary>
/// Updates the diff that is visible in the view pane.
/// Updates the diff that is visible in the view pane.
/// </summary>
/// </summary>
private
void
UpdateVisibleDiff
()
private
void
UpdateVisibleDiff
()
{
{
this
.
Visible
Diff
=
Patch
Dialog
ViewModel
.
FormatDiff
(
this
.
SelectedFile
?.
Diff
);
this
.
Formatted
Diff
=
PatchViewModel
.
FormatDiff
(
this
.
SelectedFile
?.
Diff
);
}
}
/// <summary>
/// <summary>
...
...
UI/Views/DialogHostView.xaml
View file @
1510fb85
...
@@ -33,7 +33,7 @@
...
@@ -33,7 +33,7 @@
<DataTemplate DataType="{x:Type vm:InputMessageBoxViewModel}">
<DataTemplate DataType="{x:Type vm:InputMessageBoxViewModel}">
<v:InputMessageBoxView />
<v:InputMessageBoxView />
</DataTemplate>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Patch
Dialog
ViewModel}">
<DataTemplate DataType="{x:Type vm:PatchViewModel}">
<v:PatchDialogView />
<v:PatchDialogView />
</DataTemplate>
</DataTemplate>
</UserControl.Resources>
</UserControl.Resources>
...
...
UI/Views/DialogHostView.xaml.cs
View file @
1510fb85
...
@@ -81,7 +81,7 @@ namespace pEp.UI.Views
...
@@ -81,7 +81,7 @@ namespace pEp.UI.Views
case
Dialog
.
Type
.
Patch
:
case
Dialog
.
Type
.
Patch
:
{
{
Debug
.
Assert
(
dialog
is
PatchDialog
);
Debug
.
Assert
(
dialog
is
PatchDialog
);
this
.
Content
=
new
Patch
Dialog
ViewModel
(
dialog
as
PatchDialog
,
closeDialogAction
);
this
.
Content
=
new
PatchViewModel
(
dialog
as
PatchDialog
,
closeDialogAction
);
}
}
break
;
break
;
default
:
default
:
...
...
UI/Views/FormControlPatchView.xaml
View file @
1510fb85
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
FontFamily="Segoe UI"
FontFamily="Segoe UI"
FontSize="12"
FontSize="12"
Background="{Binding Background}"
Background="{Binding Background}"
d:DataContext="{d:DesignInstance Type=vm:
FormControl
PatchViewModel}"
d:DataContext="{d:DesignInstance Type=vm:PatchViewModel}"
d:DesignHeight="450" d:DesignWidth="800">
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary>
...
@@ -41,16 +41,60 @@
...
@@ -41,16 +41,60 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid.RowDefinitions>
<!--Hides all other content while the form region is being loaded-->
<Rectangle Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.RowSpan="8"
Grid.ZIndex="1000"
Fill="White"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="{Binding Loaded, Converter={StaticResource InvertBoolToVisibility}}" />
<!--Overlay during API calls-->
<Rectangle Grid.Column="0"
<Rectangle Grid.Column="0"
Grid.ColumnSpan="3"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.Row="0"
Grid.RowSpan="8"
Grid.RowSpan="8"
Grid.ZIndex="100"
Grid.ZIndex="100"
Fill="White"
Fill="White"
Opacity="0.
6
"
Opacity="0.
7
"
HorizontalAlignment="Stretch"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}" />
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}" />
<!--Loading animation during API calls-->
<Image Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.RowSpan="8"
Grid.ZIndex="100"
Width="100"
Height="41"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}"
Source="pack://application:,,,/pEp;component/Resources/ImageLogoMedium.png"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.1"
Duration="0:0:1"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Label Grid.Column="0"
<Label Grid.Column="0"
Grid.ColumnSpan="2"
Grid.ColumnSpan="2"
Grid.Row="0"
Grid.Row="0"
...
@@ -60,7 +104,7 @@
...
@@ -60,7 +104,7 @@
<Label Grid.Column="0"
<Label Grid.Column="0"
Grid.ColumnSpan="2"
Grid.ColumnSpan="2"
Grid.Row="1"
Grid.Row="1"
Content="{Binding
Submitter
.DisplayString}"
Content="{Binding
From
.DisplayString}"
Margin="10,1" />
Margin="10,1" />
<Label Grid.Column="0"
<Label Grid.Column="0"
Grid.ColumnSpan="2"
Grid.ColumnSpan="2"
...
@@ -87,7 +131,7 @@
...
@@ -87,7 +131,7 @@
BorderBrush="Black"
BorderBrush="Black"
BorderThickness="1"
BorderThickness="1"
Margin="5,10,10,10"
Margin="5,10,10,10"
Document="{Binding
Display
Diff}" />
Document="{Binding
Formatted
Diff}" />
<Label Grid.Column="0"
<Label Grid.Column="0"
Grid.Row="4"
Grid.Row="4"
Content="Uri"
Content="Uri"
...
@@ -98,7 +142,7 @@
...
@@ -98,7 +142,7 @@
Background="{Binding Background}"
Background="{Binding Background}"
VerticalAlignment="Center"
VerticalAlignment="Center"
Padding="2"
Padding="2"
Text="{Binding
Uri
}"
Text="{Binding
RootPath
}"
IsReadOnly="True"
IsReadOnly="True"
Margin="5,10,10,10"/>
Margin="5,10,10,10"/>
<Label Grid.Column="0"
<Label Grid.Column="0"
...
@@ -132,15 +176,13 @@
...
@@ -132,15 +176,13 @@
Grid.Row="7"
Grid.Row="7"
Orientation="Horizontal"
Orientation="Horizontal"
HorizontalAlignment="Center">
HorizontalAlignment="Center">
<Button Content="
{Binding OKButtonText}
"
<Button Content="
Support
"
Command="{Binding OKButtonCommand}"
Command="{Binding OKButtonCommand}"
IsDefault="True"
Visibility="{Binding IsOKButtonVisible, Converter={StaticResource BoolToVisibility}}"
Style="{StaticResource StyleButtonGray}"
Style="{StaticResource StyleButtonGray}"
IsDefault="True"
Margin="10" />
Margin="10" />
<Button Content="{
Binding
RejectButton
Text
}"
<Button Content="{
x:Static p:Resources.SyncWizard_
RejectButton}"
Command="{Binding RejectButtonCommand}"
Command="{Binding RejectButtonCommand}"
Visibility="{Binding IsRejectButtonVisible, Converter={StaticResource BoolToVisibility}}"
Style="{StaticResource StyleButtonGray}"
Style="{StaticResource StyleButtonGray}"
Margin="10" />
Margin="10" />
</StackPanel>
</StackPanel>
...
...
UI/Views/OptionsView.xaml
View file @
1510fb85
...
@@ -157,20 +157,6 @@
...
@@ -157,20 +157,6 @@
VerticalAlignment="Center" />
VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
</ListBoxItem>-->
</ListBoxItem>-->
<!--<ListBoxItem Visibility="{Binding Path=IsDPEEnabled, Converter={StaticResource BoolToVisibility}}">-->
<ListBoxItem>
<StackPanel Orientation="Horizontal"
Height="{StaticResource PageSelectorHeight}">
<Image Source="pack://application:,,,/pEp;component/Resources/ImageOptionsGroupEncryption.png"
Width="26"
Height="13"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock Text="Patch management"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem Visibility="{Binding Path=IsDeveloperModeEnabled, Converter={StaticResource BoolToVisibility}}">
<ListBoxItem Visibility="{Binding Path=IsDeveloperModeEnabled, Converter={StaticResource BoolToVisibility}}">
<StackPanel Orientation="Horizontal"
<StackPanel Orientation="Horizontal"
Height="{StaticResource PageSelectorHeight}">
Height="{StaticResource PageSelectorHeight}">
...
@@ -965,26 +951,6 @@
...
@@ -965,26 +951,6 @@
</StackPanel>
</StackPanel>
</TabItem>-->
</TabItem>-->
<!--Distributed Policy Management-->
<TabItem>
<StackPanel Margin="{StaticResource PagePadding}">
<!-- DPE title -->
<TextBlock Text="Distributed Policy Management"
Margin="6,6,0,0"
FontWeight="Bold"
FontSize="16" />
<Separator />
<!--New patch button-->
<Button Content="New patch"
Style="{StaticResource StyleButtonGray}"
Margin="10,5,5,5"
Command="{Binding CommandButtonNewPatch}"
HorizontalAlignment="Left"/>
</StackPanel>
</TabItem>
<!-- Developer -->
<!-- Developer -->
<TabItem>
<TabItem>
<Grid Margin="{StaticResource PagePadding}">
<Grid Margin="{StaticResource PagePadding}">
...
...
UI/Views/PatchDialogView.xaml
View file @
1510fb85
...
@@ -3,10 +3,11 @@
...
@@ -3,10 +3,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p="clr-namespace:pEp.Properties"
xmlns:ui="clr-namespace:pEp.UI"
xmlns:ui="clr-namespace:pEp.UI"
xmlns:vm="clr-namespace:pEp.UI.ViewModels"
xmlns:vm="clr-namespace:pEp.UI.ViewModels"
mc:Ignorable="d"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:Patch
Dialog
ViewModel}"
d:DataContext="{d:DesignInstance Type=vm:PatchViewModel}"
d:DesignHeight="450" d:DesignWidth="800">
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary>
...
@@ -34,6 +35,48 @@
...
@@ -34,6 +35,48 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid.RowDefinitions>
<!--Overlay during API calls-->
<Rectangle Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.RowSpan="5"
Grid.ZIndex="99"
Fill="White"
Opacity="0.7"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}" />
<!--Loading animation during API calls-->
<Image Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.RowSpan="5"
Grid.ZIndex="100"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}"
Source="pack://application:,,,/pEp;component/Resources/ImageLogoMedium.png"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.1"
Duration="0:0:1"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Label Grid.Row="0"
<Label Grid.Row="0"
Grid.Column="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Grid.ColumnSpan="3"
...
@@ -73,38 +116,35 @@
...
@@ -73,38 +116,35 @@
BorderThickness="1"
BorderThickness="1"
Margin="5,10,10,10"
Margin="5,10,10,10"
HorizontalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
Document="{Binding
Visible
Diff}"/>
Document="{Binding
Formatted
Diff}"/>
<ui:TextBoxWithPlaceholder Grid.Column="0"
<ui:TextBoxWithPlaceholder Grid.Column="0"
Grid.ColumnSpan="3"
Grid.ColumnSpan="3"
Grid.Row="2"
Grid.Row="2"
Text="{Binding CommitMessage, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding CommitMessage, UpdateSourceTrigger=PropertyChanged}"
IsValidInput="{Binding IsCommitMessageValid}"
IsValidInput="{Binding IsCommitMessageValid}"
Placeholder="Commit message"
Placeholder="Commit message"
Margin="5,10,10,10"
Margin="5,10,10,10" />
Visibility="{Binding IsEditable, Converter={StaticResource BoolToVisibility}}"/>
<ui:TextBoxWithPlaceholder Grid.Column="0"
<ui:TextBoxWithPlaceholder Grid.Column="0"
Grid.ColumnSpan="3"
Grid.ColumnSpan="3"
Grid.Row="3"
Grid.Row="3"
Text="{Binding Tag, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Tag, UpdateSourceTrigger=PropertyChanged}"
Placeholder="Tag (optional)"
Placeholder="Tag (optional)"
Margin="5,10,10,10"
Margin="5,10,10,10" />
Visibility="{Binding IsEditable, Converter={StaticResource BoolToVisibility}}"/>
<StackPanel Grid.Column="0"
<StackPanel Grid.Column="0"
Grid.ColumnSpan="3"
Grid.ColumnSpan="3"
Grid.Row="4"
Grid.Row="4"
Orientation="Horizontal"
Orientation="Horizontal"
HorizontalAlignment="Center"
HorizontalAlignment="Center"
Margin="10">
Margin="10">
<Button Content="{
Binding OKButton
Text}"
<Button Content="{
x:Static p:Resources.Options_OK
Text}"
Command="{Binding OKButtonCommand}"
Command="{Binding OKButtonCommand}"
IsEnabled="{Binding IsValid}"
IsDefault="True"
IsDefault="True"
Margin="10"
Margin="10"
Style="{StaticResource StyleButtonGray}"/>
Style="{StaticResource StyleButtonGray}"/>
<Button Content="{
Binding CancelButton
Text}"
<Button Content="{
x:Static p:Resources.Options_Cancel
Text}"
Margin="10"
Margin="10"
Command="{Binding CancelButtonCommand}"
Command="{Binding CancelButtonCommand}"
Style="{StaticResource StyleButtonGray}"/>
Style="{StaticResource StyleButtonGray}"/>
</StackPanel>
</StackPanel>
</Grid>
</Grid>
</UserControl>
</UserControl>
UI/Views/PatchDialogView.xaml.cs
View file @
1510fb85
...
@@ -24,7 +24,7 @@ namespace pEp.UI.Views
...
@@ -24,7 +24,7 @@ namespace pEp.UI.Views
private
void
ListBoxItem_MouseDoubleClick
(
object
sender
,
MouseButtonEventArgs
e
)
private
void
ListBoxItem_MouseDoubleClick
(
object
sender
,
MouseButtonEventArgs
e
)
{
{
// Edit the file that is being double-clicked
// Edit the file that is being double-clicked
(
this
.
DataContext
as
Patch
Dialog
ViewModel
)?.
AddOrEditFileCommand
?.
Execute
(((
sender
as
ContentControl
)?.
DataContext
as
ConfigFile
)?.
FileName
);
(
this
.
DataContext
as
PatchViewModel
)?.
AddOrEditFileCommand
?.
Execute
(((
sender
as
ContentControl
)?.
DataContext
as
ConfigFile
)?.
FileName
);
}
}
}
}
}
}
pEpForOutlook.csproj
View file @
1510fb85
...
@@ -440,14 +440,13 @@
...
@@ -440,14 +440,13 @@
<Compile
Include=
"UI\TextBoxWithPlaceholder.xaml.cs"
>
<Compile
Include=
"UI\TextBoxWithPlaceholder.xaml.cs"
>
<DependentUpon>
TextBoxWithPlaceholder.xaml
</DependentUpon>
<DependentUpon>
TextBoxWithPlaceholder.xaml
</DependentUpon>
</Compile>
</Compile>
<Compile
Include=
"UI\ViewModels\FormControlPatchViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\GroupWizardAddMembersViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\GroupWizardAddMembersViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\GroupWizardNewListViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\GroupWizardNewListViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\GroupWizardViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\GroupWizardViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\CustomMessageBoxViewModel.cs"
/>
<Compile
Include=
"UI\ViewModels\CustomMessageBoxViewModel.cs"
/>
<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\Patch
Dialog
ViewModel.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>
...
...