add ProxyJump function
This commit is contained in:
1 parent
56e20e5464
commit
7f0e0fdb23
48 files changed
+3875
-44
No files matched your search
@@ -0,0 +1,283 @@
|
||||
namespace BulkArchiveMailer.AddIn.UI
|
||||
{
|
||||
partial class PreviewForm
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && components != null)
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private System.Windows.Forms.Label lblPasswordReminder;
|
||||
private System.Windows.Forms.Label lblSignatureWarning;
|
||||
private System.Windows.Forms.DataGridView grid;
|
||||
private System.Windows.Forms.DataGridViewCheckBoxColumn colSelect;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colLogin;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colEmail;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colKind;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colFile;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colStatus;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn colComment;
|
||||
|
||||
private System.Windows.Forms.Label lblSummary;
|
||||
private System.Windows.Forms.CheckBox chkTestMode;
|
||||
private System.Windows.Forms.Button btnCheckAllReady;
|
||||
private System.Windows.Forms.Button btnUncheckAll;
|
||||
private System.Windows.Forms.Button btnRescan;
|
||||
|
||||
private System.Windows.Forms.ProgressBar progressSend;
|
||||
private System.Windows.Forms.Label lblProgressStatus;
|
||||
|
||||
private System.Windows.Forms.Button btnSend;
|
||||
private System.Windows.Forms.Button btnClose;
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblPasswordReminder = new System.Windows.Forms.Label();
|
||||
this.lblSignatureWarning = new System.Windows.Forms.Label();
|
||||
|
||||
this.grid = new System.Windows.Forms.DataGridView();
|
||||
this.colSelect = new System.Windows.Forms.DataGridViewCheckBoxColumn();
|
||||
this.colLogin = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.colEmail = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.colKind = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.colFile = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.colStatus = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.colComment = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
|
||||
this.lblSummary = new System.Windows.Forms.Label();
|
||||
this.chkTestMode = new System.Windows.Forms.CheckBox();
|
||||
this.btnCheckAllReady = new System.Windows.Forms.Button();
|
||||
this.btnUncheckAll = new System.Windows.Forms.Button();
|
||||
this.btnRescan = new System.Windows.Forms.Button();
|
||||
|
||||
this.progressSend = new System.Windows.Forms.ProgressBar();
|
||||
this.lblProgressStatus = new System.Windows.Forms.Label();
|
||||
|
||||
this.btnSend = new System.Windows.Forms.Button();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
|
||||
((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
|
||||
//
|
||||
// lblPasswordReminder
|
||||
//
|
||||
this.lblPasswordReminder.Location = new System.Drawing.Point(12, 10);
|
||||
this.lblPasswordReminder.Size = new System.Drawing.Size(900, 20);
|
||||
this.lblPasswordReminder.Text = "Напоминание: пароль от архивов сообщается получателям отдельно (вручную, не через это письмо и не через этот инструмент).";
|
||||
this.lblPasswordReminder.ForeColor = System.Drawing.Color.DarkRed;
|
||||
this.lblPasswordReminder.Font = new System.Drawing.Font(this.lblPasswordReminder.Font, System.Drawing.FontStyle.Bold);
|
||||
this.lblPasswordReminder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// lblSignatureWarning
|
||||
//
|
||||
this.lblSignatureWarning.Location = new System.Drawing.Point(12, 32);
|
||||
this.lblSignatureWarning.Size = new System.Drawing.Size(900, 20);
|
||||
this.lblSignatureWarning.Text = "Подпись не найдена — письма будут отправлены без подписи. Настройте .htm-файл вручную в Настройках.";
|
||||
this.lblSignatureWarning.ForeColor = System.Drawing.Color.DarkOrange;
|
||||
this.lblSignatureWarning.Visible = false;
|
||||
this.lblSignatureWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// grid
|
||||
//
|
||||
this.grid.Location = new System.Drawing.Point(12, 58);
|
||||
this.grid.Size = new System.Drawing.Size(900, 420);
|
||||
this.grid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.grid.AllowUserToAddRows = false;
|
||||
this.grid.AllowUserToDeleteRows = false;
|
||||
this.grid.AllowUserToResizeRows = false;
|
||||
this.grid.RowHeadersVisible = false;
|
||||
this.grid.AutoGenerateColumns = false;
|
||||
this.grid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
|
||||
this.grid.MultiSelect = false;
|
||||
this.grid.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
|
||||
this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||
this.colSelect,
|
||||
this.colLogin,
|
||||
this.colEmail,
|
||||
this.colKind,
|
||||
this.colFile,
|
||||
this.colStatus,
|
||||
this.colComment});
|
||||
this.grid.CurrentCellDirtyStateChanged += new System.EventHandler(this.grid_CurrentCellDirtyStateChanged);
|
||||
this.grid.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.grid_CellValueChanged);
|
||||
this.grid.DataBindingComplete += new System.Windows.Forms.DataGridViewBindingCompleteEventHandler(this.grid_DataBindingComplete);
|
||||
|
||||
// colSelect
|
||||
this.colSelect.Name = "colSelect";
|
||||
this.colSelect.HeaderText = "Отправить";
|
||||
this.colSelect.DataPropertyName = "IsChecked";
|
||||
this.colSelect.Width = 70;
|
||||
this.colSelect.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
// colLogin
|
||||
this.colLogin.Name = "colLogin";
|
||||
this.colLogin.HeaderText = "Логин";
|
||||
this.colLogin.DataPropertyName = "Login";
|
||||
this.colLogin.ReadOnly = true;
|
||||
this.colLogin.Width = 100;
|
||||
this.colLogin.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
// colEmail
|
||||
this.colEmail.Name = "colEmail";
|
||||
this.colEmail.HeaderText = "Получатель";
|
||||
this.colEmail.DataPropertyName = "Email";
|
||||
this.colEmail.ReadOnly = true;
|
||||
this.colEmail.Width = 190;
|
||||
this.colEmail.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
// colKind
|
||||
this.colKind.Name = "colKind";
|
||||
this.colKind.HeaderText = "Вид архива";
|
||||
this.colKind.DataPropertyName = "KindDisplay";
|
||||
this.colKind.ReadOnly = true;
|
||||
this.colKind.Width = 170;
|
||||
this.colKind.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
// colFile
|
||||
this.colFile.Name = "colFile";
|
||||
this.colFile.HeaderText = "Файл";
|
||||
this.colFile.DataPropertyName = "FileName";
|
||||
this.colFile.ReadOnly = true;
|
||||
this.colFile.Width = 190;
|
||||
this.colFile.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
// colStatus
|
||||
this.colStatus.Name = "colStatus";
|
||||
this.colStatus.HeaderText = "Статус";
|
||||
this.colStatus.DataPropertyName = "StatusDisplay";
|
||||
this.colStatus.ReadOnly = true;
|
||||
this.colStatus.Width = 120;
|
||||
this.colStatus.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
// colComment
|
||||
this.colComment.Name = "colComment";
|
||||
this.colComment.HeaderText = "Комментарий";
|
||||
this.colComment.DataPropertyName = "Comment";
|
||||
this.colComment.ReadOnly = true;
|
||||
this.colComment.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.colComment.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
|
||||
//
|
||||
// lblSummary
|
||||
//
|
||||
this.lblSummary.Location = new System.Drawing.Point(12, 486);
|
||||
this.lblSummary.Size = new System.Drawing.Size(500, 20);
|
||||
this.lblSummary.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// chkTestMode
|
||||
//
|
||||
this.chkTestMode.Location = new System.Drawing.Point(12, 510);
|
||||
this.chkTestMode.Size = new System.Drawing.Size(360, 24);
|
||||
this.chkTestMode.Text = "Тестовый режим (сохранять в Черновики, не отправлять)";
|
||||
this.chkTestMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.chkTestMode.UseVisualStyleBackColor = true;
|
||||
|
||||
//
|
||||
// btnRescan
|
||||
//
|
||||
this.btnRescan.Location = new System.Drawing.Point(12, 540);
|
||||
this.btnRescan.Size = new System.Drawing.Size(110, 27);
|
||||
this.btnRescan.Text = "Пересканировать";
|
||||
this.btnRescan.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnRescan.UseVisualStyleBackColor = true;
|
||||
this.btnRescan.Click += new System.EventHandler(this.btnRescan_Click);
|
||||
|
||||
//
|
||||
// btnCheckAllReady
|
||||
//
|
||||
this.btnCheckAllReady.Location = new System.Drawing.Point(130, 540);
|
||||
this.btnCheckAllReady.Size = new System.Drawing.Size(150, 27);
|
||||
this.btnCheckAllReady.Text = "Отметить все готовые";
|
||||
this.btnCheckAllReady.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnCheckAllReady.UseVisualStyleBackColor = true;
|
||||
this.btnCheckAllReady.Click += new System.EventHandler(this.btnCheckAllReady_Click);
|
||||
|
||||
//
|
||||
// btnUncheckAll
|
||||
//
|
||||
this.btnUncheckAll.Location = new System.Drawing.Point(288, 540);
|
||||
this.btnUncheckAll.Size = new System.Drawing.Size(120, 27);
|
||||
this.btnUncheckAll.Text = "Снять все отметки";
|
||||
this.btnUncheckAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnUncheckAll.UseVisualStyleBackColor = true;
|
||||
this.btnUncheckAll.Click += new System.EventHandler(this.btnUncheckAll_Click);
|
||||
|
||||
//
|
||||
// progressSend
|
||||
//
|
||||
this.progressSend.Location = new System.Drawing.Point(430, 543);
|
||||
this.progressSend.Size = new System.Drawing.Size(300, 20);
|
||||
this.progressSend.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// lblProgressStatus
|
||||
//
|
||||
this.lblProgressStatus.Location = new System.Drawing.Point(430, 565);
|
||||
this.lblProgressStatus.Size = new System.Drawing.Size(482, 20);
|
||||
this.lblProgressStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Location = new System.Drawing.Point(720, 540);
|
||||
this.btnClose.Size = new System.Drawing.Size(90, 30);
|
||||
this.btnClose.Text = "Закрыть";
|
||||
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnClose.UseVisualStyleBackColor = true;
|
||||
this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
|
||||
//
|
||||
// btnSend
|
||||
//
|
||||
this.btnSend.Location = new System.Drawing.Point(816, 540);
|
||||
this.btnSend.Size = new System.Drawing.Size(96, 30);
|
||||
this.btnSend.Text = "Отправить";
|
||||
this.btnSend.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnSend.Font = new System.Drawing.Font(this.btnSend.Font, System.Drawing.FontStyle.Bold);
|
||||
this.btnSend.UseVisualStyleBackColor = true;
|
||||
this.btnSend.Click += new System.EventHandler(this.btnSend_Click);
|
||||
|
||||
//
|
||||
// PreviewForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(924, 582);
|
||||
this.MinimumSize = new System.Drawing.Size(760, 420);
|
||||
this.Controls.Add(this.lblPasswordReminder);
|
||||
this.Controls.Add(this.lblSignatureWarning);
|
||||
this.Controls.Add(this.grid);
|
||||
this.Controls.Add(this.lblSummary);
|
||||
this.Controls.Add(this.chkTestMode);
|
||||
this.Controls.Add(this.btnRescan);
|
||||
this.Controls.Add(this.btnCheckAllReady);
|
||||
this.Controls.Add(this.btnUncheckAll);
|
||||
this.Controls.Add(this.progressSend);
|
||||
this.Controls.Add(this.lblProgressStatus);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Controls.Add(this.btnSend);
|
||||
this.Text = "BulkArchiveMailer — предпросмотр и рассылка";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
|
||||
this.MaximizeBox = true;
|
||||
this.CancelButton = this.btnClose;
|
||||
|
||||
((System.ComponentModel.ISupportInitialize)(this.grid)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using BulkArchiveMailer.Core;
|
||||
using Outlook = Microsoft.Office.Interop.Outlook;
|
||||
|
||||
namespace BulkArchiveMailer.AddIn.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Scans the configured archive folder, shows one row per login (plus
|
||||
/// unrecognized files) in a grid, and sends the checked rows through
|
||||
/// MailSender. This is the only form that actually touches Outlook
|
||||
/// COM (via the Outlook.Application passed into its constructor) or
|
||||
/// writes to the send log.
|
||||
/// </summary>
|
||||
public partial class PreviewForm : Form
|
||||
{
|
||||
private readonly Outlook.Application outlookApplication;
|
||||
|
||||
private AppSettingsData settings;
|
||||
private SendLogStore sendLog;
|
||||
private string signatureHtml;
|
||||
private BindingList<PreviewRow> rows;
|
||||
|
||||
public PreviewForm(Outlook.Application outlookApplication)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.outlookApplication = outlookApplication ?? throw new ArgumentNullException(nameof(outlookApplication));
|
||||
Load += PreviewForm_Load;
|
||||
}
|
||||
|
||||
private void PreviewForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
settings = AppSettings.Load();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, "Не удалось прочитать настройки: " + ex.Message, "BulkArchiveMailer", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
chkTestMode.Checked = settings.TestModeDefault;
|
||||
|
||||
signatureHtml = ResolveSignatureHtml();
|
||||
lblSignatureWarning.Visible = string.IsNullOrEmpty(signatureHtml);
|
||||
|
||||
RunScan();
|
||||
}
|
||||
|
||||
private string ResolveSignatureHtml()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(settings.SignatureHtmlPath))
|
||||
{
|
||||
return File.Exists(settings.SignatureHtmlPath) ? File.ReadAllText(settings.SignatureHtmlPath) : null;
|
||||
}
|
||||
|
||||
return SignatureReader.TryAutoDetectSignatureHtml(outlookApplication);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null; // never block preview over a signature problem
|
||||
}
|
||||
}
|
||||
|
||||
private void RunScan()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(settings.ArchiveFolder) || !Directory.Exists(settings.ArchiveFolder))
|
||||
{
|
||||
MessageBox.Show(
|
||||
this,
|
||||
"Папка с архивами не настроена или не существует:\n" + settings.ArchiveFolder,
|
||||
"BulkArchiveMailer",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
var logPath = Path.Combine(settings.ArchiveFolder, "send-log.csv");
|
||||
sendLog = new SendLogStore(logPath);
|
||||
|
||||
var templates = settings.ToTemplateSet();
|
||||
var scan = ArchiveScanner.Scan(settings.ArchiveFolder, settings.EmailDomain, templates, sendLog);
|
||||
|
||||
rows = new BindingList<PreviewRow>(scan.AllRows.Select(job => new PreviewRow(job)).ToList());
|
||||
grid.DataSource = rows;
|
||||
|
||||
UpdateSummaryLabel();
|
||||
}
|
||||
|
||||
private void ApplyRowStyles()
|
||||
{
|
||||
foreach (DataGridViewRow row in grid.Rows)
|
||||
{
|
||||
if (!(row.DataBoundItem is PreviewRow previewRow))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
row.Cells[colSelect.Index].ReadOnly = !previewRow.Job.IsSelectable;
|
||||
|
||||
switch (previewRow.Job.Status)
|
||||
{
|
||||
case SendStatus.Error:
|
||||
row.DefaultCellStyle.BackColor = System.Drawing.Color.MistyRose;
|
||||
break;
|
||||
case SendStatus.Skipped:
|
||||
row.DefaultCellStyle.BackColor = System.Drawing.Color.WhiteSmoke;
|
||||
row.DefaultCellStyle.ForeColor = System.Drawing.Color.Gray;
|
||||
break;
|
||||
case SendStatus.AlreadySent:
|
||||
row.DefaultCellStyle.BackColor = System.Drawing.Color.LightYellow;
|
||||
break;
|
||||
default:
|
||||
row.DefaultCellStyle.BackColor = grid.DefaultCellStyle.BackColor;
|
||||
row.DefaultCellStyle.ForeColor = grid.DefaultCellStyle.ForeColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSummaryLabel()
|
||||
{
|
||||
var ready = rows.Count(r => r.Job.Status == SendStatus.Ready);
|
||||
var already = rows.Count(r => r.Job.Status == SendStatus.AlreadySent);
|
||||
var errors = rows.Count(r => r.Job.Status == SendStatus.Error);
|
||||
var skipped = rows.Count(r => r.Job.Status == SendStatus.Skipped);
|
||||
var checkedCount = rows.Count(r => r.IsChecked);
|
||||
|
||||
lblSummary.Text = $"Готово: {ready} Уже отправлено: {already} Ошибки: {errors} Пропущено: {skipped} | Отмечено к отправке: {checkedCount}";
|
||||
}
|
||||
|
||||
private void grid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
|
||||
{
|
||||
ApplyRowStyles();
|
||||
UpdateSummaryLabel();
|
||||
}
|
||||
|
||||
private void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (grid.IsCurrentCellDirty && grid.CurrentCell is DataGridViewCheckBoxCell)
|
||||
{
|
||||
grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
}
|
||||
}
|
||||
|
||||
private void grid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
UpdateSummaryLabel();
|
||||
}
|
||||
|
||||
private void btnRescan_Click(object sender, EventArgs e)
|
||||
{
|
||||
RunScan();
|
||||
}
|
||||
|
||||
private void btnCheckAllReady_Click(object sender, EventArgs e)
|
||||
{
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (row.Job.IsSelectable)
|
||||
{
|
||||
row.IsChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
grid.Refresh();
|
||||
UpdateSummaryLabel();
|
||||
}
|
||||
|
||||
private void btnUncheckAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
foreach (var row in rows)
|
||||
{
|
||||
row.IsChecked = false;
|
||||
}
|
||||
|
||||
grid.Refresh();
|
||||
UpdateSummaryLabel();
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnSend_Click(object sender, EventArgs e)
|
||||
{
|
||||
var toSend = rows.Where(r => r.IsChecked && r.Job.IsSelectable).ToList();
|
||||
if (toSend.Count == 0)
|
||||
{
|
||||
MessageBox.Show(this, "Не выбрано ни одной строки для отправки.", "BulkArchiveMailer", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var testMode = chkTestMode.Checked;
|
||||
var confirmText = testMode
|
||||
? $"Сохранить {toSend.Count} писем в Черновики (тестовый режим, реальная отправка НЕ произойдёт)?"
|
||||
: $"Отправить {toSend.Count} писем? Это реальная отправка получателям.";
|
||||
if (MessageBox.Show(this, confirmText, "Подтверждение", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
btnSend.Enabled = false;
|
||||
btnRescan.Enabled = false;
|
||||
progressSend.Minimum = 0;
|
||||
progressSend.Maximum = toSend.Count;
|
||||
progressSend.Value = 0;
|
||||
|
||||
var mailSender = new MailSender(outlookApplication, signatureHtml);
|
||||
var sentCount = 0;
|
||||
var failedCount = 0;
|
||||
|
||||
foreach (var row in toSend)
|
||||
{
|
||||
lblProgressStatus.Text = $"Обработка: {row.Job.Email} ({row.Job.KindDisplay})...";
|
||||
// No async/await here on purpose: Outlook COM objects are
|
||||
// STA-thread-affine, and MailSender.SendOne must run on
|
||||
// this UI thread. DoEvents keeps the form responsive
|
||||
// (progress bar, ability to see the current item) during
|
||||
// what is otherwise a synchronous per-item loop.
|
||||
Application.DoEvents();
|
||||
|
||||
var outcome = mailSender.SendOne(row.Job, testMode);
|
||||
|
||||
try
|
||||
{
|
||||
sendLog.Append(new SendLogEntry
|
||||
{
|
||||
TimestampUtc = DateTime.UtcNow,
|
||||
Login = row.Job.Login,
|
||||
Email = row.Job.Email,
|
||||
Kind = row.Job.Kind ?? default,
|
||||
ArchivePath = row.Job.ArchivePath ?? "",
|
||||
FileSha256 = row.Job.FileSha256 ?? "",
|
||||
Status = outcome.Success ? (testMode ? SendLogStatus.Draft : SendLogStatus.Sent) : SendLogStatus.Failed,
|
||||
Error = outcome.Success ? "" : outcome.ErrorMessage,
|
||||
});
|
||||
}
|
||||
catch (Exception logEx)
|
||||
{
|
||||
// A logging failure must not be confused with a send
|
||||
// failure, but it also must not silently disappear -
|
||||
// surface it in the comment column.
|
||||
row.ResultText = (outcome.Success ? "Отправлено, но" : "Ошибка отправки, и") + " не удалось записать в журнал: " + logEx.Message;
|
||||
row.IsChecked = false;
|
||||
grid.Refresh();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (outcome.Success)
|
||||
{
|
||||
sentCount++;
|
||||
row.ResultText = testMode ? "Сохранено в Черновики" : "Отправлено";
|
||||
}
|
||||
else
|
||||
{
|
||||
failedCount++;
|
||||
row.ResultText = "Ошибка: " + outcome.ErrorMessage;
|
||||
}
|
||||
|
||||
row.IsChecked = false;
|
||||
progressSend.Value++;
|
||||
grid.Refresh();
|
||||
}
|
||||
|
||||
btnSend.Enabled = true;
|
||||
btnRescan.Enabled = true;
|
||||
lblProgressStatus.Text = "Готово.";
|
||||
UpdateSummaryLabel();
|
||||
|
||||
MessageBox.Show(
|
||||
this,
|
||||
$"Отправлено/сохранено: {sentCount}\nОшибок: {failedCount}\n\nЖурнал: {sendLog.CsvPath}",
|
||||
"Массовая рассылка — итог",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI-only wrapper around a Core SendJob: adds the checkbox state
|
||||
/// and post-send result text, neither of which belong in the
|
||||
/// portable Core model.
|
||||
/// </summary>
|
||||
private sealed class PreviewRow
|
||||
{
|
||||
public PreviewRow(SendJob job)
|
||||
{
|
||||
Job = job;
|
||||
// AlreadySent starts unchecked by default (per spec) but
|
||||
// remains selectable if the operator wants to resend it
|
||||
// anyway (e.g. recipient claims they never got it).
|
||||
IsChecked = job.Status == SendStatus.Ready;
|
||||
}
|
||||
|
||||
public SendJob Job { get; }
|
||||
|
||||
public bool IsChecked { get; set; }
|
||||
|
||||
public string ResultText { get; set; } = "";
|
||||
|
||||
public string Login => Job.Login;
|
||||
|
||||
public string Email => Job.Email;
|
||||
|
||||
public string KindDisplay => Job.KindDisplay;
|
||||
|
||||
public string FileName => Job.FileName;
|
||||
|
||||
public string StatusDisplay => ToRussian(Job.Status);
|
||||
|
||||
public string Comment => !string.IsNullOrEmpty(ResultText) ? ResultText : (Job.ErrorMessage ?? "");
|
||||
|
||||
private static string ToRussian(SendStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case SendStatus.Ready:
|
||||
return "Готово";
|
||||
case SendStatus.AlreadySent:
|
||||
return "Уже отправлено";
|
||||
case SendStatus.Error:
|
||||
return "Ошибка";
|
||||
case SendStatus.Skipped:
|
||||
return "Пропущено";
|
||||
default:
|
||||
return status.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
namespace BulkArchiveMailer.AddIn.UI
|
||||
{
|
||||
partial class SettingsForm
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && components != null)
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private System.Windows.Forms.TabControl tabControl;
|
||||
private System.Windows.Forms.TabPage tabGeneral;
|
||||
private System.Windows.Forms.TabPage tabSshAccess;
|
||||
private System.Windows.Forms.TabPage tabConfigUpdate;
|
||||
|
||||
private System.Windows.Forms.Label lblArchiveFolder;
|
||||
private System.Windows.Forms.TextBox txtArchiveFolder;
|
||||
private System.Windows.Forms.Button btnBrowseFolder;
|
||||
|
||||
private System.Windows.Forms.Label lblDomain;
|
||||
private System.Windows.Forms.TextBox txtDomain;
|
||||
|
||||
private System.Windows.Forms.Label lblSignaturePath;
|
||||
private System.Windows.Forms.TextBox txtSignaturePath;
|
||||
private System.Windows.Forms.Button btnBrowseSignature;
|
||||
private System.Windows.Forms.Button btnAutoDetectSignature;
|
||||
|
||||
private System.Windows.Forms.CheckBox chkTestModeDefault;
|
||||
|
||||
private System.Windows.Forms.Label lblSshSubject;
|
||||
private System.Windows.Forms.TextBox txtSshSubject;
|
||||
private System.Windows.Forms.Label lblSshBody;
|
||||
private System.Windows.Forms.TextBox txtSshBody;
|
||||
private System.Windows.Forms.Label lblSshHint;
|
||||
|
||||
private System.Windows.Forms.Label lblCfgSubject;
|
||||
private System.Windows.Forms.TextBox txtCfgSubject;
|
||||
private System.Windows.Forms.Label lblCfgBody;
|
||||
private System.Windows.Forms.TextBox txtCfgBody;
|
||||
private System.Windows.Forms.Label lblCfgHint;
|
||||
|
||||
private System.Windows.Forms.Button btnSave;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.Button btnResetDefaults;
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tabControl = new System.Windows.Forms.TabControl();
|
||||
this.tabGeneral = new System.Windows.Forms.TabPage();
|
||||
this.tabSshAccess = new System.Windows.Forms.TabPage();
|
||||
this.tabConfigUpdate = new System.Windows.Forms.TabPage();
|
||||
|
||||
this.lblArchiveFolder = new System.Windows.Forms.Label();
|
||||
this.txtArchiveFolder = new System.Windows.Forms.TextBox();
|
||||
this.btnBrowseFolder = new System.Windows.Forms.Button();
|
||||
|
||||
this.lblDomain = new System.Windows.Forms.Label();
|
||||
this.txtDomain = new System.Windows.Forms.TextBox();
|
||||
|
||||
this.lblSignaturePath = new System.Windows.Forms.Label();
|
||||
this.txtSignaturePath = new System.Windows.Forms.TextBox();
|
||||
this.btnBrowseSignature = new System.Windows.Forms.Button();
|
||||
this.btnAutoDetectSignature = new System.Windows.Forms.Button();
|
||||
|
||||
this.chkTestModeDefault = new System.Windows.Forms.CheckBox();
|
||||
|
||||
this.lblSshSubject = new System.Windows.Forms.Label();
|
||||
this.txtSshSubject = new System.Windows.Forms.TextBox();
|
||||
this.lblSshBody = new System.Windows.Forms.Label();
|
||||
this.txtSshBody = new System.Windows.Forms.TextBox();
|
||||
this.lblSshHint = new System.Windows.Forms.Label();
|
||||
|
||||
this.lblCfgSubject = new System.Windows.Forms.Label();
|
||||
this.txtCfgSubject = new System.Windows.Forms.TextBox();
|
||||
this.lblCfgBody = new System.Windows.Forms.Label();
|
||||
this.txtCfgBody = new System.Windows.Forms.TextBox();
|
||||
this.lblCfgHint = new System.Windows.Forms.Label();
|
||||
|
||||
this.btnSave = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnResetDefaults = new System.Windows.Forms.Button();
|
||||
|
||||
this.tabControl.SuspendLayout();
|
||||
this.tabGeneral.SuspendLayout();
|
||||
this.tabSshAccess.SuspendLayout();
|
||||
this.tabConfigUpdate.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
|
||||
//
|
||||
// tabControl
|
||||
//
|
||||
this.tabControl.Location = new System.Drawing.Point(12, 12);
|
||||
this.tabControl.Size = new System.Drawing.Size(660, 470);
|
||||
this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.tabControl.TabPages.AddRange(new System.Windows.Forms.TabPage[] {
|
||||
this.tabGeneral,
|
||||
this.tabSshAccess,
|
||||
this.tabConfigUpdate});
|
||||
|
||||
//
|
||||
// tabGeneral
|
||||
//
|
||||
this.tabGeneral.Text = "Общие";
|
||||
this.tabGeneral.Padding = new System.Windows.Forms.Padding(12);
|
||||
this.tabGeneral.UseVisualStyleBackColor = true;
|
||||
this.tabGeneral.Controls.Add(this.lblArchiveFolder);
|
||||
this.tabGeneral.Controls.Add(this.txtArchiveFolder);
|
||||
this.tabGeneral.Controls.Add(this.btnBrowseFolder);
|
||||
this.tabGeneral.Controls.Add(this.lblDomain);
|
||||
this.tabGeneral.Controls.Add(this.txtDomain);
|
||||
this.tabGeneral.Controls.Add(this.lblSignaturePath);
|
||||
this.tabGeneral.Controls.Add(this.txtSignaturePath);
|
||||
this.tabGeneral.Controls.Add(this.btnBrowseSignature);
|
||||
this.tabGeneral.Controls.Add(this.btnAutoDetectSignature);
|
||||
this.tabGeneral.Controls.Add(this.chkTestModeDefault);
|
||||
|
||||
// lblArchiveFolder
|
||||
this.lblArchiveFolder.Location = new System.Drawing.Point(15, 20);
|
||||
this.lblArchiveFolder.Size = new System.Drawing.Size(400, 20);
|
||||
this.lblArchiveFolder.Text = "Папка с архивами ssh-access-tui (общий выходной каталог):";
|
||||
|
||||
// txtArchiveFolder
|
||||
this.txtArchiveFolder.Location = new System.Drawing.Point(15, 43);
|
||||
this.txtArchiveFolder.Size = new System.Drawing.Size(500, 23);
|
||||
|
||||
// btnBrowseFolder
|
||||
this.btnBrowseFolder.Location = new System.Drawing.Point(525, 41);
|
||||
this.btnBrowseFolder.Size = new System.Drawing.Size(100, 25);
|
||||
this.btnBrowseFolder.Text = "Обзор...";
|
||||
this.btnBrowseFolder.UseVisualStyleBackColor = true;
|
||||
this.btnBrowseFolder.Click += new System.EventHandler(this.btnBrowseFolder_Click);
|
||||
|
||||
// lblDomain
|
||||
this.lblDomain.Location = new System.Drawing.Point(15, 85);
|
||||
this.lblDomain.Size = new System.Drawing.Size(400, 20);
|
||||
this.lblDomain.Text = "Домен электронной почты (login → login@домен):";
|
||||
|
||||
// txtDomain
|
||||
this.txtDomain.Location = new System.Drawing.Point(15, 108);
|
||||
this.txtDomain.Size = new System.Drawing.Size(300, 23);
|
||||
|
||||
// lblSignaturePath
|
||||
this.lblSignaturePath.Location = new System.Drawing.Point(15, 150);
|
||||
this.lblSignaturePath.Size = new System.Drawing.Size(610, 20);
|
||||
this.lblSignaturePath.Text = "Файл подписи (.htm). Пусто = определять автоматически по умолчанию Outlook при каждой отправке:";
|
||||
|
||||
// txtSignaturePath
|
||||
this.txtSignaturePath.Location = new System.Drawing.Point(15, 173);
|
||||
this.txtSignaturePath.Size = new System.Drawing.Size(500, 23);
|
||||
|
||||
// btnBrowseSignature
|
||||
this.btnBrowseSignature.Location = new System.Drawing.Point(525, 171);
|
||||
this.btnBrowseSignature.Size = new System.Drawing.Size(100, 25);
|
||||
this.btnBrowseSignature.Text = "Обзор...";
|
||||
this.btnBrowseSignature.UseVisualStyleBackColor = true;
|
||||
this.btnBrowseSignature.Click += new System.EventHandler(this.btnBrowseSignature_Click);
|
||||
|
||||
// btnAutoDetectSignature
|
||||
this.btnAutoDetectSignature.Location = new System.Drawing.Point(15, 205);
|
||||
this.btnAutoDetectSignature.Size = new System.Drawing.Size(280, 25);
|
||||
this.btnAutoDetectSignature.Text = "Определить автоматически сейчас";
|
||||
this.btnAutoDetectSignature.UseVisualStyleBackColor = true;
|
||||
this.btnAutoDetectSignature.Click += new System.EventHandler(this.btnAutoDetectSignature_Click);
|
||||
|
||||
// chkTestModeDefault
|
||||
this.chkTestModeDefault.Location = new System.Drawing.Point(15, 250);
|
||||
this.chkTestModeDefault.Size = new System.Drawing.Size(610, 24);
|
||||
this.chkTestModeDefault.Text = "По умолчанию открывать предпросмотр в тестовом режиме (сохранять в Черновики)";
|
||||
this.chkTestModeDefault.UseVisualStyleBackColor = true;
|
||||
|
||||
//
|
||||
// tabSshAccess
|
||||
//
|
||||
this.tabSshAccess.Text = "Шаблон: новый доступ";
|
||||
this.tabSshAccess.Padding = new System.Windows.Forms.Padding(12);
|
||||
this.tabSshAccess.UseVisualStyleBackColor = true;
|
||||
this.tabSshAccess.Controls.Add(this.lblSshSubject);
|
||||
this.tabSshAccess.Controls.Add(this.txtSshSubject);
|
||||
this.tabSshAccess.Controls.Add(this.lblSshBody);
|
||||
this.tabSshAccess.Controls.Add(this.txtSshBody);
|
||||
this.tabSshAccess.Controls.Add(this.lblSshHint);
|
||||
|
||||
// lblSshSubject
|
||||
this.lblSshSubject.Location = new System.Drawing.Point(15, 20);
|
||||
this.lblSshSubject.Size = new System.Drawing.Size(200, 20);
|
||||
this.lblSshSubject.Text = "Тема письма:";
|
||||
|
||||
// txtSshSubject
|
||||
this.txtSshSubject.Location = new System.Drawing.Point(15, 43);
|
||||
this.txtSshSubject.Size = new System.Drawing.Size(610, 23);
|
||||
|
||||
// lblSshBody
|
||||
this.lblSshBody.Location = new System.Drawing.Point(15, 78);
|
||||
this.lblSshBody.Size = new System.Drawing.Size(200, 20);
|
||||
this.lblSshBody.Text = "Тело письма (HTML):";
|
||||
|
||||
// txtSshBody
|
||||
this.txtSshBody.Location = new System.Drawing.Point(15, 101);
|
||||
this.txtSshBody.Size = new System.Drawing.Size(610, 300);
|
||||
this.txtSshBody.Multiline = true;
|
||||
this.txtSshBody.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
|
||||
this.txtSshBody.AcceptsReturn = true;
|
||||
this.txtSshBody.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
|
||||
// lblSshHint
|
||||
this.lblSshHint.Location = new System.Drawing.Point(15, 408);
|
||||
this.lblSshHint.Size = new System.Drawing.Size(610, 20);
|
||||
this.lblSshHint.Text = "Доступные плейсхолдеры: {Login}, {Email}, {Date}";
|
||||
this.lblSshHint.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
this.lblSshHint.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// tabConfigUpdate
|
||||
//
|
||||
this.tabConfigUpdate.Text = "Шаблон: обновление конфига";
|
||||
this.tabConfigUpdate.Padding = new System.Windows.Forms.Padding(12);
|
||||
this.tabConfigUpdate.UseVisualStyleBackColor = true;
|
||||
this.tabConfigUpdate.Controls.Add(this.lblCfgSubject);
|
||||
this.tabConfigUpdate.Controls.Add(this.txtCfgSubject);
|
||||
this.tabConfigUpdate.Controls.Add(this.lblCfgBody);
|
||||
this.tabConfigUpdate.Controls.Add(this.txtCfgBody);
|
||||
this.tabConfigUpdate.Controls.Add(this.lblCfgHint);
|
||||
|
||||
// lblCfgSubject
|
||||
this.lblCfgSubject.Location = new System.Drawing.Point(15, 20);
|
||||
this.lblCfgSubject.Size = new System.Drawing.Size(200, 20);
|
||||
this.lblCfgSubject.Text = "Тема письма:";
|
||||
|
||||
// txtCfgSubject
|
||||
this.txtCfgSubject.Location = new System.Drawing.Point(15, 43);
|
||||
this.txtCfgSubject.Size = new System.Drawing.Size(610, 23);
|
||||
|
||||
// lblCfgBody
|
||||
this.lblCfgBody.Location = new System.Drawing.Point(15, 78);
|
||||
this.lblCfgBody.Size = new System.Drawing.Size(200, 20);
|
||||
this.lblCfgBody.Text = "Тело письма (HTML):";
|
||||
|
||||
// txtCfgBody
|
||||
this.txtCfgBody.Location = new System.Drawing.Point(15, 101);
|
||||
this.txtCfgBody.Size = new System.Drawing.Size(610, 300);
|
||||
this.txtCfgBody.Multiline = true;
|
||||
this.txtCfgBody.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
|
||||
this.txtCfgBody.AcceptsReturn = true;
|
||||
this.txtCfgBody.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
|
||||
// lblCfgHint
|
||||
this.lblCfgHint.Location = new System.Drawing.Point(15, 408);
|
||||
this.lblCfgHint.Size = new System.Drawing.Size(610, 20);
|
||||
this.lblCfgHint.Text = "Доступные плейсхолдеры: {Login}, {Email}, {Date}";
|
||||
this.lblCfgHint.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
this.lblCfgHint.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
|
||||
//
|
||||
// btnSave / btnCancel / btnResetDefaults
|
||||
//
|
||||
this.btnResetDefaults.Location = new System.Drawing.Point(12, 494);
|
||||
this.btnResetDefaults.Size = new System.Drawing.Size(220, 30);
|
||||
this.btnResetDefaults.Text = "Сбросить домен/шаблоны по умолчанию";
|
||||
this.btnResetDefaults.UseVisualStyleBackColor = true;
|
||||
this.btnResetDefaults.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnResetDefaults.Click += new System.EventHandler(this.btnResetDefaults_Click);
|
||||
|
||||
this.btnCancel.Location = new System.Drawing.Point(479, 494);
|
||||
this.btnCancel.Size = new System.Drawing.Size(90, 30);
|
||||
this.btnCancel.Text = "Отмена";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
|
||||
this.btnSave.Location = new System.Drawing.Point(575, 494);
|
||||
this.btnSave.Size = new System.Drawing.Size(97, 30);
|
||||
this.btnSave.Text = "Сохранить";
|
||||
this.btnSave.UseVisualStyleBackColor = true;
|
||||
this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
|
||||
|
||||
//
|
||||
// SettingsForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(684, 536);
|
||||
this.MinimumSize = new System.Drawing.Size(560, 420);
|
||||
this.Controls.Add(this.tabControl);
|
||||
this.Controls.Add(this.btnResetDefaults);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.btnSave);
|
||||
this.Text = "BulkArchiveMailer — настройки";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
|
||||
this.MaximizeBox = true;
|
||||
this.AcceptButton = null;
|
||||
this.CancelButton = this.btnCancel;
|
||||
|
||||
this.tabControl.ResumeLayout(false);
|
||||
this.tabGeneral.ResumeLayout(false);
|
||||
this.tabGeneral.PerformLayout();
|
||||
this.tabSshAccess.ResumeLayout(false);
|
||||
this.tabSshAccess.PerformLayout();
|
||||
this.tabConfigUpdate.ResumeLayout(false);
|
||||
this.tabConfigUpdate.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using BulkArchiveMailer.Core;
|
||||
using Outlook = Microsoft.Office.Interop.Outlook;
|
||||
|
||||
namespace BulkArchiveMailer.AddIn.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Archive folder, email domain, signature override, test-mode default,
|
||||
/// and the two editable mail templates. Saves straight to
|
||||
/// %APPDATA%\BulkArchiveMailer\config.json via AppSettings.
|
||||
/// </summary>
|
||||
public partial class SettingsForm : Form
|
||||
{
|
||||
private readonly Outlook.Application outlookApplication;
|
||||
|
||||
public SettingsForm(Outlook.Application outlookApplication)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.outlookApplication = outlookApplication;
|
||||
Load += SettingsForm_Load;
|
||||
}
|
||||
|
||||
private void SettingsForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
AppSettingsData settings;
|
||||
try
|
||||
{
|
||||
settings = AppSettings.Load();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Не удалось прочитать config.json (" + AppSettings.ConfigFilePath + "):\n" + ex.Message +
|
||||
"\n\nБудут показаны пустые настройки — сохранение перезапишет файл заново.",
|
||||
"BulkArchiveMailer",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning);
|
||||
settings = new AppSettingsData();
|
||||
}
|
||||
|
||||
PopulateFrom(settings);
|
||||
}
|
||||
|
||||
private void PopulateFrom(AppSettingsData settings)
|
||||
{
|
||||
txtArchiveFolder.Text = settings.ArchiveFolder ?? "";
|
||||
txtDomain.Text = settings.EmailDomain ?? "";
|
||||
txtSignaturePath.Text = settings.SignatureHtmlPath ?? "";
|
||||
chkTestModeDefault.Checked = settings.TestModeDefault;
|
||||
|
||||
var templates = settings.ToTemplateSet();
|
||||
txtSshSubject.Text = templates.SshAccess.Subject;
|
||||
txtSshBody.Text = templates.SshAccess.BodyHtml;
|
||||
txtCfgSubject.Text = templates.ConfigUpdate.Subject;
|
||||
txtCfgBody.Text = templates.ConfigUpdate.BodyHtml;
|
||||
}
|
||||
|
||||
private void btnBrowseFolder_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var dialog = new FolderBrowserDialog())
|
||||
{
|
||||
dialog.Description = "Папка с архивами, которые ssh-access-tui выкладывает для рассылки";
|
||||
if (System.IO.Directory.Exists(txtArchiveFolder.Text))
|
||||
{
|
||||
dialog.SelectedPath = txtArchiveFolder.Text;
|
||||
}
|
||||
|
||||
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
txtArchiveFolder.Text = dialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnBrowseSignature_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var dialog = new OpenFileDialog())
|
||||
{
|
||||
dialog.Title = "Выберите файл подписи";
|
||||
dialog.Filter = "HTML-подпись (*.htm;*.html)|*.htm;*.html|Все файлы (*.*)|*.*";
|
||||
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
txtSignaturePath.Text = dialog.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnAutoDetectSignature_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (outlookApplication == null)
|
||||
{
|
||||
MessageBox.Show(this, "Outlook.Application недоступен в этом окне.", "BulkArchiveMailer", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var path = SignatureReader.TryAutoDetectSignaturePath(outlookApplication);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
MessageBox.Show(
|
||||
this,
|
||||
"Не удалось определить подпись автоматически (не настроена подпись по умолчанию для новых писем, либо файл не найден). Выберите файл вручную кнопкой \"Обзор...\".",
|
||||
"BulkArchiveMailer",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
txtSignaturePath.Text = path;
|
||||
}
|
||||
|
||||
private void btnResetDefaults_Click(object sender, EventArgs e)
|
||||
{
|
||||
var confirm = MessageBox.Show(
|
||||
this,
|
||||
"Заменить домен и оба шаблона писем значениями по умолчанию? Папка с архивами и файл подписи не изменятся. Несохранённые правки в этом окне будут потеряны.",
|
||||
"Подтверждение",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question);
|
||||
if (confirm != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var defaults = AppSettings.LoadDefaults();
|
||||
txtDomain.Text = defaults.EmailDomain;
|
||||
|
||||
var templates = defaults.ToTemplateSet();
|
||||
txtSshSubject.Text = templates.SshAccess.Subject;
|
||||
txtSshBody.Text = templates.SshAccess.BodyHtml;
|
||||
txtCfgSubject.Text = templates.ConfigUpdate.Subject;
|
||||
txtCfgBody.Text = templates.ConfigUpdate.BodyHtml;
|
||||
}
|
||||
|
||||
private void btnSave_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(txtDomain.Text))
|
||||
{
|
||||
MessageBox.Show(this, "Домен электронной почты не может быть пустым.", "BulkArchiveMailer", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
tabControl.SelectedTab = tabGeneral;
|
||||
txtDomain.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(txtArchiveFolder.Text) && !System.IO.Directory.Exists(txtArchiveFolder.Text))
|
||||
{
|
||||
var proceed = MessageBox.Show(
|
||||
this,
|
||||
"Указанная папка с архивами не существует:\n" + txtArchiveFolder.Text + "\n\nВсё равно сохранить?",
|
||||
"BulkArchiveMailer",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning);
|
||||
if (proceed != DialogResult.Yes)
|
||||
{
|
||||
tabControl.SelectedTab = tabGeneral;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var data = new AppSettingsData
|
||||
{
|
||||
ArchiveFolder = txtArchiveFolder.Text.Trim(),
|
||||
EmailDomain = txtDomain.Text.Trim(),
|
||||
SignatureHtmlPath = txtSignaturePath.Text.Trim(),
|
||||
TestModeDefault = chkTestModeDefault.Checked,
|
||||
};
|
||||
data.SetTemplate(ArchiveKind.SshAccess, new MailTemplate { Subject = txtSshSubject.Text, BodyHtml = txtSshBody.Text });
|
||||
data.SetTemplate(ArchiveKind.ConfigUpdate, new MailTemplate { Subject = txtCfgSubject.Text, BodyHtml = txtCfgBody.Text });
|
||||
|
||||
try
|
||||
{
|
||||
AppSettings.Save(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, "Не удалось сохранить настройки: " + ex.Message, "BulkArchiveMailer", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user