public override IReadOnlyCollection <ActionComponentSetup> GetGlobalNavActions()
        {
            var navButtonSetups = new List <ActionComponentSetup>();

            if (CreateSystem.GetInfo().IsIdenticalToCurrent())
            {
                return(navButtonSetups);
            }

            // This will hide itself because Contact Us requires a logged-in user, and the standard library test web site has no users.
            var contactPage = EnterpriseWebFramework.EnterpriseWebLibrary.WebSite.ContactSupport.GetInfo(EwfPage.Instance.InfoAsBaseType.GetUrl());

            navButtonSetups.Add(new HyperlinkSetup(contactPage, contactPage.ResourceName));

            navButtonSetups.Add(
                new ButtonSetup(
                    "Test",
                    behavior: new MenuButtonBehavior(
                        new EwfButton(
                            new StandardButtonStyle("Test method"),
                            behavior: new PostBackBehavior(
                                postBack: PostBack.CreateFull(
                                    id: "testMethod",
                                    firstModificationMethod: () => EwfPage.AddStatusMessage(StatusMessageType.Info, "Successful method execution.")))).ToCollection())));

            return(navButtonSetups);
        }
 /// <summary>
 /// Call this during ModifyData.
 /// </summary>
 // NOTE SJR: This needs to change: You can't see this comment unless you're scrolling through all of the methods. It's easy to not call this
 // even though the radio button for generating a new password and emailing it to the user is always there.
 public void SendEmailIfNecessary()
 {
     if (passwordToEmail == null)
     {
         return;
     }
     FormsAuthStatics.SendPassword(Email, passwordToEmail);
     EwfPage.AddStatusMessage(StatusMessageType.Info, "Password reset email sent.");
 }
        void ControlTreeDataLoader.LoadData()
        {
            EwfPage.Instance.SetContentContainer(contentPlace);

            globalPlace.AddControlsReturnThis(getGlobalBlock());
            entityAndTopTabPlace.AddControlsReturnThis(getEntityAndTopTabBlock());
            if (entityUsesTabMode(TabMode.Vertical))
            {
                setUpSideTabs();
            }
            pageActionPlace.AddControlsReturnThis(getPageActionList());
            contentFootCell.Attributes.Add("class", CssElementCreator.ContentFootCellCssClass);
            var contentFootBlock = getContentFootBlock();

            if (contentFootBlock != null)
            {
                contentFootCell.Controls.AddAt(0, contentFootBlock);
            }
            var globalFootBlock = getGlobalFootBlock();

            if (globalFootBlock != null)
            {
                globalFootPlace.AddControlsReturnThis(globalFootBlock);
            }

            BasicPage.Instance.Body.Attributes["class"] = CssElementCreator.BodyCssClass;

            if (!EwfUiStatics.AppProvider.BrowserWarningDisabled())
            {
                if (AppRequestState.Instance.Browser.IsOldVersionOfMajorBrowser() && !StandardLibrarySessionState.Instance.HideBrowserWarningForRemainderOfSession)
                {
                    EwfPage.AddStatusMessage(
                        StatusMessageType.Warning,
                        StringTools.ConcatenateWithDelimiter(
                            " ",
                            new[]
                    {
                        "We've detected that you are not using the latest version of your browser.",
                        "While most features of this site will work, and you will be safe browsing here, we strongly recommend using the newest version of your browser in order to provide a better experience on this site and a safer experience throughout the Internet."
                    }) + "<br/>" +
                        NetTools.BuildBasicLink("Click here to get Firefox (it's free)", new ExternalResourceInfo("http://www.getfirefox.com").GetUrl(), true) + "<br />" +
                        NetTools.BuildBasicLink(
                            "Click here to get Chrome (it's free)",
                            new ExternalResourceInfo("https://www.google.com/intl/en/chrome/browser/").GetUrl(),
                            true) + "<br />" +
                        NetTools.BuildBasicLink(
                            "Click here to get the latest Internet Explorer (it's free)",
                            new ExternalResourceInfo("http://www.beautyoftheweb.com/").GetUrl(),
                            true));
                }
                StandardLibrarySessionState.Instance.HideBrowserWarningForRemainderOfSession = true;
            }
        }
        /// <summary>
        /// Creates a file collection manager.
        /// </summary>
        /// <param name="fileCollectionId"></param>
        /// <param name="displaySetup"></param>
        /// <param name="postBackIdBase">Do not pass null.</param>
        /// <param name="sortByName"></param>
        /// <param name="thumbnailResourceGetter">A function that takes a file ID and returns the corresponding thumbnail resource. Do not return null.</param>
        /// <param name="openedFileIds">The file IDs that should not be marked with a UI element drawing the user’s attention to the fact that they haven’t read it.
        /// All other files not in this collection will be marked. The collection can be null, and will result as nothing being shown as new.</param>
        /// <param name="unopenedFileOpenedNotifier">A method that executes when an unopened file is opened. Use to update the app’s database with an indication
        /// that the file has been seen by the user.</param>
        /// <param name="disableModifications">Pass true if there should be no way to upload or delete files.</param>
        /// <param name="uploadValidationMethod"></param>
        /// <param name="fileCreatedOrReplacedNotifier">A method that executes after a file is created or replaced.</param>
        /// <param name="filesDeletedNotifier">A method that executes after one or more files are deleted.</param>
        public BlobFileCollectionManager(
            int fileCollectionId, DisplaySetup displaySetup  = null, string postBackIdBase              = "", bool sortByName = false,
            Func <int, ResourceInfo> thumbnailResourceGetter = null, IEnumerable <int> openedFileIds    = null, MarkFileAsReadMethod unopenedFileOpenedNotifier = null,
            bool disableModifications = false, Action <RsFile, Validator> uploadValidationMethod        = null,
            NewFileNotificationMethod fileCreatedOrReplacedNotifier = null, Action filesDeletedNotifier = null)
        {
            postBackIdBase = PostBack.GetCompositeId("ewfFileCollection", postBackIdBase);

            var columnSetups = new List <EwfTableField>();

            if (thumbnailResourceGetter != null)
            {
                columnSetups.Add(new EwfTableField(size: 10.ToPercentage()));
            }
            columnSetups.Add(new EwfTableField(classes: new ElementClass("ewfOverflowedCell")));
            columnSetups.Add(new EwfTableField(size: 13.ToPercentage()));
            columnSetups.Add(new EwfTableField(size: 7.ToPercentage()));

            var table = EwfTable.Create(
                postBackIdBase: postBackIdBase,
                caption: "Files",
                selectedItemActions: disableModifications
                                                             ? null
                                                             : SelectedItemAction.CreateWithFullPostBackBehavior <int>(
                    "Delete Selected Files",
                    ids => {
                foreach (var i in ids)
                {
                    BlobStorageStatics.SystemProvider.DeleteFile(i);
                }
                filesDeletedNotifier?.Invoke();
                EwfPage.AddStatusMessage(StatusMessageType.Info, "Selected files deleted successfully.");
            })
                .ToCollection(),
                fields: columnSetups);

            IReadOnlyCollection <BlobFile> files = BlobStorageStatics.SystemProvider.GetFilesLinkedToFileCollection(fileCollectionId);

            files = (sortByName ? files.OrderByName() : files.OrderByUploadedDateDescending()).Materialize();

            foreach (var file in files)
            {
                addFileRow(postBackIdBase, thumbnailResourceGetter, openedFileIds, unopenedFileOpenedNotifier, table, file);
            }

            children = files.Any() || !disableModifications
                                           ? table.Concat(
                !disableModifications
                ?getUploadComponents( fileCollectionId, files, displaySetup, postBackIdBase, uploadValidationMethod, fileCreatedOrReplacedNotifier )
                : Enumerable.Empty <FlowComponent>())
                       .Materialize()
                                           : Enumerable.Empty <FlowComponent>().Materialize();
        }
        private IReadOnlyCollection <FlowComponent> getUploadComponents(
            int fileCollectionId, IReadOnlyCollection <BlobFile> files, DisplaySetup displaySetup, string postBackIdBase,
            Action <RsFile, Validator> uploadValidationMethod, NewFileNotificationMethod fileCreatedOrReplacedNotifier)
        {
            RsFile file = null;
            var    dm   = PostBack.CreateFull(
                id: PostBack.GetCompositeId(postBackIdBase, "add"),
                firstModificationMethod: () => {
                if (file == null)
                {
                    return;
                }

                var existingFile = files.SingleOrDefault(i => i.FileName == file.FileName);
                int newFileId;
                if (existingFile != null)
                {
                    BlobStorageStatics.SystemProvider.UpdateFile(
                        existingFile.FileId,
                        file.FileName,
                        file.Contents,
                        BlobStorageStatics.GetContentTypeForPostedFile(file));
                    newFileId = existingFile.FileId;
                }
                else
                {
                    newFileId = BlobStorageStatics.SystemProvider.InsertFile(
                        fileCollectionId,
                        file.FileName,
                        file.Contents,
                        BlobStorageStatics.GetContentTypeForPostedFile(file));
                }

                fileCreatedOrReplacedNotifier?.Invoke(newFileId);
                EwfPage.AddStatusMessage(StatusMessageType.Info, "File uploaded successfully.");
            });

            return(FormState.ExecuteWithDataModificationsAndDefaultAction(
                       dm.ToCollection(),
                       () => new StackList(
                           new FileUpload(
                               validationMethod: (postBackValue, validator) => {
                file = postBackValue;
                uploadValidationMethod?.Invoke(postBackValue, validator);
            }).ToFormItem()
                           .ToListItem()
                           .Append(new EwfButton(new StandardButtonStyle("Upload new file")).ToCollection().ToComponentListItem())).ToFormItem(
                           setup: new FormItemSetup(displaySetup: displaySetup),
                           label: "Select and upload a new file:".ToComponents())
                       .ToComponentCollection()));
        }
 IReadOnlyCollection <ActionComponentSetup> UiEntitySetupBase.GetActions() =>
 new ButtonSetup(
     "Delegate action",
     behavior: new PostBackBehavior(
         postBack: PostBack.CreateFull(
             id: "delegate",
             firstModificationMethod: () => EwfPage.AddStatusMessage(StatusMessageType.Info, "Did Something.")))).ToCollection <ActionComponentSetup>()
 .Append(new HyperlinkSetup(new ExternalResourceInfo("http://www.google.com").ToHyperlinkNewTabBehavior(), "Go to Google in new window"))
 .Append(
     new ButtonSetup(
         "Generate error",
         behavior: new PostBackBehavior(
             postBack: PostBack.CreateFull(id: "error", firstModificationMethod: () => { throw new ApplicationException(); }))))
 .Materialize();
        private ControlList getUploadControlList()
        {
            var dm = PostBack.CreateFull(id: PostBack.GetCompositeId(postBackIdBase, "add"));

            RsFile file = null;
            var    fi   = FormItem.Create(
                "",
                new EwfFileUpload(),
                validationGetter: control => new EwfValidation(
                    (pbv, validator) => {
                BlobFileOps.ValidateUploadedFile(validator, control, acceptableFileExtensions, ValidateImage, AcceptOnlyImages);
                file = control.GetPostBackValue(pbv);
            },
                    dm));

            dm.AddModificationMethod(
                () => {
                if (file == null)
                {
                    return;
                }

                var existingFile = files.SingleOrDefault(i => i.FileName == file.FileName);
                int newFileId;
                if (existingFile != null)
                {
                    BlobFileOps.SystemProvider.UpdateFile(existingFile.FileId, file.FileName, file.Contents, BlobFileOps.GetContentTypeForPostedFile(file));
                    newFileId = existingFile.FileId;
                }
                else
                {
                    newFileId = BlobFileOps.SystemProvider.InsertFile(fileCollectionId, file.FileName, file.Contents, BlobFileOps.GetContentTypeForPostedFile(file));
                }

                if (NewFileNotificationMethod != null)
                {
                    NewFileNotificationMethod(newFileId);
                }
                EwfPage.AddStatusMessage(StatusMessageType.Info, "File uploaded successfully.");
            });

            return(ControlList.CreateWithControls(
                       true,
                       "Select and upload a new file:",
                       fi.ToControl(),
                       new PostBackButton(dm, new ButtonActionControlStyle("Upload new file"), false)));
        }
        private static void addStatusMessageIfClockNotSynchronized(DataValue <string> utcOffset)
        {
            try {
                // IE uses a "UTC" suffix and Firefox uses a "GMT" suffix.  For our purposes, they are the same thing. Ironically, Microsoft fails to parse the time
                // generated by its own product, so we convert it to be the same as the time Firefox gives, which parses fine.
                var clockDifference = DateTime.Parse(utcOffset.Value.Replace("UTC", "GMT")) - DateTime.Now;

                if (Math.Abs(clockDifference.TotalMinutes) > 5)
                {
                    EwfPage.AddStatusMessage(
                        StatusMessageType.Warning,
                        Translation.YourClockIsWrong + " " + DateTime.Now.ToShortTimeString() + " " +
                        (TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now) ? TimeZone.CurrentTimeZone.DaylightName : TimeZone.CurrentTimeZone.StandardName) + ".");
                }
            }
            catch {}             // NOTE: Figure out why the date time field passed from javascript might be empty, and get rid of this catch
        }
        private static void addStatusMessageIfClockNotSynchronized(DataValue <string> clientTime)
        {
            var clientParseResult = InstantPattern.ExtendedIso.Parse(clientTime.Value);

            if (!clientParseResult.Success)
            {
                throw new DataModificationException("Your browser did not submit the current time.");
            }

            var clockDifference = clientParseResult.GetValueOrThrow() - AppRequestState.RequestTime;

            if (Math.Abs(clockDifference.TotalMinutes) > 5)
            {
                var timeZone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
                EwfPage.AddStatusMessage(
                    StatusMessageType.Warning,
                    Translation.YourClockIsWrong + " " + AppRequestState.RequestTime.InZone(timeZone).ToDateTimeUnspecified().ToHourAndMinuteString() + " " +
                    timeZone.GetZoneInterval(AppRequestState.RequestTime).Name + ".");
            }
        }
Beispiel #10
0
        /// <summary>
        /// Returns a JavaScript function call getter that opens a Stripe Checkout modal window. If the window's submit button is clicked, the credit card is
        /// charged or otherwise used. Do not execute the getter before all controls have IDs.
        /// </summary>
        /// <param name="etherealControlParent">The control to which any necessary ethereal controls will be added.</param>
        /// <param name="testPublishableKey">Your test publishable API key. Will be used in non-live installations. Do not pass null.</param>
        /// <param name="livePublishableKey">Your live publishable API key. Will be used in live installations. Do not pass null.</param>
        /// <param name="name">See https://stripe.com/docs/checkout. Do not pass null.</param>
        /// <param name="description">See https://stripe.com/docs/checkout. Do not pass null.</param>
        /// <param name="amountInDollars">See https://stripe.com/docs/checkout, but note that this parameter is in dollars, not cents</param>
        /// <param name="testSecretKey">Your test secret API key. Will be used in non-live installations. Do not pass null.</param>
        /// <param name="liveSecretKey">Your live secret API key. Will be used in live installations. Do not pass null.</param>
        /// <param name="successHandler">A method that executes if the credit-card submission is successful. The first parameter is the charge ID and the second
        /// parameter is the amount of the charge, in dollars.</param>
        /// <param name="prefilledEmailAddressOverride">By default, the email will be prefilled with AppTools.User.Email if AppTools.User is not null. You can
        /// override this with either a specified email address (if user is paying on behalf of someone else) or the empty string (to force the user to type in the
        /// email address).</param>
        public static Func <string> GetCreditCardCollectionJsFunctionCall(
            Control etherealControlParent, string testPublishableKey, string livePublishableKey, string name, string description, decimal?amountInDollars,
            string testSecretKey, string liveSecretKey, Func <string, decimal, StatusMessageAndDestination> successHandler, string prefilledEmailAddressOverride = null)
        {
            if (!EwfApp.Instance.RequestIsSecure(HttpContext.Current.Request))
            {
                throw new ApplicationException("Credit-card collection can only be done from secure pages.");
            }
            EwfPage.Instance.ClientScript.RegisterClientScriptInclude(
                typeof(PaymentProcessingStatics),
                "Stripe Checkout",
                "https://checkout.stripe.com/v2/checkout.js");

            if (amountInDollars.HasValue && amountInDollars.Value.DollarValueHasFractionalCents())
            {
                throw new ApplicationException("Amount must not include fractional cents.");
            }

            ResourceInfo successDestination = null;
            var          postBack           = PostBack.CreateFull(
                id: PostBack.GetCompositeId("ewfCreditCardCollection", description),
                actionGetter: () => new PostBackAction(successDestination));
            var token = new DataValue <string>();

            Func <PostBackValueDictionary, string> tokenHiddenFieldValueGetter;            // unused
            Func <string> tokenHiddenFieldClientIdGetter;

            EwfHiddenField.Create(
                etherealControlParent,
                "",
                postBackValue => token.Value = postBackValue,
                postBack,
                out tokenHiddenFieldValueGetter,
                out tokenHiddenFieldClientIdGetter);

            postBack.AddModificationMethod(
                () => {
                // We can add support later for customer creation, subscriptions, etc. as needs arise.
                if (!amountInDollars.HasValue)
                {
                    throw new ApplicationException("Only simple charges are supported at this time.");
                }

                var apiKey       = ConfigurationStatics.IsLiveInstallation ? liveSecretKey : testSecretKey;
                dynamic response = new StripeClient(apiKey).CreateCharge(
                    amountInDollars.Value,
                    "usd",
                    new CreditCardToken(token.Value),
                    description: description.Any() ? description : null);
                if (response.IsError)
                {
                    if (response.error.type == "card_error")
                    {
                        throw new DataModificationException(response.error.message);
                    }
                    throw new ApplicationException("Stripe error: " + response);
                }

                try {
                    var messageAndDestination = successHandler((string)response.id, amountInDollars.Value);
                    if (messageAndDestination.Message.Any())
                    {
                        EwfPage.AddStatusMessage(StatusMessageType.Info, messageAndDestination.Message);
                    }
                    successDestination = messageAndDestination.Destination;
                }
                catch (Exception e) {
                    throw new ApplicationException("An exception occurred after a credit card was charged.", e);
                }
            });

            EwfPage.Instance.AddPostBack(postBack);
            return(() => {
                var jsTokenHandler = "function( res ) { $( '#" + tokenHiddenFieldClientIdGetter() + "' ).val( res.id ); " +
                                     PostBackButton.GetPostBackScript(postBack, includeReturnFalse: false) + "; }";
                return "StripeCheckout.open( { key: '" + (ConfigurationStatics.IsLiveInstallation ? livePublishableKey : testPublishableKey) + "', name: '" + name +
                "', description: '" + description + "', " + (amountInDollars.HasValue ? "amount: " + amountInDollars.Value * 100 + ", " : "") + "token: " +
                jsTokenHandler + ", email: '" + (prefilledEmailAddressOverride ?? (AppTools.User == null ? "" : AppTools.User.Email)) + "' } )";
            });
        }
Beispiel #11
0
        public override List <ActionButtonSetup> GetGlobalNavActionControls()
        {
            var navButtonSetups = new List <ActionButtonSetup>();

            // This will hide itself because Contact Us requires a logged-in user, and the standard library test web site has no users.
            navButtonSetups.Add(
                new ActionButtonSetup(
                    "Contact us",
                    new EwfLink(
                        RedStapler.StandardLibrary.EnterpriseWebFramework.EnterpriseWebLibrary.WebSite.ContactUs.Page.GetInfo(EwfPage.Instance.InfoAsBaseType.GetUrl()))));

            var menu = EwfTable.Create();

            menu.AddItem(
                () =>
                new EwfTableItem(
                    new EwfTableItemSetup(
                        clickScript:
                        ClickScript.CreatePostBackScript(
                            PostBack.CreateFull(id: "testMethod", firstModificationMethod: () => EwfPage.AddStatusMessage(StatusMessageType.Info, "Successful method execution.")))),
                    "Test method"));
            navButtonSetups.Add(new ActionButtonSetup("Test", new ToolTipButton(menu)));

            navButtonSetups.Add(
                new ActionButtonSetup(
                    "Calendar",
                    new EwfLink(
                        CalendarDemo.GetInfo(
                            new EntitySetup.OptionalParameterPackage(),
                            new CalendarDemo.OptionalParameterPackage {
                ReturnUrl = EwfPage.Instance.InfoAsBaseType.GetUrl(), Date = DateTime.Now
            }))));
            return(navButtonSetups);
        }
        void ControlTreeDataLoader.LoadData()
        {
            CssClass = CssClass.ConcatenateWithSpace("ewfStandardFileCollectionManager");

            if (AppRequestState.Instance.Browser.IsInternetExplorer())
            {
                base.Controls.Add(
                    new HtmlGenericControl("p")
                {
                    InnerText =
                        "Because you are using Internet Explorer, clicking on a file below will result in a yellow warning bar appearing near the top of the browser.  You will need to then click the warning bar and tell Internet Explorer you are sure you want to download the file."
                });
            }

            var columnSetups = new List <ColumnSetup>();

            if (ThumbnailResourceInfoCreator != null)
            {
                columnSetups.Add(new ColumnSetup {
                    Width = Unit.Percentage(10)
                });
            }
            columnSetups.Add(new ColumnSetup {
                CssClassOnAllCells = "ewfOverflowedCell"
            });
            columnSetups.Add(new ColumnSetup {
                Width = Unit.Percentage(13)
            });
            columnSetups.Add(new ColumnSetup {
                Width = Unit.Percentage(7)
            });
            columnSetups.Add(new ColumnSetup {
                Width = Unit.Percentage(23), CssClassOnAllCells = "ewfRightAlignCell"
            });

            var table = new DynamicTable(columnSetups.ToArray())
            {
                Caption = Caption
            };

            files = BlobFileOps.SystemProvider.GetFilesLinkedToFileCollection(fileCollectionId);
            files = (sortByName ? files.OrderByName() : files.OrderByUploadedDateDescending()).ToArray();

            var deletePb         = PostBack.CreateFull(id: PostBack.GetCompositeId(postBackIdBase, "delete"));
            var deleteModMethods = new List <Func <bool> >();

            foreach (var file in files)
            {
                addFileRow(table, file, deletePb, deleteModMethods);
            }
            if (!ReadOnly)
            {
                table.AddRow(
                    getUploadControlList().ToCell(new TableCellSetup(fieldSpan: ThumbnailResourceInfoCreator != null ? 3 : 2)),
                    (files.Any() ? new PostBackButton(deletePb, new ButtonActionControlStyle("Delete Selected Files"), usesSubmitBehavior: false) : null).ToCell(
                        new TableCellSetup(fieldSpan: 2, classes: "ewfRightAlignCell".ToSingleElementArray())));
            }
            deletePb.AddModificationMethod(
                () => {
                if (deleteModMethods.Aggregate(false, (deletesOccurred, method) => method() || deletesOccurred))
                {
                    EwfPage.AddStatusMessage(StatusMessageType.Info, "Selected files deleted successfully.");
                }
            });

            Controls.Add(table);

            if (ReadOnly && !files.Any())
            {
                Visible = false;
            }
        }
        public override List <ActionButtonSetup> GetGlobalNavActionControls()
        {
            var navButtonSetups = new List <ActionButtonSetup>();

            if (CreateSystem.GetInfo().IsIdenticalToCurrent())
            {
                return(navButtonSetups);
            }

            // This will hide itself because Contact Us requires a logged-in user, and the standard library test web site has no users.
            var contactPage = EnterpriseWebFramework.EnterpriseWebLibrary.WebSite.ContactSupport.GetInfo(EwfPage.Instance.InfoAsBaseType.GetUrl());

            navButtonSetups.Add(new ActionButtonSetup(contactPage.ResourceName, new EwfLink(contactPage)));

            var menu = EwfTable.Create();

            menu.AddItem(
                () =>
                new EwfTableItem(
                    new EwfTableItemSetup(
                        clickScript:
                        ClickScript.CreatePostBackScript(
                            PostBack.CreateFull(id: "testMethod", firstModificationMethod: () => EwfPage.AddStatusMessage(StatusMessageType.Info, "Successful method execution.")))),
                    "Test method"));
            navButtonSetups.Add(new ActionButtonSetup("Test", new ToolTipButton(menu)));

            return(navButtonSetups);
        }
        public List <ActionButtonSetup> CreateActionButtonSetups()
        {
            var actionButtonSetups = new List <ActionButtonSetup>();

            actionButtonSetups.Add(
                new ActionButtonSetup(
                    "Delegate action",
                    new PostBackButton(
                        PostBack.CreateFull(id: "delegate", firstModificationMethod: () => EwfPage.AddStatusMessage(StatusMessageType.Info, "Did Something.")))));
            actionButtonSetups.Add(new ActionButtonSetup("Go to Google", new EwfLink(new ExternalResourceInfo("http://www.google.com"))));
            actionButtonSetups.Add(
                new ActionButtonSetup(
                    "Generate error",
                    new PostBackButton(PostBack.CreateFull(id: "error", firstModificationMethod: () => { throw new ApplicationException(); }))));
            return(actionButtonSetups);
        }
        /// <summary>
        /// Returns credit-card-collection hidden fields and a JavaScript function call getter that opens a Stripe Checkout modal window. If the window's submit
        /// button is clicked, the credit card is charged or otherwise used. Do not execute the getter until after the page tree has been built.
        /// </summary>
        /// <param name="testPublishableKey">Your test publishable API key. Will be used in non-live installations. Do not pass null.</param>
        /// <param name="livePublishableKey">Your live publishable API key. Will be used in live installations. Do not pass null.</param>
        /// <param name="name">See https://stripe.com/docs/checkout. Do not pass null.</param>
        /// <param name="description">See https://stripe.com/docs/checkout. Do not pass null.</param>
        /// <param name="amountInDollars">See https://stripe.com/docs/checkout, but note that this parameter is in dollars, not cents</param>
        /// <param name="testSecretKey">Your test secret API key. Will be used in non-live installations. Do not pass null.</param>
        /// <param name="liveSecretKey">Your live secret API key. Will be used in live installations. Do not pass null.</param>
        /// <param name="successHandler">A method that executes if the credit-card submission is successful. The first parameter is the charge ID and the second
        /// parameter is the amount of the charge, in dollars.</param>
        /// <param name="prefilledEmailAddressOverride">By default, the email will be prefilled with AppTools.User.Email if AppTools.User is not null. You can
        /// override this with either a specified email address (if user is paying on behalf of someone else) or the empty string (to force the user to type in the
        /// email address).</param>
        public static Tuple <IReadOnlyCollection <EtherealComponentOrElement>, Func <string> > GetCreditCardCollectionHiddenFieldsAndJsFunctionCall(
            string testPublishableKey, string livePublishableKey, string name, string description, decimal?amountInDollars, string testSecretKey, string liveSecretKey,
            Func <string, decimal, StatusMessageAndDestination> successHandler, string prefilledEmailAddressOverride = null)
        {
            if (!EwfApp.Instance.RequestIsSecure(HttpContext.Current.Request))
            {
                throw new ApplicationException("Credit-card collection can only be done from secure pages.");
            }
            EwfPage.Instance.ClientScript.RegisterClientScriptInclude(typeof(PaymentProcessingStatics), "Stripe Checkout", "https://checkout.stripe.com/checkout.js");

            if (amountInDollars.HasValue && amountInDollars.Value.DollarValueHasFractionalCents())
            {
                throw new ApplicationException("Amount must not include fractional cents.");
            }

            ResourceInfo successDestination = null;
            var          postBack           = PostBack.CreateFull(
                id: PostBack.GetCompositeId("ewfCreditCardCollection", description),
                actionGetter: () => new PostBackAction(successDestination));
            var token = new DataValue <string>();

            var hiddenFieldId = new HiddenFieldId();
            List <EtherealComponentOrElement> hiddenFields = new List <EtherealComponentOrElement>();

            FormState.ExecuteWithDataModificationsAndDefaultAction(
                postBack.ToCollection(),
                () => hiddenFields.Add(new EwfHiddenField("", (postBackValue, validator) => token.Value = postBackValue.Value, id: hiddenFieldId).PageComponent));

            postBack.AddModificationMethod(
                () => {
                // We can add support later for customer creation, subscriptions, etc. as needs arise.
                if (!amountInDollars.HasValue)
                {
                    throw new ApplicationException("Only simple charges are supported at this time.");
                }

                StripeCharge response;
                try {
                    response =
                        new StripeGateway(ConfigurationStatics.IsLiveInstallation ? liveSecretKey : testSecretKey).Post(
                            new ChargeStripeCustomer
                    {
                        Amount      = (int)(amountInDollars.Value * 100),
                        Currency    = "usd",
                        Description = description.Any() ? description : null,
                        Card        = token.Value
                    });
                }
                catch (StripeException e) {
                    if (e.Type == "card_error")
                    {
                        throw new DataModificationException(e.Message);
                    }
                    throw new ApplicationException("A credit-card charge failed.", e);
                }

                try {
                    var messageAndDestination = successHandler(response.Id, amountInDollars.Value);
                    if (messageAndDestination.Message.Any())
                    {
                        EwfPage.AddStatusMessage(StatusMessageType.Info, messageAndDestination.Message);
                    }
                    successDestination = messageAndDestination.Destination;
                }
                catch (Exception e) {
                    throw new ApplicationException("An exception occurred after a credit card was charged.", e);
                }
            });

            FormAction action = new PostBackFormAction(postBack);

            action.AddToPageIfNecessary();
            return(Tuple.Create <IReadOnlyCollection <EtherealComponentOrElement>, Func <string> >(
                       hiddenFields,
                       () => {
                var jsTokenHandler = "function( token, args ) { " + hiddenFieldId.GetJsValueModificationStatements("token.id") + " " + action.GetJsStatements() + " }";
                return "StripeCheckout.open( { key: '" + (ConfigurationStatics.IsLiveInstallation ? livePublishableKey : testPublishableKey) + "', token: " +
                jsTokenHandler + ", name: '" + name + "', description: '" + description + "', " +
                (amountInDollars.HasValue ? "amount: " + amountInDollars.Value * 100 + ", " : "") + "email: '" +
                (prefilledEmailAddressOverride ?? (AppTools.User == null ? "" : AppTools.User.Email)) + "' } )";
            }));
        }