public GetCloneFromInternetDialog(GetCloneFromInternetModel model) { _model = model; //#if !MONO Font = SystemFonts.MessageBoxFont; //#endif InitializeComponent(); Font = SystemFonts.MessageBoxFont; _backgroundWorker = new BackgroundWorker(); _backgroundWorker.WorkerSupportsCancellation = true; _backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted; _backgroundWorker.DoWork += _backgroundWorker_DoWork; _logBox.ShowCopyToClipboardMenuItem = true; _logBox.ShowDetailsMenuItem = true; _logBox.ShowDiagnosticsMenuItem = true; _logBox.ShowFontMenuItem = true; _model.AddProgress(_statusProgress); _statusProgress.Text = ""; _statusProgress.Visible = false; _model.AddMessageProgress(_logBox); _model.ProgressIndicator = _progressBar; _model.UIContext = SynchronizationContext.Current; _serverSettingsControl = new ServerSettingsControl(){Model=_model}; _serverSettingsControl.TabIndex = 0; _serverSettingsControl.Anchor = (AnchorStyles.Top | AnchorStyles.Left); Controls.Add(_serverSettingsControl); _targetFolderControl = new TargetFolderControl(_model); _targetFolderControl.Anchor = (AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right); _targetFolderControl._downloadButton.Click+=OnDownloadClick; _targetFolderControl.Location = new Point(0, _serverSettingsControl.Height +10); MinimumSize = new Size(_targetFolderControl.MinimumSize.Width+20, _targetFolderControl.Bottom +20); if (_targetFolderControl.Bottom +30> Bottom) { this.Size = new Size(this.Width,_targetFolderControl.Bottom + 30); } _targetFolderControl.TabIndex = 1; this.Controls.Add(_targetFolderControl); _okButton.TabIndex = 90; _cancelButton.TabIndex = 91; _fixSettingsButton.Left = _cancelButton.Left; _targetFolderControl._downloadButton.Top = _okButton.Top-_targetFolderControl.Top ; _targetFolderControl._downloadButton.Left = _okButton.Left - 15; _logBox.GetDiagnosticsMethod = (progress) => { var hg = new HgRepository(PathToNewlyClonedFolder, progress); hg.GetDiagnosticInformationForRemoteProject(progress, ThreadSafeUrl); }; }
public void URL_AfterConstruction_GoodDefault() { using (var testFolder = new TemporaryFolder("clonetest")) { var model = new GetCloneFromInternetModel(testFolder.Path); model.AccountName = "account"; model.Password = "******"; model.ProjectId = "id"; Assert.AreEqual("http://*****:*****@resumable.languagedepot.org/id", model.URL.ToLower()); } }
public void InitFromUri_GivenCompleteUri_AllPropertiesCorrect() { using (var testFolder = new TemporaryFolder("clonetest")) { var model = new GetCloneFromInternetModel(testFolder.Path); model.InitFromUri("http://*****:*****@hg-languagedepot.org/tpi?localFolder=tokPisin"); Assert.AreEqual("tokPisin", model.LocalFolderName); Assert.IsTrue(model.ReadyToDownload); Assert.AreEqual("http://*****:*****@hg-languagedepot.org/tpi",model.URL); } }
public void UpdateDisplay_BadModelDoesNotThrow() { using (var testFolder = new TemporaryFolder("clonetest")) { var model = new GetCloneFromInternetModel(testFolder.Path); model.AccountName = "account"; model.Password = "******"; model.ProjectId = "id"; model.LocalFolderName = "Some<Folder"; var ctrl = new TargetFolderControl(model); Assert.DoesNotThrow(() => { ctrl._localFolderName.Text = "Some<Folders"; }); } }
public void CleanUpAfterErrorOrCancel_DirectoryDeleted() { using (var testFolder = new TemporaryFolder("clonetest")) { var model = new GetCloneFromInternetModel(testFolder.Path); model.LocalFolderName = "SomeFolder"; // Ideally would call model to start the clone - but that's in the dialog for now so fake it instead. Directory.CreateDirectory(model.TargetDestination); Assert.That(Directory.Exists(model.TargetDestination), Is.True); model.CleanUpAfterErrorOrCancel(); Assert.That(Directory.Exists(model.TargetDestination), Is.False); } }
/// <summary> /// Get a teammate's shared project from the specified source. /// </summary> /// <param name="parent">Window that will be parent of progress window</param> /// <param name="projectFilter">Function taking a directory path and telling whether it contains the right sort of repo</param> /// <param name="hubQuery">String on which to build a URL query to ChorusHub to accomplish the purpose of 'projectFilter' /// in the ChorusHub environment</param> /// <example>FLExBridge sends "fileExtension=.lift|._custom_properties" to get both LIFT and FLExBridge repos, but not Bloom ones, /// for instance. The server looks in the project's .hg/store/data folder for a file ending in .lift.i or ._custom_properties.i</example> /// <param name="baseProjectDirForNewClone">The base folder for the new clone, if created.</param> /// <param name="baseProjectDirInWhichToSearchForRepositories">The directory which contains projects we already have, and where the result should go</param> /// <param name="lowerLevelRepoPath">Optionally specifies another place to look for existing repos: look in this subfolder of each folder in baseProjectDirInWhichToSearchForRepositories. /// This is used in FLEx (passing "OtherRepositories") so existing LIFT repos linked to FW projects can be found. Pass null if not used.</param> /// <param name="preferredClonedFolderName"></param> /// <param name="howToSendReceiveMessageText">This string is appended to the message we build when we have received a repo and can't keep it, because /// it has the same hash as an existing project. We think it is likely the user actually intended to Send/Receive that project rather than obtaining /// a duplicate. This message thus typically tells him how to do so, in the particular client program. May also be empty.</param> /// <returns> /// A CloneResult that provides the clone results (e.g., success or failure) and the actual clone location (null if not created). /// </returns> public CloneResult GetSharedProjectUsing(Form parent, string baseProjectDirForNewClone, string preferredClonedFolderName, Func <string, bool> projectFilter, string hubQuery, string baseProjectDirInWhichToSearchForRepositories, string lowerLevelRepoPath, string howToSendReceiveMessageText) { Guard.AgainstNull(parent, "parent"); Guard.AgainstNullOrEmptyString(baseProjectDirForNewClone, "baseProjectDirForNewClone"); Guard.AgainstNullOrEmptyString(baseProjectDirInWhichToSearchForRepositories, "baseProjectDirInWhichToSearchForRepositories"); if (preferredClonedFolderName == string.Empty) { preferredClonedFolderName = null; } Dictionary <string, string> existingRepositories; try { existingRepositories = ExtantRepoIdentifiers(baseProjectDirInWhichToSearchForRepositories, lowerLevelRepoPath); } catch (ApplicationException e) { // FLEx issue LT-14301: one reason we may throw is that we can't get the identifier of some project because we don't have // sufficient permissions. // We think this will be very rare...try to get an automatic notification if it happens. UsageReporter.SendEvent("UnusualProblems", "Chorus", "ExtantRepoIdentifiersFailed", null, 0); MessageBox.Show( string.Format(LocalizationManager.GetString("Messages.CantGetInfo", "You can't get a project from a colleague at present, because some required information about the projects you already have is unavailable. " + "This may be because you don't have permission to access a file in one of the projects in {0}.\n\n" + "You will probably need technical support to resolve this problem. The following information may be helpful to tech support:") + "\n\n{1}", baseProjectDirInWhichToSearchForRepositories, e.Message), LocalizationManager.GetString("Messages.CantGetProject", "Cannot get project")); return(new CloneResult(null, CloneStatus.NotCreated)); } var existingProjectNames = new HashSet <string>(from dir in Directory.GetDirectories(baseProjectDirInWhichToSearchForRepositories) select Path.GetFileName(dir)); // "existingRepositoryIdentifiers" is currently not used, but the expectation is that the various models/views could use it how they see fit. // "Seeing fit' may mean to warn the user they already have some repository, or as a filter to not show ones that already exist. // What to do with the list of extant repos is left up to a view+model pair. // Select basic source type. using (var getSharedProjectDlg = new GetSharedProjectDlg()) { getSharedProjectDlg.InitFromModel(this); getSharedProjectDlg.ShowDialog(parent); if (getSharedProjectDlg.DialogResult != DialogResult.OK) { return(new CloneResult(null, CloneStatus.NotCreated)); } } // Make clone from some source. string actualCloneLocation = null; var cloneStatus = CloneStatus.NotCreated; switch (RepositorySource) { case ExtantRepoSource.Internet: var cloneFromInternetModel = new GetCloneFromInternetModel(baseProjectDirForNewClone) { LocalFolderName = preferredClonedFolderName }; using (var cloneFromInternetDialog = new GetCloneFromInternetDialog(cloneFromInternetModel)) { switch (cloneFromInternetDialog.ShowDialog(parent)) { default: cloneStatus = CloneStatus.NotCreated; break; case DialogResult.Cancel: cloneStatus = CloneStatus.Cancelled; break; case DialogResult.OK: actualCloneLocation = cloneFromInternetDialog.PathToNewlyClonedFolder; cloneStatus = CloneStatus.Created; break; } } break; case ExtantRepoSource.ChorusHub: var getCloneFromChorusHubModel = new GetCloneFromChorusHubModel(baseProjectDirForNewClone) { ProjectFilter = hubQuery, ExistingProjects = existingProjectNames, ExistingRepositoryIdentifiers = existingRepositories }; using (var getCloneFromChorusHubDialog = new GetCloneFromChorusHubDialog(getCloneFromChorusHubModel)) { switch (getCloneFromChorusHubDialog.ShowDialog(parent)) { default: cloneStatus = CloneStatus.NotCreated; break; case DialogResult.Cancel: cloneStatus = CloneStatus.Cancelled; break; case DialogResult.OK: if (getCloneFromChorusHubModel.CloneSucceeded) { actualCloneLocation = getCloneFromChorusHubDialog.PathToNewlyClonedFolder; cloneStatus = CloneStatus.Created; } else { cloneStatus = CloneStatus.NotCreated; } break; } } break; case ExtantRepoSource.Usb: using (var cloneFromUsbDialog = new GetCloneFromUsbDialog(baseProjectDirForNewClone)) { cloneFromUsbDialog.Model.ProjectFilter = projectFilter ?? DefaultProjectFilter; cloneFromUsbDialog.Model.ReposInUse = existingRepositories; cloneFromUsbDialog.Model.ExistingProjects = existingProjectNames; switch (cloneFromUsbDialog.ShowDialog(parent)) { default: cloneStatus = CloneStatus.NotCreated; break; case DialogResult.Cancel: cloneStatus = CloneStatus.Cancelled; break; case DialogResult.OK: actualCloneLocation = cloneFromUsbDialog.PathToNewlyClonedFolder; cloneStatus = CloneStatus.Created; break; } } break; } // Warn the user if they already have this by another name. // Not currently needed for USB, since those have already been checked. if (RepositorySource != ExtantRepoSource.Usb && cloneStatus == CloneStatus.Created) { var repo = new HgRepository(actualCloneLocation, new NullProgress()); string projectWithExistingRepo; if (repo.Identifier != null && existingRepositories.TryGetValue(repo.Identifier, out projectWithExistingRepo)) { using (var warningDlg = new DuplicateProjectWarningDialog()) warningDlg.Run(projectWithExistingRepo, howToSendReceiveMessageText); Directory.Delete(actualCloneLocation, true); actualCloneLocation = null; cloneStatus = CloneStatus.Cancelled; } } return(new CloneResult(actualCloneLocation, cloneStatus)); }
public TargetFolderControl(GetCloneFromInternetModel model) { _model = model; InitializeComponent(); }
public TargetFolderControl(GetCloneFromInternetModel model) { _model = model; InitializeComponent(); }
private void LaunchCustomUrl(string url) { using (var targetComputer = new TemporaryFolder("clonetest-targetComputer")) { var model = new GetCloneFromInternetModel(targetComputer.Path); model.InitFromUri(url); using (var dlg = new GetCloneFromInternetDialog(model)) { if (DialogResult.OK != dlg.ShowDialog()) return; } } }
public GetCloneFromInternetDialog(GetCloneFromInternetModel model) { _model = model; Font = SystemFonts.MessageBoxFont; InitializeComponent(); Font = SystemFonts.MessageBoxFont; _backgroundWorker = new BackgroundWorker(); _backgroundWorker.WorkerSupportsCancellation = true; _backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted; _backgroundWorker.DoWork += _backgroundWorker_DoWork; _logBox.ShowCopyToClipboardMenuItem = true; _logBox.ShowDetailsMenuItem = true; _logBox.ShowDiagnosticsMenuItem = true; _logBox.ShowFontMenuItem = true; _model.AddProgress(_statusProgress); _statusProgress.Text = ""; _statusProgress.Visible = false; _model.AddMessageProgress(_logBox); _model.ProgressIndicator = _progressBar; _model.UIContext = SynchronizationContext.Current; _serverSettingsControl = new ServerSettingsControl() { Model = _model }; _serverSettingsControl.TabIndex = 0; _serverSettingsControl.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right); Controls.Add(_serverSettingsControl); _targetFolderControl = new TargetFolderControl(_model); _targetFolderControl.Anchor = (AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right); _targetFolderControl._downloadButton.Click += OnDownloadClick; _targetFolderControl.Location = new Point(0, _serverSettingsControl.Height + 10); var minimumWidth = Math.Max(_serverSettingsControl.MinimumSize.Width, _targetFolderControl.MinimumSize.Width) + 20; MinimumSize = new Size(minimumWidth, _targetFolderControl.Bottom + 20); // On Linux, we have to set the dialog width, then set the control width back to what it had been. TODO: different order var sscWidth = _serverSettingsControl.Width; Width = sscWidth + 30; _serverSettingsControl.Width = sscWidth; if (_targetFolderControl.Bottom + 30 > Bottom) { this.Size = new Size(this.Width, _targetFolderControl.Bottom + 30); } _targetFolderControl.TabIndex = 1; this.Controls.Add(_targetFolderControl); _okButton.TabIndex = 90; _cancelButton.TabIndex = 91; _fixSettingsButton.Left = _cancelButton.Left; var fixBtnWidth = _fixSettingsButton.Width; _fixSettingsButton.AutoSize = true; if (_fixSettingsButton.Width > fixBtnWidth) { // The button was too small before autosizing, but now it may extend off the dialog... var diff = _fixSettingsButton.Width - fixBtnWidth; if (diff < _cancelButton.Left) { _fixSettingsButton.Left = _cancelButton.Left - diff; } } _targetFolderControl._downloadButton.Top = _okButton.Top - _targetFolderControl.Top; _targetFolderControl._downloadButton.Left = _okButton.Left - 15; _logBox.GetDiagnosticsMethod = (progress) => { var hg = new HgRepository(PathToNewlyClonedFolder, progress); hg.GetDiagnosticInformationForRemoteProject(progress, ThreadSafeUrl); }; }
/// <summary> /// Get a teammate's shared project from the specified source. /// </summary> /// <param name="parent">Window that will be parent of progress window</param> /// <param name="projectFilter">Function taking a directory path and telling whether it contains the right sort of repo</param> /// <param name="hubQuery">String on which to build a URL query to ChorusHub to accomplish the purpose of 'projectFilter' /// in the ChorusHub environment</param> /// <example>FLExBridge sends "fileExtension=.lift|._custom_properties" to get both LIFT and FLExBridge repos, but not Bloom ones, /// for instance. The server looks in the project's .hg/store/data folder for a file ending in .lift.i or ._custom_properties.i</example> /// <param name="baseProjectDirForNewClone">The base folder for the new clone, if created.</param> /// <param name="baseProjectDirInWhichToSearchForRepositories">The directory which contains projects we already have, and where the result should go</param> /// <param name="lowerLevelRepoPath">Optionally specifies another place to look for existing repos: look in this subfolder of each folder in baseProjectDirInWhichToSearchForRepositories. /// This is used in FLEx (passing "OtherRepositories") so existing LIFT repos linked to FW projects can be found. Pass null if not used.</param> /// <param name="preferredClonedFolderName"></param> /// <param name="howToSendReceiveMessageText">This string is appended to the message we build when we have received a repo and can't keep it, because /// it has the same hash as an existing project. We think it is likely the user actually intended to Send/Receive that project rather than obtaining /// a duplicate. This message thus typically tells him how to do so, in the particular client program. May also be empty.</param> /// <returns> /// A CloneResult that provides the clone results (e.g., success or failure) and the actual clone location (null if not created). /// </returns> public CloneResult GetSharedProjectUsing(Form parent, string baseProjectDirForNewClone, string preferredClonedFolderName, Func<string, bool> projectFilter, string hubQuery, string baseProjectDirInWhichToSearchForRepositories, string lowerLevelRepoPath, string howToSendReceiveMessageText) { Guard.AgainstNull(parent, "parent"); Guard.AgainstNullOrEmptyString(baseProjectDirForNewClone, "baseProjectDirForNewClone"); Guard.AgainstNullOrEmptyString(baseProjectDirInWhichToSearchForRepositories, "baseProjectDirInWhichToSearchForRepositories"); if (preferredClonedFolderName == string.Empty) preferredClonedFolderName = null; Dictionary<string, string> existingRepositories; try { existingRepositories = ExtantRepoIdentifiers(baseProjectDirInWhichToSearchForRepositories, lowerLevelRepoPath); } catch (ApplicationException e) { // FLEx issue LT-14301: one reason we may throw is that we can't get the identifier of some project because we don't have // sufficient permissions. // We think this will be very rare...try to get an automatic notification if it happens. UsageReporter.SendEvent("UnusualProblems", "Chorus", "ExtantRepoIdentifiersFailed", null, 0); MessageBox.Show( string.Format(LocalizationManager.GetString("Messages.CantGetInfo", "You can't get a project from a colleague at present, because some required information about the projects you already have is unavailable. " + "This may be because you don't have permission to access a file in one of the projects in {0}.\n\n" + "You will probably need technical support to resolve this problem. The following information may be helpful to tech support:") + "\n\n{1}", baseProjectDirInWhichToSearchForRepositories, e.Message), LocalizationManager.GetString("Messages.CantGetProject", "Cannot get project")); return new CloneResult(null, CloneStatus.NotCreated); } var existingProjectNames = new HashSet<string>(from dir in Directory.GetDirectories(baseProjectDirInWhichToSearchForRepositories) select Path.GetFileName(dir)); // "existingRepositoryIdentifiers" is currently not used, but the expectation is that the various models/views could use it how they see fit. // "Seeing fit' may mean to warn the user they already have some repository, or as a filter to not show ones that already exist. // Waht to do with the list of extant repos is left up to a view+model pair. // Select basic source type. using (var getSharedProjectDlg = new GetSharedProjectDlg()) { getSharedProjectDlg.InitFromModel(this); getSharedProjectDlg.ShowDialog(parent); if (getSharedProjectDlg.DialogResult != DialogResult.OK) { return new CloneResult(null, CloneStatus.NotCreated); } } // Make clone from some source. string actualCloneLocation = null; var cloneStatus = CloneStatus.NotCreated; switch (RepositorySource) { case ExtantRepoSource.Internet: var cloneFromInternetModel = new GetCloneFromInternetModel(baseProjectDirForNewClone) { LocalFolderName = preferredClonedFolderName }; using (var cloneFromInternetDialog = new GetCloneFromInternetDialog(cloneFromInternetModel)) { switch (cloneFromInternetDialog.ShowDialog(parent)) { default: cloneStatus = CloneStatus.NotCreated; break; case DialogResult.Cancel: cloneStatus = CloneStatus.Cancelled; break; case DialogResult.OK: actualCloneLocation = cloneFromInternetDialog.PathToNewlyClonedFolder; cloneStatus = CloneStatus.Created; break; } } break; case ExtantRepoSource.ChorusHub: var getCloneFromChorusHubModel = new GetCloneFromChorusHubModel(baseProjectDirForNewClone) { ProjectFilter = hubQuery, ExistingProjects = existingProjectNames, ExistingRepositoryIdentifiers = existingRepositories }; using (var getCloneFromChorusHubDialog = new GetCloneFromChorusHubDialog(getCloneFromChorusHubModel)) { switch (getCloneFromChorusHubDialog.ShowDialog(parent)) { default: cloneStatus = CloneStatus.NotCreated; break; case DialogResult.Cancel: cloneStatus = CloneStatus.Cancelled; break; case DialogResult.OK: if (getCloneFromChorusHubModel.CloneSucceeded) { actualCloneLocation = getCloneFromChorusHubDialog.PathToNewlyClonedFolder; cloneStatus = CloneStatus.Created; } else { cloneStatus = CloneStatus.NotCreated; } break; } } break; case ExtantRepoSource.Usb: using (var cloneFromUsbDialog = new GetCloneFromUsbDialog(baseProjectDirForNewClone)) { cloneFromUsbDialog.Model.ProjectFilter = projectFilter ?? DefaultProjectFilter; cloneFromUsbDialog.Model.ReposInUse = existingRepositories; cloneFromUsbDialog.Model.ExistingProjects = existingProjectNames; switch (cloneFromUsbDialog.ShowDialog(parent)) { default: cloneStatus = CloneStatus.NotCreated; break; case DialogResult.Cancel: cloneStatus = CloneStatus.Cancelled; break; case DialogResult.OK: actualCloneLocation = cloneFromUsbDialog.PathToNewlyClonedFolder; cloneStatus = CloneStatus.Created; break; } } break; } // Warn the user if they already have this by another name. // Not currently needed for USB, since those have already been checked. if (RepositorySource != ExtantRepoSource.Usb && cloneStatus == CloneStatus.Created) { var repo = new HgRepository(actualCloneLocation, new NullProgress()); string projectWithExistingRepo; if (repo.Identifier != null && existingRepositories.TryGetValue(repo.Identifier, out projectWithExistingRepo)) { using (var warningDlg = new DuplicateProjectWarningDialog()) warningDlg.Run(projectWithExistingRepo, howToSendReceiveMessageText); Directory.Delete(actualCloneLocation, true); actualCloneLocation = null; cloneStatus = CloneStatus.Cancelled; } } return new CloneResult(actualCloneLocation, cloneStatus); }