Example #1
0
        /// <summary>
        /// Renders the specified writer.
        /// </summary>
        /// <param name="badge">The badge.</param>
        /// <param name="writer">The writer.</param>
        public override void Render( PersonBadgeCache badge, System.Web.UI.HtmlTextWriter writer )
        {
            string displayText = GetAttributeValue( badge, "DisplayText" );
            if ( Person != null )
            {
                Dictionary<string, object> mergeValues = new Dictionary<string, object>();
                mergeValues.Add( "Person", Person );
                displayText = displayText.ResolveMergeFields( mergeValues );

                if ( GetAttributeValue( badge, "EnableDebug" ).AsBoolean() )
                {
                    string debugInfo = string.Format( @"
                            <small><a data-toggle='collapse' data-parent='#accordion' href='#badge-debug'><i class='fa fa-eye'></i></a></small>
                            <pre id='badge-debug' class='collapse well badge-debug'>
                                {0}
                            </pre>
                        ", mergeValues.LiquidHelpText() );

                    displayText += debugInfo;
                }

            }
            writer.Write( displayText );
        }
Example #2
0
        /// <summary>
        /// Renders the Ads using Liquid.
        /// </summary>
        private void Render()
        {
            var rockContext = new RockContext();

            MarketingCampaignAdService marketingCampaignAdService = new MarketingCampaignAdService( rockContext );
            var qry = marketingCampaignAdService.Queryable();

            // limit to date range
            DateTime currentDateTime = RockDateTime.Now.Date;
            qry = qry.Where( a => ( a.StartDate <= currentDateTime ) && ( currentDateTime <= a.EndDate ) );

            // limit to approved
            qry = qry.Where( a => a.MarketingCampaignAdStatus == MarketingCampaignAdStatus.Approved );

            /* Block Attributes */

            // Audience
            string audience = GetAttributeValue( "Audience" );
            if ( !string.IsNullOrWhiteSpace( audience ) )
            {
                var idList = new List<int>();
                foreach ( string guid in audience.Split( new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries ) )
                {
                    var definedValue = DefinedValueCache.Read( new Guid( guid ) );
                    if ( definedValue != null )
                    {
                        idList.Add( definedValue.Id );
                    }
                }
                qry = qry.Where( a => a.MarketingCampaign.MarketingCampaignAudiences.Any( x => idList.Contains( x.AudienceTypeValueId ) ) );
            }

            // AudiencePrimarySecondary
            string audiencePrimarySecondary = GetAttributeValue( "AudiencePrimarySecondary" );
            if ( !string.IsNullOrWhiteSpace( audiencePrimarySecondary ) )
            {
                // 1 = Primary, 2 = Secondary
                List<int> idlist = audiencePrimarySecondary.SplitDelimitedValues().Select( a => int.Parse( a ) ).ToList();

                if ( idlist.Contains( 1 ) && !idlist.Contains( 2 ) )
                {
                    // only show to Primary Audiences
                    qry = qry.Where( a => a.MarketingCampaign.MarketingCampaignAudiences.Any( x => x.IsPrimary == true ) );
                }
                else if ( idlist.Contains( 2 ) && !idlist.Contains( 1 ) )
                {
                    // only show to Secondary Audiences
                    qry = qry.Where( a => a.MarketingCampaign.MarketingCampaignAudiences.Any( x => x.IsPrimary == false ) );
                }
            }

            // Campuses
            string campuses = GetAttributeValue( "Campuses" );
            if ( !string.IsNullOrWhiteSpace( campuses ) )
            {
                List<int> idlist = campuses.SplitDelimitedValues().Select( a => int.Parse( a ) ).ToList();
                qry = qry.Where( a => a.MarketingCampaign.MarketingCampaignCampuses.Any( x => idlist.Contains( x.CampusId ) ) );
            }

            // Ad Types
            string adtypes = GetAttributeValue( "AdTypes" );
            if ( !string.IsNullOrWhiteSpace( adtypes ) )
            {
                List<int> idlist = adtypes.SplitDelimitedValues().Select( a => int.Parse( a ) ).ToList();
                qry = qry.Where( a => idlist.Contains( a.MarketingCampaignAdTypeId ) );
            }

            // Image Types
            string imageTypes = GetAttributeValue( "ImageTypes" );
            List<string> imageTypeFilter = null;
            if ( !string.IsNullOrWhiteSpace( imageTypes ) )
            {
                imageTypeFilter = imageTypes.SplitDelimitedValues().ToList();
            }

            // Campus Context
            Campus campusContext = this.ContextEntity<Campus>();
            if ( campusContext != null )
            {
                // limit to ads that are targeted to the current campus context
                qry = qry.Where( a => a.MarketingCampaign.MarketingCampaignCampuses.Any( x => x.CampusId.Equals( campusContext.Id ) ) );
            }

            // Max Items
            string maxItems = GetAttributeValue( "MaxItems" );
            int? maxAdCount = null;
            if ( !string.IsNullOrWhiteSpace( maxItems ) )
            {
                int parsedCount = 0;
                if ( int.TryParse( maxItems, out parsedCount ) )
                {
                    maxAdCount = parsedCount;
                }
            }

            List<MarketingCampaignAd> marketingCampaignAdList;
            qry = qry.OrderBy( a => a.Priority ).ThenBy( a => a.StartDate ).ThenBy( a => a.MarketingCampaign.Title );
            if ( maxAdCount == null )
            {
                marketingCampaignAdList = qry.ToList();
            }
            else
            {
                marketingCampaignAdList = qry.Take( maxAdCount.Value ).ToList();
            }

            // build dictionary for liquid
            var ads = new List<Dictionary<string, object>>();

            foreach ( var marketingCampaignAd in marketingCampaignAdList )
            {
                var ad = new Dictionary<string, object>();

                // DetailPage
                string detailPageUrl = string.Empty;
                string detailPageGuid = GetAttributeValue( "DetailPage" );
                if ( !string.IsNullOrWhiteSpace( detailPageGuid ) )
                {
                    Rock.Model.Page detailPage = new PageService( rockContext ).Get( new Guid( detailPageGuid ) );
                    if ( detailPage != null )
                    {
                        Dictionary<string, string> queryString = new Dictionary<string, string>();
                        queryString.Add( "ad", marketingCampaignAd.Id.ToString() );
                        detailPageUrl = new PageReference( detailPage.Id, 0, queryString ).BuildUrl();
                    }
                }

                string eventGroupName = marketingCampaignAd.MarketingCampaign.EventGroup != null ? marketingCampaignAd.MarketingCampaign.EventGroup.Name : string.Empty;

                // Marketing Campaign Fields
                ad.Add( "Title", marketingCampaignAd.MarketingCampaign.Title );
                ad.Add( "ContactEmail", marketingCampaignAd.MarketingCampaign.ContactEmail );
                ad.Add( "ContactFullName", marketingCampaignAd.MarketingCampaign.ContactFullName );
                ad.Add( "ContactPhoneNumber", marketingCampaignAd.MarketingCampaign.ContactPhoneNumber );
                ad.Add( "LinkedEvent", eventGroupName );

                // Specific Ad Fields
                ad.Add( "AdType", marketingCampaignAd.MarketingCampaignAdType.Name );
                ad.Add( "StartDate", marketingCampaignAd.StartDate.ToString() );
                ad.Add( "EndDate", marketingCampaignAd.EndDate.ToString() );
                ad.Add( "Priority", marketingCampaignAd.Priority );
                ad.Add( "Url", marketingCampaignAd.Url );
                ad.Add( "DetailPageUrl", detailPageUrl );

                // Ad Attributes
                var attributes = new List<Dictionary<string, object>>();
                ad.Add( "Attributes", attributes );

                marketingCampaignAd.LoadAttributes();
                Rock.Attribute.Helper.AddDisplayControls( marketingCampaignAd, phContent );

                // create image resize width/height from block settings
                Dictionary<string, Rock.Field.ConfigurationValue> imageConfig = new Dictionary<string, Rock.Field.ConfigurationValue>();
                if ( !string.IsNullOrWhiteSpace( GetAttributeValue( "ImageWidth" ) )
                    && Int32.Parse( GetAttributeValue( "ImageWidth" ) ) != Int16.MinValue )
                    imageConfig.Add( "width", new Rock.Field.ConfigurationValue( GetAttributeValue( "ImageWidth" ) ) );

                if ( !string.IsNullOrWhiteSpace( GetAttributeValue( "ImageHeight" ) )
                    && Int32.Parse( GetAttributeValue( "ImageHeight" ) ) != Int16.MinValue )
                    imageConfig.Add( "height", new Rock.Field.ConfigurationValue( GetAttributeValue( "ImageHeight" ) ) );

                foreach ( var item in marketingCampaignAd.Attributes )
                {
                    AttributeCache attribute = item.Value;
                    List<AttributeValue> attributeValues = marketingCampaignAd.AttributeValues[attribute.Key];
                    foreach ( AttributeValue attributeValue in attributeValues )
                    {
                        string valueHtml = string.Empty;

                        // if block attributes limit image types, limit images
                        if ( attribute.FieldType.Guid.Equals( new Guid( Rock.SystemGuid.FieldType.IMAGE ) ) )
                        {
                            if ( imageTypeFilter != null )
                            {
                                if ( !imageTypeFilter.Contains( attribute.Key ) )
                                {
                                    // skip to next attribute if this is an image attribute and it doesn't match the image key filter
                                    continue;
                                }
                                else
                                {
                                    valueHtml = attribute.FieldType.Field.FormatValue( this, attributeValue.Value, imageConfig, false );
                                }
                            }
                        }
                        else
                        {
                            valueHtml = attribute.FieldType.Field.FormatValue( this, attributeValue.Value, attribute.QualifierValues, false );
                        }

                        var valueNode = new Dictionary<string, object>();
                        valueNode.Add( "Key", attribute.Key );
                        valueNode.Add( "Name", attribute.Name );
                        valueNode.Add( "Value", valueHtml );
                        attributes.Add( valueNode );
                    }
                }

                ads.Add( ad );
            }

            var data = new Dictionary<string, object>();
            data.Add( "Ads", ads );
            data.Add( "ApplicationPath", HttpRuntime.AppDomainAppVirtualPath );

            string content;
            try
            {
                content = GetTemplate().Render( Hash.FromDictionary( data ) );
            }
            catch ( Exception ex )
            {
                // liquid compile error
                string exMessage = "An excception occurred while compiling the Liquid template.";

                if ( ex.InnerException != null )
                    exMessage += "<br /><em>" + ex.InnerException.Message + "</em>";

                content = "<div class='alert warning' style='margin: 24px auto 0 auto; max-width: 500px;' ><strong>Liquid Compile Error</strong><p>" + exMessage + "</p></div>";
            }

            // check for errors
            if (content.Contains("No such template"))
            {
                // get template name
                Match match = Regex.Match(GetAttributeValue("Template"), @"'([^']*)");
                if (match.Success)
                {
                    content = String.Format("<div class='alert alert-warning'><h4>Warning</h4>Could not find the template _{1}.liquid in {0}.</div>", ResolveRockUrl("~~/Assets/Liquid"), match.Groups[1].Value);
                }
                else
                {
                    content = "<div class='alert alert-warning'><h4>Warning</h4>Unable to parse the template name from settings.</div>";
                }
            }

            if (content.Contains("error"))
            {
                content = "<div class='alert alert-warning'><h4>Warning</h4>" + content + "</div>";
            }

            phContent.Controls.Clear();
            phContent.Controls.Add( new LiteralControl( content ) );

            // add debug info
            if (GetAttributeValue("EnableDebug").AsBoolean())
            {
                StringBuilder debugInfo = new StringBuilder();
                debugInfo.Append("<p /><div class='alert alert-info'><h4>Debug Info</h4>");

                debugInfo.Append("<pre>");

                debugInfo.Append("<p /><strong>Ad Data</strong> (referenced as 'Ads.' in Liquid)<br>");
                debugInfo.Append(data.LiquidHelpText() + "</pre>");

                debugInfo.Append("</div>");
                phContent.Controls.Add(new LiteralControl(debugInfo.ToString()));
            }
        }
        /// <summary>
        /// Gets the HTML for a LiquidDashboardWidget block
        /// </summary>
        /// <param name="blockId">The block identifier.</param>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <param name="entityId">The entity identifier.</param>
        /// <returns></returns>
        public string GetHtmlForBlock( int blockId, int? entityTypeId = null, int? entityId = null )
        {
            RockContext rockContext = this.Service.Context as RockContext ?? new RockContext();
            Block block = new BlockService( rockContext ).Get( blockId );
            if ( block != null )
            {
                block.LoadAttributes();

                string liquidTemplate = block.GetAttributeValue( "LiquidTemplate" );

                var metricCategoryPairList = Rock.Attribute.MetricCategoriesFieldAttribute.GetValueAsGuidPairs( block.GetAttributeValue( "MetricCategories" ) );

                var metricGuids = metricCategoryPairList.Select( a => a.MetricGuid ).ToList();

                bool roundYValues = block.GetAttributeValue( "RoundValues" ).AsBooleanOrNull() ?? true;

                MetricService metricService = new MetricService( rockContext );
                var metrics = metricService.GetByGuids( metricGuids );
                List<object> metricsData = new List<object>();

                if ( metrics.Count() == 0 )
                {
                    return @"<div class='alert alert-warning'>
                                Please select a metric in the block settings.
                            </div>";
                }

                MetricValueService metricValueService = new MetricValueService( rockContext );

                DateTime firstDayOfYear = new DateTime( RockDateTime.Now.Year, 1, 1 );
                DateTime currentDateTime = RockDateTime.Now;
                DateTime firstDayOfNextYear = new DateTime( RockDateTime.Now.Year + 1, 1, 1 );

                foreach ( var metric in metrics )
                {
                    var metricYTDData = JsonConvert.DeserializeObject( metric.ToJson(), typeof( MetricYTDData ) ) as MetricYTDData;
                    var qryMeasureValues = metricValueService.Queryable()
                        .Where( a => a.MetricId == metricYTDData.Id )
                        .Where( a => a.MetricValueDateTime >= firstDayOfYear && a.MetricValueDateTime < currentDateTime )
                        .Where( a => a.MetricValueType == MetricValueType.Measure );

                    //// if an entityTypeId/EntityId filter was specified, and the entityTypeId is the same as the metrics.EntityTypeId, filter the values to the specified entityId
                    //// Note: if a Metric or it's Metric Value doesn't have a context, include it regardless of Context setting
                    if ( entityTypeId.HasValue && metric.EntityTypeId == entityTypeId || metric.EntityTypeId == null )
                    {
                        if ( entityId.HasValue )
                        {
                            qryMeasureValues = qryMeasureValues.Where( a => a.EntityId == entityId || a.EntityId == null );
                        }
                    }

                    var lastMetricValue = qryMeasureValues.OrderByDescending( a => a.MetricValueDateTime ).FirstOrDefault();
                    if ( lastMetricValue != null )
                    {
                        metricYTDData.LastValue = lastMetricValue.YValue.HasValue ? Math.Round( lastMetricValue.YValue.Value, roundYValues ? 0 : 2) : (decimal?)null;
                        metricYTDData.LastValueDate = lastMetricValue.MetricValueDateTime.HasValue ? lastMetricValue.MetricValueDateTime.Value.Date : DateTime.MinValue;
                    }

                    decimal? sum = qryMeasureValues.Sum( a => a.YValue );
                    metricYTDData.CumulativeValue = sum.HasValue ? Math.Round(sum.Value, roundYValues ? 0 : 2) : (decimal?)null;

                    // figure out goal as of current date time by figuring out the slope of the goal
                    var qryGoalValuesCurrentYear = metricValueService.Queryable()
                        .Where( a => a.MetricId == metricYTDData.Id )
                        .Where( a => a.MetricValueDateTime >= firstDayOfYear && a.MetricValueDateTime < firstDayOfNextYear )
                        .Where( a => a.MetricValueType == MetricValueType.Goal );

                    // if an entityTypeId/EntityId filter was specified, and the entityTypeId is the same as the metrics.EntityTypeId, filter the values to the specified entityId
                    if ( entityTypeId.HasValue && metric.EntityTypeId == entityTypeId )
                    {
                        if ( entityId.HasValue )
                        {
                            qryGoalValuesCurrentYear = qryGoalValuesCurrentYear.Where( a => a.EntityId == entityId );
                        }
                    }

                    MetricValue goalLineStartPoint = qryGoalValuesCurrentYear.Where( a => a.MetricValueDateTime <= currentDateTime ).OrderByDescending( a => a.MetricValueDateTime ).FirstOrDefault();
                    MetricValue goalLineEndPoint = qryGoalValuesCurrentYear.Where( a => a.MetricValueDateTime >= currentDateTime ).FirstOrDefault();
                    if ( goalLineStartPoint != null && goalLineEndPoint != null )
                    {
                        var changeInX = goalLineEndPoint.DateTimeStamp - goalLineStartPoint.DateTimeStamp;
                        var changeInY = goalLineEndPoint.YValue - goalLineStartPoint.YValue;
                        if ( changeInX != 0 )
                        {
                            decimal? slope = changeInY / changeInX;
                            decimal goalValue = ( ( slope * ( currentDateTime.ToJavascriptMilliseconds() - goalLineStartPoint.DateTimeStamp ) ) + goalLineStartPoint.YValue ).Value;
                            metricYTDData.GoalValue = Math.Round( goalValue, roundYValues ? 0 : 2 );
                        }
                    }
                    else
                    {
                        // if there isn't a both a start goal and end goal within the date range, there wouldn't be a goal line shown in a line chart, so don't display a goal in liquid either
                        metricYTDData.GoalValue = null;
                    }

                    metricsData.Add( metricYTDData.ToLiquid() );
                }

                Dictionary<string, object> mergeValues = new Dictionary<string, object>();
                mergeValues.Add( "Metrics", metricsData );

                string resultHtml = liquidTemplate.ResolveMergeFields( mergeValues );

                // show liquid help for debug
                if ( block.GetAttributeValue( "EnableDebug" ).AsBoolean() )
                {
                    StringBuilder debugInfo = new StringBuilder();
                    debugInfo.Append( "<p /><div class='alert alert-info'><h4>Debug Info</h4>" );

                    debugInfo.Append( "<pre>" );

                    debugInfo.Append( "<p /><strong>Liquid Data</strong> <br>" );
                    debugInfo.Append( mergeValues.LiquidHelpText() + "</pre>" );

                    debugInfo.Append( "</div>" );

                    resultHtml += debugInfo.ToString();
                }

                return resultHtml;
            }

            return string.Format(
                @"<div class='alert alert-danger'>
                    unable to find block_id: {1}
                </div>",
                blockId );
        }
        // helper functional methods (like BindGrid(), etc.)
        private void ShowContent()
        {
            // get pledges for current user
            if ( CurrentPerson != null )
            {
                var rockContext = new RockContext();
                FinancialScheduledTransactionService transactionService = new FinancialScheduledTransactionService( rockContext );

                var schedules = transactionService.Queryable( "ScheduledTransactionDetails.Account" )
                                .Where( s => s.AuthorizedPersonId == CurrentPerson.Id && s.IsActive == true );

                List<Dictionary<string, object>> scheduleSummaries = new List<Dictionary<string, object>>();

                foreach ( FinancialScheduledTransaction schedule in schedules )
                {
                    decimal totalAmount = 0;

                    Dictionary<string, object> scheduleSummary = new Dictionary<string, object>();
                    scheduleSummary.Add("Id", schedule.Id);
                    scheduleSummary.Add("Guid", schedule.Guid);
                    scheduleSummary.Add("StartDate", schedule.StartDate);
                    scheduleSummary.Add("EndDate", schedule.EndDate);
                    scheduleSummary.Add("NextPaymentDate", schedule.NextPaymentDate);

                    if ( schedule.NextPaymentDate.HasValue )
                    {
                        scheduleSummary.Add( "DaysTillNextPayment", (schedule.NextPaymentDate.Value - DateTime.Now).Days );
                    }
                    else
                    {
                        scheduleSummary.Add( "DaysTillNextPayment", null );
                    }

                    DateTime? lastPaymentDate = schedule.Transactions.Max(t => t.TransactionDateTime);
                    scheduleSummary.Add("LastPaymentDate", lastPaymentDate);

                    if ( lastPaymentDate.HasValue )
                    {
                        scheduleSummary.Add("DaysSinceLastPayment",  (DateTime.Now - lastPaymentDate.Value).Days);
                    }
                    else
                    {
                        scheduleSummary.Add( "DaysSinceLastPayment", null );
                    }

                    scheduleSummary.Add("CurrencyType", schedule.CurrencyTypeValue.Value);
                    scheduleSummary.Add("CreditCardType", schedule.CreditCardTypeValue.Value);
                    scheduleSummary.Add("UrlEncryptedKey", schedule.UrlEncodedKey);
                    scheduleSummary.Add("Frequency",  schedule.TransactionFrequencyValue.Value);
                    scheduleSummary.Add("FrequencyDescription", schedule.TransactionFrequencyValue.Description);

                    List<Dictionary<string, object>> summaryDetails = new List<Dictionary<string,object>>();

                    foreach ( FinancialScheduledTransactionDetail detail in schedule.ScheduledTransactionDetails )
                    {
                        Dictionary<string, object> detailSummary = new Dictionary<string,object>();
                        detailSummary.Add("AccountId", detail.Id);
                        detailSummary.Add("AccountName", detail.Account.Name);
                        detailSummary.Add("Amount", detail.Amount);
                        detailSummary.Add("Summary", detail.Summary);

                        summaryDetails.Add( detailSummary );

                        totalAmount += detail.Amount;
                    }

                    scheduleSummary.Add("ScheduledAmount", totalAmount);
                    scheduleSummary.Add( "TransactionDetails", summaryDetails );

                    scheduleSummaries.Add( scheduleSummary );
                }

                // added linked pages to mergefields
                Dictionary<string, object> linkedPages = new Dictionary<string, object>();
                linkedPages.Add( "ManageScheduledTransactionsPage", LinkedPageUrl( "ManageScheduledTransactionsPage", null ));
                linkedPages.Add( "TransactionHistoryPage", LinkedPageUrl( "TransactionHistoryPage", null ) );
                linkedPages.Add( "TransactionEntryPage", LinkedPageUrl( "TransactionEntryPage", null ) );

                var scheduleValues = new Dictionary<string, object>();
                scheduleValues.Add( "ScheduledTransactions", scheduleSummaries.ToList() );
                scheduleValues.Add( "LinkedPages", linkedPages );
                scheduleValues.Add( "Person", CurrentPerson );

                string content = GetAttributeValue( "Template" ).ResolveMergeFields( scheduleValues );

                // show merge fields if needed
                if ( GetAttributeValue( "EnableDebug" ).AsBoolean() )
                {
                    string debugInfo = string.Format( @"

                        <pre>
            {0}
                        </pre>
                    ", scheduleValues.LiquidHelpText() );

                    content += debugInfo;
                }

                lContent.Text = content;
            }
        }
Example #5
0
        /// <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>
        /// <exception cref="System.NotImplementedException"></exception>
        protected void btnSave_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();
            var financialPledgeService = new FinancialPledgeService( rockContext );
            var financialAccountService = new FinancialAccountService( rockContext );
            var definedValueService = new DefinedValueService( rockContext );
            var person = FindPerson( rockContext );

            FinancialPledge financialPledge = new FinancialPledge();

            financialPledge.PersonId = person.Id;
            var financialAccount = financialAccountService.Get( GetAttributeValue( "Account" ).AsGuid() );
            if ( financialAccount != null )
            {
                financialPledge.AccountId = financialAccount.Id;
            }

            financialPledge.TotalAmount = tbTotalAmount.Text.AsDecimal();

            var pledgeFrequencySelection = DefinedValueCache.Read( bddlFrequency.SelectedValue.AsInteger() );
            if ( pledgeFrequencySelection != null )
            {
                financialPledge.PledgeFrequencyValueId = pledgeFrequencySelection.Id;
            }

            financialPledge.StartDate = drpDateRange.LowerValue ?? DateTime.MinValue;
            financialPledge.EndDate = drpDateRange.UpperValue ?? DateTime.MaxValue;

            if ( sender != btnConfirm )
            {
                var duplicatePledges = financialPledgeService.Queryable()
                    .Where( a => a.PersonId == person.Id )
                    .Where( a => a.AccountId == financialPledge.AccountId )
                    .Where( a => a.StartDate == financialPledge.StartDate )
                    .Where( a => a.EndDate == financialPledge.EndDate ).ToList();

                if ( duplicatePledges.Any() )
                {
                    pnlAddPledge.Visible = false;
                    pnlConfirm.Visible = true;
                    nbDuplicatePledgeWarning.Text = "The following pledges have already been entered for you:";
                    nbDuplicatePledgeWarning.Text += "<ul>";
                    foreach ( var pledge in duplicatePledges.OrderBy( a => a.StartDate ).ThenBy( a => a.Account.Name ) )
                    {
                        nbDuplicatePledgeWarning.Text += string.Format( "<li>{0} {1} {2}</li>", pledge.Account, pledge.PledgeFrequencyValue, pledge.TotalAmount );
                    }

                    nbDuplicatePledgeWarning.Text += "</ul>";

                    return;
                }
            }

            financialPledgeService.Add( financialPledge );

            rockContext.SaveChanges();

            // populate account so that Liquid can access it
            financialPledge.Account = financialAccount;

            // populate PledgeFrequencyValue so that Liquid can access it
            financialPledge.PledgeFrequencyValue = definedValueService.Get( financialPledge.PledgeFrequencyValueId ?? 0 );

            var mergeObjects = new Dictionary<string, object>();
            mergeObjects.Add( "Person", person );
            mergeObjects.Add( "FinancialPledge", financialPledge );
            mergeObjects.Add( "PledgeFrequency", pledgeFrequencySelection );
            mergeObjects.Add( "Account", financialAccount );
            lReceipt.Text = GetAttributeValue( "ReceiptText" ).ResolveMergeFields( mergeObjects );

            // show liquid help for debug
            if ( GetAttributeValue( "EnableDebug" ).AsBooleanOrNull() ?? false )
            {
                StringBuilder debugInfo = new StringBuilder();
                debugInfo.Append( "<p /><div class='alert alert-info'><h4>Debug Info</h4>" );

                debugInfo.Append( "<pre>" );

                debugInfo.Append( "<p /><strong>Liquid Data</strong> <br>" );
                debugInfo.Append( mergeObjects.LiquidHelpText() + "</pre>" );

                debugInfo.Append( "</div>" );

                lReceipt.Text += debugInfo.ToString();
            }

            lReceipt.Visible = true;
            pnlAddPledge.Visible = false;
            pnlConfirm.Visible = false;

            // if a ConfirmationEmailTemplate is configured, send an email
            var confirmationEmailTemplateGuid = GetAttributeValue( "ConfirmationEmailTemplate" ).AsGuidOrNull();
            if ( confirmationEmailTemplateGuid.HasValue )
            {
                var recipients = new Dictionary<string, Dictionary<string, object>>();

                // add person and the mergeObjects (same mergeobjects as receipt)
                recipients.Add( person.Email, mergeObjects );

                Rock.Communication.Email.Send( confirmationEmailTemplateGuid.Value, recipients, ResolveRockUrl( "~/" ), ResolveRockUrl( "~~/" ) );
            }
        }
        protected void rptScheduledTransactions_ItemDataBound( object sender, RepeaterItemEventArgs e )
        {
            if ( e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem )
            {
                var transactionSchedule = e.Item.DataItem as FinancialScheduledTransaction;

                HiddenField hfScheduledTransactionId = (HiddenField)e.Item.FindControl( "hfScheduledTransactionId" );
                hfScheduledTransactionId.Value = transactionSchedule.Id.ToString();

                // create dictionary for liquid
                Dictionary<string, object> scheduleSummary = new Dictionary<string, object>();
                scheduleSummary.Add( "Id", transactionSchedule.Id );
                scheduleSummary.Add( "Guid", transactionSchedule.Guid );
                scheduleSummary.Add( "StartDate", transactionSchedule.StartDate );
                scheduleSummary.Add( "EndDate", transactionSchedule.EndDate );
                scheduleSummary.Add( "NextPaymentDate", transactionSchedule.NextPaymentDate );

                if ( transactionSchedule.NextPaymentDate.HasValue )
                {
                    scheduleSummary.Add( "DaysTillNextPayment", (transactionSchedule.NextPaymentDate.Value - DateTime.Now).Days );
                }
                else
                {
                    scheduleSummary.Add( "DaysTillNextPayment", null );
                }

                DateTime? lastPaymentDate = transactionSchedule.Transactions.Max( t => t.TransactionDateTime );
                scheduleSummary.Add( "LastPaymentDate", lastPaymentDate );

                if ( lastPaymentDate.HasValue )
                {
                    scheduleSummary.Add( "DaysSinceLastPayment", (DateTime.Now - lastPaymentDate.Value).Days );
                }
                else
                {
                    scheduleSummary.Add( "DaysSinceLastPayment", null );
                }

                scheduleSummary.Add( "CurrencyType", transactionSchedule.CurrencyTypeValue.Value );
                scheduleSummary.Add( "CreditCardType", transactionSchedule.CreditCardTypeValue.Value );
                scheduleSummary.Add( "UrlEncryptedKey", transactionSchedule.UrlEncodedKey );
                scheduleSummary.Add( "Frequency", transactionSchedule.TransactionFrequencyValue.Value );
                scheduleSummary.Add( "FrequencyDescription", transactionSchedule.TransactionFrequencyValue.Description );

                List<Dictionary<string, object>> summaryDetails = new List<Dictionary<string, object>>();
                decimal totalAmount = 0;

                foreach ( FinancialScheduledTransactionDetail detail in transactionSchedule.ScheduledTransactionDetails )
                {
                    Dictionary<string, object> detailSummary = new Dictionary<string, object>();
                    detailSummary.Add( "AccountId", detail.Id );
                    detailSummary.Add( "AccountName", detail.Account.Name );
                    detailSummary.Add( "Amount", detail.Amount );
                    detailSummary.Add( "Summary", detail.Summary );

                    summaryDetails.Add( detailSummary );

                    totalAmount += detail.Amount;
                }

                scheduleSummary.Add( "ScheduledAmount", totalAmount );
                scheduleSummary.Add( "TransactionDetails", summaryDetails );

                Dictionary<string, object> schedule = new Dictionary<string, object>();
                schedule.Add( "ScheduledTransaction", scheduleSummary );

                // merge into content
                Literal lLiquidContent = (Literal)e.Item.FindControl( "lLiquidContent" );
                lLiquidContent.Text = GetAttributeValue( "Template" ).ResolveMergeFields( schedule );

                // set debug info
                if ( GetAttributeValue( "EnableDebug" ).AsBoolean() )
                {
                    string debugInfo = string.Format( @"
                        <pre>
            {0}
                        </pre>
                    ", schedule.LiquidHelpText() );

                    lDebug.Text = debugInfo;
                }
            }
        }