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
Commits
3bfe6a7d
Commit
3bfe6a7d
authored
Sep 30, 2021
by
Thomas
Browse files
Rename patch view model
parent
6f78066a
Changes
9
Hide whitespace changes
Inline
Side-by-side
UI/FormRegionDPE.cs
View file @
3bfe6a7d
...
@@ -63,7 +63,7 @@ namespace pEp
...
@@ -63,7 +63,7 @@ namespace pEp
// 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
Patch
Dialog
ViewModel
(
patch
,
submitter
,
patchStatus
,
editDate
);
this
.
FormControlPatchView
.
DataContext
=
new
PatchViewModel
(
patch
,
submitter
,
patchStatus
,
editDate
);
}
}
else
else
{
{
...
...
UI/ViewModels/FormControlPatchViewModel.cs
deleted
100644 → 0
View file @
6f78066a
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
bool
_Loaded
=
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 or sets whether the screen is fully loaded.
/// </summary>
public
bool
Loaded
{
get
=>
this
.
_Loaded
;
set
=>
this
.
SetProperty
(
ref
this
.
_Loaded
,
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 root path of the patch.
/// </summary>
public
string
RootPath
{
get
=>
this
.
patch
?.
RootPath
.
AbsoluteUri
;
set
{
this
.
patch
.
RootPath
=
new
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
);
this
.
Loaded
=
true
;
}
#
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
)
{
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>
/// 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/Patch
Dialog
ViewModel.cs
→
UI/ViewModels/PatchViewModel.cs
View file @
3bfe6a7d
...
@@ -18,7 +18,7 @@ using System.Windows.Media;
...
@@ -18,7 +18,7 @@ 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
...
@@ -229,7 +229,7 @@ namespace pEp.UI.ViewModels
...
@@ -229,7 +229,7 @@ namespace pEp.UI.ViewModels
/// </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
)
{
{
...
@@ -260,7 +260,7 @@ namespace pEp.UI.ViewModels
...
@@ -260,7 +260,7 @@ namespace pEp.UI.ViewModels
/// <param name="submitter">The submitter of the patch.</param>
/// <param name="submitter">The submitter of the patch.</param>
/// <param name="status">The status 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>
/// <param name="editDate">The last time this patch was edited (accepted/supported/rejected).</param>
public
Patch
Dialog
ViewModel
(
Patch
patch
,
public
PatchViewModel
(
Patch
patch
,
PEPIdentity
submitter
,
PEPIdentity
submitter
,
DistributedPolicyEngine
.
PatchStatus
status
=
DistributedPolicyEngine
.
PatchStatus
.
Open
,
DistributedPolicyEngine
.
PatchStatus
status
=
DistributedPolicyEngine
.
PatchStatus
.
Open
,
DateTime
?
editDate
=
null
)
:
this
(
new
PatchDialog
(
PatchDialog
.
PatchAction
.
ShowPatch
,
patch
),
null
)
DateTime
?
editDate
=
null
)
:
this
(
new
PatchDialog
(
PatchDialog
.
PatchAction
.
ShowPatch
,
patch
),
null
)
...
@@ -482,7 +482,7 @@ namespace pEp.UI.ViewModels
...
@@ -482,7 +482,7 @@ namespace pEp.UI.ViewModels
return
null
;
return
null
;
}
}
return
diff
.
Insert
(
0
,
$"
{
Patch
Dialog
ViewModel
.
FILE_A_PREFIX
}{
fileNameB
}
\n
{
Patch
Dialog
ViewModel
.
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>
/// <summary>
...
@@ -736,8 +736,8 @@ namespace pEp.UI.ViewModels
...
@@ -736,8 +736,8 @@ 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
.
RootPath
+
"/"
,
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
.
RootPath
+
"/"
,
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'
);
}
}
...
@@ -750,7 +750,7 @@ namespace pEp.UI.ViewModels
...
@@ -750,7 +750,7 @@ namespace pEp.UI.ViewModels
{
{
this
.
Status
=
patchStatus
;
this
.
Status
=
patchStatus
;
this
.
SetExplanation
();
this
.
SetExplanation
();
this
.
FormattedDiff
=
Patch
Dialog
ViewModel
.
FormatDiff
(
this
.
Dialog
.
Patch
.
Diff
,
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
);
this
.
FormattedDiff
=
PatchViewModel
.
FormatDiff
(
this
.
Dialog
.
Patch
.
Diff
,
this
.
Status
==
DistributedPolicyEngine
.
PatchStatus
.
Open
);
this
.
Background
=
(
patchStatus
==
DistributedPolicyEngine
.
PatchStatus
.
Open
)
?
Brushes
.
White
:
Brushes
.
WhiteSmoke
;
this
.
Background
=
(
patchStatus
==
DistributedPolicyEngine
.
PatchStatus
.
Open
)
?
Brushes
.
White
:
Brushes
.
WhiteSmoke
;
}
}
...
@@ -759,7 +759,7 @@ namespace pEp.UI.ViewModels
...
@@ -759,7 +759,7 @@ namespace pEp.UI.ViewModels
/// </summary>
/// </summary>
private
void
UpdateVisibleDiff
()
private
void
UpdateVisibleDiff
()
{
{
this
.
FormattedDiff
=
Patch
Dialog
ViewModel
.
FormatDiff
(
this
.
SelectedFile
?.
Diff
);
this
.
FormattedDiff
=
PatchViewModel
.
FormatDiff
(
this
.
SelectedFile
?.
Diff
);
}
}
/// <summary>
/// <summary>
...
...
UI/Views/DialogHostView.xaml
View file @
3bfe6a7d
...
@@ -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 @
3bfe6a7d
...
@@ -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 @
3bfe6a7d
...
@@ -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: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>
...
...
UI/Views/PatchDialogView.xaml
View file @
3bfe6a7d
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
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>
...
...
UI/Views/PatchDialogView.xaml.cs
View file @
3bfe6a7d
...
@@ -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 @
3bfe6a7d
...
@@ -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>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment