/// <summary> /// Sets the value. /// </summary> /// <param name="mergeTemplate">The merge template.</param> public void SetValue(MergeTemplate mergeTemplate) { if (mergeTemplate != null) { ItemId = mergeTemplate.Id.ToString(); var parentCategoryIds = new List <string>(); var parentCategory = mergeTemplate.Category; while (parentCategory != null) { if (!parentCategoryIds.Contains(parentCategory.Id.ToString())) { parentCategoryIds.Insert(0, parentCategory.Id.ToString()); } else { // infinite recursion break; } parentCategory = parentCategory.ParentCategory; } InitialItemParentIds = parentCategoryIds.AsDelimited(","); ItemName = mergeTemplate.Name; } else { ItemId = Constants.None.IdValue; ItemName = Constants.None.TextHtml; } }
private async Task <IEnumerable <MergeTemplate> > GetLegacyMergeTemplates(string spreadsheetId, CancellationToken cancellationToken = default(CancellationToken)) { var returnValue = new List <MergeTemplate>(); foreach (var row in await _sheetsService.GetValuesAsync(spreadsheetId, range: string.Concat("mm-config!", MM_CONFIG_ENTIRE_SHEET_RANGE))) { if (row.Count == 0) { _logger.Warning("Row {RowNumber} had no data, not contributing to returned merge templates", returnValue.Count + 1); } else if (row.Count == 1) { _logger.Warning("Row {RowNumber} had only 1 cell, not contributing to returned merge templates", returnValue.Count + 1); } else if (row[1] == null) { _logger.Warning("Row {RowNumber} did not contain a value in the second cell, not contributing to returned merge templates", returnValue.Count + 1); } else { returnValue.Add(MergeTemplate.CreateFrom(row[0].ToString(), spreadsheetId, row[1].ToString())); } } return(returnValue); }
/// <summary> /// Sets the value. /// </summary> /// <param name="mergeTemplate">The merge template.</param> public void SetValue( MergeTemplate mergeTemplate ) { if ( mergeTemplate != null ) { ItemId = mergeTemplate.Id.ToString(); var parentCategoryIds = new List<string>(); var parentCategory = mergeTemplate.Category; while ( parentCategory != null ) { if ( !parentCategoryIds.Contains( parentCategory.Id.ToString() ) ) { parentCategoryIds.Insert( 0, parentCategory.Id.ToString() ); } else { // infinite recursion break; } parentCategory = parentCategory.ParentCategory; } InitialItemParentIds = parentCategoryIds.AsDelimited( "," ); ItemName = mergeTemplate.Name; } else { ItemId = Constants.None.IdValue; ItemName = Constants.None.TextHtml; } }
public async Task <MergeTemplate> AddMergeTemplateAsync(MergeTemplate mergeTemplate, CancellationToken cancellationToken = default(CancellationToken)) { _mergeTemplateContext.MergeTemplates.Add(mergeTemplate); await _mergeTemplateContext.SaveChangesAsync(cancellationToken); return(mergeTemplate); }
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The merge object list.</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public override BinaryFile CreateDocument( MergeTemplate mergeTemplate, List<object> mergeObjectList, Dictionary<string, object> globalMergeFields ) { this.Exceptions = new List<Exception>(); BinaryFile outputBinaryFile = null; var rockContext = new RockContext(); var binaryFileService = new BinaryFileService( rockContext ); var templateBinaryFile = binaryFileService.Get( mergeTemplate.TemplateBinaryFileId ); if ( templateBinaryFile == null ) { return null; } string templateHtml = templateBinaryFile.ContentsToString(); var htmlMergeObjects = GetHtmlMergeObjects( mergeObjectList, globalMergeFields ); string outputHtml = templateHtml.ResolveMergeFields( htmlMergeObjects ); HtmlDocument outputDoc = new HtmlDocument(); outputDoc.LoadHtml( outputHtml ); var outputStream = new MemoryStream(); outputDoc.Save( outputStream ); outputBinaryFile = new BinaryFile(); outputBinaryFile.IsTemporary = true; outputBinaryFile.ContentStream = outputStream; outputBinaryFile.FileName = "MergeTemplateOutput" + Path.GetExtension( templateBinaryFile.FileName ); outputBinaryFile.MimeType = templateBinaryFile.MimeType; outputBinaryFile.BinaryFileTypeId = new BinaryFileTypeService( rockContext ).Get( Rock.SystemGuid.BinaryFiletype.DEFAULT.AsGuid() ).Id; binaryFileService.Add( outputBinaryFile ); rockContext.SaveChanges(); return outputBinaryFile; }
public async Task <MergeTemplate> UpdateMergeTemplateAsync(MergeTemplate mergeTemplate, CancellationToken cancellationToken = default(CancellationToken)) { var entry = _mergeTemplateContext.Entry(mergeTemplate); entry.State = EntityState.Modified; await _mergeTemplateContext.SaveChangesAsync(cancellationToken); return(mergeTemplate); }
/// <summary> /// Gets the type of the merge template. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="mergeTemplate">The merge template.</param> /// <returns></returns> private MergeTemplateType GetMergeTemplateType(RockContext rockContext, MergeTemplate mergeTemplate) { mergeTemplate = new MergeTemplateService(rockContext).Get(mtPicker.SelectedValue.AsInteger()); if (mergeTemplate == null) { return(null); } return(mergeTemplate.GetMergeTemplateType()); }
public void CreateMergeTemplateFromSerializedDataWithBadHeaderRowNumber() { dynamic templateJsonObject = JObject.Parse(Mocks.SAMPLE_MM_JSON); templateJsonObject.mergeData.headerRow = "NotANumber"; string templateJson = templateJsonObject.ToString(); var template = MergeTemplate.CreateFrom("Id1", "SheetId", templateJson); template.HeaderRowNumber.Should().Be(1); }
public void CreateMergeTempalteFromSerializedDataWithInvalidTypeThrows() { dynamic templateJsonObject = JObject.Parse(Mocks.SAMPLE_MM_JSON); templateJsonObject.mergeData.type = "badType"; string templateJson = templateJsonObject.ToString(); Action action = () => MergeTemplate.CreateFrom("Id1", "SheetId", templateJson); action.Should().Throw <InvalidOperationException>(); }
public void CreateMergeTemplateFromSerializedData(bool timeStampShouldPrefixNameWithMergeTemplateTitle) { dynamic templateJsonObject = JObject.Parse(Mocks.SAMPLE_MM_JSON); // set "usetitle" as per the timeStampShouldPrefixNameWithMergeTemplateTitle parameter templateJsonObject.mergeData.usetitle = timeStampShouldPrefixNameWithMergeTemplateTitle ? "true" : "false"; string templateJson = templateJsonObject.ToString(); var template = MergeTemplate.CreateFrom("Id1", "SheetId", templateJson); ValidateTemplateFromJson(template, templateJson); template.SpreadSheetId.Should().Be("SheetId"); }
public void CreateMergeTemplateFromSerializedDataWithWithoutCreatedDate() { dynamic templateJsonObject = JObject.Parse(Mocks.SAMPLE_MM_JSON); templateJsonObject.createdDatetime = null; string templateJson = templateJsonObject.ToString(); DateTime utcMin = DateTime.UtcNow; var template = MergeTemplate.CreateFrom("Id1", "SheetId", templateJson); DateTime utcMax = DateTime.UtcNow; template.CreatedDateUtc.Should().BeOnOrAfter(utcMin).And.BeOnOrBefore(utcMax); }
protected void ValidateTemplateFromJson(MergeTemplate template, string templateJson) { template.Should().NotBeNull(); dynamic templateJsonObject = JObject.Parse(templateJson); string expectedTitle = templateJsonObject.mergeData.title; string expectedCreatedBy = templateJsonObject.createdBy; DateTime expectedCreatedDate = templateJsonObject.createdDatetime; expectedCreatedDate = expectedCreatedDate.ToUniversalTime(); int expectedHeaderRowNumber = templateJsonObject.mergeData.headerRow; string expectedSheetname = templateJsonObject.mergeData.sheet; string expectedTimestampColumn = templateJsonObject.mergeData.timestampColumn; bool expectedShouldPrefixNameWithMergeTemplateTitle = templateJsonObject.mergeData.usetitle; template.Title.Should().Be(expectedTitle); template.CreatedBy.Should().Be(expectedCreatedBy); template.CreatedDateUtc.Should().Be(expectedCreatedDate); template.HeaderRowNumber.Should().Be(expectedHeaderRowNumber); template.SheetName.Should().Be(expectedSheetname); template.TimestampColumn.Should().NotBeNull(); template.TimestampColumn.Name.Should().Be(expectedTimestampColumn); // validation if the mergeTemplate is of type "Email" var emailMergeTemplate = template as EmailMergeTemplate; if (emailMergeTemplate != null) { template.Type.Should().Be(MergeTemplateType.Email); string expectedTo = templateJsonObject.mergeData.data.to; string expectedCc = templateJsonObject.mergeData.data.cc; string expectedBcc = templateJsonObject.mergeData.data.bcc; string expectedSubject = templateJsonObject.mergeData.data.subject; string expectedBody = templateJsonObject.mergeData.data.body; emailMergeTemplate.EmailTemplate.Should().NotBeNull(); emailMergeTemplate.EmailTemplate.To.Should().Be(expectedTo); emailMergeTemplate.EmailTemplate.Cc.Should().Be(expectedCc); emailMergeTemplate.EmailTemplate.Bcc.Should().Be(expectedBcc); emailMergeTemplate.EmailTemplate.Subject.Should().Be(expectedSubject); emailMergeTemplate.EmailTemplate.Body.Should().Be(expectedBody); } // default should be to add template.TimestampColumn.ShouldPrefixNameWithMergeTemplateTitle.Should().Be(expectedShouldPrefixNameWithMergeTemplateTitle); }
public void CreateMergeTemplateFromSerializedDataWithoutUser() { var template = MergeTemplate.CreateFrom("Id1", "SheetId", Mocks.SAMPLE_MM_JSON); template.Should().NotBeNull(); template.CreatedBy.Should().Be("Unknown user"); dynamic templateJsonObject = JObject.Parse(Mocks.SAMPLE_MM_JSON); templateJsonObject.createdBy = null; string templateJson = templateJsonObject.ToString(); template = MergeTemplate.CreateFrom("Id1", "SheetId", templateJson); template.Should().NotBeNull(); template.CreatedBy.Should().Be("Unknown user"); }
/// <summary> /// Gets the type of the merge template. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="mergeTemplate">The merge template.</param> /// <returns></returns> private MergeTemplateType GetMergeTemplateType(RockContext rockContext, MergeTemplate mergeTemplate) { mergeTemplate = new MergeTemplateService(rockContext).Get(mtPicker.SelectedValue.AsInteger()); if (mergeTemplate == null) { return(null); } var mergeTemplateTypeEntityType = EntityTypeCache.Read(mergeTemplate.MergeTemplateTypeEntityTypeId); if (mergeTemplateTypeEntityType == null) { return(null); } return(MergeTemplateTypeContainer.GetComponent(mergeTemplateTypeEntityType.Name)); }
/// <summary> /// Shows the readonly details. /// </summary> /// <param name="mergeTemplate">The merge template.</param> private void ShowReadonlyDetails(MergeTemplate mergeTemplate) { SetEditMode(false); hfMergeTemplateId.SetValue(mergeTemplate.Id); lReadOnlyTitle.Text = mergeTemplate.Name.FormatAsHtmlTitle(); DescriptionList descriptionListCol1 = new DescriptionList() .Add("Template File", string.Format("<a href='{0}'>{1}</a>", mergeTemplate.TemplateBinaryFile.Url, mergeTemplate.TemplateBinaryFile.FileName)) .Add("Description", mergeTemplate.Description ?? string.Empty) .Add("Type", mergeTemplate.MergeTemplateTypeEntityType); DescriptionList descriptionListCol2 = new DescriptionList() .Add("Category", mergeTemplate.Category != null ? mergeTemplate.Category.Name : string.Empty) .Add("Person", mergeTemplate.PersonAlias, false); lblMainDetailsCol1.Text = descriptionListCol1.Html; lblMainDetailsCol2.Text = descriptionListCol2.Html; }
private bool ShouldProcess(MergeTemplate mergeTemplate, IDictionary <string, object> values) { //TODO: skip rows that don't meet if (!string.IsNullOrWhiteSpace(mergeTemplate.Conditional)) { string conditional = Render(mergeTemplate.Conditional, values); bool shouldProcess; if (bool.TryParse(conditional, out shouldProcess)) { return(shouldProcess); } else { _logger.Warning("Could not interpret conditional value {Conditional}", conditional); } } return(true); }
/// <summary> /// Handles the Delete event of the gMergeTemplates control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param> protected void gMergeTemplates_Delete(object sender, RowEventArgs e) { var rockContext = new RockContext(); MergeTemplateService service = new MergeTemplateService(rockContext); MergeTemplate item = service.Get(e.RowKeyId); if (item != null) { string errorMessage; if (!service.CanDelete(item, out errorMessage)) { mdGridWarning.Show(errorMessage, ModalAlertType.Information); return; } service.Delete(item); rockContext.SaveChanges(); } BindGrid(); }
/// <summary> /// Sets the value. /// Expects value as a MergeTemplate.Guid as string /// </summary> /// <param name="control">The control.</param> /// <param name="configurationValues">The configuration values.</param> /// <param name="value">The value.</param> public override void SetEditValue(System.Web.UI.Control control, Dictionary <string, ConfigurationValue> configurationValues, string value) { var picker = control as MergeTemplatePicker; if (picker != null) { MergeTemplate item = null; Guid? itemGuid = value.AsGuidOrNull(); if (itemGuid.HasValue) { using (var rockContext = new RockContext()) { item = new MergeTemplateService(rockContext).Get(itemGuid.Value); picker.SetValue(item); } } else { picker.SetValue(null); } } }
/// <summary> /// Sets the value. /// </summary> /// <param name="mergeTemplate">The merge template.</param> public void SetValue( MergeTemplate mergeTemplate ) { if ( mergeTemplate != null ) { ItemId = mergeTemplate.Id.ToString(); string parentCategoryIds = string.Empty; var parentCategory = mergeTemplate.Category; while ( parentCategory != null ) { parentCategoryIds = parentCategory.Id + "," + parentCategoryIds; parentCategory = parentCategory.ParentCategory; } InitialItemParentIds = parentCategoryIds.TrimEnd( new[] { ',' } ); ItemName = mergeTemplate.Name; } else { ItemId = Constants.None.IdValue; ItemName = Constants.None.TextHtml; } }
/// <summary> /// Shows the readonly details. /// </summary> /// <param name="mergeTemplate">The merge template.</param> private void ShowReadonlyDetails(MergeTemplate mergeTemplate) { SetEditMode(false); hfMergeTemplateId.SetValue(mergeTemplate.Id); lReadOnlyTitle.Text = mergeTemplate.Name.FormatAsHtmlTitle(); var getFileUrl = string.Format( "{0}GetFile.ashx?guid={1}", System.Web.VirtualPathUtility.ToAbsolute("~"), mergeTemplate.TemplateBinaryFile != null ? mergeTemplate.TemplateBinaryFile.Guid : (Guid?)null); DescriptionList descriptionListCol1 = new DescriptionList() .Add("Template File", string.Format("<a href='{0}'>{1}</a>", getFileUrl, mergeTemplate.TemplateBinaryFile.FileName)) .Add("Description", mergeTemplate.Description ?? string.Empty) .Add("Type", mergeTemplate.MergeTemplateTypeEntityType); DescriptionList descriptionListCol2 = new DescriptionList() .Add("Category", mergeTemplate.Category != null ? mergeTemplate.Category.Name : string.Empty) .Add("Person", mergeTemplate.PersonAlias, false); lblMainDetailsCol1.Text = descriptionListCol1.Html; lblMainDetailsCol2.Text = descriptionListCol2.Html; }
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The merge object list.</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public override BinaryFile CreateDocument(MergeTemplate mergeTemplate, List <object> mergeObjectList, Dictionary <string, object> globalMergeFields) { this.Exceptions = new List <Exception>(); BinaryFile outputBinaryFile = null; var rockContext = new RockContext(); var binaryFileService = new BinaryFileService(rockContext); var templateBinaryFile = binaryFileService.Get(mergeTemplate.TemplateBinaryFileId); if (templateBinaryFile == null) { return(null); } string templateHtml = templateBinaryFile.ContentsToString(); var htmlMergeObjects = GetHtmlMergeObjects(mergeObjectList, globalMergeFields); string outputHtml = templateHtml.ResolveMergeFields(htmlMergeObjects); HtmlDocument outputDoc = new HtmlDocument(); outputDoc.LoadHtml(outputHtml); var outputStream = new MemoryStream(); outputDoc.Save(outputStream); outputBinaryFile = new BinaryFile(); outputBinaryFile.IsTemporary = true; outputBinaryFile.ContentStream = outputStream; outputBinaryFile.FileName = "MergeTemplateOutput" + Path.GetExtension(templateBinaryFile.FileName); outputBinaryFile.MimeType = templateBinaryFile.MimeType; outputBinaryFile.BinaryFileTypeId = new BinaryFileTypeService(rockContext).Get(Rock.SystemGuid.BinaryFiletype.DEFAULT.AsGuid()).Id; binaryFileService.Add(outputBinaryFile); rockContext.SaveChanges(); return(outputBinaryFile); }
/// <summary> /// Shows the detail. /// </summary> /// <param name="mergeTemplateId">The merge template identifier.</param> /// <param name="parentCategoryId">The parent category identifier.</param> public void ShowDetail( int mergeTemplateId, int? parentCategoryId ) { pnlDetails.Visible = false; var rockContext = new RockContext(); var mergeTemplateService = new MergeTemplateService( rockContext ); MergeTemplate mergeTemplate = null; if ( !mergeTemplateId.Equals( 0 ) ) { mergeTemplate = mergeTemplateService.Get( mergeTemplateId ); } var mergeTemplateOwnership = this.GetAttributeValue( "MergeTemplatesOwnership" ).ConvertToEnum<MergeTemplateOwnership>( MergeTemplateOwnership.Global ); if ( mergeTemplate == null ) { mergeTemplate = new MergeTemplate(); mergeTemplate.CategoryId = parentCategoryId ?? 0; if ( mergeTemplateOwnership == MergeTemplateOwnership.Personal ) { mergeTemplate.PersonAliasId = this.CurrentPersonAliasId; mergeTemplate.PersonAlias = this.CurrentPersonAlias; } } pnlDetails.Visible = true; hfMergeTemplateId.Value = mergeTemplateId.ToString(); // render UI based on Authorized bool readOnly = false; nbEditModeMessage.Text = string.Empty; if ( mergeTemplate.PersonAliasId.HasValue && mergeTemplate.PersonAlias.PersonId == this.CurrentPersonId ) { // Allow Person to edit their own Merge Templates } else if ( !IsUserAuthorized( Authorization.EDIT ) ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed( MergeTemplate.FriendlyTypeName ); } if ( readOnly ) { btnEdit.Visible = false; btnDelete.Visible = false; ShowReadonlyDetails( mergeTemplate ); } else { btnEdit.Visible = true; string errorMessage = string.Empty; btnDelete.Visible = mergeTemplateService.CanDelete( mergeTemplate, out errorMessage ); if ( mergeTemplate.Id > 0 ) { ShowReadonlyDetails( mergeTemplate ); } else { ShowEditDetails( mergeTemplate ); } } }
/// <summary> /// Handles the Click event of the btnSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void btnSave_Click( object sender, EventArgs e ) { MergeTemplate mergeTemplate; var rockContext = new RockContext(); MergeTemplateService mergeTemplateService = new MergeTemplateService( rockContext ); int mergeTemplateId = hfMergeTemplateId.Value.AsInteger(); int? origBinaryFileId = null; if ( mergeTemplateId == 0 ) { mergeTemplate = new MergeTemplate(); mergeTemplateService.Add( mergeTemplate ); } else { mergeTemplate = mergeTemplateService.Get( mergeTemplateId ); origBinaryFileId = mergeTemplate.TemplateBinaryFileId; } mergeTemplate.Name = tbName.Text; mergeTemplate.Description = tbDescription.Text; mergeTemplate.MergeTemplateTypeEntityTypeId = ddlMergeTemplateType.SelectedValue.AsInteger(); mergeTemplate.TemplateBinaryFileId = fuTemplateBinaryFile.BinaryFileId ?? 0; mergeTemplate.PersonAliasId = ppPerson.PersonAliasId; mergeTemplate.CategoryId = cpCategory.SelectedValue.AsInteger(); int personalMergeTemplateCategoryId = CategoryCache.Read( Rock.SystemGuid.Category.PERSONAL_MERGE_TEMPLATE.AsGuid() ).Id; if ( mergeTemplate.PersonAliasId.HasValue ) { if ( mergeTemplate.CategoryId == 0 ) { // if the category picker isn't shown and/or the category isn't selected, and it's a personal filter... mergeTemplate.CategoryId = personalMergeTemplateCategoryId; } // ensure Personal templates are only in the Personal merge template category if ( mergeTemplate.CategoryId != personalMergeTemplateCategoryId ) { // prohibit personal templates from being in something other than the Personal category cpCategory.Visible = true; cpCategory.ShowErrorMessage( "Personal Merge Templates must be in Personal category" ); } } else { if ( mergeTemplate.CategoryId == personalMergeTemplateCategoryId ) { // prohibit global templates from being in Personal category cpCategory.ShowErrorMessage( "Person is required when using the Personal category" ); } } if ( !mergeTemplate.IsValid || !Page.IsValid ) { // Controls will render the error messages return; } BinaryFileService binaryFileService = new BinaryFileService( rockContext ); if ( origBinaryFileId.HasValue && origBinaryFileId.Value != mergeTemplate.TemplateBinaryFileId ) { // if a new the binaryFile was uploaded, mark the old one as Temporary so that it gets cleaned up var oldBinaryFile = binaryFileService.Get( origBinaryFileId.Value ); if ( oldBinaryFile != null && !oldBinaryFile.IsTemporary ) { oldBinaryFile.IsTemporary = true; } } // ensure the IsTemporary is set to false on binaryFile associated with this MergeTemplate var binaryFile = binaryFileService.Get( mergeTemplate.TemplateBinaryFileId ); if ( binaryFile != null && binaryFile.IsTemporary ) { binaryFile.IsTemporary = false; } rockContext.SaveChanges(); var qryParams = new Dictionary<string, string>(); qryParams["ExpandedIds"] = PageParameter( "ExpandedIds" ); qryParams["MergeTemplateId"] = mergeTemplate.Id.ToString(); NavigateToPage( RockPage.Guid, qryParams ); }
/// <summary> /// Shows the readonly details. /// </summary> /// <param name="mergeTemplate">The merge template.</param> private void ShowReadonlyDetails( MergeTemplate mergeTemplate ) { SetEditMode( false ); hfMergeTemplateId.SetValue( mergeTemplate.Id ); lReadOnlyTitle.Text = mergeTemplate.Name.FormatAsHtmlTitle(); DescriptionList descriptionListCol1 = new DescriptionList() .Add( "Template File", string.Format( "<a href='{0}'>{1}</a>", mergeTemplate.TemplateBinaryFile.Url, mergeTemplate.TemplateBinaryFile.FileName ) ) .Add( "Description", mergeTemplate.Description ?? string.Empty ) .Add( "Type", mergeTemplate.MergeTemplateTypeEntityType ); DescriptionList descriptionListCol2 = new DescriptionList() .Add( "Category", mergeTemplate.Category != null ? mergeTemplate.Category.Name : string.Empty ) .Add( "Person", mergeTemplate.PersonAlias, false ); lblMainDetailsCol1.Text = descriptionListCol1.Html; lblMainDetailsCol2.Text = descriptionListCol2.Html; }
public async Task <RunMergeTemplateProgress> RunMergeTemplateAsync( MergeTemplate mergeTemplate, Action <RunMergeTemplateProgress> progressReporter = null, CancellationToken cancellationToken = default(CancellationToken)) { if (mergeTemplate == null) { throw new ArgumentNullException(nameof(mergeTemplate)); } var emailMergeTemplate = mergeTemplate as EmailMergeTemplate; if (emailMergeTemplate == null) { throw new InvalidOperationException("Currently only mergeTemplates of type Email are supported."); } if (emailMergeTemplate.EmailTemplate == null) { throw new InvalidOperationException("MergeTemplate.EmailTemplate cannot ben null"); } string range = mergeTemplate.SheetName; var dataRange = await _sheetsService.GetDataRangeAsync( mergeTemplate.SpreadSheetId, mergeTemplate.SheetName); if (mergeTemplate.HeaderRowNumber > 1) { // we'll define a custom range range = new A1Notation(mergeTemplate.SheetName, dataRange.StartColumn, mergeTemplate.HeaderRowNumber, dataRange.EndColumn, dataRange.EndRow).ToString(); } if (!dataRange.EndRow.HasValue) { throw new InvalidOperationException($"Could not retrieve the number of rows in sheet {mergeTemplate.SheetName}"); } var progress = new RunMergeTemplateProgress( dataRange.EndRow.Value - (dataRange.StartRow ?? 1)); await _sheetsService.GetValuesAsDictionaryDataPump(mergeTemplate.SpreadSheetId, range, async values => { if (!ShouldProcess(mergeTemplate, values)) { progress.AddSkipped(); return; } try { await ProcessMergeTemplateAsync(emailMergeTemplate.EmailTemplate, values); progress.AddProcessed(); } catch (Exception err) { _logger.Error(err, "Error processing merge template"); progress.AddError(); } finally { progressReporter?.Invoke(progress); } }); return(progress); }
/// <summary> /// Shows the edit details. /// </summary> /// <param name="mergeTemplate">The merge template.</param> public void ShowEditDetails( MergeTemplate mergeTemplate ) { if ( mergeTemplate.Id > 0 ) { lActionTitle.Text = ActionTitle.Edit( mergeTemplate.ToString() ).FormatAsHtmlTitle(); } else { lActionTitle.Text = ActionTitle.Add( MergeTemplate.FriendlyTypeName ).FormatAsHtmlTitle(); } SetEditMode( true ); var mergeTemplateOwnership = this.GetAttributeValue( "MergeTemplatesOwnership" ).ConvertToEnum<MergeTemplateOwnership>( MergeTemplateOwnership.Global ); if ( this.IsUserAuthorized( Authorization.EDIT ) ) { // If Authorized to EDIT, owner should be able to be changed, or converted to Global ppPerson.Visible = true; ppPerson.Required = false; cpCategory.Visible = true; cpCategory.ExcludedCategoryIds = string.Empty; } else if ( mergeTemplateOwnership == MergeTemplateOwnership.Global ) { ppPerson.Visible = false; ppPerson.Required = false; cpCategory.Visible = true; cpCategory.ExcludedCategoryIds = CategoryCache.Read( Rock.SystemGuid.Category.PERSONAL_MERGE_TEMPLATE.AsGuid() ).Id.ToString(); } else if ( mergeTemplateOwnership == MergeTemplateOwnership.Personal ) { // if ONLY personal merge templates are permitted, hide the category since it'll always be saved in the personal category cpCategory.Visible = false; // merge template should be only for the current person, so hide the person picker ppPerson.Visible = false; // it is required, but not shown.. ppPerson.Required = false; } else if ( mergeTemplateOwnership == MergeTemplateOwnership.PersonalAndGlobal ) { ppPerson.Visible = true; ppPerson.Required = false; cpCategory.Visible = true; cpCategory.ExcludedCategoryIds = string.Empty; } tbName.Text = mergeTemplate.Name; tbDescription.Text = mergeTemplate.Description; LoadDropDowns(); ddlMergeTemplateType.SetValue( mergeTemplate.MergeTemplateTypeEntityTypeId ); fuTemplateBinaryFile.BinaryFileTypeGuid = Rock.SystemGuid.BinaryFiletype.MERGE_TEMPLATE.AsGuid(); fuTemplateBinaryFile.BinaryFileId = mergeTemplate.TemplateBinaryFileId; cpCategory.AllowMultiSelect = false; cpCategory.SetValue( mergeTemplate.CategoryId ); if ( mergeTemplate.PersonAliasId.HasValue ) { ppPerson.SetValue( mergeTemplate.PersonAlias.Person ); } else { ppPerson.SetValue( null ); } }
/// <summary> /// Shows the detail. /// </summary> /// <param name="mergeTemplateId">The merge template identifier.</param> /// <param name="parentCategoryId">The parent category identifier.</param> public void ShowDetail(int mergeTemplateId, int?parentCategoryId) { pnlDetails.Visible = false; var rockContext = new RockContext(); var mergeTemplateService = new MergeTemplateService(rockContext); MergeTemplate mergeTemplate = null; if (!mergeTemplateId.Equals(0)) { mergeTemplate = mergeTemplateService.Get(mergeTemplateId); pdAuditDetails.SetEntity(mergeTemplate, ResolveRockUrl("~")); } var mergeTemplateOwnership = this.GetAttributeValue("MergeTemplatesOwnership").ConvertToEnum <MergeTemplateOwnership>(MergeTemplateOwnership.Global); if (mergeTemplate == null) { mergeTemplate = new MergeTemplate(); mergeTemplate.CategoryId = parentCategoryId ?? 0; if (mergeTemplateOwnership == MergeTemplateOwnership.Personal) { mergeTemplate.PersonAliasId = this.CurrentPersonAliasId; mergeTemplate.PersonAlias = this.CurrentPersonAlias; // Don't show security on a personal merge template. btnSecurity.Visible = false; } // hide the panel drawer that show created and last modified dates pdAuditDetails.Visible = false; } pnlDetails.Visible = true; hfMergeTemplateId.Value = mergeTemplateId.ToString(); // render UI based on Authorized bool readOnly = false; nbEditModeMessage.Text = string.Empty; if (mergeTemplate.PersonAliasId.HasValue && mergeTemplate.PersonAlias.PersonId == this.CurrentPersonId) { // Allow Person to edit their own Merge Templates } else if (!IsUserAuthorized(Authorization.EDIT)) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed(MergeTemplate.FriendlyTypeName); } btnSecurity.Visible = mergeTemplate.IsAuthorized(Authorization.ADMINISTRATE, CurrentPerson); btnSecurity.Title = mergeTemplate.Name; btnSecurity.EntityId = mergeTemplate.Id; if (readOnly) { btnEdit.Visible = false; btnDelete.Visible = false; ShowReadonlyDetails(mergeTemplate); } else { btnEdit.Visible = true; string errorMessage = string.Empty; btnDelete.Visible = mergeTemplateService.CanDelete(mergeTemplate, out errorMessage); if (mergeTemplate.Id > 0) { ShowReadonlyDetails(mergeTemplate); } else { ShowEditDetails(mergeTemplate); } } }
/// <summary> /// Shows the edit details. /// </summary> /// <param name="mergeTemplate">The merge template.</param> public void ShowEditDetails(MergeTemplate mergeTemplate) { if (mergeTemplate.Id > 0) { lActionTitle.Text = ActionTitle.Edit(mergeTemplate.ToString()).FormatAsHtmlTitle(); } else { lActionTitle.Text = ActionTitle.Add(MergeTemplate.FriendlyTypeName).FormatAsHtmlTitle(); } SetEditMode(true); var mergeTemplateOwnership = this.GetAttributeValue("MergeTemplatesOwnership").ConvertToEnum <MergeTemplateOwnership>(MergeTemplateOwnership.Global); if (this.IsUserAuthorized(Authorization.EDIT)) { // If Authorized to EDIT, owner should be able to be changed, or converted to Global ppPerson.Visible = true; ppPerson.Required = false; cpCategory.Visible = true; cpCategory.ExcludedCategoryIds = string.Empty; } else if (mergeTemplateOwnership == MergeTemplateOwnership.Global) { ppPerson.Visible = false; ppPerson.Required = false; cpCategory.Visible = true; cpCategory.ExcludedCategoryIds = CategoryCache.Get(Rock.SystemGuid.Category.PERSONAL_MERGE_TEMPLATE.AsGuid()).Id.ToString(); } else if (mergeTemplateOwnership == MergeTemplateOwnership.Personal) { // if ONLY personal merge templates are permitted, hide the category since it'll always be saved in the personal category cpCategory.Visible = false; // merge template should be only for the current person, so hide the person picker ppPerson.Visible = false; // it is required, but not shown.. ppPerson.Required = false; } else if (mergeTemplateOwnership == MergeTemplateOwnership.PersonalAndGlobal) { ppPerson.Visible = true; ppPerson.Required = false; cpCategory.Visible = true; cpCategory.ExcludedCategoryIds = string.Empty; } tbName.Text = mergeTemplate.Name; tbDescription.Text = mergeTemplate.Description; LoadDropDowns(); ddlMergeTemplateType.SetValue(mergeTemplate.MergeTemplateTypeEntityTypeId); fuTemplateBinaryFile.BinaryFileTypeGuid = Rock.SystemGuid.BinaryFiletype.MERGE_TEMPLATE.AsGuid(); fuTemplateBinaryFile.BinaryFileId = mergeTemplate.TemplateBinaryFileId; cpCategory.AllowMultiSelect = false; cpCategory.SetValue(mergeTemplate.CategoryId); if (mergeTemplate.PersonAliasId.HasValue) { ppPerson.SetValue(mergeTemplate.PersonAlias.Person); } else { ppPerson.SetValue(null); } }
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The merge object list.</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public abstract BinaryFile CreateDocument( MergeTemplate mergeTemplate, List<object> mergeObjectList, Dictionary<string, object> globalMergeFields );
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The merge object list.</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public abstract BinaryFile CreateDocument(MergeTemplate mergeTemplate, List <object> mergeObjectList, Dictionary <string, object> globalMergeFields);
public async Task DeleteMergeTemplateAsync(MergeTemplate mergeTemplate, CancellationToken cancellationToken = default(CancellationToken)) { _mergeTemplateContext.MergeTemplates.Remove(mergeTemplate); await _mergeTemplateContext.SaveChangesAsync(cancellationToken); }
/// <summary> /// Gets the type of the merge template. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="mergeTemplate">The merge template.</param> /// <returns></returns> private MergeTemplateType GetMergeTemplateType( RockContext rockContext, MergeTemplate mergeTemplate ) { mergeTemplate = new MergeTemplateService( rockContext ).Get( mtPicker.SelectedValue.AsInteger() ); if ( mergeTemplate == null ) { return null; } return mergeTemplate.GetMergeTemplateType(); }
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The merge object list.</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public override BinaryFile CreateDocument( MergeTemplate mergeTemplate, List<object> mergeObjectList, Dictionary<string, object> globalMergeFields ) { this.Exceptions = new List<Exception>(); BinaryFile outputBinaryFile = null; var rockContext = new RockContext(); var binaryFileService = new BinaryFileService( rockContext ); var templateBinaryFile = binaryFileService.Get( mergeTemplate.TemplateBinaryFileId ); if ( templateBinaryFile == null ) { return null; } var sourceTemplateStream = templateBinaryFile.ContentStream; // Start by creating a new document with the contents of the Template (so that Styles, etc get included) using ( MemoryStream outputDocStream = new MemoryStream() ) { sourceTemplateStream.CopyTo( outputDocStream ); outputDocStream.Seek( 0, SeekOrigin.Begin ); // now that we have the outputdoc started, simplify the sourceTemplate var simplifiedDoc = WordprocessingDocument.Open( sourceTemplateStream, true ); MarkupSimplifier.SimplifyMarkup( simplifiedDoc, this.simplifyMarkupSettingsAll ); //// simplify any nodes that have Lava in it that might not have been caught by the MarkupSimplifier //// MarkupSimplifier only merges superfluous runs that are children of a paragraph var simplifiedDocX = simplifiedDoc.MainDocumentPart.GetXDocument(); OpenXmlRegex.Match( simplifiedDocX.Elements(), this.lavaRegEx, ( x, m ) => { foreach ( var nonParagraphRunsParent in x.DescendantNodes().OfType<XElement>().Where( a => a.Parent != null && a.Name != null ) .Where( a => ( a.Name.LocalName == "r" ) ).Select( a => a.Parent ).Distinct().ToList() ) { if ( lavaRegEx.IsMatch( nonParagraphRunsParent.Value ) ) { var tempParent = XElement.Parse( new Paragraph().OuterXml ); tempParent.Add( nonParagraphRunsParent.Nodes() ); tempParent = MarkupSimplifier.MergeAdjacentSuperfluousRuns( tempParent ); nonParagraphRunsParent.ReplaceNodes( tempParent.Nodes() ); } } } ); XElement lastLavaNode = simplifiedDocX.DescendantNodes().OfType<XElement>().LastOrDefault( a => lavaRegEx.IsMatch( a.Value ) ); // ensure there is a { Next } indicator after the last lava node in the template if (lastLavaNode != null) { var nextRecordMatch = nextRecordRegEx.Match( lastLavaNode.Value ); if ( nextRecordMatch == null || !nextRecordMatch.Success ) { // if the last lava node doesn't have a { next }, append to the end lastLavaNode.Value += " {% next %} "; } else { if ( !lastLavaNode.Value.EndsWith( nextRecordMatch.Value ) ) { // if the last lava node does have a { next }, but there is stuff after it, add it (just in case) lastLavaNode.Value += " {% next %} "; } } } simplifiedDoc.MainDocumentPart.PutXDocument(); sourceTemplateStream.Seek( 0, SeekOrigin.Begin ); bool? allSameParent = null; using ( WordprocessingDocument outputDoc = WordprocessingDocument.Open( outputDocStream, true ) ) { var xdoc = outputDoc.MainDocumentPart.GetXDocument(); var outputBodyNode = xdoc.DescendantNodes().OfType<XElement>().FirstOrDefault( a => a.Name.LocalName.Equals( "body" ) ); outputBodyNode.RemoveNodes(); int recordIndex = 0; int? lastRecordIndex = null; int recordCount = mergeObjectList.Count(); while ( recordIndex < recordCount ) { if ( lastRecordIndex.HasValue && lastRecordIndex == recordIndex ) { // something went wrong, so throw to avoid spinning infinately throw new Exception( "Unexpected unchanged recordIndex" ); } lastRecordIndex = recordIndex; using ( var tempMergeTemplateStream = new MemoryStream() ) { sourceTemplateStream.Position = 0; sourceTemplateStream.CopyTo( tempMergeTemplateStream ); tempMergeTemplateStream.Position = 0; var tempMergeWordDoc = WordprocessingDocument.Open( tempMergeTemplateStream, true ); var tempMergeTemplateX = tempMergeWordDoc.MainDocumentPart.GetXDocument(); var tempMergeTemplateBodyNode = tempMergeTemplateX.DescendantNodes().OfType<XElement>().FirstOrDefault( a => a.Name.LocalName.Equals( "body" ) ); // find all the Nodes that have a {% next %}. List<XElement> nextIndicatorNodes = new List<XElement>(); OpenXmlRegex.Match( tempMergeTemplateX.Elements(), this.nextRecordRegEx, ( x, m ) => { nextIndicatorNodes.Add( x ); } ); allSameParent = allSameParent ?? nextIndicatorNodes.Count > 1 && nextIndicatorNodes.Select( a => a.Parent ).Distinct().Count() == 1; List<XContainer> recordContainerNodes = new List<XContainer>(); foreach ( var nextIndicatorNodeParent in nextIndicatorNodes.Select( a => a.Parent ).Where( a => a != null ) ) { XContainer recordContainerNode = nextIndicatorNodeParent; if ( !allSameParent.Value ) { // go up the parent nodes until we have more than one "Next" descendent so that we know what to consider our record container while ( recordContainerNode.Parent != null ) { if ( this.nextRecordRegEx.Matches( recordContainerNode.Parent.Value ).Count == 1 ) { // still just the one "next" indicator, so go out another parent recordContainerNode = recordContainerNode.Parent; } else { // we went too far up the parents and found multiple "next" children, so use this node as the recordContainerNode break; } } } if ( !recordContainerNodes.Contains( recordContainerNode ) ) { recordContainerNodes.Add( recordContainerNode ); } } foreach ( var recordContainerNode in recordContainerNodes ) { //// loop thru each of the recordContainerNodes //// If we have more records than nodes, we'll jump out to the outer "while" and append another template and keep going XContainer mergedXRecord; var recordContainerNodeXml = recordContainerNode.ToString( SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces ).ReplaceWordChars(); if ( recordIndex >= recordCount ) { // out of records, so clear out any remaining template nodes that haven't been merged string xml = recordContainerNodeXml; mergedXRecord = XElement.Parse( xml ) as XContainer; OpenXmlRegex.Replace( mergedXRecord.Nodes().OfType<XElement>(), this.regExDot, string.Empty, ( a, b ) => { return true; } ); recordIndex++; } else { //// just in case they have shared parent node, or if there is trailing {{ next }} after the last lava //// on the page, split the XML for each record and reassemble it when done List<string> xmlChunks = this.nextRecordRegEx.Split( recordContainerNodeXml ).ToList(); string mergedXml = string.Empty; foreach ( var xml in xmlChunks ) { if ( xml.HasMergeFields() ) { if ( recordIndex < recordCount ) { try { DotLiquid.Hash wordMergeObjects = new DotLiquid.Hash(); wordMergeObjects.Add( "Row", mergeObjectList[recordIndex] ); foreach ( var field in globalMergeFields ) { wordMergeObjects.Add( field.Key, field.Value ); } mergedXml += xml.ResolveMergeFields( wordMergeObjects, true, true ); } catch ( Exception ex ) { // if ResolveMergeFields failed, log the exception, then just return the orig xml this.Exceptions.Add( ex ); mergedXml += xml; } recordIndex++; } else { // out of records, so just keep it as the templated xml mergedXml += xml; } } else { mergedXml += xml; } } mergedXRecord = XElement.Parse( mergedXml ) as XContainer; } // remove the orig nodes and replace with merged nodes recordContainerNode.RemoveNodes(); recordContainerNode.Add( mergedXRecord.Nodes().OfType<XElement>() ); var mergedRecordContainer = XElement.Parse( recordContainerNode.ToString( SaveOptions.DisableFormatting ) ); if ( recordContainerNode.Parent != null ) { // the recordContainerNode is some child/descendent of <body> recordContainerNode.ReplaceWith( mergedRecordContainer ); } else { // the recordContainerNode is the <body> recordContainerNode.RemoveNodes(); recordContainerNode.Add( mergedRecordContainer.Nodes() ); if ( recordIndex < recordCount ) { // add page break var pageBreakXml = new Paragraph( new Run( new Break() { Type = BreakValues.Page } ) ).OuterXml; var pageBreak = XElement.Parse( pageBreakXml, LoadOptions.None ); var lastParagraph = recordContainerNode.Nodes().OfType<XElement>().Where( a => a.Name.LocalName == "p" ).LastOrDefault(); if ( lastParagraph != null ) { lastParagraph.AddAfterSelf( pageBreak ); } } } } outputBodyNode.Add( tempMergeTemplateBodyNode.Nodes() ); } } // remove all the 'next' delimiters OpenXmlRegex.Replace( outputBodyNode.Nodes().OfType<XElement>(), this.nextRecordRegEx, string.Empty, ( xx, mm ) => { return true; } ); // remove all but the last SectionProperties element (there should only be one per section (body)) var sectPrItems = outputBodyNode.Nodes().OfType<XElement>().Where( a => a.Name.LocalName == "sectPr" ); foreach ( var extra in sectPrItems.Where( a => a != sectPrItems.Last() ).ToList() ) { extra.Remove(); } // renumber all the ids to make sure they are unique var idAttrs = xdoc.DescendantNodes().OfType<XElement>().Where( a => a.HasAttributes ).Select( a => a.Attribute( "id" ) ).Where( s => s != null ); int lastId = 1; foreach ( var attr in idAttrs ) { attr.Value = lastId.ToString(); lastId++; } // remove the last pagebreak MarkupSimplifier.SimplifyMarkup( outputDoc, new SimplifyMarkupSettings { RemoveLastRenderedPageBreak = true } ); // If you want to see validation errors /* var validator = new OpenXmlValidator(); var errors = validator.Validate( outputDoc ).ToList(); */ } outputBinaryFile = new BinaryFile(); outputBinaryFile.IsTemporary = true; outputBinaryFile.ContentStream = outputDocStream; outputBinaryFile.FileName = "MergeTemplateOutput" + Path.GetExtension( templateBinaryFile.FileName ); outputBinaryFile.MimeType = templateBinaryFile.MimeType; outputBinaryFile.BinaryFileTypeId = new BinaryFileTypeService( rockContext ).Get( Rock.SystemGuid.BinaryFiletype.DEFAULT.AsGuid() ).Id; binaryFileService.Add( outputBinaryFile ); rockContext.SaveChanges(); } return outputBinaryFile; }
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The merge object list.</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public override BinaryFile CreateDocument(MergeTemplate mergeTemplate, List <object> mergeObjectList, Dictionary <string, object> globalMergeFields) { this.Exceptions = new List <Exception>(); BinaryFile outputBinaryFile = null; var rockContext = new RockContext(); var binaryFileService = new BinaryFileService(rockContext); var templateBinaryFile = binaryFileService.Get(mergeTemplate.TemplateBinaryFileId); if (templateBinaryFile == null) { return(null); } // Start by creating a new document with the contents of the Template (so that Styles, etc get included) XDocument sourceTemplateDocX; // NOTE: On using multiple IDisposable, see https://stackoverflow.com/a/12603126/1755417 and https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement using (MemoryStream sourceTemplateStream = new MemoryStream(), outputDocStream = new MemoryStream()) { templateBinaryFile.ContentStream.CopyTo(outputDocStream); outputDocStream.Seek(0, SeekOrigin.Begin); // now that we have the outputdoc started, simplify the sourceTemplate templateBinaryFile.ContentStream.CopyTo(sourceTemplateStream); sourceTemplateStream.Seek(0, SeekOrigin.Begin); var simplifiedDoc = WordprocessingDocument.Open(sourceTemplateStream, true); MarkupSimplifier.SimplifyMarkup(simplifiedDoc, this.simplifyMarkupSettingsAll); //// simplify any nodes that have Lava in it that might not have been caught by the MarkupSimplifier //// MarkupSimplifier only merges superfluous runs that are children of a paragraph sourceTemplateDocX = simplifiedDoc.MainDocumentPart.GetXDocument(); OpenXmlRegex.Match( sourceTemplateDocX.Elements(), this.lavaRegEx, (x, m) => { foreach (var nonParagraphRunsParent in x.DescendantNodes().OfType <XElement>().Where(a => a.Parent != null && a.Name != null) .Where(a => (a.Name.LocalName == "r")).Select(a => a.Parent).Distinct().ToList()) { if (lavaRegEx.IsMatch(nonParagraphRunsParent.Value)) { var tempParent = XElement.Parse(new Paragraph().OuterXml); tempParent.Add(nonParagraphRunsParent.Nodes()); tempParent = MarkupSimplifier.MergeAdjacentSuperfluousRuns(tempParent); nonParagraphRunsParent.ReplaceNodes(tempParent.Nodes()); } } }); XElement lastLavaNode = sourceTemplateDocX.DescendantNodes().OfType <XElement>().LastOrDefault(a => lavaRegEx.IsMatch(a.Value)); // ensure there is a { Next } indicator after the last lava node in the template if (lastLavaNode != null) { var nextRecordMatch = nextRecordRegEx.Match(lastLavaNode.Value); if (nextRecordMatch == null || !nextRecordMatch.Success) { // if the last lava node doesn't have a { next }, append to the end lastLavaNode.Value += " {% next %} "; } else { if (!lastLavaNode.Value.EndsWith(nextRecordMatch.Value)) { // if the last lava node does have a { next }, but there is stuff after it, add it (just in case) lastLavaNode.Value += " {% next %} "; } } } bool?allSameParent = null; using (WordprocessingDocument outputDoc = WordprocessingDocument.Open(outputDocStream, true)) { var xdoc = outputDoc.MainDocumentPart.GetXDocument(); var outputBodyNode = xdoc.DescendantNodes().OfType <XElement>().FirstOrDefault(a => a.Name.LocalName.Equals("body")); outputBodyNode.RemoveNodes(); int recordIndex = 0; int?lastRecordIndex = null; int recordCount = mergeObjectList.Count(); while (recordIndex < recordCount) { if (lastRecordIndex.HasValue && lastRecordIndex == recordIndex) { // something went wrong, so throw to avoid spinning infinitely throw new Exception("Unexpected unchanged recordIndex"); } lastRecordIndex = recordIndex; using (var tempMergeTemplateStream = new MemoryStream()) { sourceTemplateStream.Position = 0; sourceTemplateStream.CopyTo(tempMergeTemplateStream); tempMergeTemplateStream.Position = 0; var tempMergeTemplateX = new XDocument(sourceTemplateDocX); var tempMergeTemplateBodyNode = tempMergeTemplateX.DescendantNodes().OfType <XElement>().FirstOrDefault(a => a.Name.LocalName.Equals("body")); // find all the Nodes that have a {% next %}. List <XElement> nextIndicatorNodes = new List <XElement>(); OpenXmlRegex.Match( tempMergeTemplateX.Elements(), this.nextRecordRegEx, (x, m) => { nextIndicatorNodes.Add(x); }); allSameParent = allSameParent ?? nextIndicatorNodes.Count > 1 && nextIndicatorNodes.Select(a => a.Parent).Distinct().Count() == 1; List <XContainer> recordContainerNodes = new List <XContainer>(); foreach (var nextIndicatorNodeParent in nextIndicatorNodes.Select(a => a.Parent).Where(a => a != null)) { XContainer recordContainerNode = nextIndicatorNodeParent; if (!allSameParent.Value) { // go up the parent nodes until we have more than one "Next" descendent so that we know what to consider our record container while (recordContainerNode.Parent != null) { if (this.nextRecordRegEx.Matches(recordContainerNode.Parent.Value).Count == 1) { // still just the one "next" indicator, so go out another parent recordContainerNode = recordContainerNode.Parent; } else { // we went too far up the parents and found multiple "next" children, so use this node as the recordContainerNode break; } } } if (!recordContainerNodes.Contains(recordContainerNode)) { recordContainerNodes.Add(recordContainerNode); } } foreach (var recordContainerNode in recordContainerNodes) { //// loop thru each of the recordContainerNodes //// If we have more records than nodes, we'll jump out to the outer "while" and append another template and keep going XContainer mergedXRecord; var recordContainerNodeXml = recordContainerNode.ToString(SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces).ReplaceWordChars(); if (recordIndex >= recordCount) { // out of records, so clear out any remaining template nodes that haven't been merged string xml = recordContainerNodeXml; mergedXRecord = XElement.Parse(xml) as XContainer; OpenXmlRegex.Replace(mergedXRecord.Nodes().OfType <XElement>(), this.regExDot, string.Empty, (a, b) => { return(true); }); recordIndex++; } else { //// just in case they have shared parent node, or if there is trailing {{ next }} after the last lava //// on the page, split the XML for each record and reassemble it when done List <string> xmlChunks = this.nextRecordRegEx.Split(recordContainerNodeXml).ToList(); string mergedXml = string.Empty; foreach (var xml in xmlChunks) { bool incRecordIndex = true; if (lavaRegEx.IsMatch(xml)) { if (recordIndex < recordCount) { try { var wordMergeObjects = new LavaDataDictionary(); wordMergeObjects.Add("Row", mergeObjectList[recordIndex]); foreach (var field in globalMergeFields) { wordMergeObjects.Add(field.Key, field.Value); } var resolvedXml = xml.ResolveMergeFields(wordMergeObjects, true, true); mergedXml += resolvedXml; if (resolvedXml == xml) { // there weren't any MergeFields after all, so don't move to the next record incRecordIndex = false; } } catch (Exception ex) { // if ResolveMergeFields failed, log the exception, then just return the orig xml this.Exceptions.Add(ex); mergedXml += xml; } if (incRecordIndex) { recordIndex++; } } else { // out of records, so put a special '{% next_empty %}' that we can use to clear up unmerged parts of the template mergedXml += " {% next_empty %} " + xml; } } else { mergedXml += xml; } } mergedXRecord = XElement.Parse(mergedXml) as XContainer; } // remove the orig nodes and replace with merged nodes recordContainerNode.RemoveNodes(); recordContainerNode.Add(mergedXRecord.Nodes().OfType <XElement>()); var mergedRecordContainer = XElement.Parse(recordContainerNode.ToString(SaveOptions.DisableFormatting)); if (recordContainerNode.Parent != null) { // the recordContainerNode is some child/descendent of <body> recordContainerNode.ReplaceWith(mergedRecordContainer); } else { // the recordContainerNode is the <body> recordContainerNode.RemoveNodes(); recordContainerNode.Add(mergedRecordContainer.Nodes()); if (recordIndex < recordCount) { // add page break var pageBreakXml = new Paragraph(new Run(new Break() { Type = BreakValues.Page })).OuterXml; var pageBreak = XElement.Parse(pageBreakXml, LoadOptions.None); var lastParagraph = recordContainerNode.Nodes().OfType <XElement>().Where(a => a.Name.LocalName == "p").LastOrDefault(); if (lastParagraph != null) { lastParagraph.AddAfterSelf(pageBreak); // Add page formatting for the page before the page break. var lastSectPr = recordContainerNode.Nodes().OfType <XElement>().Where(a => a.Name.LocalName == "sectPr").LastOrDefault(); if (lastSectPr != null) { var paragraphPropertiesXml = new Paragraph(new ParagraphProperties(new SectionProperties(lastSectPr.ToString()))).OuterXml; var paragraphProperties = XElement.Parse(paragraphPropertiesXml, LoadOptions.None); pageBreak.AddAfterSelf(paragraphProperties); } } } } } outputBodyNode.Add(tempMergeTemplateBodyNode.Nodes()); } } // remove all the 'next' delimiters OpenXmlRegex.Replace(outputBodyNode.Nodes().OfType <XElement>(), this.nextRecordRegEx, string.Empty, (xx, mm) => { return(true); }); // find all the 'next_empty' delimiters that we might have added and clear out the content in the paragraph nodes that follow OpenXmlRegex.Match( outputBodyNode.Nodes().OfType <XElement>(), this.nextEmptyRecordRegEx, (xx, mm) => { var afterSiblings = xx.ElementsAfterSelf().ToList(); // get all the paragraph elements after the 'next_empty' node and clear out the content var nodesToClean = afterSiblings.Where(a => a.Name.LocalName == "p").ToList(); // if the next_empty node has lava, clean that up too var xxContent = xx.ToString(); if (lavaRegEx.IsMatch(xxContent)) { nodesToClean.Add(xx); } foreach (var node in nodesToClean) { // remove all child nodes from each paragraph node if (node.HasElements) { node.RemoveNodes(); } } }); // remove all the 'next_empty' delimiters OpenXmlRegex.Replace(outputBodyNode.Nodes().OfType <XElement>(), this.nextEmptyRecordRegEx, string.Empty, (xx, mm) => { return(true); }); // remove all but the last SectionProperties element (there should only be one per section (body)) var sectPrItems = outputBodyNode.Nodes().OfType <XElement>().Where(a => a.Name.LocalName == "sectPr"); foreach (var extra in sectPrItems.Where(a => a != sectPrItems.Last()).ToList()) { extra.Remove(); } // renumber all the ids to make sure they are unique var idAttrs = xdoc.DescendantNodes().OfType <XElement>().Where(a => a.HasAttributes).Select(a => a.Attribute("id")).Where(s => s != null); int lastId = 1; foreach (var attr in idAttrs) { attr.Value = lastId.ToString(); lastId++; } LavaDataDictionary globalMergeHash = new LavaDataDictionary(); foreach (var field in globalMergeFields) { globalMergeHash.Add(field.Key, field.Value); } HeaderFooterGlobalMerge(outputDoc, globalMergeHash); // sweep thru any remaining un-merged body parts for any Lava having to do with Global merge fields foreach (var bodyTextPart in outputDoc.MainDocumentPart.Document.Body.Descendants <Text>()) { string nodeText = bodyTextPart.Text.ReplaceWordChars(); if (lavaRegEx.IsMatch(nodeText)) { bodyTextPart.Text = nodeText.ResolveMergeFields(globalMergeHash, true, true); } } // remove the last pagebreak MarkupSimplifier.SimplifyMarkup(outputDoc, new SimplifyMarkupSettings { RemoveLastRenderedPageBreak = true }); // If you want to see validation errors /* * var validator = new OpenXmlValidator(); * var errors = validator.Validate( outputDoc ).ToList(); */ } outputBinaryFile = new BinaryFile(); outputBinaryFile.IsTemporary = true; outputBinaryFile.ContentStream = outputDocStream; outputBinaryFile.FileName = "MergeTemplateOutput" + Path.GetExtension(templateBinaryFile.FileName); outputBinaryFile.MimeType = templateBinaryFile.MimeType; outputBinaryFile.BinaryFileTypeId = new BinaryFileTypeService(rockContext).Get(Rock.SystemGuid.BinaryFiletype.DEFAULT.AsGuid()).Id; binaryFileService.Add(outputBinaryFile); rockContext.SaveChanges(); } return(outputBinaryFile); }
/// <summary> /// Creates the document. /// </summary> /// <param name="mergeTemplate">The merge template.</param> /// <param name="mergeObjectList">The list of merge objects (rows).</param> /// <param name="globalMergeFields">The global merge fields.</param> /// <returns></returns> public override BinaryFile CreateDocument(MergeTemplate mergeTemplate, List <object> mergeObjectList, Dictionary <string, object> globalMergeFields) { this.Exceptions = new List <Exception>(); var rockContext = new RockContext(); var binaryFileTypeService = new BinaryFileTypeService(rockContext); int defaultFileTypeId = binaryFileTypeService.Get(Rock.SystemGuid.BinaryFiletype.DEFAULT.AsGuid()).Id; var binaryFileService = new BinaryFileService(rockContext); var outputBinaryFile = new BinaryFile(); int recordCount = mergeObjectList.Count(); IDictionary <string, object> template; var results = new List <object>(); // Load the template file BinaryFile templateBinaryFile = binaryFileService.Get(mergeTemplate.TemplateBinaryFileId); if (templateBinaryFile == null) { return(null); } using (var templateStream = new StreamReader(templateBinaryFile.ContentStream)) using (var csv = new CsvReader(templateStream)) { // Read the headers csv.Read(); csv.ReadHeader(); // Read the first row of values csv.Read(); template = csv.GetRecord <dynamic>(); } // Merge each row for (int recordIndex = 0; recordIndex < recordCount; recordIndex++) { var result = new ExpandoObject() as IDictionary <string, object>; var mergeObjects = GetMergeObjects(mergeObjectList, globalMergeFields, recordIndex); // Merge each field in the row foreach (var field in template) { result.Add(field.Key, field.Value.ToString().ResolveMergeFields(mergeObjects)); } results.Add(result); } using (var outputStream = new MemoryStream()) using (var outputWriter = new StreamWriter(outputStream)) using (var csv = new CsvWriter(outputWriter)) { csv.WriteRecords(results); outputWriter.Flush(); outputBinaryFile.IsTemporary = true; outputBinaryFile.ContentStream = outputStream; outputBinaryFile.FileName = "MergeTemplateOutput" + Path.GetExtension(templateBinaryFile.FileName); outputBinaryFile.MimeType = templateBinaryFile.MimeType; outputBinaryFile.BinaryFileTypeId = defaultFileTypeId; binaryFileService.Add(outputBinaryFile); rockContext.SaveChanges(); } return(outputBinaryFile); }
/// <summary> /// Shows the detail. /// </summary> /// <param name="mergeTemplateId">The merge template identifier.</param> /// <param name="parentCategoryId">The parent category identifier.</param> public void ShowDetail(int mergeTemplateId, int?parentCategoryId) { pnlDetails.Visible = false; var rockContext = new RockContext(); var mergeTemplateService = new MergeTemplateService(rockContext); MergeTemplate mergeTemplate = null; if (!mergeTemplateId.Equals(0)) { mergeTemplate = mergeTemplateService.Get(mergeTemplateId); } var mergeTemplateOwnership = this.GetAttributeValue("MergeTemplatesOwnership").ConvertToEnum <MergeTemplateOwnership>(MergeTemplateOwnership.Global); if (mergeTemplate == null) { mergeTemplate = new MergeTemplate(); mergeTemplate.CategoryId = parentCategoryId ?? 0; if (mergeTemplateOwnership == MergeTemplateOwnership.Personal) { mergeTemplate.PersonAliasId = this.CurrentPersonAliasId; mergeTemplate.PersonAlias = this.CurrentPersonAlias; } } pnlDetails.Visible = true; hfMergeTemplateId.Value = mergeTemplateId.ToString(); // render UI based on Authorized bool readOnly = false; nbEditModeMessage.Text = string.Empty; if (mergeTemplate.PersonAliasId.HasValue && mergeTemplate.PersonAlias.PersonId == this.CurrentPersonId) { // Allow Person to edit their own Merge Templates } else if (!IsUserAuthorized(Authorization.EDIT)) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed(MergeTemplate.FriendlyTypeName); } if (readOnly) { btnEdit.Visible = false; btnDelete.Visible = false; ShowReadonlyDetails(mergeTemplate); } else { btnEdit.Visible = true; string errorMessage = string.Empty; btnDelete.Visible = mergeTemplateService.CanDelete(mergeTemplate, out errorMessage); if (mergeTemplate.Id > 0) { ShowReadonlyDetails(mergeTemplate); } else { ShowEditDetails(mergeTemplate); } } }
/// <summary> /// Handles the Click event of the btnSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void btnSave_Click(object sender, EventArgs e) { MergeTemplate mergeTemplate; var rockContext = new RockContext(); MergeTemplateService mergeTemplateService = new MergeTemplateService(rockContext); int mergeTemplateId = hfMergeTemplateId.Value.AsInteger(); int?origBinaryFileId = null; if (mergeTemplateId == 0) { mergeTemplate = new MergeTemplate(); mergeTemplateService.Add(mergeTemplate); } else { mergeTemplate = mergeTemplateService.Get(mergeTemplateId); origBinaryFileId = mergeTemplate.TemplateBinaryFileId; } mergeTemplate.Name = tbName.Text; mergeTemplate.Description = tbDescription.Text; mergeTemplate.MergeTemplateTypeEntityTypeId = ddlMergeTemplateType.SelectedValue.AsInteger(); mergeTemplate.TemplateBinaryFileId = fuTemplateBinaryFile.BinaryFileId ?? 0; mergeTemplate.PersonAliasId = ppPerson.PersonAliasId; mergeTemplate.CategoryId = cpCategory.SelectedValue.AsInteger(); int personalMergeTemplateCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.PERSONAL_MERGE_TEMPLATE.AsGuid()).Id; if (mergeTemplate.PersonAliasId.HasValue) { if (mergeTemplate.CategoryId == 0) { // if the category picker isn't shown and/or the category isn't selected, and it's a personal filter... mergeTemplate.CategoryId = personalMergeTemplateCategoryId; } // ensure Personal templates are only in the Personal merge template category if (mergeTemplate.CategoryId != personalMergeTemplateCategoryId) { // prohibit personal templates from being in something other than the Personal category cpCategory.Visible = true; cpCategory.ShowErrorMessage("Personal Merge Templates must be in Personal category"); } } else { if (mergeTemplate.CategoryId == personalMergeTemplateCategoryId) { // prohibit global templates from being in Personal category cpCategory.ShowErrorMessage("Person is required when using the Personal category"); } } if (!mergeTemplate.IsValid || !Page.IsValid) { // Controls will render the error messages return; } BinaryFileService binaryFileService = new BinaryFileService(rockContext); if (origBinaryFileId.HasValue && origBinaryFileId.Value != mergeTemplate.TemplateBinaryFileId) { // if a new the binaryFile was uploaded, mark the old one as Temporary so that it gets cleaned up var oldBinaryFile = binaryFileService.Get(origBinaryFileId.Value); if (oldBinaryFile != null && !oldBinaryFile.IsTemporary) { oldBinaryFile.IsTemporary = true; } } // ensure the IsTemporary is set to false on binaryFile associated with this MergeTemplate var binaryFile = binaryFileService.Get(mergeTemplate.TemplateBinaryFileId); if (binaryFile != null && binaryFile.IsTemporary) { binaryFile.IsTemporary = false; } rockContext.SaveChanges(); var qryParams = new Dictionary <string, string>(); qryParams["ExpandedIds"] = PageParameter("ExpandedIds"); qryParams["MergeTemplateId"] = mergeTemplate.Id.ToString(); NavigateToPage(RockPage.Guid, qryParams); }
/// <summary> /// Shows the readonly details. /// </summary> /// <param name="mergeTemplate">The merge template.</param> private void ShowReadonlyDetails( MergeTemplate mergeTemplate ) { SetEditMode( false ); hfMergeTemplateId.SetValue( mergeTemplate.Id ); lReadOnlyTitle.Text = mergeTemplate.Name.FormatAsHtmlTitle(); var getFileUrl = string.Format( "{0}GetFile.ashx?guid={1}", System.Web.VirtualPathUtility.ToAbsolute( "~" ), mergeTemplate.TemplateBinaryFile != null ? mergeTemplate.TemplateBinaryFile.Guid : (Guid?)null ); DescriptionList descriptionListCol1 = new DescriptionList() .Add( "Template File", string.Format( "<a href='{0}'>{1}</a>", getFileUrl, mergeTemplate.TemplateBinaryFile.FileName ) ) .Add( "Description", mergeTemplate.Description ?? string.Empty ) .Add( "Type", mergeTemplate.MergeTemplateTypeEntityType ); DescriptionList descriptionListCol2 = new DescriptionList() .Add( "Category", mergeTemplate.Category != null ? mergeTemplate.Category.Name : string.Empty ) .Add( "Person", mergeTemplate.PersonAlias, false ); lblMainDetailsCol1.Text = descriptionListCol1.Html; lblMainDetailsCol2.Text = descriptionListCol2.Html; }
/// <summary> /// Gets the type of the merge template. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="mergeTemplate">The merge template.</param> /// <returns></returns> private MergeTemplateType GetMergeTemplateType( RockContext rockContext, MergeTemplate mergeTemplate ) { mergeTemplate = new MergeTemplateService( rockContext ).Get( mtPicker.SelectedValue.AsInteger() ); if ( mergeTemplate == null ) { return null; } var mergeTemplateTypeEntityType = EntityTypeCache.Read( mergeTemplate.MergeTemplateTypeEntityTypeId ); if ( mergeTemplateTypeEntityType == null ) { return null; } return MergeTemplateTypeContainer.GetComponent( mergeTemplateTypeEntityType.Name ); }