Reopening cases logic

This commit is contained in:
2026-03-17 15:13:05 +00:00
parent 7699086df4
commit f4e456ed92
3 changed files with 425 additions and 95 deletions
+193 -67
View File
@@ -26,6 +26,7 @@ namespace OutlookCaseHelper
{
trayMenu = new ContextMenuStrip();
trayMenu.Items.Add("Create Rule (Selected Email)", null, ProcessEmail_Click);
trayMenu.Items.Add("Create Rule (Manual ID)", null, CreateRuleManual_Click);
trayMenu.Items.Add("Remove Rule (Selected Email)", null, RemoveRuleFromSelected_Click);
trayMenu.Items.Add("Remove Rule (Manual ID)", null, RemoveRule_Click);
trayMenu.Items.Add("-");
@@ -42,17 +43,14 @@ namespace OutlookCaseHelper
private void InitializeTimer()
{
monitorTimer = new System.Windows.Forms.Timer();
monitorTimer.Interval = 60000; // verifica a cada 60 segundos
monitorTimer.Interval = 60000;
monitorTimer.Tick += MonitorTimer_Tick;
monitorTimer.Start();
}
private void MonitorTimer_Tick(object? sender, EventArgs e)
{
try
{
outlookHelper.ProcessActiveRules();
}
try { outlookHelper.ProcessActiveRules(); }
catch { }
}
@@ -65,30 +63,93 @@ namespace OutlookCaseHelper
{
MessageBox.Show(
"No email selected or ID not found in subject.\n\nExpected format: Title - TrackingID#1111111111111111",
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
bool success = outlookHelper.CreateFolderAndMoveEmails(trackingId);
if (success)
// Verifica se já existe regra ativa
if (outlookHelper.FindRuleByTrackingId(trackingId) != null)
{
var activeCount = outlookHelper.GetActiveRules().Count;
MessageBox.Show(
$"Rule created! Emails moved and monitoring started.\n\nTrackingID: {trackingId}\nActive rules: {activeCount}",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
MessageBox.Show($"Rule for TrackingID#{trackingId} already exists!",
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Verifica se existe em Closed — reabre sem pedir nome
if (outlookHelper.ExistsInClosed(trackingId))
{
var triggerEmail = outlookHelper.GetSelectedEmail();
bool reopened = outlookHelper.ReopenFromClosed(trackingId, triggerEmail);
if (reopened)
MessageBox.Show(
$"Case reopened! Folder moved from Closed to Active.\n\nTrackingID: {trackingId}",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
{
MessageBox.Show(
"Error processing email. Make sure Outlook is open.",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
MessageBox.Show("Error reopening case.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Caso novo — pede nome
var ruleForm = new CreateRuleForm(trackingId, readonlyId: true);
if (ruleForm.ShowDialog() != DialogResult.OK) return;
string folderName = ruleForm.FolderName;
bool success = outlookHelper.CreateFolderAndMoveEmails(trackingId, folderName);
if (success)
MessageBox.Show(
$"Rule created! Emails moved and monitoring started.\n\nFolder: {folderName}\nActive rules: {outlookHelper.GetActiveRules().Count}",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("Error processing email. Make sure Outlook is open.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void CreateRuleManual_Click(object? sender, EventArgs e)
{
try
{
var ruleForm = new CreateRuleForm("", readonlyId: false);
if (ruleForm.ShowDialog() != DialogResult.OK) return;
string trackingId = ruleForm.TrackingId;
string folderName = ruleForm.FolderName;
if (outlookHelper.FindRuleByTrackingId(trackingId) != null)
{
MessageBox.Show($"Rule for TrackingID#{trackingId} already exists!",
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Verifica se existe em Closed — reabre sem pedir nome
if (outlookHelper.ExistsInClosed(trackingId))
{
bool reopened = outlookHelper.ReopenFromClosed(trackingId);
if (reopened)
MessageBox.Show(
$"Case reopened! Folder moved from Closed to Active.\n\nTrackingID: {trackingId}",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("Error reopening case.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
bool success = outlookHelper.CreateFolderAndMoveEmails(trackingId, folderName);
if (success)
MessageBox.Show(
$"Rule created! Emails moved and monitoring started.\n\nFolder: {folderName}\nActive rules: {outlookHelper.GetActiveRules().Count}",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("Error creating rule. Make sure Outlook is open.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
@@ -101,25 +162,24 @@ namespace OutlookCaseHelper
try
{
var form = new InputForm("Enter TrackingID to remove:", "Remove Rule");
if (form.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(form.TrackingId))
if (form.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(form.Value))
{
bool success = outlookHelper.RemoveRuleAndMoveToClosed(form.TrackingId);
string? folderName = outlookHelper.FindRuleByTrackingId(form.Value.Trim());
if (folderName == null)
{
MessageBox.Show($"No active rule found for TrackingID#{form.Value}.",
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
bool success = outlookHelper.RemoveRuleAndMoveToClosed(folderName);
if (success)
{
MessageBox.Show(
"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nMonitoring stopped.",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
$"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nFolder: {folderName}\nMonitoring stopped.",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
{
MessageBox.Show(
"Error removing rule. Check if folder exists.",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
MessageBox.Show("Error removing rule. Check if folder exists.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
@@ -137,29 +197,26 @@ namespace OutlookCaseHelper
{
MessageBox.Show(
"No email selected or ID not found in subject.\n\nExpected format: Title - TrackingID#1111111111111111",
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
bool success = outlookHelper.RemoveRuleAndMoveToClosed(trackingId);
string? folderName = outlookHelper.FindRuleByTrackingId(trackingId);
if (folderName == null)
{
MessageBox.Show($"No active rule found for TrackingID#{trackingId}.",
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
bool success = outlookHelper.RemoveRuleAndMoveToClosed(folderName);
if (success)
{
MessageBox.Show(
$"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nTrackingID: {trackingId}\nMonitoring stopped.",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
$"Rule removed!\n\nFolder moved to: Inbox > Cases > Closed\nFolder: {folderName}\nMonitoring stopped.",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
{
MessageBox.Show(
"Error removing rule. Check if folder exists and Outlook is open.",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
MessageBox.Show("Error removing rule. Check if folder exists and Outlook is open.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
@@ -179,36 +236,105 @@ namespace OutlookCaseHelper
Application.Exit();
}
private class CreateRuleForm : Form
{
private TextBox txtId;
private TextBox txtName;
private Label lblPreview;
public string TrackingId { get; private set; } = "";
public string FolderName { get; private set; } = "";
public CreateRuleForm(string trackingId, bool readonlyId)
{
this.Text = "Create Rule";
this.Size = new Size(420, 230);
this.MinimumSize = new Size(420, 230);
this.MaximumSize = new Size(420, 230);
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
var lblId = new Label { Text = "TrackingID (required):", Left = 20, Top = 15, Width = 370, Height = 20 };
txtId = new TextBox { Left = 20, Top = 38, Width = 370, Height = 24, Text = trackingId, ReadOnly = readonlyId };
if (readonlyId) txtId.BackColor = System.Drawing.SystemColors.Control;
var lblName = new Label { Text = "Additional name (optional):", Left = 20, Top = 72, Width = 370, Height = 20 };
txtName = new TextBox { Left = 20, Top = 95, Width = 370, Height = 24 };
lblPreview = new Label { Left = 20, Top = 128, Width = 370, Height = 20, ForeColor = System.Drawing.Color.Gray };
UpdatePreview(trackingId);
var btnOk = new Button { Text = "OK", Left = 220, Top = 158, Width = 80, DialogResult = DialogResult.OK };
var btnCancel = new Button { Text = "Cancel", Left = 310, Top = 158, Width = 80, DialogResult = DialogResult.Cancel };
txtId.TextChanged += (s, e) => UpdatePreview(txtId.Text.Trim());
txtName.TextChanged += (s, e) => UpdatePreview(txtId.Text.Trim());
this.Controls.AddRange(new Control[] { lblId, txtId, lblName, txtName, lblPreview, btnOk, btnCancel });
this.AcceptButton = btnOk;
this.CancelButton = btnCancel;
}
private void UpdatePreview(string id)
{
string name = txtName?.Text.Trim() ?? "";
string preview = string.IsNullOrEmpty(name) ? id : $"{id} | {name}";
if (lblPreview != null)
lblPreview.Text = $"Folder: {preview}";
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (this.DialogResult == DialogResult.OK)
{
TrackingId = txtId.Text.Trim();
string name = txtName.Text.Trim();
if (string.IsNullOrEmpty(TrackingId))
{
MessageBox.Show("TrackingID is required!", "Validation",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
e.Cancel = true;
return;
}
FolderName = string.IsNullOrEmpty(name) ? TrackingId : $"{TrackingId} | {name}";
}
base.OnFormClosing(e);
}
}
private class InputForm : Form
{
private TextBox txtInput;
public string TrackingId { get; private set; } = "";
public string Value { get; private set; } = "";
public InputForm(string prompt, string title)
{
this.Text = title;
this.Width = 400;
this.Height = 150;
this.Size = new Size(380, 150);
this.MinimumSize = new Size(380, 150);
this.MaximumSize = new Size(380, 150);
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
var label = new Label { Text = prompt, Left = 20, Top = 20, Width = 360, Height = 30 };
txtInput = new TextBox { Left = 20, Top = 60, Width = 340, Height = 30 };
var label = new Label { Text = prompt, Left = 20, Top = 15, Width = 330, Height = 25 };
txtInput = new TextBox { Left = 20, Top = 45, Width = 330, Height = 24 };
var btnOk = new Button { Text = "OK", Left = 200, Top = 100, Width = 80, DialogResult = DialogResult.OK };
var btnCancel = new Button { Text = "Cancel", Left = 290, Top = 100, Width = 80, DialogResult = DialogResult.Cancel };
this.Controls.Add(label);
this.Controls.Add(txtInput);
this.Controls.Add(btnOk);
this.Controls.Add(btnCancel);
var btnOk = new Button { Text = "OK", Left = 155, Top = 80, Width = 80, DialogResult = DialogResult.OK };
var btnCancel = new Button { Text = "Cancel", Left = 245, Top = 80, Width = 80, DialogResult = DialogResult.Cancel };
this.Controls.AddRange(new Control[] { label, txtInput, btnOk, btnCancel });
this.AcceptButton = btnOk;
this.CancelButton = btnCancel;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
TrackingId = txtInput.Text;
Value = txtInput.Text;
base.OnFormClosing(e);
}
}
+113 -29
View File
@@ -44,7 +44,6 @@ namespace OutlookCaseHelper
inboxItems = inboxFolder.Items;
inboxItems.ItemAdd += OnEmailReceived;
}
if (sentFolder != null)
{
sentItems = sentFolder.Items;
@@ -62,8 +61,9 @@ namespace OutlookCaseHelper
if (item is not Outlook.MailItem mail) return;
if (mail.Subject == null) return;
foreach (var trackingId in activeRules)
foreach (var folderName in activeRules)
{
string trackingId = ExtractTrackingId(folderName);
if (mail.Subject.Contains($"TrackingID#{trackingId}"))
{
Outlook.Folder? inboxFolder =
@@ -72,7 +72,7 @@ namespace OutlookCaseHelper
Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases");
Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active");
Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, trackingId);
Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, folderName);
mail.Move(trackingFolder);
break;
@@ -90,8 +90,9 @@ namespace OutlookCaseHelper
if (item is not Outlook.MailItem mail) return;
if (mail.Subject == null) return;
foreach (var trackingId in activeRules)
foreach (var folderName in activeRules)
{
string trackingId = ExtractTrackingId(folderName);
if (mail.Subject.Contains($"TrackingID#{trackingId}"))
{
Outlook.Folder? inboxFolder =
@@ -100,7 +101,7 @@ namespace OutlookCaseHelper
Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases");
Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active");
Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, trackingId);
Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, folderName);
mail.Move(trackingFolder);
break;
@@ -110,6 +111,42 @@ namespace OutlookCaseHelper
catch { }
}
private string ExtractTrackingId(string folderName)
{
var match = Regex.Match(folderName, @"^(\d+)");
return match.Success ? match.Groups[1].Value : folderName;
}
public string? FindRuleByTrackingId(string trackingId)
{
foreach (var rule in activeRules)
{
if (ExtractTrackingId(rule) == trackingId)
return rule;
}
return null;
}
// Verifica se existe pasta em Closed com este trackingId
public bool ExistsInClosed(string trackingId)
{
try
{
Outlook.Folder? inboxFolder =
outlookNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder;
if (inboxFolder == null) return false;
Outlook.Folder? casesFolder = GetFolder(inboxFolder, "Cases");
if (casesFolder == null) return false;
Outlook.Folder? closedFolder = GetFolder(casesFolder, "Closed");
if (closedFolder == null) return false;
return GetFolderStartingWith(closedFolder, trackingId) != null;
}
catch { return false; }
}
private void LoadRules()
{
try
@@ -159,7 +196,8 @@ namespace OutlookCaseHelper
catch { return null; }
}
public bool CreateFolderAndMoveEmails(string trackingId)
// Reabre caso existente em Closed e move o email trigger para lá
public bool ReopenFromClosed(string trackingId, Outlook.MailItem? triggerEmail = null)
{
try
{
@@ -169,17 +207,48 @@ namespace OutlookCaseHelper
Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases");
Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active");
Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, trackingId);
Outlook.Folder? closedFolder = GetFolder(casesFolder, "Closed");
if (closedFolder == null) return false;
Outlook.Folder? closedTracking = GetFolderStartingWith(closedFolder, trackingId);
if (closedTracking == null) return false;
string existingName = closedTracking.Name;
closedTracking.MoveTo(activeFolder);
// Move o email que originou a reabertura para a pasta
if (triggerEmail != null)
{
Outlook.Folder? movedFolder = GetFolder(activeFolder, existingName);
if (movedFolder != null)
triggerEmail.Move(movedFolder);
}
activeRules.Add(existingName);
SaveRules();
return true;
}
catch { return false; }
}
public bool CreateFolderAndMoveEmails(string trackingId, string folderName)
{
try
{
Outlook.Folder? inboxFolder =
outlookNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder;
if (inboxFolder == null) return false;
Outlook.Folder casesFolder = GetOrCreateFolder(inboxFolder, "Cases");
Outlook.Folder activeFolder = GetOrCreateFolder(casesFolder, "Active");
Outlook.Folder trackingFolder = GetOrCreateFolder(activeFolder, folderName);
// Usa Restrict para mover apenas emails com o TrackingID
string filter = $"@SQL=\"urn:schemas:httpmail:subject\" LIKE '%TrackingID#{trackingId}%'";
Outlook.Items restricted = inboxFolder.Items.Restrict(filter);
var toMove = new List<Outlook.MailItem>();
foreach (object item in restricted)
{
if (item is Outlook.MailItem mail)
toMove.Add(mail);
}
if (item is Outlook.MailItem mail) toMove.Add(mail);
foreach (var mail in toMove)
mail.Move(trackingFolder);
@@ -190,15 +259,12 @@ namespace OutlookCaseHelper
Outlook.Items restrictedSent = sentFolder.Items.Restrict(filter);
var toMoveSent = new List<Outlook.MailItem>();
foreach (object item in restrictedSent)
{
if (item is Outlook.MailItem mail)
toMoveSent.Add(mail);
}
if (item is Outlook.MailItem mail) toMoveSent.Add(mail);
foreach (var mail in toMoveSent)
mail.Move(trackingFolder);
}
activeRules.Add(trackingId);
activeRules.Add(folderName);
SaveRules();
return true;
@@ -210,7 +276,7 @@ namespace OutlookCaseHelper
}
}
public bool RemoveRuleAndMoveToClosed(string trackingId)
public bool RemoveRuleAndMoveToClosed(string folderName)
{
try
{
@@ -224,13 +290,13 @@ namespace OutlookCaseHelper
Outlook.Folder? activeFolder = GetFolder(casesFolder, "Active");
if (activeFolder == null) return false;
Outlook.Folder? trackingFolder = GetFolder(activeFolder, trackingId);
Outlook.Folder? trackingFolder = GetFolder(activeFolder, folderName);
if (trackingFolder == null) return false;
Outlook.Folder closedFolder = GetOrCreateFolder(casesFolder, "Closed");
trackingFolder.MoveTo(closedFolder);
activeRules.Remove(trackingId);
activeRules.Remove(folderName);
SaveRules();
return true;
@@ -238,29 +304,47 @@ namespace OutlookCaseHelper
catch { return false; }
}
public void ProcessActiveRules()
{
// Mantido para compatibilidade mas os eventos tratam tudo em tempo real
}
public void ProcessActiveRules() { }
private Outlook.Folder GetOrCreateFolder(Outlook.Folder parent, string name)
{
foreach (Outlook.Folder folder in parent.Folders)
{
if (folder.Name == name)
return folder;
}
if (folder.Name == name) return folder;
return (Outlook.Folder)parent.Folders.Add(name);
}
private Outlook.Folder? GetFolder(Outlook.Folder parent, string name)
{
foreach (Outlook.Folder folder in parent.Folders)
if (folder.Name == name) return folder;
return null;
}
private Outlook.Folder? GetFolderStartingWith(Outlook.Folder parent, string trackingId)
{
if (folder.Name == name)
foreach (Outlook.Folder folder in parent.Folders)
{
string name = folder.Name;
if (name == trackingId ||
name.StartsWith(trackingId + " ") ||
name.StartsWith(trackingId + "|"))
return folder;
}
return null;
}
// Expõe o email selecionado para ser usado no reopen
public Outlook.MailItem? GetSelectedEmail()
{
try
{
Outlook.Explorer activeExplorer = outlookApp.ActiveExplorer();
if (activeExplorer == null) return null;
Outlook.Selection selection = activeExplorer.Selection;
if (selection.Count == 0) return null;
return selection[1] as Outlook.MailItem;
}
catch { return null; }
}
}
}
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>