private async Task InitializeDetails(TemplateViewModel selection) { if (!string.IsNullOrEmpty(selection.RemoteUrl)) { try { var repo = await _githubClient.GetRepositoryDetails(selection.RepositoryOwner, selection.RepositoryName); selection.Description = repo.Description; selection.AvatarUrl = repo.Owner.AvatarUrl; selection.OwnerUrl = repo.Owner.HtmlUrl; } catch (WebException) { } } else { selection.Description = string.Empty; selection.AvatarUrl = string.Empty; selection.OwnerUrl = string.Empty; } }
private static void PrintTemplate(TemplateViewModel template) { Console.WriteLine($"DisplayName: '{template.DisplayName}', RemoteUrl: '{template.RemoteUrl}', ClonedPath: '{template.ClonedPath}', Desc: '{template.Description}'"); }
private async Task RefreshContextAsync(TemplateViewModel selection) { if (!await EnsureCookiecutterIsInstalled()) { return; } try { LoadingStatus = OperationStatus.InProgress; _outputWindow.ShowAndActivate(); _outputWindow.WriteLine(Strings.LoadingTemplateStarted.FormatUI(selection.DisplayName)); var result = await _cutterClient.LoadContextAsync(selection.ClonedPath, UserConfigFilePath); ContextItems.Clear(); foreach (var item in result) { ContextItems.Add(new ContextItemViewModel(item.Name, item.Selector, item.Label, item.Description, item.Url, item.DefaultValue, item.Values)); } LoadingStatus = OperationStatus.Succeeded; _outputWindow.WriteLine(Strings.LoadingTemplateSuccess.FormatUI(selection.DisplayName)); ReportTemplateEvent(CookiecutterTelemetry.TelemetryArea.Template, CookiecutterTelemetry.TemplateEvents.Load, selection); // Go to the context page ContextLoaded?.Invoke(this, EventArgs.Empty); } catch (Exception ex) when (!ex.IsCriticalException()) { LoadingStatus = OperationStatus.Failed; _outputWindow.WriteErrorLine(ex.Message); _outputWindow.WriteLine(Strings.LoadingTemplateFailed.FormatUI(selection.DisplayName)); ReportTemplateEvent(CookiecutterTelemetry.TelemetryArea.Template, CookiecutterTelemetry.TemplateEvents.Load, selection, ex); } }
private async Task RefreshSelectedDescriptionAsync(TemplateViewModel selection) { if (selection == null) { SelectedDescription = string.Empty; SelectedImage = null; return; } if (!selection.HasDetails) { await InitializeDetails(selection); } SelectedDescription = selection.Description ?? string.Empty; try { // Create an ImageSource because binding to that feels significantly faster than binding to the image url SelectedImage = !string.IsNullOrEmpty(selection.AvatarUrl) ? new BitmapImage(new Uri(selection.AvatarUrl)) : null; } catch (Exception ex) when (!ex.IsCriticalException()) { SelectedImage = null; } }
public async Task SelectTemplate(TemplateViewModel template) { SelectedTemplate = template; await RefreshSelectedDescriptionAsync(template); }
private async Task AddFromSource( ITemplateSource source, string searchTerm, CategorizedViewModel parent, CancellationToken ct, string continuationToken = null, VisualStudio.Imaging.Interop.ImageMoniker? updateableImage = null ) { var loading = new LoadingViewModel(); parent.Templates.Add(loading); try { var result = await source.GetTemplatesAsync(searchTerm, continuationToken, ct); foreach (var t in result.Templates) { ct.ThrowIfCancellationRequested(); var vm = new TemplateViewModel(); vm.DisplayName = t.Name; vm.Description = t.Description; vm.AvatarUrl = t.AvatarUrl; vm.OwnerUrl = t.OwnerUrl; vm.RemoteUrl = t.RemoteUrl; vm.ClonedPath = t.LocalFolderPath; vm.IsUpdateAvailable = t.UpdateAvailable == true; parent.Templates.Add(vm); } ct.ThrowIfCancellationRequested(); if (result.ContinuationToken != null) { parent.Templates.Add(new ContinuationViewModel(result.ContinuationToken)); } } catch (TemplateEnumerationException ex) { var template = new ErrorViewModel() { ErrorDescription = ex.Message, ErrorDetails = ex.InnerException?.Message, }; parent.Templates.Add(template); } finally { parent.Templates.Remove(loading); } }
public bool IsCloneNeeded(TemplateViewModel template) { // TODO: every search clears the ClonedPath for the online templates, so this will trigger cloning more often than we desire // If it's from online (recommended or github) and hasn't been cloned then we need to clone it return !string.IsNullOrEmpty(template.RemoteUrl) && string.IsNullOrEmpty(template.ClonedPath); }
public bool IsCloneCollision(TemplateViewModel template, out TemplateViewModel collidingTemplate) { // If an installed template has the same repository name, we'll have a collision, // unless the installed template is a perfect match, ie. same repo owner. var result = Installed.Templates.OfType<TemplateViewModel>().Where(t => t.RepositoryName == template.RepositoryName && t.RepositoryFullName != template.RepositoryFullName).ToArray(); if (result.Length > 0) { collidingTemplate = result.First(); return true; } else { collidingTemplate = null; return false; } }
private async Task RefreshTemplatesAsync(string searchTerm, CancellationToken ct) { Custom.Templates.Clear(); Recommended.Templates.Clear(); GitHub.Templates.Clear(); Installed.Templates.Clear(); SearchResults.Clear(); if (!string.IsNullOrEmpty(searchTerm)) { var searchTermTemplate = new TemplateViewModel(); searchTermTemplate.IsSearchTerm = true; if (searchTerm.StartsWith("http")) { searchTermTemplate.DisplayName = searchTerm; searchTermTemplate.RemoteUrl = searchTerm; Custom.Templates.Add(searchTermTemplate); SearchResults.Add(Custom); return; } else if (Directory.Exists(searchTerm)) { searchTermTemplate.DisplayName = searchTerm; searchTermTemplate.ClonedPath = searchTerm; Custom.Templates.Add(searchTermTemplate); SearchResults.Add(Custom); return; } } SearchResults.Add(Installed); SearchResults.Add(Recommended); SearchResults.Add(GitHub); var recommendedTask = AddFromSource(_recommendedSource, searchTerm, Recommended, ct); var installedTask = AddFromSource(_installedSource, searchTerm, Installed, ct); var githubTask = AddFromSource(_githubSource, searchTerm, GitHub, ct); await Task.WhenAll(recommendedTask, installedTask, githubTask); }
public async Task DeleteTemplateAsync(TemplateViewModel template) { try { string remote = template.RemoteUrl; _outputWindow.ShowAndActivate(); _outputWindow.WriteLine(String.Empty); _outputWindow.WriteLine(Strings.DeletingTemplateStarted.FormatUI(template.ClonedPath)); await _installedSource.DeleteTemplateAsync(template.ClonedPath); _outputWindow.WriteLine(Strings.DeletingTemplateSuccess.FormatUI(template.ClonedPath)); ReportTemplateEvent(CookiecutterTelemetry.TelemetryArea.Template, CookiecutterTelemetry.TemplateEvents.Delete, template); if (!string.IsNullOrEmpty(remote)) { var t = Installed.Templates.SingleOrDefault(current => (current as TemplateViewModel)?.RemoteUrl == remote) as TemplateViewModel; if (t != null) { Installed.Templates.Remove(t); } t = Recommended.Templates.SingleOrDefault(current => (current as TemplateViewModel)?.RemoteUrl == remote) as TemplateViewModel; if (t != null) { t.ClonedPath = string.Empty; } t = GitHub.Templates.SingleOrDefault(current => (current as TemplateViewModel)?.RemoteUrl == remote) as TemplateViewModel; if (t != null) { t.ClonedPath = string.Empty; } } else { if (Installed.Templates.Contains(template)) { Installed.Templates.Remove(template); } } } catch (Exception ex) when (!ex.IsCriticalException()) { _outputWindow.WriteErrorLine(ex.Message); _outputWindow.WriteLine(Strings.DeletingTemplateFailed.FormatUI(template.ClonedPath)); ReportTemplateEvent(CookiecutterTelemetry.TelemetryArea.Template, CookiecutterTelemetry.TemplateEvents.Delete, template, ex); } }
private void ReportTemplateEvent(string area, string eventName, TemplateViewModel selection, Exception error = null) { try { if (!_telemetry.TelemetryService.IsEnabled) { return; } var repoUrl = selection.RemoteUrl?.ToLowerInvariant() ?? string.Empty; var repoFullName = selection.RepositoryFullName?.ToLowerInvariant() ?? string.Empty; var repoOwner = selection.RepositoryOwner?.ToLowerInvariant() ?? string.Empty; var repoName = selection.RepositoryName?.ToLowerInvariant() ?? string.Empty; var projKind = TargetProjectLocation?.ProjectKind ?? string.Empty; var obj = new { Success = error == null, RepoUrl = new TelemetryPiiProperty(repoUrl), RepoFullName = new TelemetryPiiProperty(repoFullName), RepoOwner = new TelemetryPiiProperty(repoOwner), RepoName = new TelemetryPiiProperty(repoName), ProjectKind = projKind, }; ReportEvent(area, eventName, obj); } catch (Exception ex) { Debug.Fail($"Error reporting event.\n{ex.Message}"); } }
private void ReportTemplateEvent(string area, string eventName, TemplateViewModel selection, Exception error = null) { try { if (!_telemetry.TelemetryService.IsEnabled) { return; } var repoUrl = selection.RemoteUrl?.ToLowerInvariant(); var repoFullName = selection.RepositoryFullName?.ToLowerInvariant(); var repoOwner = selection.RepositoryOwner?.ToLowerInvariant(); var repoName = selection.RepositoryName?.ToLowerInvariant(); var obj = new { Success = error == null, RepoUrl = repoUrl?.GetSha512(), RepoFullName = repoFullName?.GetSha512(), RepoOwner = repoOwner?.GetSha512(), RepoName = repoName?.GetSha512(), }; ReportEvent(area, eventName, obj); } catch (Exception ex) { Debug.Fail($"Error reporting event.\n{ex.Message}"); } }
private async Task RefreshSelectedDescriptionAsync(TemplateViewModel selection) { if (selection == null) { SelectedDescription = string.Empty; return; } if (string.IsNullOrEmpty(selection.Description)) { await InitializeDescription(selection); } SelectedDescription = selection.Description ?? string.Empty; }