#region Disclaimer / License // Copyright (C) 2009, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Text; using System.Windows.Forms; using OSGeo.MapGuide.MaestroAPI.Services; using OSGeo.MapGuide.MaestroAPI; using Maestro.Shared.UI; namespace Maestro.Packaging { /// /// Progress dialog for packaging operations /// public partial class PackageProgress : Form { /// /// The stages required to build a package /// private readonly static ProgressType[] BUILD_STAGES = null; static PackageProgress() { BUILD_STAGES = new ProgressType[] { ProgressType.ReadingFileList, ProgressType.PreparingFolder, ProgressType.MovingResources, ProgressType.Compressing }; } private bool m_allowClose = true; private volatile System.Threading.Thread m_thread; private PackageBuilder m_invokeObj = null; private object[] m_invokeArgs = null; private System.Reflection.MethodInfo m_invokeMethod = null; private object m_invokeResult = null; /// /// Lets the user choose a file to upload, and then transfers it /// /// The owner form /// The connection to use /// A DialogResult object that indicates the upload result public static DialogResult UploadPackage(Form owner, IServerConnection con) { OpenFileDialog dlg = Maestro.Shared.UI.DialogFactory.OpenFile(); //Mono does NOT like this one //dlg.AutoUpgradeEnabled = true; dlg.CheckFileExists = true; dlg.CheckPathExists = true; dlg.DefaultExt = ".mgp"; dlg.Filter = Properties.Resources.UploadPackageDialogFilter; dlg.FilterIndex = 0; dlg.Multiselect = false; dlg.ValidateNames = true; dlg.Title = Properties.Resources.UploadPackageDialogTitle; if (dlg.ShowDialog(owner) == DialogResult.OK) { var optDiag = new PackageUploadOptionDialog(); optDiag.ShowDialog(); if (optDiag.Method == PackageUploadMethod.Transactional) { return UploadPackage(owner, con, dlg.FileName); } else { return StartNonTransactionalUploadLoop(owner, con, dlg.FileName); } } else return DialogResult.Cancel; } /// /// Initiates a user-controlled upload loop whereby failed operations from a non-transactional /// package upload can be retried as many times until either all failed operations have been /// accounted for, or the user has decided to stop /// /// /// /// /// public static DialogResult StartNonTransactionalUploadLoop(Form owner, IServerConnection conn, string fileName) { var result = new UploadPackageResult(); var res = UploadPackageNonTransactional(owner, conn, fileName, result); //We do this until either there are no failures or the user has given up retrying while (result.Failed.Count > 0) { using (var resultDiag = new PackageUploadResultDialog(result)) { if (resultDiag.ShowDialog() == DialogResult.Retry) { var success = result.Successful; var skipped = result.SkipOperations; //Create a new result object and skip any previous ones that were //either successful or skipped themselves result = new UploadPackageResult(success); foreach (var skip in skipped) result.SkipOperations.Add(skip); res = UploadPackageNonTransactional(owner, conn, fileName, result); } else //Not retrying { break; } } } return res; } /// /// Lists the contents of a package /// /// The owner form /// The connection used to deserialize the items /// The package file to read /// A list of resources in the package public static Dictionary ListPackageContents(Form owner, IServerConnection connection, string packageFile) { PackageProgress pkgp = new PackageProgress(); pkgp.m_invokeArgs = new object[] { packageFile }; pkgp.m_invokeObj = new PackageBuilder(connection); pkgp.m_invokeMethod = pkgp.m_invokeObj.GetType().GetMethod("ListPackageContents"); if (pkgp.ShowDialog(owner) == DialogResult.OK) return (Dictionary)pkgp.m_invokeResult; else return null; } /// /// Uploads a package file to the server /// /// The owner form /// The connection used to upload the package /// The package file to upload /// A DialogResult object that indicates the result of the operation public static DialogResult UploadPackage(Form owner, IServerConnection connection, string packageFile) { PackageProgress pkgp = new PackageProgress(); pkgp.Text = Properties.Resources.TitleUploading; pkgp.m_invokeArgs = new object[] { packageFile }; pkgp.m_invokeObj = new PackageBuilder(connection); pkgp.m_invokeMethod = pkgp.m_invokeObj.GetType().GetMethod("UploadPackage"); return pkgp.ShowDialog(owner); } /// /// Uploads a package file to the server in a non-transactional fashion /// /// The owner form /// The connection used to upload the package /// The package file to upload /// An object containing an optional list of operations to skip. It will be populated with the list of operations that passed and failed as the process executes /// A DialogResult object that indicates the result of the operation public static DialogResult UploadPackageNonTransactional(Form owner, IServerConnection connection, string packageFile, UploadPackageResult result) { PackageProgress pkgp = new PackageProgress(); pkgp.Text = Properties.Resources.TitleUploading; pkgp.m_invokeArgs = new object[] { packageFile, result }; pkgp.m_invokeObj = new PackageBuilder(connection); pkgp.m_invokeMethod = pkgp.m_invokeObj.GetType().GetMethod("UploadPackageNonTransactional"); return pkgp.ShowDialog(owner); } /// /// Rebuilds a package /// /// The owner form /// The connection used to serialize the items /// The package file that the new file is based on /// The items that describes the layout of the new package /// The output package file /// True if each resource should have a delete operation inserted before the actual operation, false otherwise /// A DialogResult object that indicates the result of the operation public static DialogResult RebuildPackage(Form owner, IServerConnection connection, string sourcePackageFile, List items, string targetfile, bool insertEraseCommands) { PackageProgress pkgp = new PackageProgress(); pkgp.m_invokeArgs = new object[] { sourcePackageFile, items, targetfile, insertEraseCommands }; pkgp.m_invokeObj = new PackageBuilder(connection); pkgp.m_invokeMethod = pkgp.m_invokeObj.GetType().GetMethod("RebuildPackage"); return pkgp.ShowDialog(owner); } /// /// Creates a new package from a server folder /// /// The owner form /// The connection used to retrieve the resources /// The folder to create the package from /// The name of the output package file /// A list of allowed extensions, set to null for all types. The special value "*" matches all unknown extensions. /// A flag indicating if the package should contain a delete instruction to delete the target area before restore /// The folder path where the package should be restore, set to null or empty string to use the source path /// public static DialogResult CreatePackage(Form owner, IServerConnection connection, string folderResourceId, string zipfilename, IEnumerable allowedExtensions, bool removeExistingFiles, string alternateTargetResourceId) { PackageProgress pkgp = new PackageProgress(); pkgp.m_invokeArgs = new object[] {folderResourceId, zipfilename, allowedExtensions, removeExistingFiles, alternateTargetResourceId }; pkgp.m_invokeObj = new PackageBuilder(connection); pkgp.m_invokeMethod = pkgp.m_invokeObj.GetType().GetMethod("CreatePackage"); return pkgp.ShowDialog(owner); } private PackageProgress() { InitializeComponent(); } private void PackageProgress_Load(object sender, EventArgs e) { m_allowClose = false; backgroundWorker.RunWorkerAsync(); m_invokeObj.Progress += new ProgressDelegate(SetCurrentProgress); } private void PackageProgress_FormClosing(object sender, FormClosingEventArgs e) { if (m_thread != null && !m_allowClose && e.CloseReason == CloseReason.UserClosing) { if (MessageBox.Show(this, Properties.Resources.CancelConfirmation, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3) == DialogResult.Yes) { try { m_thread.Abort(); } catch { } } else e.Cancel = true; } } private delegate void SetCurrentProgressDelegate(ProgressType type, string resource, int total, double pg); private void SetCurrentProgress(ProgressType type, string resource, int total, double pg) { if (this.InvokeRequired) this.Invoke(new SetCurrentProgressDelegate(SetCurrentProgress), new object[] { type, resource, total, pg }); else { int span = this.CurrentProgress.Maximum - this.CurrentProgress.Minimum; double v = (((double)pg / total) * span) + this.CurrentProgress.Minimum; this.CurrentProgress.Value = (int)Math.Max(Math.Min(v, this.CurrentProgress.Maximum), this.CurrentProgress.Minimum); OperationLabel.Text = resource; int largePg = (int)Array.IndexOf(BUILD_STAGES, type); if (largePg >= 0) { this.TotalProgress.Maximum = 100; this.TotalProgress.Minimum = 0; double tick = 100 / BUILD_STAGES.Length; double lv = (tick * largePg) + (tick * (pg / total)); this.TotalProgress.Value = (int)Math.Max(Math.Min((int)lv, this.TotalProgress.Maximum), this.TotalProgress.Minimum); } if (type == ProgressType.Uploading) { HideTotal(); if (pg == total) { OperationLabel.Text = Properties.Resources.ProgressWaitingForServer; CurrentProgress.Style = ProgressBarStyle.Marquee; } else { OperationLabel.Text = string.Format(Properties.Resources.ProgressUploading, ((double)pg / 1024), ((double)total / 1024)); this.Refresh(); } } else if (type == ProgressType.ListingFiles) { HideTotal(); OperationLabel.Text = Properties.Resources.ProgressReadingPackage; CurrentProgress.Style = ProgressBarStyle.Marquee; } else if (type == ProgressType.Compressing) { OperationLabel.Text = Properties.Resources.ProgressCompressing; } else if (type == ProgressType.MovingResources) { OperationLabel.Text = Properties.Resources.ProgressUpdatingReferences; } else if (type == ProgressType.PreparingFolder) { OperationLabel.Text = resource; } else if (type == ProgressType.SetResource) { CurrentProgress.Style = ProgressBarStyle.Continuous; CurrentProgress.Maximum = 100; CurrentProgress.Value = Convert.ToInt32(Math.Min(100.0, pg)); OperationLabel.Text = string.Format(Properties.Resources.ProgressSetResource, resource, pg); } else if (type == ProgressType.SetResourceData) { CurrentProgress.Style = ProgressBarStyle.Continuous; CurrentProgress.Maximum = 100; CurrentProgress.Value = Convert.ToInt32(Math.Min(100.0, pg)); OperationLabel.Text = string.Format(Properties.Resources.ProgressSetResourceData, resource, pg); } } } private void HideTotal() { if (TotalProgress.Visible) { this.Height -= TotalProgress.Height; TotalProgress.Visible = false; TotalLabel.Visible = false; } } private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { try { m_thread = System.Threading.Thread.CurrentThread; e.Result = m_invokeMethod.Invoke(m_invokeObj, m_invokeArgs); } catch (System.Threading.ThreadAbortException) { System.Threading.Thread.ResetAbort(); e.Cancel = true; } catch (System.Reflection.TargetInvocationException tai) { throw tai.InnerException; } finally { m_thread = null; } } private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { m_allowClose = true; if (e.Cancelled) { this.DialogResult = DialogResult.Cancel; this.Close(); return; } else if (e.Error != null) { ErrorDialog.Show(e.Error); } m_invokeResult = e.Result; this.DialogResult = DialogResult.OK; this.Close(); } private void CancelBtn_Click(object sender, EventArgs e) { FormClosingEventArgs ev = new FormClosingEventArgs(CloseReason.UserClosing, false); PackageProgress_FormClosing(sender, ev); if (!ev.Cancel) { try { m_thread.Abort(); } catch { } } } } }