├── LearningKit ├── App_Data │ └── CMSModules │ │ └── WebFarm │ │ └── webfarm.sync ├── Kentico │ ├── Scripts │ │ ├── builders │ │ │ ├── components │ │ │ │ ├── index.esm.js │ │ │ │ ├── index.system.js │ │ │ │ ├── app-globals-8931cc91.js │ │ │ │ └── app-globals-d5563cb5.system.js │ │ │ ├── Core-icons.eot │ │ │ ├── Core-icons.ttf │ │ │ ├── Core-icons.woff │ │ │ └── web-components.css │ │ └── modal-dialog.js │ └── Content │ │ ├── Selectors │ │ ├── Dialogs │ │ │ ├── Pages │ │ │ │ └── page-selector.css │ │ │ ├── Shared │ │ │ │ └── error-page.css │ │ │ ├── Attachments │ │ │ │ └── attachment-selector.css │ │ │ ├── PageTemplates │ │ │ │ ├── DefaultTemplateImage.png │ │ │ │ └── page-template-selector.js │ │ │ └── MediaFiles │ │ │ │ ├── media-files-selector.css │ │ │ │ └── media-files-selector.js │ │ └── FormComponents │ │ │ ├── Pages │ │ │ └── page-selector.admin.js │ │ │ ├── Path │ │ │ └── path-selector.admin.js │ │ │ ├── Attachments │ │ │ └── attachment-selector.admin.js │ │ │ └── MediaFiles │ │ │ └── media-files-selector.admin.js │ │ └── InlineEditors │ │ └── DropdownEditor │ │ ├── DropdownEditor.css │ │ └── dropdown-editor.js ├── Views │ ├── _ViewStart.cshtml │ ├── Shared │ │ ├── Widgets │ │ │ ├── _RandomWidget.cshtml │ │ │ ├── _ExtendedFormWidget.cshtml │ │ │ ├── _ColorWidget.cshtml │ │ │ ├── _NumberWidget.cshtml │ │ │ └── _SelectorsWidget.cshtml │ │ ├── Sections │ │ │ ├── _DefaultSection.cshtml │ │ │ ├── _Col5050.cshtml │ │ │ └── _CustomSection.cshtml │ │ ├── FormSections │ │ │ └── _TwoColumnFormSection.cshtml │ │ ├── Personalization │ │ │ └── ConditionTypes │ │ │ │ └── _HasGivenConsentConfiguration.cshtml │ │ ├── FormComponents │ │ │ ├── _CustomDropDownComponent.cshtml │ │ │ ├── _CustomPropertyComponent.cshtml │ │ │ ├── _ColorFormComponent.cshtml │ │ │ ├── _CustomFormComponent.cshtml │ │ │ └── _RgbInputComponent.cshtml │ │ ├── InlineEditors │ │ │ ├── _NumberEditor.cshtml │ │ │ └── _ColorEditor.cshtml │ │ ├── PageTemplates │ │ │ └── _LandingPageTemplate.cshtml │ │ ├── _PageTemplateLayout.cshtml │ │ ├── ModalDialogs │ │ │ └── ColorModalDialog │ │ │ │ └── _ColorModalDialog.cshtml │ │ ├── _Layout.cshtml │ │ └── PageTypes │ │ │ └── LearningKit_PageBuilder.cshtml │ ├── HttpErrors │ │ └── NotFound.cshtml │ ├── EmailRegister │ │ ├── ConfirmUser.cshtml │ │ ├── CheckYourEmail.cshtml │ │ ├── EmailConfirmationFailed.cshtml │ │ └── RegisterWithEmailConfirmation.cshtml │ ├── PasswordReset │ │ ├── ResetPasswordSucceeded.cshtml │ │ ├── ResetPasswordInvalid.cshtml │ │ ├── CheckYourEmail.cshtml │ │ ├── RequestPasswordReset.cshtml │ │ └── ResetPassword.cshtml │ ├── NewsletterSubscription │ │ ├── UnsubscribeFailure.cshtml │ │ ├── UnsubscribeSuccess.cshtml │ │ ├── ConfirmSubscription.cshtml │ │ ├── SubscribeSuccess.cshtml │ │ └── Subscribe.cshtml │ ├── ExternalAuthentication │ │ ├── ExternalAuthenticationFailure.cshtml │ │ ├── Lockout.cshtml │ │ └── SignInCallback.cshtml │ ├── Roles │ │ ├── RestrictedPage.cshtml │ │ └── ManageRoles.cshtml │ ├── Checkout │ │ ├── ThankYou.cshtml │ │ └── PreviewAndPay.cshtml │ ├── PrivacyPage │ │ ├── ConsentDetails.cshtml │ │ └── Index.cshtml │ ├── MediaLibrary │ │ └── ShowMediaFiles.cshtml │ ├── TrackingConsent │ │ └── Consent.cshtml │ ├── Personalization │ │ └── PersonalizedGreeting.cshtml │ ├── Search │ │ └── SearchIndex.cshtml │ ├── Order │ │ ├── MyOrders.cshtml │ │ └── OrderDetail.cshtml │ ├── ProductFilter │ │ ├── FilterPageProperty.cshtml │ │ ├── FilterSKUProperty.cshtml │ │ └── FilterForeignProperty.cshtml │ ├── PageBuilder │ │ └── PageBuilder.cshtml │ ├── ProductListing │ │ └── Listing.cshtml │ ├── RegisterWithConsent │ │ └── RegisterWithConsent.cshtml │ ├── Register │ │ └── Register.cshtml │ ├── web.config │ ├── Account │ │ └── SignIn.cshtml │ └── AzureSearch │ │ └── AzureSearch.cshtml ├── AppSettings.config.template ├── Global.asax ├── ConnectionStrings.config.template ├── Content │ ├── Images │ │ └── default-avatar.png │ ├── FormComponents │ │ ├── RgbInputComponent │ │ │ └── colorInputParser.js │ │ └── ColorFormComponent │ │ │ └── colorFormComponent.js │ └── InlineEditors │ │ ├── NumberEditor │ │ └── number-editor.js │ │ └── ColorEditor │ │ └── color-editor.js ├── Models │ ├── Register │ │ ├── RegisterWithConsentViewModel.cs │ │ └── RegisterViewModel.cs │ ├── Account │ │ ├── EditUserAccountViewModel.cs │ │ └── SignInViewModel.cs │ ├── MediaLibrary │ │ └── MediaFileViewModel.cs │ ├── ModalDialogs │ │ └── ColorModalDialogViewModel.cs │ ├── PageBuilder │ │ └── PageBuilderModel.cs │ ├── InlineEditors │ │ ├── ColorEditor │ │ │ └── ColorEditorModel.cs │ │ └── NumberEditor │ │ │ └── NumberEditorModel.cs │ ├── Checkout │ │ ├── OrderPaymentResultViewModel.cs │ │ ├── DeliveryDetailsViewModel.cs │ │ ├── PreviewAndPayViewModel.cs │ │ ├── ResponseViewModel.cs │ │ ├── ShoppingCartItemViewModel.cs │ │ ├── PaymentMethodViewModel.cs │ │ ├── ShippingOptionViewModel.cs │ │ ├── OrderViewModel.cs │ │ └── ShoppingCartViewModel.cs │ ├── ProductFilter │ │ ├── ProductFilterCheckboxViewModel.cs │ │ └── ProductFilterViewModel.cs │ ├── PrivacyPage │ │ ├── ConsentDetailsModel.cs │ │ └── ConsentListingModel.cs │ ├── TrackingConsent │ │ └── TrackingConsentViewModel.cs │ ├── Search │ │ ├── AzureSearch │ │ │ ├── FacetViewModel.cs │ │ │ ├── DocumentViewModel.cs │ │ │ └── AzureSearchViewModel.cs │ │ └── SmartSearch │ │ │ └── SearchResultModel.cs │ ├── FormComponents │ │ ├── FormComponentProperties │ │ │ ├── CustomDropDownComponentProperties.cs │ │ │ ├── ColorFormComponentProperties.cs │ │ │ ├── CustomFormComponentProperties.cs │ │ │ ├── RgbInputComponentProperties.cs │ │ │ └── CharacterSizeProperties.cs │ │ ├── ColorFormComponent.cs │ │ ├── CustomPropertyComponent.cs │ │ ├── CustomFormComponent.cs │ │ ├── CustomDropDownComponent.cs │ │ └── RgbInputComponent.cs │ ├── Personalization │ │ ├── ConditionTypes │ │ │ └── HasGivenConsentViewModel.cs │ │ └── CurrentContactViewModel.cs │ ├── Widgets │ │ ├── SelectorsWidget │ │ │ ├── SelectorsWidgetViewModel.cs │ │ │ └── SelectorsWidgetProperties.cs │ │ ├── ColorWidget │ │ │ └── ColorWidgetProperties.cs │ │ ├── NumberWidget │ │ │ └── NumberWidgetProperties.cs │ │ └── ExtendedFormWidget │ │ │ └── ExtendedFormWidgetProperties.cs │ ├── Products │ │ ├── PriceDetailViewModel.cs │ │ └── ProductListItemViewModel.cs │ ├── PasswordReset │ │ ├── RequestPasswordResetViewModel.cs │ │ └── ResetPasswordViewModel.cs │ ├── Sections │ │ └── CustomSection │ │ │ └── CustomSectionProperties.cs │ ├── NewsletterSubscription │ │ ├── NewsletterConfirmSubscriptionModel.cs │ │ ├── NewsletterSubscribeViewModel.cs │ │ └── MarketingEmailUnsubscribeModel.cs │ └── PageTemplates │ │ └── LandingPage │ │ └── LandingPageProperties.cs ├── Controllers │ ├── HomeController.cs │ ├── HttpErrorsController.cs │ ├── Builders │ │ ├── ModalDialogs │ │ │ └── ColorModalDialogController.cs │ │ ├── Sections │ │ │ └── Col5050Controller.cs │ │ ├── FormSections │ │ │ └── TwoColumnFormSectionController.cs │ │ ├── Widgets │ │ │ └── RandomWidgetController.cs │ │ ├── PageBuilderController.cs │ │ └── Personalization │ │ │ └── ConditionTypes │ │ │ └── HasGivenConsentController.cs │ ├── AdminRedirectController.cs │ ├── OnlineMarketing │ │ └── PersonalizationController.cs │ └── Search │ │ └── SearchController.cs ├── Properties │ ├── WebAssemblyInfo.cs │ └── AssemblyInfo.cs ├── FormBuilder │ ├── FormComponentFilters │ │ ├── FormComponentsFilter.cs │ │ └── IndividualFormComponentFilter.cs │ ├── VisibilityConditions │ │ ├── AdministratorPropertyCondition.cs │ │ ├── NumberSignPropertyCondition.cs │ │ ├── IsBetweenVisibilityCondition.cs │ │ ├── CustomVisibilityCondition.cs │ │ └── IsInPersonaVisibilityCondition.cs │ ├── FormBuilderHelper.cs │ └── ValidationRules │ │ ├── IsHexadecimalNumber.cs │ │ ├── ValueLiesBetween.cs │ │ ├── CustomValidationRule.cs │ │ └── ValueOfDependeeLiesBetween.cs ├── Scripts │ ├── variantSelector.js │ ├── countryStateSelector.js │ └── addressSelector.js ├── App_Start │ ├── ApplicationConfig.cs │ ├── RouteConfig.cs │ ├── DependencyResolverConfig.cs │ ├── PageBuilderComponentRegister.cs │ └── Startup.Auth.Basic.cs ├── Areas │ └── CodeSnippets │ │ └── Views │ │ ├── CodeSnippets.cshtml │ │ └── web.config ├── PageTemplateFilters │ └── LandingPageTemplateFilter.cs ├── CMSRegistrationSource.cs └── Personalization │ └── ConditionTypes │ ├── HasGivenConsentConditionTypeCustom.cs │ └── HasGivenConsentConditionType.cs ├── LearningKitCustomizations ├── OutputCache │ ├── OutputCacheKeyOptionsExtensions.cs │ └── ContactGenderOutputCacheKey.cs ├── Properties │ └── AssemblyInfo.cs ├── EventLog │ └── CsvEventWriter.cs ├── FormBuilder │ ├── FormBuilderCustomizations.cs │ ├── FormWidgetMarkupInjection.cs │ └── FormBuilderStaticMarkupConfiguration.cs └── Membership │ └── ExtendedUser.cs ├── LICENSE ├── LearningKit.sln └── README.md /LearningKit/App_Data/CMSModules/WebFarm/webfarm.sync: -------------------------------------------------------------------------------- 1 | True -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/components/index.esm.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LearningKit/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/Pages/page-selector.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } -------------------------------------------------------------------------------- /LearningKit/AppSettings.config.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /LearningKit/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="LearningKit.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/components/index.system.js: -------------------------------------------------------------------------------- 1 | System.register([],(function(){"use strict";return{execute:function(){}}})); -------------------------------------------------------------------------------- /LearningKit/ConnectionStrings.config.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /LearningKit/Content/Images/default-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kentico/LearningKit-Mvc/HEAD/LearningKit/Content/Images/default-avatar.png -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/Core-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kentico/LearningKit-Mvc/HEAD/LearningKit/Kentico/Scripts/builders/Core-icons.eot -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/Core-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kentico/LearningKit-Mvc/HEAD/LearningKit/Kentico/Scripts/builders/Core-icons.ttf -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/Core-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kentico/LearningKit-Mvc/HEAD/LearningKit/Kentico/Scripts/builders/Core-icons.woff -------------------------------------------------------------------------------- /LearningKit/Views/Shared/Widgets/_RandomWidget.cshtml: -------------------------------------------------------------------------------- 1 | @model int? 2 | 3 |

Your lucky number for today is: @Model

4 | -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/Shared/error-page.css: -------------------------------------------------------------------------------- 1 | body { 2 | height: 100%; 3 | margin: 0; 4 | } 5 | 6 | .ktc-message-box { 7 | margin: 16px; 8 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/Sections/_DefaultSection.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 |
5 | @Html.Kentico().WidgetZone() 6 |
-------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/Attachments/attachment-selector.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | height: 100%; 7 | margin: 0; 8 | } -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/PageTemplates/DefaultTemplateImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kentico/LearningKit-Mvc/HEAD/LearningKit/Kentico/Content/Selectors/Dialogs/PageTemplates/DefaultTemplateImage.png -------------------------------------------------------------------------------- /LearningKit/Models/Register/RegisterWithConsentViewModel.cs: -------------------------------------------------------------------------------- 1 | public class RegisterWithConsentViewModel : RegisterViewModel 2 | { 3 | public string ConsentShortText { get; set; } 4 | 5 | public bool ConsentIsAgreed { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/MediaFiles/media-files-selector.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | height: 100%; 7 | margin: 0; 8 | } 9 | 10 | .ktc-message-box { 11 | margin: 16px; 12 | } -------------------------------------------------------------------------------- /LearningKit/Models/Account/EditUserAccountViewModel.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Membership; 2 | 3 | 4 | public class EditUserAccountViewModel 5 | { 6 | public User User { get; set; } 7 | 8 | public bool AvatarUpdateFailed { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /LearningKit/Models/MediaLibrary/MediaFileViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.MediaLibrary 2 | { 3 | public class MediaFileViewModel 4 | { 5 | public string FileTitle { get; set; } 6 | public string RelativeUrl { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /LearningKit/Views/HttpErrors/NotFound.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Error 404"; 4 | } 5 | 6 |

Error 404

7 |

A page was not found for the requested URL.

8 |

9 | @Html.ActionLink("> Back to the home index", "Index", "Home") 10 |

-------------------------------------------------------------------------------- /LearningKit/Models/ModalDialogs/ColorModalDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LearningKit.Models.ModalDialogs 4 | { 5 | public class ColorModalDialogViewModel 6 | { 7 | public IEnumerable Colors { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /LearningKit/Models/PageBuilder/PageBuilderModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.PageBuilder 2 | { 3 | public class PageBuilderModel 4 | { 5 | public string HeadingText 6 | { 7 | get; 8 | set; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /LearningKit/Models/InlineEditors/ColorEditor/ColorEditorModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.InlineEditors.ColorEditor 2 | { 3 | public class ColorEditorModel 4 | { 5 | public string PropertyName { get; set; } 6 | 7 | public string Color { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /LearningKit/Models/InlineEditors/NumberEditor/NumberEditorModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.InlineEditors.NumberEditor 2 | { 3 | public class NumberEditorModel 4 | { 5 | public string PropertyName { get; set; } 6 | 7 | public int Number { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /LearningKit/Models/Checkout/OrderPaymentResultViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.Checkout 2 | { 3 | public class OrderPaymentResultViewModel 4 | { 5 | public string PaymentMethodName { get; set; } 6 | 7 | public bool PaymentIsCompleted { get; set; } 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /LearningKit/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Mvc; 3 | 4 | namespace LearningKit.Controllers 5 | { 6 | public class HomeController : Controller 7 | { 8 | public ActionResult Index() 9 | { 10 | return View(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /LearningKit/Models/ProductFilter/ProductFilterCheckboxViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.ProductFilter 2 | { 3 | public class ProductFilterCheckboxViewModel 4 | { 5 | public string Id { get; set; } 6 | 7 | public bool IsChecked { get; set; } 8 | 9 | public string DisplayName { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /LearningKit/Views/EmailRegister/ConfirmUser.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | ViewBag.Title = "New user account confirmed"; 5 | } 6 | 7 |

New user account confirmed

8 |

9 | Thank you for confirming your user account. You can now @(Html.ActionLink("Sign in", "SignIn", "Account")). 10 |

-------------------------------------------------------------------------------- /LearningKit/Models/PrivacyPage/ConsentDetailsModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.PrivacyPage 2 | { 3 | public class ConsentDetailsModel 4 | { 5 | public string ConsentShortText { get; set; } 6 | 7 | public string ConsentFullText { get; set; } 8 | 9 | public string ConsentDisplayName { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /LearningKit/Models/TrackingConsent/TrackingConsentViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.TrackingConsent 2 | { 3 | //DocSection:ViewModel 4 | public class TrackingConsentViewModel 5 | { 6 | public string ShortText { get; set; } 7 | 8 | public bool IsAgreed { get; set; } 9 | } 10 | //EndDocSection:ViewModel 11 | } -------------------------------------------------------------------------------- /LearningKit/Views/PasswordReset/ResetPasswordSucceeded.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Password reset successful"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

Password reset successful

8 |

9 | Your password was successfully changed. You can now @(Html.ActionLink("Sign in", "SignIn", "Account")). 10 |

-------------------------------------------------------------------------------- /LearningKit/Models/Search/AzureSearch/FacetViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.Search.AzureSearch 2 | { 3 | // Encapsulates facet data 4 | public class FacetViewModel 5 | { 6 | public string Name { get; set; } 7 | 8 | public string Value { get; set; } 9 | 10 | public bool Selected { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/FormComponentProperties/CustomDropDownComponentProperties.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | 3 | 4 | namespace LearningKit.FormBuilder.FormComponentProperties 5 | { 6 | public class CustomDropDownComponentProperties : SelectorProperties 7 | { 8 | // Implement any required custom properties here 9 | } 10 | } -------------------------------------------------------------------------------- /LearningKit/Models/PrivacyPage/ConsentListingModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | using CMS.DataProtection; 5 | 6 | namespace LearningKit.Models.PrivacyPage 7 | { 8 | public class ConsentListingModel 9 | { 10 | public IEnumerable Consents { get; set; } = Enumerable.Empty(); 11 | } 12 | } -------------------------------------------------------------------------------- /LearningKit/Views/NewsletterSubscription/UnsubscribeFailure.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Unsubscription Failure"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

Unsubscription failed

8 |

The newsletter unsubscription did not succeed.

9 | 10 |

11 | @Html.ActionLink("> Back to the home index", "Index", "Home") 12 |

13 | -------------------------------------------------------------------------------- /LearningKit/Views/Shared/Sections/_Col5050.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 |
5 |
6 | @Html.Kentico().WidgetZone() 7 |
8 |
9 | @Html.Kentico().WidgetZone() 10 |
11 |
12 |
-------------------------------------------------------------------------------- /LearningKit/Views/Shared/FormSections/_TwoColumnFormSection.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.Forms.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 |
5 |
6 | @Html.Kentico().FormZone() 7 |
8 |
9 | @Html.Kentico().FormZone() 10 |
11 |
12 |
-------------------------------------------------------------------------------- /LearningKit/Models/Personalization/ConditionTypes/HasGivenConsentViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace LearningKit.Personalization.ConditionTypes 4 | { 5 | public class HasGivenConsentViewModel 6 | { 7 | [Required] 8 | [Display(Name = "Consent code name")] 9 | public string ConsentCodeName { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /LearningKit/Views/NewsletterSubscription/UnsubscribeSuccess.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Unsubscription Success"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

Unsubscription succeeded

8 |

You were successfully unsubscribed from the newsletter.

9 | 10 |

11 | @Html.ActionLink("> Back to the home index", "Index", "Home") 12 |

13 | -------------------------------------------------------------------------------- /LearningKit/Properties/WebAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using CMS; 2 | 3 | // Ensures that the Xperience API can discover and work with custom classes/components registered in the MVC web project 4 | // Placed into a separate class because AssemblyInfo.cs cannot access code from external libraries in cases where 5 | // the web project is precompiled with outputs merged into a single assembly 6 | [assembly: AssemblyDiscoverable] -------------------------------------------------------------------------------- /LearningKit/Views/PasswordReset/ResetPasswordInvalid.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Password reset failed"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

Password reset failed

8 |

9 | The link that you followed is invalid and you cannot reset your password. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

14 | -------------------------------------------------------------------------------- /LearningKit/Views/EmailRegister/CheckYourEmail.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | ViewBag.Title = "Check your email"; 5 | } 6 | 7 |

New account requires confirmation

8 |

9 | You need to confirm your new user account before you can sign in. Please check your email. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

-------------------------------------------------------------------------------- /LearningKit/Views/ExternalAuthentication/ExternalAuthenticationFailure.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Failed external authentication"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

External authentication failed

8 |

9 | Authentication via the external service was not successful. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

-------------------------------------------------------------------------------- /LearningKit/Controllers/HttpErrorsController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace LearningKit.Controllers 4 | { 5 | public class HttpErrorsController : Controller 6 | { 7 | //DocSection:NotFound 8 | public ActionResult NotFound() 9 | { 10 | Response.StatusCode = 404; 11 | 12 | return View(); 13 | } 14 | //EndDocSection:NotFound 15 | } 16 | } -------------------------------------------------------------------------------- /LearningKit/Views/EmailRegister/EmailConfirmationFailed.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | ViewBag.Title = "Email confirmation failed"; 5 | } 6 | 7 |

Email confirmation failed

8 |

9 | The link that you followed is invalid and email confirmation of the new user account failed. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

-------------------------------------------------------------------------------- /LearningKit/Views/Shared/Personalization/ConditionTypes/_HasGivenConsentConfiguration.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Personalization.ConditionTypes.HasGivenConsentViewModel 2 | 3 | @using (Html.BeginForm("Validate", "HasGivenConsent")) 4 | { 5 | @Html.LabelFor(model => model.ConsentCodeName) 6 |
7 | @Html.EditorFor(model => model.ConsentCodeName) 8 |
9 | @Html.ValidationMessage("ConsentCodeName") 10 | } -------------------------------------------------------------------------------- /LearningKit/Views/Roles/RestrictedPage.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | ViewBag.Title = "Restricted page"; 5 | } 6 | 7 |

Restricted page

8 |

9 | This page is only available for users who are assigned to a role with the 'KenticoRole' code name within the connected Kentico application. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

-------------------------------------------------------------------------------- /LearningKit/Models/Widgets/SelectorsWidget/SelectorsWidgetViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LearningKit.Models.Widgets.SelectorsWidget 4 | { 5 | public class SelectorsWidgetViewModel 6 | { 7 | public string MediaFileUrl { get; set; } 8 | 9 | public string DocumentPath { get; set; } 10 | 11 | public Guid? DocumentGuid { get; set; } 12 | 13 | public string AttachmentUrl { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /LearningKit/Views/Checkout/ThankYou.cshtml: -------------------------------------------------------------------------------- 1 | @*DocSection:ThankYou*@ 2 | @{ 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | ViewBag.Title = "Thank-you page"; 5 | } 6 | 7 |

Thank-you page

8 | 9 |

Order number: @ViewBag.OrderID

10 |

Your order was succesfully created. Thank you for your patronage!

11 | @*EndDocSection:ThankYou*@ 12 | 13 |

14 | @Html.ActionLink("> Back to the home index", "Index", "Home") 15 |

-------------------------------------------------------------------------------- /LearningKit/Views/Shared/Sections/_CustomSection.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 | @using LearningKit.Models.Sections.CustomSection 5 | 6 | @model ComponentViewModel 7 | 8 | @* Shows a sample section with a background color specified in the section's configuration dialog *@ 9 |
10 | @Html.Kentico().WidgetZone() 11 |
-------------------------------------------------------------------------------- /LearningKit/Models/Search/SmartSearch/SearchResultModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using CMS.Search; 4 | 5 | namespace LearningKit.Models.Search.SmartSearch 6 | { 7 | //DocSection:SearchResultModel 8 | public class SearchResultModel 9 | { 10 | public string Query { get; set; } 11 | 12 | public IEnumerable Items { get; set; } 13 | } 14 | //EndDocSection:SearchResultModel 15 | } -------------------------------------------------------------------------------- /LearningKit/Views/ExternalAuthentication/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Account disabled"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

User account disabled

8 |

9 | Authentication via the external service was successful, but the corresponding Xperience user account is currently disabled. You cannot sign in. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

14 | -------------------------------------------------------------------------------- /LearningKit/Views/PasswordReset/CheckYourEmail.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Password reset email sent"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

Password reset email sent

8 |

9 | The system has sent a password reset email to the specified address. Click the link within the email to reset your password. 10 |

11 |

12 | @Html.ActionLink("> Back to the home index", "Index", "Home") 13 |

14 | 15 | -------------------------------------------------------------------------------- /LearningKit/Models/Search/AzureSearch/DocumentViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LearningKit.Models.Search.AzureSearch 4 | { 5 | // Encapsulates document search results 6 | public class DocumentViewModel 7 | { 8 | public string DocumentTitle { get; set; } 9 | 10 | public string DocumentShortDescription { get; set; } 11 | 12 | public IDictionary> Highlights { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /LearningKit/Models/Widgets/ColorWidget/ColorWidgetProperties.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using Kentico.PageBuilder.Web.Mvc; 3 | using LearningKit.FormBuilder.FormComponents; 4 | 5 | namespace LearningKit.Models.Widgets.ColorWidget 6 | { 7 | public class ColorWidgetProperties : IWidgetProperties 8 | { 9 | [EditingComponent(ColorFormComponent.IDENTIFIER, Order = 0, Label = "Color")] 10 | public string Color { get; set; } = "white"; 11 | } 12 | } -------------------------------------------------------------------------------- /LearningKit/Models/Checkout/DeliveryDetailsViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace LearningKit.Models.Checkout 2 | { 3 | //DocSection:DeliveryDetailsViewModel 4 | public class DeliveryDetailsViewModel 5 | { 6 | 7 | public CustomerViewModel Customer { get; set; } 8 | 9 | public BillingAddressViewModel BillingAddress { get; set; } 10 | 11 | public ShippingOptionViewModel ShippingOption { get; set; } 12 | } 13 | //EndDocSection:DeliveryDetailsViewModel 14 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/FormComponents/_CustomDropDownComponent.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.Forms.Web.Mvc 2 | @using LearningKit.FormBuilder.FormComponents 3 | 4 | @model CustomDropDownComponent 5 | 6 | @{ 7 | var htmlAttributes = ViewData.Kentico().GetEditorHtmlAttributes(); 8 | } 9 | 10 | @* Invoking a Get operation on the component's 'Items' property executes the GetItems method. *@ 11 | @Html.DropDownListFor(x => x.SelectedValue, Model.HtmlOptions.ToSelectListItems(), null, htmlAttributes) -------------------------------------------------------------------------------- /LearningKit/Models/Products/PriceDetailViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace LearningKit.Models.Products 7 | { 8 | //DocSection:PriceDetailViewModel 9 | public class PriceDetailViewModel 10 | { 11 | public decimal Price; 12 | 13 | public decimal ListPrice; 14 | 15 | public string CurrencyFormatString; 16 | 17 | } 18 | //EndDocSection:PriceDetailViewModel 19 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/InlineEditors/_NumberEditor.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 | @model LearningKit.Models.InlineEditors.NumberEditor.NumberEditorModel 5 | 6 | @using (Html.Kentico().BeginInlineEditor("number-editor", Model.PropertyName)) 7 | { 8 |
9 | 10 | 11 |
12 | } -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/components/app-globals-8931cc91.js: -------------------------------------------------------------------------------- 1 | import{C as n}from"./index-4dc45cd4.js";n.getString=function(n,r){for(var t=[],o=2;o 8 | 9 | @* Configurable heading added above the default form widget *@ 10 |

@Model.Properties.HeadingText

11 | 12 | @* Renders the nested form widget *@ 13 | @Html.Kentico().RenderNestedWidget(SystemComponentIdentifiers.FORM_WIDGET_IDENTIFIER, Model.Properties) -------------------------------------------------------------------------------- /LearningKit/Views/NewsletterSubscription/ConfirmSubscription.cshtml: -------------------------------------------------------------------------------- 1 | @model NewsletterConfirmSubscriptionViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Subscription Confirmation"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Subscription confirmation

9 | 10 | @if (Html.ViewData.ModelState.IsValid) 11 | { 12 |

You have successfully confirmed your newsletter subscription.

13 | } 14 | else 15 | { 16 | @Html.ValidationSummary(true, "") 17 | } 18 | 19 |

20 | @Html.ActionLink("> Back to the home index", "Index", "Home") 21 |

-------------------------------------------------------------------------------- /LearningKit/Views/Shared/InlineEditors/_ColorEditor.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 | @model LearningKit.Models.InlineEditors.ColorEditor.ColorEditorModel 5 | 6 | @using (Html.Kentico().BeginInlineEditor("color-editor", 7 | Model.PropertyName, 8 | new { data_url = Url.Action("Index", "ColorModalDialog") })) 9 | { 10 |
11 | 12 |
13 | } 14 | -------------------------------------------------------------------------------- /LearningKit/Models/PasswordReset/RequestPasswordResetViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | public class RequestPasswordResetViewModel 5 | { 6 | [DataType(DataType.EmailAddress)] 7 | [Required(ErrorMessage = "The Email address cannot be empty.")] 8 | [DisplayName("Email address")] 9 | [EmailAddress(ErrorMessage = "Invalid email address.")] 10 | [MaxLength(254, ErrorMessage = "The Email address cannot be longer than 254 characters.")] 11 | public string Email 12 | { 13 | get; 14 | set; 15 | } 16 | } -------------------------------------------------------------------------------- /LearningKit/Models/ProductFilter/ProductFilterViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using LearningKit.Models.Products; 4 | 5 | namespace LearningKit.Models.ProductFilter 6 | { 7 | public class ProductFilterViewModel 8 | { 9 | public List Manufacturers { get; set; } 10 | 11 | public List FilteredProducts { get; set; } 12 | 13 | public bool LPTWithFeature { get; set; } 14 | 15 | public decimal PriceFrom { get; set; } 16 | 17 | public decimal PriceTo { get; set; } 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /LearningKitCustomizations/OutputCache/OutputCacheKeyOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Web.Mvc; 2 | 3 | 4 | namespace OutputCacheCustomization 5 | { 6 | public static class OutputCacheKeyOptionsExtensions 7 | { 8 | // Varies the output cache based on the contact's gender 9 | public static IOutputCacheKeyOptions VaryByContactGender(this IOutputCacheKeyOptions options) 10 | { 11 | // Adds the ContactGenderOutputCacheKey to the options object 12 | options.AddCacheKey(new ContactGenderOutputCacheKey()); 13 | 14 | return options; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/components/app-globals-d5563cb5.system.js: -------------------------------------------------------------------------------- 1 | System.register(["./index-7b62609d.system.js"],(function(){"use strict";var t;return{setters:[function(e){t=e.C}],execute:function(){t.getString=function(t,e){var r=[];for(var n=2;n 4 | /// Model for a fictitious payment response. 5 | /// 6 | public class ResponseViewModel 7 | { 8 | public int InvoiceNo { get; set; } 9 | 10 | public string Message { get; set; } 11 | 12 | public bool Completed { get; set; } 13 | 14 | public string TransactionID { get; set; } 15 | 16 | public string ResponseCode { get; set; } 17 | 18 | public decimal Amount { get; set; } 19 | 20 | public bool Approved { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /LearningKit/Views/NewsletterSubscription/SubscribeSuccess.cshtml: -------------------------------------------------------------------------------- 1 | @model NewsletterSubscribeViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Subscription Success"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Newsletter subscription successful

9 | 10 | @if (Model.RequireDoubleOptIn) 11 | { 12 |

You have subscribed to the newsletter. You need to confirm your subscription via email before it becomes active.

13 | } 14 | else 15 | { 16 |

You have successfully subscribed to the newsletter.

17 | } 18 | 19 |

20 | @Html.ActionLink("> Back to the home index", "Index", "Home") 21 |

-------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/FormComponentProperties/ColorFormComponentProperties.cs: -------------------------------------------------------------------------------- 1 | using CMS.DataEngine; 2 | using Kentico.Forms.Web.Mvc; 3 | 4 | namespace LearningKit.FormBuilder.FormComponents 5 | { 6 | public class ColorFormComponentProperties : FormComponentProperties 7 | { 8 | public ColorFormComponentProperties() 9 | : base(FieldDataType.Text, size:200) 10 | { 11 | } 12 | 13 | [DefaultValueEditingComponent(TextInputComponent.IDENTIFIER)] 14 | public override string DefaultValue { 15 | get; 16 | set; 17 | } 18 | 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /LearningKit/Models/Widgets/NumberWidget/NumberWidgetProperties.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using Kentico.PageBuilder.Web.Mvc; 3 | 4 | namespace LearningKit.Models.Widgets.NumberWidget 5 | { 6 | public class NumberWidgetProperties : IWidgetProperties 7 | { 8 | // Defines a property and sets its default value 9 | // Assigns the default Xperience text input component, which allows users to enter 10 | // a numeric value for the property in the widget's configuration dialog 11 | [EditingComponent(IntInputComponent.IDENTIFIER, Order = 0, Label = "Number")] 12 | public int Number { get; set; } = 22; 13 | } 14 | } -------------------------------------------------------------------------------- /LearningKit/Models/Sections/CustomSection/CustomSectionProperties.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using Kentico.PageBuilder.Web.Mvc; 3 | 4 | namespace LearningKit.Models.Sections.CustomSection 5 | { 6 | public class CustomSectionProperties : ISectionProperties 7 | { 8 | // Defines a property and sets its default value 9 | // Assigns the default Xperience text input component, which allows users to enter 10 | // a string value for the property in the section's configuration dialog 11 | [EditingComponent(TextInputComponent.IDENTIFIER, Order = 0, Label = "Color")] 12 | public string Color { get; set; } = "#FFF"; 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /LearningKit/Controllers/Builders/ModalDialogs/ColorModalDialogController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Mvc; 3 | 4 | using LearningKit.Models.ModalDialogs; 5 | 6 | namespace LearningKit.Controllers.ModalDialogs 7 | { 8 | public class ColorModalDialogController : Controller 9 | { 10 | public ActionResult Index() 11 | { 12 | var model = new ColorModalDialogViewModel 13 | { 14 | Colors = new List() { "red", "blue", "white", "green", "black", "gray", "yellow" } 15 | }; 16 | 17 | return View("ModalDialogs/ColorModalDialog/_ColorModalDialog", model); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /LearningKit/Models/Checkout/ShoppingCartItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using CMS.Ecommerce; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace LearningKit.Models.Checkout 8 | { 9 | //DocSection:SCViewModel 10 | public class ShoppingCartItemViewModel 11 | { 12 | public string SKUName { get; set; } 13 | 14 | public int SKUID { get; set; } 15 | 16 | public string SKUImageUrl { get; set; } 17 | 18 | public int CartItemUnits { get; set; } 19 | 20 | public decimal TotalPrice { get; set; } 21 | 22 | public int CartItemID { get; set; } 23 | } 24 | //EndDocSection:SCViewModel 25 | } -------------------------------------------------------------------------------- /LearningKit/Models/NewsletterSubscription/NewsletterConfirmSubscriptionModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class NewsletterConfirmSubscriptionViewModel 4 | { 5 | /// 6 | /// Hash used to identify the subscription and for protection against forged confirmation requests. 7 | /// 8 | [Required] 9 | public string SubscriptionHash 10 | { 11 | get; 12 | set; 13 | } 14 | 15 | /// 16 | /// The date and time of the original subscription. Used to detect expired confirmation requests. 17 | /// 18 | public string DateTime 19 | { 20 | get; 21 | set; 22 | } 23 | } -------------------------------------------------------------------------------- /LearningKit/Models/PageTemplates/LandingPage/LandingPageProperties.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using Kentico.PageBuilder.Web.Mvc.PageTemplates; 3 | 4 | namespace LearningKit.Models.PageTemplates.LandingPage 5 | { 6 | public class LandingPageProperties : IPageTemplateProperties 7 | { 8 | // Defines a property and sets its default value 9 | // Assigns the default Xperience checkbox component, which saves a boolean value 10 | // depending on the state of the checkbox in the page template's configuration dialog 11 | [EditingComponent(CheckBoxComponent.IDENTIFIER, Order = 0, Label = "Show title")] 12 | public bool ShowTitle { get; set; } = true; 13 | } 14 | } -------------------------------------------------------------------------------- /LearningKit/Models/Widgets/ExtendedFormWidget/ExtendedFormWidgetProperties.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using Kentico.Forms.Web.Mvc.Widgets; 3 | 4 | namespace LearningKit.Models.Widgets.ExtendedFormWidget 5 | { 6 | public class ExtendedFormWidgetProperties : FormWidgetProperties 7 | { 8 | // Defines a property and sets its default value 9 | // Assigns the default Xperience text input component, which allows users to enter 10 | // a textual value for the property in the widget's configuration dialog 11 | [EditingComponent(TextInputComponent.IDENTIFIER, Order = 0, Label = "Heading text")] 12 | public string HeadingText { get; set; } = "Default"; 13 | } 14 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/FormComponentFilters/FormComponentsFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections.Generic; 3 | 4 | using Kentico.Forms.Web.Mvc; 5 | using Kentico.Forms.Web.Mvc.FormComponents; 6 | 7 | 8 | namespace LearningKit.FormBuilder 9 | { 10 | public class FormComponentsFilter : IFormComponentFilter 11 | { 12 | public IEnumerable Filter(IEnumerable formComponents, FormComponentFilterContext context) 13 | { 14 | // Filters out all Xperience form components from the form builder UI 15 | return formComponents.Where(component => !component.Identifier.StartsWith("Kentico")); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /LearningKit/Views/ExternalAuthentication/SignInCallback.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Unable to create external account"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |

Unable to create the external user account

8 |

9 | Authentication via the external service was successful, but a problem occurred when attempting to create a corresponding user account in Xperience. For example, a conflict with an existing username may have occurred. 10 |

11 |
12 | @if (!Html.ViewData.ModelState.IsValid) 13 | { 14 | @Html.ValidationSummary(true, "") 15 | } 16 |
17 |

18 | @Html.ActionLink("> Back to the home index", "Index", "Home") 19 |

20 | 21 | -------------------------------------------------------------------------------- /LearningKit/Views/PrivacyPage/ConsentDetails.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.PrivacyPage.ConsentDetailsModel 2 | 3 | @{ 4 | ViewBag.Title = "Consent details"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Consent details

9 |

Display name

10 |

@Model.ConsentDisplayName

11 | @*DocSection:ConsentTexts*@ 12 |

Short text

13 |

@Html.Raw(Model.ConsentShortText)

14 |

Full text

15 |

@Html.Kentico().ResolveUrls(Model.ConsentFullText)

16 | @*EndDocSection:ConsentTexts*@ 17 | 18 |

19 | @Html.ActionLink("> Back to the privacy page", "Index", "PrivacyPage") 20 |

21 |

22 | @Html.ActionLink("> Back to the home index", "Index", "Home") 23 |

-------------------------------------------------------------------------------- /LearningKit/Views/MediaLibrary/ShowMediaFiles.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | ViewBag.Title = "Media library file listing"; 4 | } 5 | 6 | @using LearningKit.Models.MediaLibrary 7 | 8 | @*DocSection:ShowMediaFiles*@ 9 | @model IEnumerable 10 | 11 | 12 |

Media library file listing

13 | 14 | @foreach (MediaFileViewModel mediaFile in Model) 15 | { 16 | @* Gets an application absolute URL for the media file *@ 17 | string url = Url.Content(mediaFile.RelativeUrl); 18 | 19 | @mediaFile.FileTitle 20 | } 21 | @*EndDocSection:ShowMediaFiles*@ 22 |

23 | @Html.ActionLink("> Back to the home index", "Index", "Home") 24 |

25 | -------------------------------------------------------------------------------- /LearningKit/Views/Shared/FormComponents/_CustomPropertyComponent.cshtml: -------------------------------------------------------------------------------- 1 | @*DocSection:PlaceholderTextView*@ 2 | @using Kentico.Forms.Web.Mvc 3 | 4 | @model LearningKit.FormBuilder.FormComponents.CustomPropertyComponent 5 | 6 | @{ 7 | @* Gets a collection of system HTML attributes necessary for the correct functionality of the form component inputs *@ 8 | var htmlAttributes = ViewData.Kentico().GetEditorHtmlAttributes(); 9 | 10 | @* Specifies additional HTML attributes for the form component *@ 11 | htmlAttributes["size"] = Model.Properties.CharacterSize; 12 | } 13 | 14 | @* Renders the input element for the 'Value' property of the form component *@ 15 | @Html.TextBoxFor(m => m.Value, htmlAttributes) 16 | @*EndDocSection:PlaceholderTextView*@ -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/InlineEditors/DropdownEditor/dropdown-editor.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | window.kentico.pageBuilder.registerInlineEditor("kentico-dropdown-editor", { 3 | init: function (options) { 4 | var editor = options.editor; 5 | var dropdown = editor.querySelector(".ktc-selector"); 6 | dropdown.addEventListener("change", function () { 7 | var event = new CustomEvent("updateProperty", { 8 | detail: { 9 | value: dropdown.value, 10 | name: options.propertyName 11 | } 12 | }); 13 | editor.dispatchEvent(event); 14 | }); 15 | } 16 | }); 17 | })(); 18 | -------------------------------------------------------------------------------- /LearningKit/Controllers/Builders/Sections/Col5050Controller.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | using Kentico.PageBuilder.Web.Mvc; 4 | 5 | using LearningKit.Controllers.Sections; 6 | 7 | // Registers the '50/50' page builder section 8 | // This section does not require a custom controller, but uses one for demonstration purposes 9 | [assembly: RegisterSection("LearningKit.Sections.Col5050", typeof(Col5050Controller), "50/50 columns", IconClass = "icon-l-cols-2")] 10 | 11 | namespace LearningKit.Controllers.Sections 12 | { 13 | public class Col5050Controller : SectionController 14 | { 15 | // GET action used to retrieve the section markup 16 | public ActionResult Index() 17 | { 18 | return PartialView("Sections/_Col5050"); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /LearningKit/Views/TrackingConsent/Consent.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.TrackingConsent.TrackingConsentViewModel 2 | 3 |
4 |

5 | @Html.Raw(Model.ShortText) 6 |

7 | @if (Model.IsAgreed) 8 | { 9 | using (Html.BeginForm("Revoke", "TrackingConsent", FormMethod.Post)) 10 | { 11 | @Html.AntiForgeryToken() 12 | 13 | } 14 | } 15 | else 16 | { 17 | using (Html.BeginForm("Accept", "TrackingConsent", FormMethod.Post)) 18 | { 19 | @Html.AntiForgeryToken() 20 | 21 | } 22 | } 23 |

24 | @Html.ActionLink("> Back to the home index", "Index", "Home") 25 |

26 |
-------------------------------------------------------------------------------- /LearningKit/Models/Personalization/CurrentContactViewModel.cs: -------------------------------------------------------------------------------- 1 | using CMS.ContactManagement; 2 | using CMS.Personas; 3 | 4 | namespace LearningKit.Models.Personalization 5 | { 6 | public class CurrentContactViewModel 7 | { 8 | public string Contact { get; } 9 | public string ContactPersonaName { get; } 10 | public string ContactPersonaDisplayName { get; } 11 | 12 | public CurrentContactViewModel(ContactInfo currentContact) 13 | { 14 | PersonaInfo contactPersona = currentContact.GetPersona(); 15 | 16 | if (contactPersona != null) 17 | { 18 | ContactPersonaName = contactPersona.PersonaName; 19 | ContactPersonaDisplayName = contactPersona.PersonaDisplayName; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LearningKit/Models/Search/AzureSearch/AzureSearchViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LearningKit.Models.Search.AzureSearch 4 | { 5 | // Encapsulates search request data and search results 6 | public class AzureSearchViewModel 7 | { 8 | public string SearchString { get; set; } 9 | 10 | public IList SearchResults { get; set; } 11 | 12 | public IList FilterFarm { get; set; } 13 | 14 | public IList FilterCountry { get; set; } 15 | 16 | public AzureSearchViewModel() 17 | { 18 | FilterCountry = new List(); 19 | FilterFarm = new List(); 20 | SearchResults = new List(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LearningKit/Models/Account/SignInViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | public class SignInViewModel 5 | { 6 | [Required(ErrorMessage = "The User name cannot be empty.")] 7 | [DisplayName("User name")] 8 | [MaxLength(100, ErrorMessage = "The User name cannot be longer than 100 characters.")] 9 | public string UserName 10 | { 11 | get; 12 | set; 13 | } 14 | 15 | [DataType(DataType.Password)] 16 | [DisplayName("Password")] 17 | [MaxLength(100, ErrorMessage = "The Password cannot be longer than 100 characters.")] 18 | public string Password 19 | { 20 | get; 21 | set; 22 | } 23 | 24 | [DisplayName("Stay signed in")] 25 | public bool SignInIsPersistent 26 | { 27 | get; 28 | set; 29 | } 30 | } -------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/ColorFormComponent.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using LearningKit.FormBuilder.FormComponents; 3 | 4 | [assembly: RegisterFormComponent(ColorFormComponent.IDENTIFIER, typeof(ColorFormComponent), "Color component", IsAvailableInFormBuilderEditor = false, IconClass = "icon-newspaper")] 5 | 6 | namespace LearningKit.FormBuilder.FormComponents 7 | { 8 | public class ColorFormComponent : FormComponent 9 | { 10 | public const string IDENTIFIER = "ColorFormComponent"; 11 | 12 | [BindableProperty] 13 | public string Value { get; set; } 14 | 15 | public override string GetValue() 16 | { 17 | return Value; 18 | } 19 | 20 | public override void SetValue(string value) 21 | { 22 | Value = value; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/Widgets/_ColorWidget.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 | @using LearningKit.Models.InlineEditors.ColorEditor 5 | @using LearningKit.Models.Widgets.ColorWidget 6 | 7 | @model ComponentViewModel 8 | 9 |
10 |

This is a colorful widget

11 |
12 | 13 | @* Shows an inline editor when rendered in the edit mode of the Pages application in Xperience *@ 14 | @if (Context.Kentico().PageBuilder().EditMode) 15 | { 16 | Html.RenderPartial("InlineEditors/_ColorEditor", new ColorEditorModel 17 | { 18 | @* Use the nameof() operator to get the name of the widget property from the widget property model *@ 19 | PropertyName = nameof(ColorWidgetProperties.Color), 20 | Color = Model.Properties.Color 21 | }); 22 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/Widgets/_NumberWidget.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.PageBuilder.Web.Mvc 2 | @using Kentico.Web.Mvc 3 | 4 | @using LearningKit.Models.InlineEditors.NumberEditor 5 | @using LearningKit.Models.Widgets.NumberWidget 6 | 7 | @model ComponentViewModel 8 | 9 |

The number you chose for today is: @Model.Properties.Number

10 | 11 | @* Shows an inline editor when rendered in the edit mode of the Pages application in Xperience *@ 12 | @if (Context.Kentico().PageBuilder().EditMode) 13 | { 14 | Html.RenderPartial("InlineEditors/_NumberEditor", new NumberEditorModel 15 | { 16 | @* Use the nameof() operator to get the name of the edited property from the widget property model *@ 17 | PropertyName = nameof(NumberWidgetProperties.Number), 18 | Number = Model.Properties.Number 19 | }); 20 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/VisibilityConditions/AdministratorPropertyCondition.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | 3 | using CMS.Base; 4 | using CMS.Membership; 5 | 6 | namespace LearningKit.FormBuilder.VisibilityConditions 7 | { 8 | // Visibility condition that evaluates whether the current user 9 | // in the administration interface has the 'Administrator' privilege level 10 | public class AdministratorPropertyCondition : VisibilityCondition 11 | { 12 | // Determines whether the property is visible 13 | public override bool IsVisible() 14 | { 15 | // True if the current user's privilege level is 'Administrator' or higher 16 | // In effect, the condition hides properties for users with the 'Editor' level 17 | return MembershipContext.AuthenticatedUser.CheckPrivilegeLevel(UserPrivilegeLevelEnum.Admin); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/FormComponents/Pages/page-selector.admin.js: -------------------------------------------------------------------------------- 1 | window.kentico = window.kentico || {}; 2 | 3 | /** 4 | * Page selector module. 5 | * @param {object} namespace Namespace under which this module operates. 6 | */ 7 | (function (namespace) { 8 | // Register the initialization function only in page builder 9 | if (!namespace.pageBuilder) { 10 | return; 11 | } 12 | 13 | var init = function (id, selectedPageData) { 14 | var component = document.getElementById(id); 15 | component.getString = window.kentico.localization.getString; 16 | component.selectedPageData = selectedPageData; 17 | }; 18 | 19 | const modalDialogInternal = namespace._modalDialog = namespace._modalDialog || {}; 20 | const pageSelector = modalDialogInternal.pageSelector = modalDialogInternal.pageSelector || {}; 21 | pageSelector.init = init; 22 | })(window.kentico); -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/FormComponents/Path/path-selector.admin.js: -------------------------------------------------------------------------------- 1 | window.kentico = window.kentico || {}; 2 | 3 | /** 4 | * Page selector module. 5 | * @param {object} namespace Namespace under which this module operates. 6 | */ 7 | (function (namespace) { 8 | // Register the initialization function only in page builder 9 | if (!namespace.pageBuilder) { 10 | return; 11 | } 12 | 13 | var init = function (id, selectedPageData) { 14 | var component = document.getElementById(id); 15 | component.getString = window.kentico.localization.getString; 16 | component.selectedPageData = selectedPageData; 17 | }; 18 | 19 | const modalDialogInternal = namespace._modalDialog = namespace._modalDialog || {}; 20 | const pathSelector = modalDialogInternal.pathSelector = modalDialogInternal.pathSelector || {}; 21 | pathSelector.init = init; 22 | })(window.kentico); -------------------------------------------------------------------------------- /LearningKit/Controllers/Builders/FormSections/TwoColumnFormSectionController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | using LearningKit.Controllers.FormSections; 4 | 5 | //DocSection:FormSectionRegistration 6 | using Kentico.Forms.Web.Mvc; 7 | 8 | [assembly: RegisterFormSection("LearningKit.FormSections.TwoColumns", typeof(TwoColumnFormSectionController), "Two columns", Description = "Organizes fields into two equal-width columns side-by-side.", IconClass = "icon-l-cols-2")] 9 | //EndDocSection:FormSectionRegistration 10 | 11 | namespace LearningKit.Controllers.FormSections 12 | { 13 | //DocSection:FormSectionController 14 | public class TwoColumnFormSectionController : Controller 15 | { 16 | // Action used to retrieve the section markup 17 | public ActionResult Index() 18 | { 19 | return PartialView("FormSections/_TwoColumnFormSection"); 20 | } 21 | } 22 | //EndDocSection:FormSectionController 23 | } -------------------------------------------------------------------------------- /LearningKit/Models/NewsletterSubscription/NewsletterSubscribeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | public class NewsletterSubscribeViewModel 5 | { 6 | [DataType(DataType.EmailAddress)] 7 | [Required(ErrorMessage = "The Email address cannot be empty.")] 8 | [DisplayName("Email address")] 9 | [EmailAddress(ErrorMessage = "Invalid email address.")] 10 | [MaxLength(254, ErrorMessage = "The Email address cannot be longer than 254 characters.")] 11 | public string Email 12 | { 13 | get; 14 | set; 15 | } 16 | 17 | /// 18 | /// Indicates whether the newsletter requires double-opt in for subscription. 19 | /// Allows the view to display appropriate information to newly subscribed users. 20 | /// 21 | [Bindable(false)] 22 | public bool RequireDoubleOptIn 23 | { 24 | get; 25 | set; 26 | } 27 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/PageTemplates/_LandingPageTemplate.cshtml: -------------------------------------------------------------------------------- 1 | @*DocSection:LandingPageTemplateView*@ 2 | @using Kentico.PageBuilder.Web.Mvc 3 | @using Kentico.Web.Mvc 4 | 5 | @using LearningKit.Models.PageTemplates.LandingPage 6 | 7 | @model ComponentViewModel 8 | 9 | @{ 10 | Layout = "~/Views/Shared/_PageTemplateLayout.cshtml"; 11 | } 12 | 13 | @{ 14 | // Sets the page title to the name of the current page 15 | ViewBag.Title = Model.Page.DocumentName; 16 | } 17 | 18 | @if (Model.Properties.ShowTitle) 19 | { 20 |

@Model.Page.DocumentName

21 | } 22 | 23 |
24 |
25 | @Html.Kentico().EditableArea("top") 26 |
27 |
28 | @Html.Kentico().EditableArea("bottom") 29 |
30 |
31 | @*EndDocSection:LandingPageTemplateView*@ 32 | 33 |

34 | @Html.ActionLink("> Back to the home index", "Index", "Home") 35 |

-------------------------------------------------------------------------------- /LearningKit/Controllers/Builders/Widgets/RandomWidgetController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Mvc; 3 | 4 | using Kentico.PageBuilder.Web.Mvc; 5 | 6 | using LearningKit.Controllers.Widgets; 7 | 8 | // Assembly attribute to register the widget for the connected Xperience instance. 9 | [assembly: RegisterWidget("LearningKit.Widgets.RandomWidget", typeof(RandomWidgetController), "Random number", IconClass = "icon-modal-question")] 10 | namespace LearningKit.Controllers.Widgets 11 | { 12 | /// 13 | /// A sample widget displaying a message with a random number. 14 | /// 15 | public class RandomWidgetController : WidgetController 16 | { 17 | private static Random rnd = new Random(); 18 | 19 | // Default GET action used to retrieve the widget markup 20 | public ActionResult Index() 21 | { 22 | return PartialView("Widgets/_RandomWidget", rnd.Next(100)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LearningKit/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("LearningKit")] 5 | [assembly: AssemblyDescription("ASP.NET MVC 5 application for learning and documentation purposes. Contains code displayed as examples within the Xperience documentation. The project does not represent a real-world website (unlike the MVC Dancing Goat sample site), but provides more basic examples suitable for learning purposes.")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("Kentico Software")] 8 | [assembly: AssemblyProduct("LearningKit")] 9 | [assembly: AssemblyCopyright("© 2016 Kentico Software. All rights reserved.")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("3561e1b0-8bf6-4187-a581-1b0801168cdd")] 14 | [assembly: AssemblyVersion("1.0.0.0")] 15 | [assembly: AssemblyFileVersion("1.0.0.0")] 16 | -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/MediaFiles/media-files-selector.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var init = function () { 3 | var component = document.getElementsByTagName("kentico-media-files")[0]; 4 | var data = window.kentico.modalDialog.getData().media; 5 | component.getString = window.kentico.localization.getString; 6 | component.values = data.selectedValues; 7 | component.libraryName = data.libraryName; 8 | component.maxFilesLimit = data.maxFilesLimit; 9 | component.allowedExtensions = data.allowedExtensions; 10 | }; 11 | 12 | const kenticoNamespace = window.kentico || {}; 13 | const modalDialog = kenticoNamespace.modalDialog = kenticoNamespace.modalDialog || {}; 14 | const mediaFilesSelector = modalDialog.mediaFilesSelector = modalDialog.mediaFilesSelector || {}; 15 | const mediaFilesSelectorDialog = mediaFilesSelector.dialog = mediaFilesSelector.dialog || {}; 16 | mediaFilesSelectorDialog._init = init; 17 | })(); -------------------------------------------------------------------------------- /LearningKit/Views/Roles/ManageRoles.cshtml: -------------------------------------------------------------------------------- 1 | @model Kentico.Membership.User 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Manage your roles"; 6 | } 7 | 8 |

Manage your roles

9 |
10 | Current user: @Model.UserName 11 |
12 |
13 |

Current roles:

14 |
    15 | @foreach (var role in Model.Roles) 16 | { 17 |
  • @role
  • 18 | } 19 |
20 |
21 |
22 | @using (Html.BeginForm("ManageRoles", "Roles", FormMethod.Post, null)) 23 | { 24 | @Html.AntiForgeryToken() 25 | 26 | 27 | } 28 |
29 | @if (!Html.ViewData.ModelState.IsValid) 30 | { 31 | @Html.ValidationSummary(true, "") 32 | } 33 |

34 | @Html.ActionLink("> Back to the home index", "Index", "Home") 35 |

-------------------------------------------------------------------------------- /LearningKit/Content/FormComponents/ColorFormComponent/colorFormComponent.js: -------------------------------------------------------------------------------- 1 | function openColorModalDialog(dialogData) { 2 | // Gets the form component's input element 3 | var inputElement = window.document.querySelector('#' + dialogData.colorInputId); 4 | 5 | // Opens the modal dialog 6 | kentico.modalDialog.open({ 7 | url: dialogData.modalDialogUrl, 8 | applyCallback: function(dialogWindow) { 9 | // Retrieves the selected value from the modal dialog 10 | var selectedValue = dialogWindow.document.querySelector('input[name="color"]:checked').value; 11 | // Updates the value of the input element in the property configuration dialog 12 | inputElement.value = selectedValue; 13 | }, 14 | applyButtonText: 'Confirm color selection', 15 | title: 'Select a color', 16 | // Passes the current color to the modal dialog data 17 | data: { value: inputElement.value } 18 | }); 19 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/FormComponentFilters/IndividualFormComponentFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections.Generic; 3 | 4 | using Kentico.Forms.Web.Mvc; 5 | using Kentico.Forms.Web.Mvc.FormComponents; 6 | 7 | 8 | namespace LearningKit.FormBuilder 9 | { 10 | public class IndividualFormComponentsFilter : IFormComponentFilter 11 | { 12 | public IEnumerable Filter(IEnumerable formComponents, FormComponentFilterContext context) 13 | { 14 | // Filters specified form components 15 | return formComponents.Where(component => !GetComponentsToFilter().Contains(component.Identifier)); 16 | } 17 | 18 | 19 | // A collection of form component identifiers to filter 20 | private IEnumerable GetComponentsToFilter() 21 | { 22 | return new string[] { TextInputComponent.IDENTIFIER, TextAreaComponent.IDENTIFIER, USPhoneComponent.IDENTIFIER }; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LearningKit/Scripts/variantSelector.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // Initializes contextual variables 5 | var url = $('.cart-item-selector').data('variant-action'), 6 | stockMessage = $("#stockMessage"), 7 | totalPrice = $('#totalPrice'), 8 | selectedSKUID = $('#selectedVariantID'); 9 | 10 | // Updates the displayed product details when a different variant 11 | // is selected from the variants drop-down selector 12 | $('.js-variant-selector').change(function () { 13 | var id = $(this).val(); 14 | updateVariantSelection(id); 15 | }); 16 | 17 | // Updates the product information from data retrieved for the selected variant 18 | function updateVariantSelection(variantId) { 19 | $.post(url, { variantID: variantId }, function (data) { 20 | stockMessage.text(data.stockMessage); 21 | totalPrice.text(data.totalPrice); 22 | selectedSKUID.val(variantId); 23 | }); 24 | } 25 | 26 | }()); -------------------------------------------------------------------------------- /LearningKit/Views/Shared/_PageTemplateLayout.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.Activities.Web.Mvc 2 | @using Kentico.OnlineMarketing.Web.Mvc 3 | @using Kentico.PageBuilder.Web.Mvc 4 | @using Kentico.Web.Mvc 5 | 6 | @{ 7 | Layout = null; 8 | } 9 | 10 | 11 | 12 | 13 | 14 | 15 | @ViewBag.Title - Learning Kit 16 | @Html.Kentico().PageBuilderStyles() 17 | 18 | @* Registers scripts that ensure logging of campaign page visits and page-related activities *@ 19 | @Html.Kentico().ActivityLoggingScript() 20 | 21 | @* Registers scripts that ensure logging of analytics data for A/B tests *@ 22 | @Html.Kentico().ABTestLoggerScript() 23 | 24 | @* Registers scripts that provide logging of web analytics data *@ 25 | @Html.Kentico().WebAnayticsLoggingScript() 26 | 27 | 28 | @RenderBody() 29 | @Html.Kentico().PageBuilderScripts() 30 | 31 | -------------------------------------------------------------------------------- /LearningKitCustomizations/OutputCache/ContactGenderOutputCacheKey.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | 3 | using CMS.ContactManagement; 4 | 5 | using Kentico.Web.Mvc; 6 | 7 | 8 | namespace OutputCacheCustomization 9 | { 10 | public class ContactGenderOutputCacheKey : IOutputCacheKey 11 | { 12 | // Used as a prefix for this cache key part 13 | public string Name => "KenticoContactGender"; 14 | 15 | // Invoked when constructing a cache key from the configured 'IOutputCacheKeyOptions' options object 16 | public string GetVaryByCustomString(HttpContextBase context, string custom) 17 | { 18 | // Gets the current contact, without creating a new anonymous contact for new visitors 19 | ContactInfo existingContact = ContactManagementContext.GetCurrentContact(createAnonymous: false); 20 | // Gets the contact's gender 21 | int? contactGender = existingContact?.ContactGender; 22 | return $"{Name}={contactGender}"; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/FormComponents/_ColorFormComponent.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.Forms.Web.Mvc 2 | 3 | @model LearningKit.FormBuilder.FormComponents.ColorFormComponent 4 | 5 | @{ 6 | // Gets a collection of system HTML attributes necessary for the functionality of form component inputs 7 | IDictionary htmlAttributes = ViewData.Kentico().GetEditorHtmlAttributes(); 8 | 9 | // Prepares data that will be passed to the function that opens the modal dialog 10 | string dialogData = Newtonsoft.Json.JsonConvert.SerializeObject(new 11 | { 12 | modalDialogUrl = Url.Action("Index", "ColorModalDialog"), 13 | colorInputId = Html.IdFor(m => m.Value).ToHtmlString() 14 | }); 15 | } 16 | 17 | @* Textbox input element that displays/edits the form component's color value *@ 18 | @Html.TextBoxFor(m => m.Value, htmlAttributes) 19 | 20 | @* Button that opens the modal dialog when clicked *@ 21 | -------------------------------------------------------------------------------- /LearningKit/Models/PasswordReset/ResetPasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | public class ResetPasswordViewModel 5 | { 6 | public int UserId 7 | { 8 | get; 9 | set; 10 | } 11 | 12 | public string Token 13 | { 14 | get; 15 | set; 16 | } 17 | 18 | [DataType(DataType.Password)] 19 | [Required(ErrorMessage = "The Password cannot be empty.")] 20 | [DisplayName("Password")] 21 | [MaxLength(100, ErrorMessage = "The Password cannot be longer than 100 characters.")] 22 | public string Password 23 | { 24 | get; 25 | set; 26 | } 27 | 28 | [DataType(DataType.Password)] 29 | [DisplayName("Password confirmation")] 30 | [MaxLength(100, ErrorMessage = "The Password cannot be longer than 100 characters.")] 31 | [Compare("Password", ErrorMessage = "The entered passwords do not match.")] 32 | public string PasswordConfirmation 33 | { 34 | get; 35 | set; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/FormComponents/Attachments/attachment-selector.admin.js: -------------------------------------------------------------------------------- 1 | window.kentico = window.kentico || {}; 2 | 3 | /** 4 | * Attachment selector module. 5 | * @param {object} namespace Namespace under which this module operates. 6 | */ 7 | (function (namespace) { 8 | // Register the initialization function only in page builder 9 | if (!namespace.pageBuilder) { 10 | return; 11 | } 12 | 13 | var init = function (id, filesData) { 14 | var component = document.getElementById(id); 15 | component.openDialog = window.kentico.modalDialog.attachmentSelector.open; 16 | component.getString = window.kentico.localization.getString; 17 | component.selectedFiles = JSON.parse(filesData); 18 | }; 19 | 20 | const modalDialogInternal = namespace._modalDialog = namespace._modalDialog || {}; 21 | const attachmentSelector = modalDialogInternal.attachmentSelector = modalDialogInternal.attachmentSelector || {}; 22 | attachmentSelector.init = init; 23 | })(window.kentico); -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/FormComponents/MediaFiles/media-files-selector.admin.js: -------------------------------------------------------------------------------- 1 | window.kentico = window.kentico || {}; 2 | 3 | /** 4 | * Media file selector module. 5 | * @param {object} namespace Namespace under which this module operates. 6 | */ 7 | (function (namespace) { 8 | // Register the initialization function only in page builder 9 | if (!namespace.pageBuilder) { 10 | return; 11 | } 12 | 13 | var init = function (id, filesData) { 14 | var component = document.getElementById(id); 15 | component.openDialog = window.kentico.modalDialog.mediaFilesSelector.open; 16 | component.getString = window.kentico.localization.getString; 17 | component.selectedFiles = JSON.parse(filesData); 18 | }; 19 | 20 | const modalDialogInternal = namespace._modalDialog = namespace._modalDialog || {}; 21 | const mediaFilesSelector = modalDialogInternal.mediaFilesSelector = modalDialogInternal.mediaFilesSelector || {}; 22 | mediaFilesSelector.init = init; 23 | })(window.kentico); -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/builders/web-components.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Core-icons;src:url(Core-icons.eot?4bfc4fa95b906c7f2991a71cfc9e873a);src:url(Core-icons.svg?171eb859bcd65eb3f409cccb45f96f89#Core-icons) format("svg"),url(Core-icons.eot?4bfc4fa95b906c7f2991a71cfc9e873a?#iefix) format("embedded-opentype"),url(Core-icons.woff?12a37fd82ed15fc12bf21afe2a420fcb) format("woff"),url(Core-icons.ttf?5d057cd285de0035d08c809b014c112e) format("truetype");font-weight:400;font-style:normal}body{border-collapse:separate;border-color:transparent;caption-side:top;color:#262524;cursor:auto;direction:ltr;empty-cells:show;font-family:Segoe UI,Helvetica,Verdana,Arial,sans-serif;font-kerning:auto;font-size:14px;font-style:normal;font-variant:normal;font-weight:400;hyphens:manual;letter-spacing:normal;line-height:20px;list-style:disc outside none;object-position:50% 50%;tab-size:8;text-align:left;text-align-last:auto;text-indent:0;text-justify:auto;text-shadow:none;text-transform:none;unicode-bidi:normal;visibility:visible;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal} -------------------------------------------------------------------------------- /LearningKitCustomizations/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | using CMS; 5 | 6 | // Ensures the assembly is visible to Kentico during initialization 7 | [assembly: AssemblyDiscoverable] 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTitle("LearningKitCustomizations")] 13 | [assembly: AssemblyDescription("Assembly containing sample customizations for the LearningKit project and referenced in the Kentico documentation.")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("e3918b5e-4dc3-4591-a885-61a315270e02")] 22 | -------------------------------------------------------------------------------- /LearningKit/Views/Shared/ModalDialogs/ColorModalDialog/_ColorModalDialog.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.Components.Web.Mvc.Dialogs 2 | 3 | @model LearningKit.Models.ModalDialogs.ColorModalDialogViewModel 4 | 5 | 6 | 7 | 8 | @Html.Kentico().ModalDialogScript() 9 | 10 | 11 | 12 |
13 | @foreach (var color in Model.Colors) 14 | { 15 | 16 |
17 | } 18 |
19 | 20 | @* Script that preselects the radio button of the current color 21 | Gets the value from the data passed when the 'kentico.modalDialog.open' function is called *@ 22 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /LearningKit/Views/Shared/Widgets/_SelectorsWidget.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.Widgets.SelectorsWidget.SelectorsWidgetViewModel 2 | 3 |

This is a dummy widget demonstrating the functionality of selector form components

4 |

Use the widget properties dialog to select media files or pages from the connected Xperience database.

5 | 6 | Selected image: 7 | 8 |
9 | 10 | @* Renders the selected image *@ 11 | @if (Model.MediaFileUrl.Equals(String.Empty)) 12 | { 13 | image placeholder 14 | } 15 | else 16 | { 17 | 18 | } 19 | 20 |

21 | Pages: 22 |

    23 |
  • Selected document: @Model.DocumentGuid
  • 24 |
  • Selected path: @Model.DocumentPath
  • 25 |
26 |

27 | 28 | Selected attachment: 29 | @* Creates a download link for the selected attachment *@ 30 | @if (Model.AttachmentUrl.Equals(String.Empty)) 31 | { 32 | placeholder 33 | } 34 | else 35 | { 36 | Click to download attachment 37 | } -------------------------------------------------------------------------------- /LearningKit/Views/PasswordReset/RequestPasswordReset.cshtml: -------------------------------------------------------------------------------- 1 | @model RequestPasswordResetViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Password reset request"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Password reset request

9 | 10 |

Enter the email address associated with your user account. The system will send you an email with a link that allows you to reset your password.

11 | @using (Html.BeginForm("RequestPasswordReset", "PasswordReset", FormMethod.Post)) 12 | { 13 | @Html.AntiForgeryToken() 14 | 15 |
16 | @if (!Html.ViewData.ModelState.IsValid) 17 | { 18 | @Html.ValidationSummary(true, "") 19 | } 20 | 21 |
22 | @Html.LabelFor(model => model.Email) 23 |
24 |
25 | @Html.EditorFor(model => model.Email) 26 | @Html.ValidationMessageFor(model => model.Email) 27 |
28 | 29 | 30 |
31 | } 32 |

33 | @Html.ActionLink("> Back to the home index", "Index", "Home") 34 |

-------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/FormComponentProperties/CustomFormComponentProperties.cs: -------------------------------------------------------------------------------- 1 | using CMS.DataEngine; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | namespace LearningKit.FormBuilder.FormComponentProperties 6 | { 7 | public class CustomFormComponentProperties : FormComponentProperties 8 | { 9 | //DocSection:PropertiesDefinition 10 | // Sets a custom editing component for the DefaultValue property 11 | // System properties of the specified editing component, such as the Label, Tooltip, and Order, remain set to system defaults unless explicitly set in the constructor 12 | [DefaultValueEditingComponent(TextInputComponent.IDENTIFIER)] 13 | public override string DefaultValue 14 | { 15 | get; 16 | set; 17 | } 18 | 19 | 20 | // Initializes a new instance of the CustomFormComponentProperties class and configures the underlying database field 21 | public CustomFormComponentProperties() 22 | : base(FieldDataType.Text, size: 200) 23 | { 24 | } 25 | //EndDocSection:PropertiesDefinition 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kentico Internal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/FormComponentProperties/RgbInputComponentProperties.cs: -------------------------------------------------------------------------------- 1 | using CMS.DataEngine; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | 6 | namespace LearningKit.FormBuilder.FormComponentProperties 7 | { 8 | //DocSection:PropertiesDefinition 9 | public class RgbInputComponentProperties : FormComponentProperties 10 | { 11 | // Sets the component as the editing component of its DefaultValue property 12 | // System properties of the specified editing component, such as the Label, Tooltip, and Order, remain set to system defaults unless explicitly set in the constructor 13 | [DefaultValueEditingComponent("RgbInputComponent", DefaultValue = "#ff0000")] 14 | public override string DefaultValue 15 | { 16 | get; 17 | set; 18 | } 19 | 20 | 21 | // Initializes a new instance of the RgbInputComponentProperties class and configures the underlying database field 22 | public RgbInputComponentProperties() 23 | : base(FieldDataType.Text, 7) 24 | { 25 | } 26 | } 27 | //EndDocSection:PropertiesDefinition 28 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/FormComponents/_CustomFormComponent.cshtml: -------------------------------------------------------------------------------- 1 | @*DocSection:View*@ 2 | @using Kentico.Forms.Web.Mvc 3 | 4 | @* Gets a collection of system HTML attributes necessary for the correct functionality of the form component inputs *@ 5 | @{ 6 | IDictionary htmlAttributes = ViewData.Kentico().GetEditorHtmlAttributes(); 7 | } 8 | 9 | @model LearningKit.FormBuilder.FormComponents.CustomFormComponent 10 | 11 | @* Specifies additional HTML attributes of the form component. Checks for the existence of certain attributes in case 12 | the returned 'htmlAttributes' collection already contains them (e.g., when the component is rendered within 13 | the administration interface, where Xperience provides CSS classes to maintain the admin UI look and feel) *@ 14 | @{ 15 | if (htmlAttributes.ContainsKey("class")) 16 | { 17 | htmlAttributes["class"] += " myclass"; 18 | } 19 | else 20 | { 21 | htmlAttributes["class"] = "myclass"; 22 | } 23 | } 24 | 25 | @* Renders the input element for the 'Value' property of the form component *@ 26 | @Html.TextBoxFor(m => m.Value, htmlAttributes) 27 | @*EndDocSection:View*@ -------------------------------------------------------------------------------- /LearningKit/Views/NewsletterSubscription/Subscribe.cshtml: -------------------------------------------------------------------------------- 1 | @model NewsletterSubscribeViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Subscribe to a newsletter"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Subscribe to a newsletter

9 | @using (Html.BeginForm("Subscribe", "NewsletterSubscription", FormMethod.Post)) 10 | { 11 | @Html.AntiForgeryToken() 12 | 13 |
14 | 15 |
16 | @Html.LabelFor(model => model.Email) 17 |
18 |
19 | @Html.EditorFor(model => model.Email) 20 | @Html.ValidationMessageFor(model => model.Email, "") 21 |
22 | 23 | 24 |
25 | } 26 | 27 | @if (!Html.ViewData.ModelState.IsValid) 28 | { 29 | @Html.ValidationSummary(true, "") 30 | } 31 | 32 |

33 | Note: This form attempts to subscribe to a newsletter with the "SampleNewsletter" code name. An error will occur if the newsletter does not exist in the connected Xperience database. 34 |

35 | 36 |

37 | @Html.ActionLink("> Back to the home index", "Index", "Home") 38 |

-------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/CustomPropertyComponent.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a sample form component used to demonstrate manipulation with custom form component properties. 3 | * For more information, visit the Xperience documentation. 4 | */ 5 | 6 | using Kentico.Forms.Web.Mvc; 7 | 8 | using LearningKit.FormBuilder.FormComponents; 9 | using LearningKit.FormBuilder.FormComponentProperties; 10 | 11 | 12 | [assembly: RegisterFormComponent(CustomPropertyComponent.IDENTIFIER, typeof(CustomPropertyComponent), "Custom text input", Description = "Custom single-line text input", IconClass = "icon-l-text")] 13 | 14 | namespace LearningKit.FormBuilder.FormComponents 15 | { 16 | public class CustomPropertyComponent : FormComponent 17 | { 18 | public const string IDENTIFIER = "CustomPropertyComponent"; 19 | 20 | 21 | [BindableProperty] 22 | public string Value { get; set; } 23 | 24 | 25 | public override string GetValue() 26 | { 27 | return Value; 28 | } 29 | 30 | 31 | public override void SetValue(string value) 32 | { 33 | Value = value; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/VisibilityConditions/NumberSignPropertyCondition.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | 3 | namespace LearningKit.FormBuilder.VisibilityConditions 4 | { 5 | // Visibility condition that evaluates whether the value of a related integer property is positive, negative or zero 6 | public class NumberSignPropertyCondition : AnotherPropertyVisibilityCondition 7 | { 8 | // Parameter indicating whether the visibility condition is fulfilled for positive/negative numbers or zero 9 | public string RequiredSign { get; set; } 10 | 11 | // Determines whether the property is visible 12 | public override bool IsVisible() 13 | { 14 | string requiredSign = RequiredSign.ToLower(); 15 | 16 | switch (requiredSign) 17 | { 18 | case "zero": 19 | return DependeePropertyValue == 0; 20 | case "positive": 21 | return DependeePropertyValue > 0; 22 | case "negative": 23 | return DependeePropertyValue < 0; 24 | default: 25 | return false; 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /LearningKitCustomizations/EventLog/CsvEventWriter.cs: -------------------------------------------------------------------------------- 1 | using CMS; 2 | using CMS.Base; 3 | using CMS.Core; 4 | 5 | using System; 6 | using System.IO; 7 | 8 | using EventLogCustomizations; 9 | 10 | 11 | [assembly: RegisterImplementation(typeof(IEventLogWriter), typeof(CsvErrorEventWriter))] 12 | namespace EventLogCustomizations 13 | { 14 | public class CsvErrorEventWriter : IEventLogWriter 15 | { 16 | public void WriteLog(EventLogData eventLogData) 17 | { 18 | if (eventLogData.EventType == EventTypeEnum.Error) 19 | { 20 | // Checks if the error event contains an exception 21 | string exception = eventLogData.Exception != null ? eventLogData.Exception.ToString() : "No exception logged."; 22 | 23 | string eventData = $"Error, {eventLogData.EventCode}, {DateTime.Now}, {eventLogData.EventDescription}, {exception}{Environment.NewLine}"; 24 | 25 | // Writes logged error events into a 'errors.csv' file in the application's root directory 26 | File.AppendAllText(SystemContext.WebApplicationPhysicalPath + "\\errors.csv", eventData); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using Kentico.Activities.Web.Mvc 2 | @using Kentico.OnlineMarketing.Web.Mvc 3 | @using Kentico.Web.Mvc 4 | 5 | @{ 6 | Layout = null; 7 | } 8 | 9 | 10 | 11 | 12 | 13 | 14 | @ViewBag.Title - Learning Kit 15 | 16 | @* Renders a canonical link element *@ 17 | 18 | 19 | @*DocSection:VisitorActionScript*@ 20 | @* Registers scripts that ensure logging of campaign page visits and page-related activities *@ 21 | @Html.Kentico().ActivityLoggingScript() 22 | @*EndDocSection:VisitorActionScript*@ 23 | 24 | @*DocSection:AbTestingScript*@ 25 | @* Registers scripts that ensure logging of analytics data for A/B tests *@ 26 | @Html.Kentico().ABTestLoggerScript() 27 | @*EndDocSection:AbTestingScript*@ 28 | 29 | @*DocSection:WebAnalyticsScript*@ 30 | @* Registers scripts that provide logging of web analytics data *@ 31 | @Html.Kentico().WebAnalyticsLoggingScript() 32 | @*EndDocSection:WebAnalyticsScript*@ 33 | 34 | 35 | @RenderBody() 36 | 37 | 38 | -------------------------------------------------------------------------------- /LearningKit/FormBuilder/FormBuilderHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace LearningKit.FormBuilder 8 | { 9 | public static class FormBuilderHelper 10 | { 11 | //DocSection:CustomInputExtensionMethod 12 | // Renders an 'input' element of the specified type and with the collection of provided attributes 13 | public static MvcHtmlString CustomInput(this HtmlHelper helper, string inputType, string name, object value, IDictionary htmlAttributes) 14 | { 15 | TagBuilder tagBuilder = new TagBuilder("input"); 16 | 17 | // Specifies the input type, name, and value attributes 18 | tagBuilder.MergeAttribute("type", inputType); 19 | tagBuilder.MergeAttribute("name", name); 20 | tagBuilder.MergeAttribute("value", value.ToString()); 21 | 22 | // Merges additional attributes into the element 23 | tagBuilder.MergeAttributes(htmlAttributes); 24 | 25 | return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.StartTag)); 26 | } 27 | //EndDocSection:CustomInputExtensionMethod 28 | } 29 | } -------------------------------------------------------------------------------- /LearningKit/App_Start/ApplicationConfig.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Web.Mvc; 2 | using Kentico.Activities.Web.Mvc; 3 | using Kentico.CampaignLogging.Web.Mvc; 4 | using Kentico.Newsletters.Web.Mvc; 5 | using Kentico.PageBuilder.Web.Mvc; 6 | using Kentico.OnlineMarketing.Web.Mvc; 7 | using Kentico.Content.Web.Mvc.Routing; 8 | 9 | using LearningKit.PageTemplateFilters; 10 | 11 | namespace LearningKit 12 | { 13 | public class ApplicationConfig 14 | { 15 | public static void RegisterFeatures(ApplicationBuilder builder) 16 | { 17 | builder.UsePageBuilder(new PageBuilderOptions() { 18 | DefaultSectionIdentifier = "LearningKit.Sections.DefaultSection", 19 | RegisterDefaultSection = false 20 | }); 21 | 22 | builder.UseDataAnnotationsLocalization(); 23 | builder.UseCampaignLogger(); 24 | builder.UseActivityTracking(); 25 | builder.UseEmailTracking(); 26 | builder.UseABTesting(); 27 | builder.UseWebAnalytics(); 28 | builder.UsePageRouting(new PageRoutingOptions 29 | { 30 | EnableAlternativeUrls = true 31 | }); 32 | 33 | PageBuilderFilters.PageTemplates.Add(new LandingPageTemplateFilter()); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/VisibilityConditions/IsBetweenVisibilityCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.VisibilityConditions; 6 | 7 | 8 | // Registers the visibility condition in the system 9 | [assembly: RegisterFormVisibilityCondition("IsBetweenVisibilityCondition", typeof(IsBetweenVisibilityCondition), "Value of another field lies between")] 10 | 11 | namespace LearningKit.FormBuilder.VisibilityConditions 12 | { 13 | [Serializable] 14 | public class IsBetweenVisibilityCondition : AnotherFieldVisibilityCondition 15 | { 16 | // Defines a configuration interface for the visibility condition 17 | // The 'EditingComponent' attribute specifies which form component is used as the property's value editor 18 | [EditingComponent(IntInputComponent.IDENTIFIER, Label = "Minimum", Order = 0)] 19 | public int Min { get; set; } = 0; 20 | 21 | [EditingComponent(IntInputComponent.IDENTIFIER, Label = "Maximum", Order = 1)] 22 | public int Max { get; set; } = 0; 23 | 24 | // Shows or hides the field based on the state of the dependee field 25 | public override bool IsVisible() 26 | { 27 | return (DependeeFieldValue >= Min) && (DependeeFieldValue <= Max); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/FormComponentProperties/CharacterSizeProperties.cs: -------------------------------------------------------------------------------- 1 | using CMS.DataEngine; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | namespace LearningKit.FormBuilder.FormComponentProperties 6 | { 7 | //DocSection:PlaceholderTextProp 8 | public class CharacterSizeProperties : FormComponentProperties 9 | { 10 | // Gets or sets the default value of the form component and the underlying field 11 | [DefaultValueEditingComponent(TextInputComponent.IDENTIFIER)] 12 | public override string DefaultValue 13 | { 14 | get; 15 | set; 16 | } 17 | 18 | 19 | // Defines a custom property and its editing component 20 | [EditingComponent(IntInputComponent.IDENTIFIER, Label = "Size", DefaultValue = 40, Tooltip = "Enter the number of characters the field's width should be set to.", ExplanationText = "Sets the field's width to exactly fit the specified number of characters", Order = -95)] 21 | public int CharacterSize { get; set; } 22 | 23 | 24 | // Initializes a new instance of the properties class and configures the underlying database field 25 | public CharacterSizeProperties() 26 | : base(FieldDataType.Text, 500) 27 | { 28 | } 29 | } 30 | //EndDocSection:PlaceholderTextProp 31 | } -------------------------------------------------------------------------------- /LearningKit/Views/Shared/PageTypes/LearningKit_PageBuilder.cshtml: -------------------------------------------------------------------------------- 1 | @using CMS.DocumentEngine 2 | @using Kentico.Content.Web.Mvc.Routing 3 | @using Kentico.PageBuilder.Web.Mvc 4 | @using Kentico.Web.Mvc 5 | 6 | @model IPageViewModel 7 | 8 | @{ 9 | ViewBag.Title = "Page with the page builder"; 10 | } 11 | 12 | @Html.Kentico().PageBuilderStyles() 13 | 14 |

@Model.Page.DocumentName

15 | 16 |

Editable area

17 |
18 | @Html.Kentico().EditableArea("area1") 19 |
20 | 21 | 22 |

Editable area with selected default section

23 |
24 | @Html.Kentico().EditableArea("areaWithSection", new EditableAreaOptions { DefaultSectionIdentifier = "LearningKit.Sections.Col5050"}) 25 |
26 | 27 |

Limited editable area

28 |
29 | @{ 30 | string[] allowedWidgets = { Kentico.Content.Web.Mvc.SystemComponentIdentifiers.FORM_WIDGET_IDENTIFIER, 31 | "LearningKit.Widgets.NumberWidget", 32 | "LearningKit.Widgets.SomeOtherWidget" }; 33 | } 34 | @Html.Kentico().EditableArea("limitedArea", new EditableAreaOptions { AllowedWidgets = allowedWidgets }) 35 |
36 | 37 | 38 |

39 | @Html.ActionLink("> Back to the home index", "Index", "Home") 40 |

41 | 42 | @Html.Kentico().PageBuilderScripts() 43 | -------------------------------------------------------------------------------- /LearningKit/Views/PasswordReset/ResetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ResetPasswordViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Password reset"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Password reset

9 | 10 | @using (Html.BeginForm("ResetPassword", "PasswordReset", FormMethod.Post)) 11 | { 12 | @Html.AntiForgeryToken() 13 | 14 |
15 | @if (!Html.ViewData.ModelState.IsValid) 16 | { 17 | @Html.ValidationSummary(true, "") 18 | } 19 | 20 | @Html.HiddenFor(model => model.UserId) 21 | @Html.HiddenFor(model => model.Token) 22 | 23 |
24 | @Html.LabelFor(model => model.Password) 25 |
26 |
27 | @Html.PasswordFor(model => model.Password) 28 | @Html.ValidationMessageFor(model => model.Password, "") 29 |
30 | 31 |
32 | @Html.LabelFor(model => model.PasswordConfirmation) 33 |
34 |
35 | @Html.PasswordFor(model => model.PasswordConfirmation) 36 | @Html.ValidationMessageFor(model => model.PasswordConfirmation) 37 |
38 | 39 | 40 |
41 | } 42 |

43 | @Html.ActionLink("> Back to the home index", "Index", "Home") 44 |

-------------------------------------------------------------------------------- /LearningKit/FormBuilder/VisibilityConditions/CustomVisibilityCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.CustomVisibilityConditions; 6 | 7 | 8 | //DocSection:VisibilityConditionRegistration 9 | [assembly: RegisterFormVisibilityCondition("CustomVisibilityCondition", typeof(CustomVisibilityCondition), "Custom visibility condition")] 10 | //EndDocSection:VisibilityConditionRegistration 11 | 12 | namespace LearningKit.FormBuilder.CustomVisibilityConditions 13 | { 14 | [Serializable] 15 | public class CustomVisibilityCondition : VisibilityCondition 16 | { 17 | //DocSection:Configuration 18 | // Defines a configuration interface for the condition 19 | // The 'EditingComponent' attribute specifies which form component is used as the property's value editor 20 | [EditingComponent(TextInputComponent.IDENTIFIER)] 21 | public string ConfigurableProperty { get; set; } 22 | //EndDocSection:Configuration 23 | 24 | 25 | //DocSection:Contract 26 | // Contains custom visibility logic evaluated by the server 27 | // True indicates the field is displayed, false indicates the field is hidden 28 | public override bool IsVisible() 29 | { 30 | return true; 31 | } 32 | //EndDocSection:Contract 33 | } 34 | } -------------------------------------------------------------------------------- /LearningKit/Controllers/AdminRedirectController.cs: -------------------------------------------------------------------------------- 1 | //DocSection:Using 2 | using System; 3 | using System.Web.Mvc; 4 | 5 | using CMS.Core; 6 | //EndDocSection:Using 7 | 8 | namespace LearningKit.Controllers 9 | { 10 | //DocSection:AdminRedirectController 11 | public class AdminRedirectController : Controller 12 | { 13 | private readonly IAppSettingsService appSettingsService; 14 | 15 | public AdminRedirectController(IAppSettingsService appSettingsService) 16 | { 17 | this.appSettingsService = appSettingsService; 18 | } 19 | 20 | // GET: Redirects to the administration interface URL of the connected Xperience application 21 | public ActionResult Index() 22 | { 23 | // Loads the administration interface URL from the 'CustomAdminUrl' appSettings key in the web.config 24 | string adminUrl = appSettingsService["CustomAdminUrl"]; 25 | 26 | if (!String.IsNullOrEmpty(adminUrl)) 27 | { 28 | // Redirects to the specified administration interface URL 29 | return RedirectPermanent(adminUrl); 30 | } 31 | 32 | // If the 'CustomAdminUrl' web.config key is not set, returns a 404 Not Found response 33 | return HttpNotFound(); 34 | } 35 | } 36 | //EndDocSection:AdminRedirectController 37 | } -------------------------------------------------------------------------------- /LearningKit/Views/Personalization/PersonalizedGreeting.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.Personalization.CurrentContactViewModel 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Personalized Greeting"; 6 | } 7 | 8 |

Personalized greeting

9 | @{ 10 | if (Model != null) 11 | { 12 | // Displays a personalized greeting for contacts belonging to the "YoungCustomers" persona 13 | if (String.Equals(Model.ContactPersonaName, "YoungCustomers", StringComparison.OrdinalIgnoreCase)) 14 | { 15 |

16 | Hiya 17 | (a personalized greeting for contacts who belong to the "YoungCustomers" persona) 18 |

19 | } 20 | else 21 | { 22 |

23 | Hello 24 | (a generic greeting for contacts who do not belong to the "YoungCustomers" persona) 25 |

26 | } 27 | } 28 | else 29 | { 30 |

31 | Contact tracking is currently disabled. 32 | To use personalized content, you need to enable the 'Settings -> On-line marketing -> Enable on-line marketing' setting in the connected Xperience administration instance. 33 |

34 | } 35 | } 36 |

37 | @Html.ActionLink("> Back to the home index", "Index", "Home") 38 |

-------------------------------------------------------------------------------- /LearningKit/Models/Checkout/PaymentMethodViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Web.Mvc; 3 | 4 | using CMS.Ecommerce; 5 | 6 | namespace LearningKit.Models.Checkout 7 | { 8 | //DocSection:PaymentViewModel 9 | public class PaymentMethodViewModel 10 | { 11 | [DisplayName("Payment method")] 12 | public int PaymentMethodID { get; set; } 13 | 14 | public SelectList PaymentMethods { get; set; } 15 | 16 | 17 | /// 18 | /// Creates a payment method model. 19 | /// 20 | /// Selected payment method. 21 | /// List of all available payment methods. 22 | public PaymentMethodViewModel(PaymentOptionInfo paymentMethod, SelectList paymentMethods) 23 | { 24 | PaymentMethods = paymentMethods; 25 | 26 | if (paymentMethod != null) 27 | { 28 | PaymentMethodID = paymentMethod.PaymentOptionID; 29 | } 30 | } 31 | 32 | 33 | /// 34 | /// Creates an empty payment method model. 35 | /// Required by the MVC framework for model binding during form submission. 36 | /// 37 | public PaymentMethodViewModel() 38 | { 39 | } 40 | } 41 | //EndDocSection:PaymentViewModel 42 | } -------------------------------------------------------------------------------- /LearningKitCustomizations/FormBuilder/FormBuilderCustomizations.cs: -------------------------------------------------------------------------------- 1 | using CMS; 2 | using CMS.DataEngine; 3 | 4 | using FormBuilderCustomizations; 5 | 6 | // Registers the custom module to Kentico. The assembly's 'AssemblyInfo.cs' file must include the 'AssemblyDiscoverable' assembly attribute. 7 | [assembly: RegisterModule(typeof(FormBuilderCustomizationsModule))] 8 | 9 | namespace FormBuilderCustomizations 10 | { 11 | public class FormBuilderCustomizationsModule : Module 12 | { 13 | public const string MODULE_NAME = "FormBuilderCustomizationsModule"; 14 | 15 | 16 | // Module class constructor, inherits from the base constructor with the code name of the module as the parameter 17 | public FormBuilderCustomizationsModule() 18 | : base(MODULE_NAME) 19 | { 20 | } 21 | 22 | 23 | // Initializes the module. Called when the application starts. 24 | protected override void OnInit() 25 | { 26 | base.OnInit(); 27 | 28 | // Sets global rendering configurations for forms built using the Form builder 29 | // and registers event handlers that contextually modify form markup 30 | FormBuilderStaticMarkupConfiguration.SetGlobalRenderingConfigurations(); 31 | FormFieldMarkupInjection.RegisterEventHandlers(); 32 | FormWidgetMarkupInjection.RegisterEventHandlers(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /LearningKit/Models/NewsletterSubscription/MarketingEmailUnsubscribeModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | public class MarketingEmailUnsubscribeModel 5 | { 6 | /// 7 | /// The email address of the recipient who is requesting unsubscription. 8 | /// 9 | [Required] 10 | public string Email 11 | { 12 | get; 13 | set; 14 | } 15 | 16 | /// 17 | /// The GUID (identifier) of the Xperience email feed related to the unsubscription request. 18 | /// 19 | [Required] 20 | public Guid NewsletterGuid 21 | { 22 | get; 23 | set; 24 | } 25 | 26 | /// 27 | /// The GUID (identifier) of the Xperience marketing email related to the unsubscription request. 28 | /// 29 | [Required] 30 | public Guid IssueGuid 31 | { 32 | get; 33 | set; 34 | } 35 | 36 | /// 37 | /// Hash for protection against forged unsubscription requests. 38 | /// 39 | [Required] 40 | public string Hash 41 | { 42 | get; 43 | set; 44 | } 45 | 46 | /// 47 | /// Indicates whether the unsubscription request is for all marketing emails or only a specific email feed. 48 | /// 49 | public bool UnsubscribeFromAll 50 | { 51 | get; 52 | set; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /LearningKit/Content/InlineEditors/NumberEditor/number-editor.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // Registers the 'number-editor' inline property editor within the page builder scripts 3 | window.kentico.pageBuilder.registerInlineEditor("number-editor", { 4 | init: function (options) { 5 | var editor = options.editor; 6 | 7 | // Click action for the 'Plus' button 8 | editor.querySelector("#plus-btn").addEventListener("click", function () { 9 | // Creates a custom event that notifies the widget about a change in the value of a property 10 | var event = new CustomEvent("updateProperty", { 11 | detail: { 12 | value: options.propertyValue + 1, 13 | name: options.propertyName 14 | } 15 | }); 16 | editor.dispatchEvent(event); 17 | }); 18 | 19 | // Click action for the 'Minus' button 20 | editor.querySelector("#minus-btn").addEventListener("click", function () { 21 | var event = new CustomEvent("updateProperty", { 22 | detail: { 23 | value: options.propertyValue - 1, 24 | name: options.propertyName 25 | } 26 | }); 27 | editor.dispatchEvent(event); 28 | }); 29 | } 30 | }); 31 | })(); -------------------------------------------------------------------------------- /LearningKit/Views/Search/SearchIndex.cshtml: -------------------------------------------------------------------------------- 1 | @using LearningKit.Models.Search.SmartSearch 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Smart search"; 6 | } 7 | 8 |

Smart search

9 | 10 |

11 | Note: This search uses a locally stored index with the code name "MVCSite.Index". To use the search functionality, you need to create the index in the connected Xperience administration application. 12 |

13 | 14 | @*DocSection:SearchForm*@ 15 | @using (Html.BeginForm("SearchIndex", "Search", FormMethod.Get)) 16 | { 17 | 18 | 19 | } 20 | @*EndDocSection:SearchForm*@ 21 | 22 | @*DocSection:SearchResultItemList*@ 23 | @model SearchResultModel 24 | 25 | @using CMS.Search; 26 | 27 | @if (!Model.Items.Any()) 28 | { 29 | if (!String.IsNullOrWhiteSpace(Model.Query)) 30 | { 31 |

No results found for "@Model.Query"

32 | } 33 | } 34 | else 35 | { 36 |

Results for "@Model.Query"

37 | foreach (SearchResultItem item in Model.Items) 38 | { 39 |
40 | @item.Title 41 |
42 |
43 | @Html.Kentico().ResolveUrls(item.Content) 44 |
45 | } 46 | } 47 | @*EndDocSection:SearchResultItemList*@ 48 | 49 |

50 | @Html.ActionLink("> Back to the home index", "Index", "Home") 51 |

-------------------------------------------------------------------------------- /LearningKit/Areas/CodeSnippets/Views/CodeSnippets.cshtml: -------------------------------------------------------------------------------- 1 | @* This is a dummy view file with code snippets used in the Xperience documentation. *@ 2 | @* These code snippets do NOT take any part in the runnable LearningKit project. *@ 3 | @{ 4 | var model = new 5 | { 6 | SKUID = 1, 7 | SKUName = "Name" 8 | }; 9 | } 10 | 11 | @* E-commerce *@ 12 | 13 | @*DocSection:ItemDetailActionLink*@ 14 | @Html.ActionLink(model.SKUName, "ItemDetail", new { skuId = model.SKUID }) 15 | @*EndDocSection:ItemDetailActionLink*@ 16 | 17 | @*DocSection:ItemDetailUrlLink*@ 18 | link text 19 | @*EndDocSection:ItemDetailUrlLink*@ 20 | 21 | @*DocSection:VariantDisplayImg*@ 22 | 23 | @*EndDocSection:VariantDisplayImg*@ 24 | 25 | @* Content personalization *@ 26 | @*DocSection:ContactGroupPersonalization*@ 27 | @using CMS.ContactManagement 28 | 29 | @{ 30 | // Gets the current contact 31 | ContactInfo currentContact = ContactManagementContext.GetCurrentContact(); 32 | } 33 | 34 | @if (currentContact != null) 35 | { 36 | // Displays personalized content for contacts belonging to the "YoungCustomers" contact group 37 | if (currentContact.IsInContactGroup("YoungCustomers")) 38 | { 39 | Hiya @currentContact.ContactFirstName 40 | } 41 | else 42 | { 43 | Hello 44 | } 45 | } 46 | @*EndDocSection:ContactGroupPersonalization*@ -------------------------------------------------------------------------------- /LearningKit/PageTemplateFilters/LandingPageTemplateFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Kentico.PageBuilder.Web.Mvc.PageTemplates; 6 | 7 | namespace LearningKit.PageTemplateFilters 8 | { 9 | public class LandingPageTemplateFilter : IPageTemplateFilter 10 | { 11 | public IEnumerable Filter(IEnumerable pageTemplates, PageTemplateFilterContext context) 12 | { 13 | // Applies filtering to a collection of page templates based on the page type of the currently edited page 14 | if (context.PageType.Equals("LearningKit.LandingPage", StringComparison.InvariantCultureIgnoreCase)) 15 | { 16 | // Filters the collection to only contain filters allowed for landing pages 17 | return pageTemplates.Where(t => GetLandingPageTemplates().Contains(t.Identifier)); 18 | } 19 | 20 | // Excludes all landing page templates from the collection if the context does not match this filter 21 | // Assumes that the categories of page templates are mutually exclusive 22 | return pageTemplates.Where(t => !GetLandingPageTemplates().Contains(t.Identifier)); 23 | } 24 | 25 | // Gets all page templates that are allowed for landing pages 26 | public IEnumerable GetLandingPageTemplates() => new string[] { "LearningKit.LandingPageTemplate" }; 27 | } 28 | } -------------------------------------------------------------------------------- /LearningKit/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Mvc.Routing.Constraints; 3 | using System.Web.Routing; 4 | using Kentico.Content.Web.Mvc; 5 | using Kentico.Web.Mvc; 6 | 7 | namespace LearningKit 8 | { 9 | public class RouteConfig 10 | { 11 | public static void RegisterRoutes(RouteCollection routes) 12 | { 13 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 14 | 15 | // Maps routes for Xperience HTTP handlers and enabled MVC features (as registered in ApplicationConfig.cs) 16 | // Must be registered first, since some Xperience URLs may otherwise match the default ASP.NET MVC route, 17 | // which would result in content being displayed incorrectly 18 | routes.Kentico().MapRoutes(); 19 | 20 | //DocSection:AdminRedirectRoute 21 | // Redirects to the connected Xperience administration interface if the URL path is '/admin' 22 | routes.MapRoute( 23 | name: "Admin", 24 | url: "admin", 25 | defaults: new { controller = "AdminRedirect", action = "Index" } 26 | ); 27 | //EndDocSection:AdminRedirectRoute 28 | 29 | routes.MapRoute( 30 | name: "Default", 31 | url: "{controller}/{action}/{id}", 32 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 33 | ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LearningKit/Models/Checkout/ShippingOptionViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Web.Mvc; 3 | 4 | using CMS.Ecommerce; 5 | 6 | namespace LearningKit.Models.Checkout 7 | { 8 | //DocSection:ShippingOptionModel 9 | public class ShippingOptionViewModel 10 | { 11 | public string ShippingOptionDisplayName { get; set; } 12 | 13 | [DisplayName("Shipping option")] 14 | public int ShippingOptionID { get; set; } 15 | 16 | public SelectList ShippingOptions { get; set; } 17 | 18 | 19 | /// 20 | /// Creates a shipping option model. 21 | /// 22 | /// Shipping option. 23 | /// List of shipping options. 24 | public ShippingOptionViewModel(ShippingOptionInfo shippingOption, SelectList shippingOptions) 25 | { 26 | ShippingOptions = shippingOptions; 27 | 28 | if (shippingOption != null) 29 | { 30 | ShippingOptionID = shippingOption.ShippingOptionID; 31 | ShippingOptionDisplayName = shippingOption.ShippingOptionDisplayName; 32 | } 33 | } 34 | 35 | 36 | /// 37 | /// Creates an empty shipping option model. 38 | /// 39 | public ShippingOptionViewModel() 40 | { 41 | } 42 | } 43 | //EndDocSection:ShippingOptionModel 44 | } -------------------------------------------------------------------------------- /LearningKit/Kentico/Content/Selectors/Dialogs/PageTemplates/page-template-selector.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var selectorWrapper = document.getElementById("selectorWrapper"); 3 | 4 | var preselectCurrentTemplate = function () { 5 | var currentTemplateIdentifier = kentico.modalDialog.getData().currentTemplateIdentifier; 6 | var item = selectorWrapper.querySelector('.ktc-template-item[data-identifier="' + currentTemplateIdentifier + '"]'); 7 | if (item !== null) { 8 | item.classList.add('ktc-FlatSelectedItem'); 9 | item.classList.remove('ktc-FlatItem'); 10 | } 11 | }; 12 | 13 | var addEventListeners = function () { 14 | var templateElements = selectorWrapper.querySelectorAll('.ktc-template-item'); 15 | Array.prototype.forEach.call(templateElements, function (templateElement) { 16 | templateElement.addEventListener("click", function () 17 | { 18 | var element = selectorWrapper.querySelector('.ktc-template-item.ktc-FlatSelectedItem'); 19 | if (element !== null) { 20 | element.classList.remove('ktc-FlatSelectedItem'); 21 | element.classList.add('ktc-FlatItem'); 22 | } 23 | 24 | templateElement.classList.add('ktc-FlatSelectedItem'); 25 | templateElement.classList.remove('ktc-FlatItem'); 26 | }); 27 | }); 28 | }; 29 | 30 | addEventListeners(); 31 | preselectCurrentTemplate(); 32 | })(); -------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/CustomFormComponent.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | 3 | using LearningKit.FormBuilder.FormComponents; 4 | using LearningKit.FormBuilder.FormComponentProperties; 5 | 6 | //DocSection:FormComponentRegistration 7 | // Registers a form component for use in the form builder 8 | [assembly: RegisterFormComponent(CustomFormComponent.IDENTIFIER, typeof(CustomFormComponent), "Custom component", Description = "This is a custom form component.", IconClass = "icon-newspaper")] 9 | //EndDocSection:FormComponentRegistration 10 | 11 | namespace LearningKit.FormBuilder.FormComponents 12 | { 13 | //DocSection:FormComponentImplementation 14 | public class CustomFormComponent : FormComponent 15 | { 16 | public const string IDENTIFIER = "CustomFormComponent"; 17 | 18 | 19 | // Specifies the property is used for data binding by the form builder 20 | [BindableProperty] 21 | // Used to store the value of the input field of the component 22 | public string Value { get; set; } 23 | 24 | 25 | // Gets the value of the form field instance passed from a view where the instance is rendered 26 | public override string GetValue() 27 | { 28 | return Value; 29 | } 30 | 31 | 32 | // Sets the default value of the form field instance 33 | public override void SetValue(string value) 34 | { 35 | Value = value; 36 | } 37 | } 38 | //EndDocSection:FormComponentImplementation 39 | } -------------------------------------------------------------------------------- /LearningKit/Controllers/Builders/PageBuilderController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | using CMS.Core; 4 | using CMS.DocumentEngine; 5 | 6 | using Kentico.Content.Web.Mvc; 7 | using Kentico.Content.Web.Mvc.Routing; 8 | 9 | using LearningKit.Models.PageBuilder; 10 | 11 | // Registers a route for handling requests that target pages of the 'LearningKit.PageBuilderAdvanced' type 12 | [assembly: RegisterPageRoute("LearningKit.PageBuilderAdvanced", typeof(LearningKit.Controllers.PageBuilderController))] 13 | 14 | namespace LearningKit.Controllers 15 | { 16 | /// 17 | /// Handles requests for a page built using the page builder feature. 18 | /// Uses advanced content tree-based routing with a custom controller. 19 | /// 20 | public class PageBuilderController : Controller 21 | { 22 | private readonly IPageDataContextRetriever dataRetriever; 23 | 24 | public PageBuilderController(IPageDataContextRetriever dataContextRetriever) 25 | { 26 | // Initializes an instance of the page data retrieval service (available when using content tree-based routing) 27 | dataRetriever = dataContextRetriever; 28 | } 29 | 30 | public ActionResult Index() 31 | { 32 | TreeNode page = dataRetriever.Retrieve().Page; 33 | var model = new PageBuilderModel() 34 | { 35 | HeadingText = page.DocumentName 36 | }; 37 | 38 | return View("PageBuilder", model); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/ValidationRules/IsHexadecimalNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.CustomValidationRules; 6 | 7 | 8 | // Registers the validation rule in the system 9 | [assembly: RegisterFormValidationRule("IsHexadecimalNumberValidationRule", typeof(IsHexadecimalNumber), "Is hexadecimal number", Description = "Checks whether the submitted input is a hexadecimal string (including the leading # character).")] 10 | 11 | namespace LearningKit.FormBuilder.CustomValidationRules 12 | { 13 | [Serializable] 14 | public class IsHexadecimalNumber : ValidationRule 15 | { 16 | // Gets the title of the validation rule as displayed in the list of applied validation rules 17 | public override string GetTitle() 18 | { 19 | return "Input is a hexadecimal number."; 20 | } 21 | 22 | 23 | // Returns true if the field value is in the hexadecimal format 24 | protected override bool Validate(string value) 25 | { 26 | // Fails if the submitted string does not contain a leading '#' character 27 | if (value[0] != '#') 28 | { 29 | return false; 30 | } 31 | 32 | // Strips the leading '#' character 33 | value = value.Substring(1); 34 | 35 | // Tries to convert the submitted value 36 | bool success = int.TryParse(value, System.Globalization.NumberStyles.AllowHexSpecifier, null, out int variable); 37 | 38 | return success; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LearningKit/Views/PrivacyPage/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.PrivacyPage.ConsentListingModel 2 | 3 | @{ 4 | ViewBag.Title = "Privacy page"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

Privacy page

9 |

This page displays all consents accepted by the current visitor.

10 |

Your accepted consents

11 | @if (Model.Consents.Any()) 12 | { 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @foreach (var item in Model.Consents) 22 | { 23 | 24 | 25 | 26 | 27 | 28 | 35 | 36 | } 37 |
Consent IDConsent display nameConsent short textConsent detailsRevoke button
@item.Id@item.DisplayName@Html.Raw(item.GetConsentText("en-US").ShortText)@Html.ActionLink("View details", "ConsentDetails", "PrivacyPage", new { consentId = item.Id }, null) 29 | @using (Html.BeginForm("Revoke", "PrivacyPage", new { consentId = item.Id }, FormMethod.Post)) 30 | { 31 | @Html.AntiForgeryToken() 32 | 33 | } 34 |
38 | } 39 | else 40 | { 41 |

You have not given an agreement for any consents.

42 | } 43 | 44 |

45 | @Html.ActionLink("> Back to the home index", "Index", "Home") 46 |

-------------------------------------------------------------------------------- /LearningKit/Controllers/OnlineMarketing/PersonalizationController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | using CMS.ContactManagement; 4 | 5 | using LearningKit.Models.Personalization; 6 | 7 | 8 | namespace LearningKit.Controllers 9 | { 10 | public class PersonalizationController : Controller 11 | { 12 | /// 13 | /// Gets the current contact, if contact tracking is enabled for the connected Xperience instance. 14 | /// 15 | private ContactInfo CurrentContact => ContactManagementContext.GetCurrentContact(); 16 | 17 | 18 | /// 19 | /// Displays a page with a personalized greeting. 20 | /// The content depends on whether the current contact belongs to the "YoungCustomers" persona. 21 | /// Caches the output for 10 minutes, with different cache versions defined by the "OnlineMarketing" custom string. 22 | /// The "OnlineMarketing" configuration separately caches each combination of persona and AB test variant variables. 23 | /// 24 | [OutputCache(Duration = 600, VaryByCustom = "OnlineMarketing")] 25 | public ActionResult PersonalizedGreeting() 26 | { 27 | CurrentContactViewModel model; 28 | 29 | // If on-line marketing is disabled, CurrentContact is null 30 | if (CurrentContact != null) 31 | { 32 | model = new CurrentContactViewModel(CurrentContact); 33 | } 34 | else 35 | { 36 | model = null; 37 | } 38 | 39 | return View(model); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/VisibilityConditions/IsInPersonaVisibilityCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using CMS.ContactManagement; 4 | using CMS.Personas; 5 | 6 | using Kentico.Forms.Web.Mvc; 7 | 8 | using LearningKit.FormBuilder.CustomVisibilityConditions; 9 | 10 | 11 | // Registers the visibility condition in the system 12 | [assembly: RegisterFormVisibilityCondition("IsInPersonaVisibilityCondition", typeof(IsInPersonaVisibilityCondition), "User is in persona")] 13 | 14 | namespace LearningKit.FormBuilder.CustomVisibilityConditions 15 | { 16 | [Serializable] 17 | // A visibility condition that checks whether a user is in the specified persona 18 | public class IsInPersonaVisibilityCondition : VisibilityCondition 19 | { 20 | // Defines a configuration interface for the visibility condition 21 | // The 'EditingComponent' attribute specifies which form component is used as the property's value editor 22 | [EditingComponent(TextInputComponent.IDENTIFIER, Label = "Required persona")] 23 | public string RequiredPersona { get; set; } 24 | 25 | 26 | // Checks whether the current user belongs to the specified persona 27 | // Called when the visibility condition is evaluated by the server 28 | public override bool IsVisible() 29 | { 30 | ContactInfo currentContact = ContactManagementContext.GetCurrentContact(); 31 | 32 | string currentPersonaName = currentContact?.GetPersona()?.PersonaName; 33 | 34 | return String.Equals(currentPersonaName, RequiredPersona, StringComparison.InvariantCultureIgnoreCase); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/ValidationRules/ValueLiesBetween.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.CustomValidationRules; 6 | 7 | 8 | // Registers the validation rule in the system 9 | [assembly: RegisterFormValidationRule("ValueLiesBetweenValidationRule", typeof(ValueLiesBetween), "Closed interval validation", Description = "Checks whether the input lies in the specified closed interval.")] 10 | 11 | namespace LearningKit.FormBuilder.CustomValidationRules 12 | { 13 | [Serializable] 14 | public class ValueLiesBetween : ValidationRule 15 | { 16 | // Defines a configuration interface for the rule 17 | // The EditingComponent attribute specifies which form component is used as an editing interface for the rule's properties 18 | [EditingComponent(IntInputComponent.IDENTIFIER, Label = "Minimum value", Order = 0)] 19 | public int MinimumValue { get; set; } 20 | 21 | [EditingComponent(IntInputComponent.IDENTIFIER, Label = "Maximum value", Order = 1)] 22 | public int MaximumValue { get; set; } 23 | 24 | 25 | // Gets the title of the validation rule as displayed in the list of applied validation rules 26 | public override string GetTitle() 27 | { 28 | return $"Value lies between [{MinimumValue};{MaximumValue}]."; 29 | } 30 | 31 | 32 | // Returns true if the component's value lies between the specified boundaries 33 | protected override bool Validate(int value) 34 | { 35 | return (MinimumValue <= value) && (value <= MaximumValue); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LearningKit/App_Start/DependencyResolverConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | using Autofac; 4 | using Autofac.Integration.Mvc; 5 | 6 | namespace LearningKit 7 | { 8 | /// 9 | /// Registers required implementations to the Autofac container and set the container as ASP.NET MVC dependency resolver 10 | /// 11 | public static class DependencyResolverConfig 12 | { 13 | public static void Register() 14 | { 15 | var builder = new ContainerBuilder(); 16 | 17 | ConfigureDependencyResolverForMvcApplication(builder); 18 | 19 | AttachCMSDependencyResolver(builder); 20 | 21 | DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build())); 22 | } 23 | 24 | 25 | private static void ConfigureDependencyResolverForMvcApplication(ContainerBuilder builder) 26 | { 27 | // Enable property injection in view pages 28 | builder.RegisterSource(new ViewRegistrationSource()); 29 | 30 | // Register web abstraction classes 31 | builder.RegisterModule(); 32 | 33 | // Register controllers 34 | builder.RegisterControllers(typeof(MvcApplication).Assembly); 35 | } 36 | 37 | 38 | /// 39 | /// Configures Autofac container to use CMS dependency resolver in case it cannot resolve a dependency. 40 | /// 41 | private static void AttachCMSDependencyResolver(ContainerBuilder builder) 42 | { 43 | builder.RegisterSource(new CMSRegistrationSource()); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /LearningKit/FormBuilder/ValidationRules/CustomValidationRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.CustomValidationRules; 6 | 7 | 8 | //DocSection:ValidationRuleRegistration 9 | // Registers the validation rule in the system 10 | [assembly: RegisterFormValidationRule("CustomValidationRule", typeof(CustomValidationRule), "Custom validation rule", Description = "Contains custom validation logic.")] 11 | //EndDocSection:ValidationRuleRegistration 12 | 13 | namespace LearningKit.FormBuilder.CustomValidationRules 14 | { 15 | [Serializable] 16 | public class CustomValidationRule : ValidationRule 17 | { 18 | //DocSection:Configuration 19 | // Defines a configuration interface for the rule 20 | // Uses the EditingComponent attribute to specify which form component is used to provide an editing interface for the property 21 | [EditingComponent(TextInputComponent.IDENTIFIER)] 22 | public string ConfigurableProperty { get; set; } 23 | //EndDocSection:Configuration 24 | 25 | 26 | //DocSection:Contract 27 | // Gets the title of the validation rule as displayed in the list of applied validation rules 28 | public override string GetTitle() 29 | { 30 | return "This title appears in the list of applied validation rules on the 'Validation' tab of individual form fields."; 31 | } 32 | 33 | 34 | // Contains custom validation logic 35 | // Invokes when validation occurs 36 | protected override bool Validate(string value) 37 | { 38 | return true; 39 | } 40 | //EndDocSection:Contract 41 | } 42 | } -------------------------------------------------------------------------------- /LearningKit/Views/Order/MyOrders.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "List of past orders"; 3 | } 4 | 5 | @using LearningKit.Models.Checkout 6 | @*DocSection:ListOrders*@ 7 | @model IEnumerable 8 | 9 | @if (Model.Any()) 10 | { 11 |

Your orders:

12 | @* Ensures basic formatting of the displayed information. *@ 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | @* Iterates over all orders in the collection and displays their properties. *@ 24 | @foreach (var order in Model) 25 | { 26 | 27 | 30 | 33 | 36 | 39 | 46 | 47 | } 48 |
IdDateStatusTotal
28 | @order.OrderID 29 | 31 | @order.OrderDate 32 | 34 | @(order.OrderStatusDisplayName) 35 | 37 | @String.Format(order.CurrencyFormatString, order.OrderTotalPrice) 38 | 40 | @using (Html.BeginForm("Reorder", "Order", FormMethod.Post)) 41 | { 42 | @Html.Hidden("OrderId", order.OrderID) 43 | 44 | } 45 |
49 | } 50 | @*EndDocSection:ListOrders*@ 51 | 52 |

53 | @Html.ActionLink("> Back to the home index", "Index", "Home") 54 |

-------------------------------------------------------------------------------- /LearningKit.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30621.155 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LearningKit", "LearningKit\LearningKit.csproj", "{3561E1B0-8BF6-4187-A581-1B0801168CDD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LearningKitCustomizations", "LearningKitCustomizations\LearningKitCustomizations.csproj", "{E3918B5E-4DC3-4591-A885-61A315270E02}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3561E1B0-8BF6-4187-A581-1B0801168CDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {3561E1B0-8BF6-4187-A581-1B0801168CDD}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {3561E1B0-8BF6-4187-A581-1B0801168CDD}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {3561E1B0-8BF6-4187-A581-1B0801168CDD}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E3918B5E-4DC3-4591-A885-61A315270E02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E3918B5E-4DC3-4591-A885-61A315270E02}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E3918B5E-4DC3-4591-A885-61A315270E02}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E3918B5E-4DC3-4591-A885-61A315270E02}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {09B9ACA6-A295-4ED1-9E22-2594DCACB170} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /LearningKit/Kentico/Scripts/modal-dialog.js: -------------------------------------------------------------------------------- 1 | window.kentico = window.kentico || {}; 2 | 3 | /** 4 | * Modal dialog API type definition. 5 | * @typedef {Object} ModalDialog 6 | * @property {Function} open Opens a modal dialog. 7 | * @property {Function} save Saves currently opened modal dialog. 8 | * @property {Function} close Closes currently opened modal dialog. 9 | * @property {Function} getData Gets the data for currently opened modal dialog. 10 | */ 11 | (function (localKenticoNamespace, parentKenticoNamespace) { 12 | localKenticoNamespace.modalDialog = localKenticoNamespace.modalDialog || {}; 13 | 14 | registerModalDialogApi(localKenticoNamespace.modalDialog, parentKenticoNamespace.modalDialog); 15 | registerLocalizationApi(); 16 | 17 | /** 18 | * Registers the modal dialog API in current window. 19 | * @param {ModalDialog} modalDialog Modal dialog service object. 20 | * @param {ModalDialog} parentModalDialog Modal dialog service object in parent window. 21 | */ 22 | function registerModalDialogApi(modalDialog, parentModalDialog) { 23 | modalDialog.open = parentModalDialog.open; 24 | modalDialog.apply = parentModalDialog.apply.bind(null, window); 25 | modalDialog.cancel = parentModalDialog.cancel.bind(null, window); 26 | modalDialog.getData = parentModalDialog.getData; 27 | 28 | modalDialog.contentSelector = parentModalDialog.contentSelector; 29 | } 30 | 31 | /** 32 | * Registers the localization API in current window. 33 | */ 34 | function registerLocalizationApi() { 35 | localKenticoNamespace.localization = parentKenticoNamespace.localization; 36 | } 37 | }(window.kentico, window.parent.kentico)); 38 | -------------------------------------------------------------------------------- /LearningKit/Models/Checkout/OrderViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using CMS.Ecommerce; 4 | 5 | 6 | namespace LearningKit.Models.Checkout 7 | { 8 | //DocSection:OrderViewModel 9 | public class OrderViewModel 10 | { 11 | public int OrderID { get; set; } 12 | 13 | public int OrderStatusID { get; set; } 14 | 15 | public string CurrencyFormatString { get; set; } 16 | 17 | public DateTime OrderDate { get; set; } 18 | 19 | public decimal OrderTotalPrice { get; set; } 20 | 21 | public bool OrderIsPaid { get; set; } 22 | 23 | public OrderPaymentResultViewModel OrderPaymentResult { get; set; } 24 | 25 | public string OrderStatusDisplayName { get; set; } 26 | 27 | public OrderViewModel(OrderInfo order, ICurrencyInfoProvider currencyInfoProvider) 28 | { 29 | OrderID = order.OrderID; 30 | OrderStatusID = order.OrderStatusID; 31 | CurrencyFormatString = currencyInfoProvider.Get(order.OrderCurrencyID).CurrencyFormatString; 32 | OrderDate = order.OrderDate; 33 | OrderTotalPrice = order.OrderTotalPrice; 34 | OrderIsPaid = order.OrderIsPaid; 35 | OrderStatusDisplayName = OrderStatusInfo.Provider.Get(order.OrderStatusID)?.StatusDisplayName; 36 | if (order.OrderPaymentResult != null) 37 | { 38 | OrderPaymentResult = new OrderPaymentResultViewModel() 39 | { 40 | PaymentMethodName = order.OrderPaymentResult.PaymentMethodName, 41 | PaymentIsCompleted = order.OrderPaymentResult.PaymentIsCompleted 42 | }; 43 | } 44 | } 45 | } 46 | //EndDocSection:OrderViewModel 47 | } -------------------------------------------------------------------------------- /LearningKit/Views/ProductFilter/FilterPageProperty.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.ProductFilter.ProductFilterViewModel 2 | @using LearningKit.Models.Products 3 | 4 | @{ 5 | ViewBag.Title = "Product filter"; 6 | Layout = "~/Views/Shared/_Layout.cshtml"; 7 | } 8 | 9 |

Product listing of the LearningProductType with filtering

10 | 11 |

12 | @Html.ActionLink("> Filter based on an SKU property (price)", "FilterSKUProperty")
13 | @Html.ActionLink("> Filter based on a foreign SKU property (manufacturers)", "FilterForeignProperty") 14 |

15 | 16 | @using (Html.BeginForm()) 17 | { 18 |

Product type property – LPTWithFeature (boolean)

19 | @Html.CheckBoxFor(m => Model.LPTWithFeature) 20 | 21 | } 22 | 23 |
24 | 25 | @foreach (ProductListItemViewModel product in Model.FilteredProducts) 26 | { 27 |

@product.Name

28 | 29 | if (!string.IsNullOrEmpty(product.PublicStatusName)) 30 | { 31 | @product.PublicStatusName 32 | } 33 | 34 | if (!string.IsNullOrEmpty(product.ImagePath)) 35 | { 36 | @product.Name 37 | } 38 | 39 |
40 | @if (!product.Available) 41 | { 42 | Out of stock 43 | } 44 | 45 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.Price) 46 | 47 | @if (product.PriceModel.ListPrice > product.PriceModel.Price) 48 | { 49 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.ListPrice) 50 | } 51 |
52 | } 53 | 54 |

55 | @Html.ActionLink("> Back to the home index", "Index", "Home") 56 |

-------------------------------------------------------------------------------- /LearningKit/Views/ProductFilter/FilterSKUProperty.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.ProductFilter.ProductFilterViewModel 2 | @using LearningKit.Models.Products 3 | 4 | @{ 5 | ViewBag.Title = "Product filter"; 6 | Layout = "~/Views/Shared/_Layout.cshtml"; 7 | } 8 | 9 |

Product listing of the LearningProductType with filtering

10 | 11 |

12 | @Html.ActionLink("> Filter based on a page property (LPTWithFeature)", "FilterPageProperty")
13 | @Html.ActionLink("> Filter based on a foreign SKU property (manufacturers)", "FilterForeignProperty") 14 |

15 | 16 | @using (Html.BeginForm()) 17 | { 18 |

SKU property – Price range (decimal)

19 | @Html.TextBoxFor(m => Model.PriceFrom) 20 | @Html.TextBoxFor(m => Model.PriceTo) 21 | 22 | } 23 | 24 |
25 | 26 | @foreach (ProductListItemViewModel product in Model.FilteredProducts) 27 | { 28 |

@product.Name

29 | 30 | if (!string.IsNullOrEmpty(product.PublicStatusName)) 31 | { 32 | @product.PublicStatusName 33 | } 34 | 35 | if (!string.IsNullOrEmpty(product.ImagePath)) 36 | { 37 | @product.Name 38 | } 39 | 40 |
41 | @if (!product.Available) 42 | { 43 | Out of stock 44 | } 45 | 46 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.Price) 47 | 48 | @if (product.PriceModel.ListPrice > product.PriceModel.Price) 49 | { 50 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.ListPrice) 51 | } 52 |
53 | } 54 | 55 |

56 | @Html.ActionLink("> Back to the home index", "Index", "Home") 57 |

-------------------------------------------------------------------------------- /LearningKit/Views/PageBuilder/PageBuilder.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.PageBuilder.PageBuilderModel 2 | 3 | @{ 4 | ViewBag.Title = "Page with the page builder"; 5 | } 6 | 7 | @*DocSection:Using*@ 8 | @using Kentico.PageBuilder.Web.Mvc 9 | @using Kentico.Web.Mvc 10 | @*EndDocSection:Using*@ 11 | 12 | @Html.Kentico().PageBuilderStyles() 13 | 14 |

@Model.HeadingText

15 | 16 |

Editable area

17 | @*DocSection:EditableArea*@ 18 |
19 | @Html.Kentico().EditableArea("area1") 20 |
21 | @*EndDocSection:EditableArea*@ 22 | 23 |

Editable area with selected default section

24 | @*DocSection:EditableAreaSection*@ 25 |
26 | @{ 27 | var optionsDefaultSection = new EditableAreaOptions 28 | { 29 | DefaultSectionIdentifier = "LearningKit.Sections.Col5050" 30 | }; 31 | } 32 | @Html.Kentico().EditableArea("areaWithSection", optionsDefaultSection) 33 |
34 | @*EndDocSection:EditableAreaSection*@ 35 | 36 |

Limited editable area

37 | @*DocSection:LimitedEditableArea*@ 38 |
39 | @{ 40 | var optionsLimited = new EditableAreaOptions 41 | { 42 | AllowedWidgets = new[] { Kentico.Content.Web.Mvc.SystemComponentIdentifiers.FORM_WIDGET_IDENTIFIER, 43 | "LearningKit.Widgets.NumberWidget", 44 | "LearningKit.Widgets.SomeOtherWidget" }, 45 | AllowedSections = AllowedComponents.ALL 46 | }; 47 | } 48 | @Html.Kentico().EditableArea("limitedArea", optionsLimited) 49 |
50 | @*EndDocSection:LimitedEditableArea*@ 51 | 52 |

53 | @Html.ActionLink("> Back to the home index", "Index", "Home") 54 |

55 | 56 | @*DocSection:RegisterScripts*@ 57 | @Html.Kentico().PageBuilderScripts() 58 | @*EndDocSection:RegisterScripts*@ -------------------------------------------------------------------------------- /LearningKit/Models/Widgets/SelectorsWidget/SelectorsWidgetProperties.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | using Kentico.Forms.Web.Mvc; 5 | using Kentico.PageBuilder.Web.Mvc; 6 | using Kentico.Components.Web.Mvc.FormComponents; 7 | 8 | 9 | namespace LearningKit.Models.Widgets.SelectorsWidget 10 | { 11 | public class SelectorsWidgetProperties : IWidgetProperties 12 | { 13 | // Assigns a selector component to the Images property 14 | [EditingComponent(MediaFilesSelector.IDENTIFIER)] 15 | // Limits the maximum number of files that can be selected at once. 16 | [EditingComponentProperty(nameof(MediaFilesSelectorProperties.MaxFilesLimit), 1)] 17 | // Returns a list of media files selector items (objects that contain the GUIDs of selected media files) 18 | public IEnumerable Images { get; set; } = Enumerable.Empty(); 19 | 20 | // Assigns a selector component to the Pages property 21 | [EditingComponent(PageSelector.IDENTIFIER)] 22 | // Returns a list of page selector items (node GUIDs) 23 | public IEnumerable Pages { get; set; } = Enumerable.Empty(); 24 | 25 | // Assigns a selector component to the Attachments property 26 | [EditingComponent(AttachmentSelector.IDENTIFIER)] 27 | // Returns a list of attachment selector items 28 | public IEnumerable Attachments { get; set; } = Enumerable.Empty(); 29 | 30 | // Assigns a selector component to the PagePaths property 31 | [EditingComponent(PathSelector.IDENTIFIER)] 32 | // Returns a list of path selector items (page paths) 33 | public IEnumerable PagePaths { get; set; } = Enumerable.Empty(); 34 | } 35 | } -------------------------------------------------------------------------------- /LearningKit/Views/ProductListing/Listing.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Product listing"; 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | } 5 | 6 | @using LearningKit.Models.Products 7 | @*DocSection:ListingTable*@ 8 | @model IEnumerable 9 | 10 |

Product listing of the LearningProductType

11 | 12 |
13 | @* Iterates over all products. *@ 14 | @foreach (ProductListItemViewModel product in Model) 15 | { 16 | @* Generates a URL leading to the product's detail page. *@ 17 | 18 |

@product.Name

19 | 20 | @* Displays information about the product's public status. *@ 21 | @if (!string.IsNullOrEmpty(product.PublicStatusName)) 22 | { 23 | @product.PublicStatusName 24 | } 25 | 26 | @* Displays the product's image. *@ 27 | @if (!string.IsNullOrEmpty(product.ImagePath)) 28 | { 29 | @product.Name 30 | } 31 | 32 | @* Displays the product's other properties. *@ 33 |
34 | @if (!product.Available) 35 | { 36 | Out of stock 37 | } 38 | 39 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.Price) 40 | 41 | @if (product.PriceModel.ListPrice > product.PriceModel.Price) 42 | { 43 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.ListPrice) 44 | } 45 |
46 |
47 | } 48 |
49 | @*EndDocSection:ListingTable*@ 50 | 51 |

52 | @Html.ActionLink("> Back to the home index", "Index", "Home") 53 |

-------------------------------------------------------------------------------- /LearningKit/FormBuilder/ValidationRules/ValueOfDependeeLiesBetween.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.CustomValidationRules; 6 | 7 | 8 | // Registers the validation rule in the system 9 | [assembly: RegisterFormValidationRule("ValueOfDependeeLiesBetweenValidationRule", typeof(ValueOfDependeeLiesBetween), "Value of another field is between", Description = "Checks whether the value of the selected field lies on the specified interval given by the value of the selected field and the specified boundary.")] 10 | 11 | namespace LearningKit.FormBuilder.CustomValidationRules 12 | { 13 | [Serializable] 14 | public class ValueOfDependeeLiesBetween : CompareToFieldValidationRule 15 | { 16 | // Defines a configuration interface for the rule 17 | // The 'EditingComponent' attribute specifies which form component is used as the property's value editor 18 | [EditingComponent(IntInputComponent.IDENTIFIER, Label = "Bound", Order = 0)] 19 | public int Bound { get; set; } = 0; 20 | 21 | 22 | // Gets the title of the validation rule as displayed in the list of applied validation rules 23 | public override string GetTitle() 24 | { 25 | return $"The submitted value must lie on the interval specified by the value of the selected field and {Bound}."; 26 | } 27 | 28 | 29 | // Validates the configured property values against the submitted value of the selected dependee field 30 | protected override bool Validate(int value) 31 | { 32 | if (Bound > DependeeFieldValue) 33 | { 34 | return (DependeeFieldValue <= value) && (value <= Bound); 35 | } 36 | else 37 | { 38 | return (Bound <= value) && (value <= DependeeFieldValue); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /LearningKit/Views/Order/OrderDetail.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.Checkout.OrderViewModel 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Order detail"; 6 | } 7 | 8 |

Display an order detail

9 | 10 |

11 | If you enter an order's ID to the text box and click the button, the order information will be displayed. 12 |

13 | 14 | @*DocSection:OrderDetailView*@ 15 | @* Renders a form field for entering order IDs. *@ 16 | @using (Html.BeginForm("OrderDetail", "Order")) 17 | { 18 | 19 | 20 | } 21 | 22 | 23 | @if (Model != null) 24 | { 25 |

Order details

26 | @* Displays order details. *@ 27 |
    28 |
  • Order number: @Model.OrderID
  • 29 |
  • Status ID: @Model.OrderStatusID
  • 30 |
  • Is paid: @Model.OrderIsPaid
  • 31 | @if (Model.OrderPaymentResult != null) 32 | { 33 |
  • Payment method name: @Model.OrderPaymentResult.PaymentMethodName
  • 34 |
  • Was payment successful: @Model.OrderPaymentResult.PaymentIsCompleted
  • 35 | } 36 |
  • Total price: @String.Format(Model.CurrencyFormatString, Model.OrderTotalPrice)
  • 37 |
38 | @* If the order is not paid for, renders a button invoking the 'MarkOrderAsPaid' action that marks the order as paid. *@ 39 | if (!Model.OrderIsPaid) 40 | { 41 | using (Html.BeginForm("MarkOrderAsPaid", "Order")) 42 | { 43 | 44 | 45 | } 46 | } 47 | } 48 | @*EndDocSection:OrderDetailView*@ 49 | 50 |

51 | @Html.ActionLink("> Back to the home index", "Index", "Home") 52 |

-------------------------------------------------------------------------------- /LearningKit/Areas/CodeSnippets/Views/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /LearningKit/Content/InlineEditors/ColorEditor/color-editor.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // Registers the 'number-editor' inline property editor within the page builder scripts 3 | window.kentico.pageBuilder.registerInlineEditor("color-editor", { 4 | //DocSection:InitFunction 5 | init: function (options) { 6 | var editor = options.editor; 7 | 8 | // Adds a click action for the 'Modal dialog' button 9 | editor.querySelector("#modal-btn").addEventListener("click", function () { 10 | // Opens the modal dialog window 11 | kentico.modalDialog.open({ 12 | // Gets the modal dialog URL from the 'data-url' attribute of the inline editor wrapping element 13 | url: editor.getAttribute("data-url"), 14 | applyCallback: function (dialogWindow) { 15 | // Creates and dispatches an event that notifies the widget about changes of properties 16 | var event = new CustomEvent("updateProperty", { 17 | detail: { 18 | // Retrieves the color value from radio buttons within the modal dialog window 19 | value: dialogWindow.document.querySelector('input[name="color"]:checked').value, 20 | name: options.propertyName 21 | } 22 | }); 23 | editor.dispatchEvent(event); 24 | }, 25 | applyButtonText: "Confirm color selection", 26 | title: "Select a color", 27 | // Passes the current color value to the modal dialog data 28 | data: { value: options.propertyValue } 29 | }); 30 | }); 31 | } 32 | //EndDocSection:InitFunction 33 | }); 34 | })(); -------------------------------------------------------------------------------- /LearningKitCustomizations/FormBuilder/FormWidgetMarkupInjection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Mvc; 3 | 4 | using Kentico.Forms.Web.Mvc.Widgets; 5 | 6 | 7 | namespace FormBuilderCustomizations 8 | { 9 | public class FormWidgetMarkupInjection 10 | { 11 | 12 | public static void RegisterEventHandlers() 13 | { 14 | FormWidgetRenderingConfiguration.GetConfiguration.Execute += FormWidgetInjectMarkup; 15 | } 16 | 17 | 18 | private static void FormWidgetInjectMarkup(object sender, GetFormWidgetRenderingConfigurationEventArgs e) 19 | { 20 | e.Configuration.FormWrapperConfiguration = new FormWrapperRenderingConfiguration 21 | { 22 | // Renders the form's display name above the form using the 'CustomHtmlEnvelopeString' property 23 | // FormWrapperRenderingConfiguration.CONTENT_PLACEHOLDER acts as a placeholder for the form's body in the resulting markup 24 | CustomHtmlEnvelopeString = $@"

{e.Form.FormDisplayName}

{FormWrapperRenderingConfiguration.CONTENT_PLACEHOLDER}" 25 | }; 26 | 27 | // Sets additional attributes only for specific forms. Since the 'class' attribute is fairly 28 | // common, checks if the key is already present and inserts or appends the key accordingly. 29 | if (e.Form.FormName.Equals("ContactUs", StringComparison.InvariantCultureIgnoreCase)) 30 | { 31 | if (e.Configuration.SubmitButtonHtmlAttributes.ContainsKey("class")) 32 | { 33 | e.Configuration.SubmitButtonHtmlAttributes["class"] += " contactus-submit-button"; 34 | } 35 | else 36 | { 37 | e.Configuration.SubmitButtonHtmlAttributes["class"] = "contactus-submit-button"; 38 | } 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /LearningKit/App_Start/PageBuilderComponentRegister.cs: -------------------------------------------------------------------------------- 1 | using LearningKit.Models.Widgets.ColorWidget; 2 | using LearningKit.Models.Widgets.NumberWidget; 3 | using LearningKit.Models.Widgets.SelectorsWidget; 4 | using LearningKit.Models.Widgets.ExtendedFormWidget; 5 | 6 | using LearningKit.Models.Sections.CustomSection; 7 | 8 | using LearningKit.Models.PageTemplates.LandingPage; 9 | 10 | using Kentico.PageBuilder.Web.Mvc; 11 | using Kentico.PageBuilder.Web.Mvc.PageTemplates; 12 | 13 | 14 | // Registers the 'Selected number' widget 15 | [assembly: RegisterWidget("LearningKit.Widgets.NumberWidget", "Selected number", typeof(NumberWidgetProperties), customViewName: "Widgets/_NumberWidget", IconClass = "icon-octothorpe")] 16 | // Registers the 'Colored widget' 17 | [assembly: RegisterWidget("LearningKit.Widgets.ColorWidget", "Colored widget", typeof(ColorWidgetProperties), customViewName: "Widgets/_ColorWidget", IconClass = "icon-palette")] 18 | // Registers the 'Extended form' widget 19 | [assembly: RegisterWidget("LearningKit.Widgets.ExtendedFormWidget", "Extended form", typeof(ExtendedFormWidgetProperties), customViewName: "Widgets/_ExtendedFormWidget", IconClass = "icon-form")] 20 | 21 | // Registers the default page builder section used by the LearningKit project 22 | [assembly: RegisterSection("LearningKit.Sections.DefaultSection", "Default section", customViewName: "Sections/_DefaultSection", IconClass = "icon-square")] 23 | // Registers a sample section with a background color customizable by a section property 24 | [assembly: RegisterSection("LearningKit.Sections.CustomSection", "Custom section", typeof(CustomSectionProperties), customViewName: "Sections/_CustomSection", IconClass = "icon-square")] 25 | 26 | // Registers the landing page template 27 | [assembly: RegisterPageTemplate("LearningKit.LandingPageTemplate", "Default Landing page template", typeof(LandingPageProperties), customViewName: "PageTemplates/_LandingPageTemplate", IconClass = "icon-l-rows-2")] -------------------------------------------------------------------------------- /LearningKit/Controllers/Builders/Personalization/ConditionTypes/HasGivenConsentController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | using Kentico.PageBuilder.Web.Mvc.Personalization; 4 | 5 | namespace LearningKit.Personalization.ConditionTypes 6 | { 7 | //DocSection:ConditionTypeController 8 | public class HasGivenConsentController : ConditionTypeController 9 | { 10 | // Displays the configuration dialog 11 | [HttpPost] 12 | public ActionResult Index() 13 | { 14 | // Gets the condition's current configuration as an instance of the condition type class 15 | var conditionType = GetParameters(); 16 | 17 | // Creates a view model object 18 | var viewModel = new HasGivenConsentViewModel 19 | { 20 | // Sets the consent code name obtained from the condition type parameters 21 | ConsentCodeName = conditionType.ConsentCodeName 22 | }; 23 | 24 | // Displays the configuration dialog's view 25 | return PartialView("Personalization/ConditionTypes/_HasGivenConsentConfiguration", viewModel); 26 | } 27 | 28 | // Submits the condition type parameters 29 | [HttpPost] 30 | public ActionResult Validate(HasGivenConsentViewModel model) 31 | { 32 | // Validates the model 33 | if (!ModelState.IsValid) 34 | { 35 | return PartialView("Personalization/ConditionTypes/_HasGivenConsentConfiguration", model); 36 | } 37 | 38 | // Creates an object of the condition type class 39 | var parameters = new HasGivenConsentConditionType 40 | { 41 | ConsentCodeName = model.ConsentCodeName, 42 | }; 43 | 44 | // Serializes the condition's configuration into JSON format and returns the data 45 | return new ConditionTypeValidationResult(parameters); 46 | } 47 | } 48 | //EndDocSection:ConditionTypeController 49 | } -------------------------------------------------------------------------------- /LearningKit/Views/ProductFilter/FilterForeignProperty.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.ProductFilter.ProductFilterViewModel 2 | @using LearningKit.Models.Products 3 | 4 | @{ 5 | ViewBag.Title = "Product filter"; 6 | Layout = "~/Views/Shared/_Layout.cshtml"; 7 | } 8 | 9 |

Product listing of the LearningProductType with filtering

10 | 11 |

12 | @Html.ActionLink("> Filter based on a page property (LPTWithFeature)", "FilterPageProperty")
13 | @Html.ActionLink("> Filter based on an SKU property (price)", "FilterSKUProperty") 14 |

15 | 16 | @using (Html.BeginForm()) 17 | { 18 |

Manufacturers

19 | for (var i = 0; i < Model.Manufacturers.Count; i++) 20 | { 21 | @Html.HiddenFor(m => Model.Manufacturers[i].Id) 22 | @Html.HiddenFor(m => Model.Manufacturers[i].DisplayName) 23 | @Html.CheckBoxFor(m => Model.Manufacturers[i].IsChecked) 24 | @Html.LabelFor(m => Model.Manufacturers[i].IsChecked, Model.Manufacturers[i].DisplayName) 25 | } 26 | 27 | } 28 | 29 |
30 | 31 | @foreach (ProductListItemViewModel product in Model.FilteredProducts) 32 | { 33 |

@product.Name

34 | 35 | if (!string.IsNullOrEmpty(product.PublicStatusName)) 36 | { 37 | @product.PublicStatusName 38 | } 39 | 40 | if (!string.IsNullOrEmpty(product.ImagePath)) 41 | { 42 | @product.Name 43 | } 44 | 45 |
46 | @if (!product.Available) 47 | { 48 | Out of stock 49 | } 50 | 51 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.Price) 52 | 53 | @if (product.PriceModel.ListPrice > product.PriceModel.Price) 54 | { 55 | @String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.ListPrice) 56 | } 57 |
58 | } 59 | 60 |

61 | @Html.ActionLink("> Back to the home index", "Index", "Home") 62 |

-------------------------------------------------------------------------------- /LearningKit/CMSRegistrationSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Autofac.Core; 6 | using Autofac.Builder; 7 | 8 | namespace LearningKit 9 | { 10 | /// 11 | /// Allows registrations to be made on-the-fly when unregistered services are requested. 12 | /// 13 | public class CMSRegistrationSource : IRegistrationSource 14 | { 15 | /// 16 | /// Gets whether the registrations provided by this source are 1:1 adapters on top of other components (I.e. like Meta, Func or Owned.) 17 | /// 18 | public bool IsAdapterForIndividualComponents => false; 19 | 20 | 21 | /// 22 | /// Retrieve registrations for an unregistered service, to be used by the container. 23 | /// 24 | /// The service that was requested. 25 | /// A function that will return existing registrations for a service. 26 | public IEnumerable RegistrationsFor(Service service, Func> registrationAccessor) 27 | { 28 | // There are other registration exists in the container 29 | if (registrationAccessor(service).Any()) 30 | { 31 | return Enumerable.Empty(); 32 | } 33 | 34 | var swt = service as IServiceWithType; 35 | if (swt == null) 36 | { 37 | return Enumerable.Empty(); 38 | } 39 | 40 | object instance = CMS.Core.Service.ResolveOptional(swt.ServiceType); 41 | 42 | if (instance == null) 43 | { 44 | return Enumerable.Empty(); 45 | } 46 | 47 | // Register the instance in the container 48 | return new[] { RegistrationBuilder.ForDelegate(swt.ServiceType, (c, p) => instance).CreateRegistration() }; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /LearningKit/Models/Register/RegisterViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | public class RegisterViewModel 5 | { 6 | [Required(ErrorMessage = "The User name cannot be empty.")] 7 | [DisplayName("User name")] 8 | [MaxLength(100, ErrorMessage = "The User name cannot be longer than 100 characters.")] 9 | public string UserName 10 | { 11 | get; 12 | set; 13 | } 14 | 15 | [DataType(DataType.EmailAddress)] 16 | [Required(ErrorMessage = "The Email address cannot be empty.")] 17 | [DisplayName("Email address")] 18 | [EmailAddress(ErrorMessage = "Invalid email address.")] 19 | [MaxLength(254, ErrorMessage = "The Email address cannot be longer than 254 characters.")] 20 | public string Email 21 | { 22 | get; 23 | set; 24 | } 25 | 26 | [DataType(DataType.Password)] 27 | [Required(ErrorMessage = "The Password cannot be empty.")] 28 | [DisplayName("Password")] 29 | [MaxLength(100, ErrorMessage = "The Password cannot be longer than 100 characters.")] 30 | public string Password 31 | { 32 | get; 33 | set; 34 | } 35 | 36 | [DataType(DataType.Password)] 37 | [DisplayName("Password confirmation")] 38 | [MaxLength(100, ErrorMessage = "The Password cannot be longer than 100 characters.")] 39 | [Compare("Password", ErrorMessage = "The entered passwords do not match.")] 40 | public string PasswordConfirmation 41 | { 42 | get; 43 | set; 44 | } 45 | 46 | [DisplayName("First name")] 47 | [Required(ErrorMessage = "The First name cannot be empty.")] 48 | [MaxLength(100, ErrorMessage = "The First name cannot be longer than 100 characters.")] 49 | public string FirstName 50 | { 51 | get; 52 | set; 53 | } 54 | 55 | [DisplayName("Last name")] 56 | [Required(ErrorMessage = "The Last name cannot be empty.")] 57 | [MaxLength(100, ErrorMessage = "The Last name cannot be longer than 100 characters.")] 58 | public string LastName 59 | { 60 | get; 61 | set; 62 | } 63 | } -------------------------------------------------------------------------------- /LearningKit/Models/Products/ProductListItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using CMS.Ecommerce; 2 | 3 | using Kentico.Content.Web.Mvc; 4 | 5 | namespace LearningKit.Models.Products 6 | { 7 | //DocSection:ProductListingModel 8 | public class ProductListItemViewModel 9 | { 10 | public readonly PriceDetailViewModel PriceModel; 11 | public string Name; 12 | public string ImagePath; 13 | public string PublicStatusName; 14 | public PageUrl ProductUrl; 15 | public bool Available; 16 | 17 | /// 18 | /// Constructor for the ProductListItemViewModel class. 19 | /// 20 | /// Product's page. 21 | /// Price of the product. 22 | /// Display name of the product's public status. 23 | public ProductListItemViewModel(SKUTreeNode productPage, ProductCatalogPrices priceDetail, IPageUrlRetriever urlRetriever, string publicStatusName) 24 | { 25 | // Sets the page information 26 | Name = productPage.DocumentName; 27 | ProductUrl = urlRetriever.Retrieve(productPage); 28 | 29 | // Sets the SKU information 30 | ImagePath = string.IsNullOrEmpty(productPage.SKU.SKUImagePath) ? 31 | null : new FileUrl(productPage.SKU.SKUImagePath, true) 32 | .WithSizeConstraint(SizeConstraint.MaxWidthOrHeight(400)) 33 | .RelativePath; 34 | 35 | Available = !productPage.SKU.SKUSellOnlyAvailable || productPage.SKU.SKUAvailableItems > 0; 36 | PublicStatusName = publicStatusName; 37 | 38 | // Sets the price format information 39 | PriceModel = new PriceDetailViewModel 40 | { 41 | Price = priceDetail.Price, 42 | ListPrice = priceDetail.ListPrice, 43 | CurrencyFormatString = priceDetail.Currency.CurrencyFormatString 44 | }; 45 | } 46 | } 47 | //EndDocSection:ProductListingModel 48 | } -------------------------------------------------------------------------------- /LearningKitCustomizations/Membership/ExtendedUser.cs: -------------------------------------------------------------------------------- 1 | using CMS.Membership; 2 | 3 | using Kentico.Membership; 4 | 5 | 6 | namespace MembershipCustomization 7 | { 8 | // Extends the default Kentico.Membership.User object 9 | public class ExtendedUser : User 10 | { 11 | // Exposes the existing 'MiddleName' property of the 'UserInfo' object 12 | public string MiddleName 13 | { 14 | get; 15 | set; 16 | } 17 | 18 | 19 | // Property that corresponds to a custom field specified in the administration interface 20 | public string CustomField 21 | { 22 | get; 23 | set; 24 | } 25 | 26 | 27 | // Ensures field mapping between Kentico's user objects and the Kentico.Membership ASP.NET Identity implementation 28 | // Called when retrieving users from Kentico via Kentico.Membership.KenticoUserManager 29 | public override void MapFromUserInfo(UserInfo source) 30 | { 31 | // Calls the base class implementation of the MapFromUserInfo method 32 | base.MapFromUserInfo(source); 33 | 34 | // Maps the 'MiddleName' property to the extended user object 35 | MiddleName = source.MiddleName; 36 | 37 | // Sets the value of the 'CustomField' property 38 | CustomField = source.GetValue("CustomField", null); 39 | } 40 | 41 | 42 | // Ensures field mapping between Kentico's user objects and the Kentico.Membership ASP.NET Identity implementation 43 | // Called when creating or updating users using Kentico.Membership.KenticoUserManager 44 | public override void MapToUserInfo(UserInfo target) 45 | { 46 | // Calls the base class implementation of the MapToUserInfo method 47 | base.MapToUserInfo(target); 48 | 49 | // Maps the 'MiddleName' property to the target 'UserInfo' object 50 | target.MiddleName = MiddleName; 51 | 52 | // Sets the value of the 'CustomField' custom user field 53 | target.SetValue("CustomField", CustomField); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **🛈 This repository is intended as a read-only source of information, and contributions by the general public are not expected.** 2 | 3 | # LearningKit 4 | 5 | LearningKit is a functional website for learning purposes. It demonstrates how to implement various Kentico Xperience features on MVC websites in the form of code snippets, which you can run if you connect the website to a Kentico Xperience database. See [Kentico Xperience sample sites](https://devnet.kentico.com/articles/kentico-xperience-sample-sites-and-their-differences) for a detailed description of this and other Xperience sample sites. 6 | 7 | ## Instructions for running the LearningKit project 8 | 9 | 1. [Install](https://docs.xperience.io/x/bAmRBg) Kentico Xperience in the ASP.NET MVC 5 development model. When choosing the installation type, select *New site*. 10 | 1. Set the **Presentation URL** of the new site to the URL where you plan to run the LearningKit project. 11 | 1. [Enable web farms](https://docs.xperience.io/x/Mw_RBg) in automatic mode. 12 | 1. Rename the `LearningKit\ConnectionStrings.config.template` file to `ConnectionStrings.config`. 13 | 1. Rename the `LearningKit\AppSettings.config.template` file to `AppSettings.config`. 14 | 1. Copy the `CMSConnectionString` connection string from the Xperience administration application's `web.config` file to the `LearningKit\ConnectionStrings.config` file. 15 | 1. Copy the `CMSHashStringSalt` app setting from the Xperience administration application's `web.config` file to the `LearningKit\AppSettings.config` file. 16 | 1. Open the `LearningKit.sln` solution in Visual Studio and run the LearningKit web application. 17 | 18 | ## Accessing older version of the repository 19 | 20 | You can find older versions of the LearningKit project under the [Releases section](https://github.com/KenticoInternal/LearningKit-Mvc/releases) (open the corresponding commit for the required version and click **Browse files**). 21 | 22 | Quick links: 23 | 24 | - [LearningKit for Kentico 12 Service Pack](https://github.com/KenticoInternal/LearningKit-Mvc/releases/tag/2.0.0) 25 | - [LearningKit for Kentico 12](https://github.com/KenticoInternal/LearningKit-Mvc/releases/tag/v1.0.0) 26 | -------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/CustomDropDownComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections.Generic; 3 | 4 | using CMS.DocumentEngine; 5 | 6 | using Kentico.Forms.Web.Mvc; 7 | using Kentico.Web.Mvc; 8 | 9 | using LearningKit.FormBuilder.FormComponents; 10 | using LearningKit.FormBuilder.FormComponentProperties; 11 | 12 | 13 | [assembly: RegisterFormComponent(CustomDropDownComponent.IDENTIFIER, typeof(CustomDropDownComponent), "Drop-down with custom data", IconClass = "icon-menu")] 14 | 15 | namespace LearningKit.FormBuilder.FormComponents 16 | { 17 | public class CustomDropDownComponent : SelectorFormComponent 18 | { 19 | public const string IDENTIFIER = "CustomDropDownComponent"; 20 | 21 | 22 | // Retrieves data to be displayed in the selector 23 | protected override IEnumerable GetHtmlOptions() 24 | { 25 | // Perform data retrieval operations here 26 | // The following example retrieves all pages of the 'DancingGoatMvc.Article' page type 27 | // located under the 'Articles' section of the Dancing Goat sample website 28 | DocumentQuery query = DocumentHelper.GetDocuments("DancingGoatMvc.Article") 29 | .Path("/Articles/", PathTypeEnum.Children) 30 | .Columns("DocumentName", "DocumentGUID") 31 | .OnSite("DancingGoatMvc") 32 | .Culture("en-us") 33 | .LatestVersion(); 34 | 35 | var sampleData = query.ToList().Select(x => new { Name = x.DocumentName, 36 | Guid = x.DocumentGUID.ToString() }); 37 | 38 | // Iterates over retrieved data and transforms it into SelectListItems 39 | foreach (var item in sampleData) 40 | { 41 | var listItem = new HtmlOptionItem() 42 | { 43 | Value = item.Guid, 44 | Text = item.Name 45 | }; 46 | 47 | yield return listItem; 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /LearningKit/Controllers/Search/SearchController.cs: -------------------------------------------------------------------------------- 1 | //DocSection:Using 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Web.Mvc; 5 | 6 | using CMS.Membership; 7 | using CMS.Search; 8 | //EndDocSection:Using 9 | 10 | using LearningKit.Models.Search.SmartSearch; 11 | 12 | namespace LearningKit.Controllers 13 | { 14 | public class SearchController : Controller 15 | { 16 | //DocSection:SearchController 17 | // Adds the smart search indexes that will be used to perform searches 18 | public static readonly string[] searchIndexes = new string[] { "MVCSite.Index" }; 19 | // Sets the limit of items per page of search results 20 | private const int PAGE_SIZE = 10; 21 | 22 | /// 23 | /// Performs a search and displays its result. 24 | /// 25 | [ValidateInput(false)] 26 | public ActionResult SearchIndex(string searchText) 27 | { 28 | // Displays the search page without any search results if the query is empty 29 | if (String.IsNullOrWhiteSpace(searchText)) 30 | { 31 | // Creates a model representing empty search results 32 | SearchResultModel emptyModel = new SearchResultModel 33 | { 34 | Items = new List(), 35 | Query = String.Empty 36 | }; 37 | 38 | return View(emptyModel); 39 | } 40 | 41 | // Searches the specified index and gets the matching results 42 | SearchParameters searchParameters = SearchParameters.PrepareForPages(searchText, searchIndexes, 1, PAGE_SIZE, MembershipContext.AuthenticatedUser, "en-us", true); 43 | SearchResult searchResult = SearchHelper.Search(searchParameters); 44 | 45 | // Creates a model with the search result items 46 | SearchResultModel model = new SearchResultModel 47 | { 48 | Items = searchResult.Items, 49 | Query = searchText 50 | }; 51 | 52 | return View(model); 53 | } 54 | //EndDocSection:SearchController 55 | } 56 | } -------------------------------------------------------------------------------- /LearningKit/Views/RegisterWithConsent/RegisterWithConsent.cshtml: -------------------------------------------------------------------------------- 1 | @model RegisterWithConsentViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Register with consent"; 5 | } 6 | 7 |

Register

8 | 9 | @using (Html.BeginForm("Register", "RegisterWithConsent", FormMethod.Post)) 10 | { 11 | @Html.AntiForgeryToken() 12 | 13 |
14 | @if (!Html.ViewData.ModelState.IsValid) 15 | { 16 | @Html.ValidationSummary(true, "") 17 | } 18 |
19 | @Html.LabelFor(model => model.FirstName) 20 |
21 |
22 | @Html.EditorFor(model => model.FirstName) 23 | @Html.ValidationMessageFor(model => model.FirstName, "") 24 |
25 |
26 | @Html.LabelFor(model => model.LastName) 27 |
28 |
29 | @Html.EditorFor(model => model.LastName) 30 | @Html.ValidationMessageFor(model => model.LastName) 31 |
32 |
33 | @Html.LabelFor(model => model.UserName) 34 |
35 |
36 | @Html.EditorFor(model => model.UserName) 37 | @Html.ValidationMessageFor(model => model.UserName, "") 38 |
39 |
40 | @Html.LabelFor(model => model.Email) 41 |
42 |
43 | @Html.EditorFor(model => model.Email) 44 | @Html.ValidationMessageFor(model => model.Email) 45 |
46 |
47 | @Html.LabelFor(model => model.Password) 48 |
49 |
50 | @Html.PasswordFor(model => model.Password) 51 | @Html.ValidationMessageFor(model => model.Password, "") 52 |
53 |
54 | @Html.LabelFor(model => model.PasswordConfirmation) 55 |
56 |
57 | @Html.PasswordFor(model => model.PasswordConfirmation) 58 | @Html.ValidationMessageFor(model => model.PasswordConfirmation) 59 |
60 | @*DocSection:Consent*@ 61 |
62 | @Html.EditorFor(model => model.ConsentIsAgreed) 63 | @Html.LabelFor(model => model.ConsentIsAgreed, Model.ConsentShortText) 64 |
65 | @*EndDocSection:Consent*@ 66 | 67 |
68 | } 69 |

70 | @Html.ActionLink("> Back to the home index", "Index", "Home") 71 |

72 | -------------------------------------------------------------------------------- /LearningKit/Views/Register/Register.cshtml: -------------------------------------------------------------------------------- 1 | @model RegisterViewModel 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Register"; 6 | } 7 | 8 |

Register

9 | 10 | @using (Html.BeginForm("Register", "Register", FormMethod.Post)) 11 | { 12 | @Html.AntiForgeryToken() 13 | 14 |
15 | @if (!Html.ViewData.ModelState.IsValid) 16 | { 17 | @Html.ValidationSummary(true, "") 18 | } 19 |
20 | @Html.LabelFor(model => model.FirstName) 21 |
22 |
23 | @Html.EditorFor(model => model.FirstName) 24 | @Html.ValidationMessageFor(model => model.FirstName, "") 25 |
26 | 27 |
28 | @Html.LabelFor(model => model.LastName) 29 |
30 |
31 | @Html.EditorFor(model => model.LastName) 32 | @Html.ValidationMessageFor(model => model.LastName) 33 |
34 | 35 |
36 | @Html.LabelFor(model => model.UserName) 37 |
38 |
39 | @Html.EditorFor(model => model.UserName) 40 | @Html.ValidationMessageFor(model => model.UserName, "") 41 |
42 | 43 |
44 | @Html.LabelFor(model => model.Email) 45 |
46 |
47 | @Html.EditorFor(model => model.Email) 48 | @Html.ValidationMessageFor(model => model.Email) 49 |
50 | 51 |
52 | @Html.LabelFor(model => model.Password) 53 |
54 |
55 | @Html.PasswordFor(model => model.Password) 56 | @Html.ValidationMessageFor(model => model.Password, "") 57 |
58 | 59 |
60 | @Html.LabelFor(model => model.PasswordConfirmation) 61 |
62 |
63 | @Html.PasswordFor(model => model.PasswordConfirmation) 64 | @Html.ValidationMessageFor(model => model.PasswordConfirmation) 65 |
66 | 67 | 68 |
69 | } 70 | 71 |

72 | @Html.ActionLink("> Back to the home index", "Index", "Home") 73 |

-------------------------------------------------------------------------------- /LearningKit/Views/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /LearningKitCustomizations/FormBuilder/FormBuilderStaticMarkupConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Kentico.Forms.Web.Mvc; 2 | using Kentico.Forms.Web.Mvc.Widgets; 3 | 4 | 5 | namespace FormBuilderCustomizations 6 | { 7 | public class FormBuilderStaticMarkupConfiguration 8 | { 9 | public static void SetGlobalRenderingConfigurations() 10 | { 11 | // Modifies the default FormFieldRenderingConfiguration for the 'Form' widget 12 | // Specifying a new FormFieldRenderingConfiguration instance completely replaces the default Kentico configuration 13 | FormFieldRenderingConfiguration.Widget.RootConfiguration = 14 | new ElementRenderingConfiguration 15 | { 16 | ElementName = "div", 17 | HtmlAttributes = { { "class", "form-field" } } 18 | }; 19 | 20 | FormFieldRenderingConfiguration.Widget.ExplanationTextWrapperConfiguration = 21 | new ElementRenderingConfiguration 22 | { 23 | ElementName = "div", 24 | HtmlAttributes = { { "class", "explanation-text" } } 25 | }; 26 | 27 | 28 | // Sets a new rendering configuration for the 'Form' widget, adding attributes 29 | // to the form element and the submit button and wrapping the form in two 'div' blocks 30 | FormWidgetRenderingConfiguration.Default = new FormWidgetRenderingConfiguration 31 | { 32 | // Form element HTML attributes 33 | FormHtmlAttributes = { { "formattribute", "attributevalue" } }, 34 | 35 | // Submit button HTML attributes 36 | SubmitButtonHtmlAttributes = { { "class", "submit-button" } }, 37 | 38 | // Elements wrapping the Form element 39 | FormWrapperConfiguration = new FormWrapperRenderingConfiguration 40 | { 41 | ElementName = "div", 42 | HtmlAttributes = { { "class", "form-widget-form" } }, 43 | // Adds an additional div element wrapper 44 | ChildConfiguration = new ElementRenderingConfiguration 45 | { 46 | ElementName = "div" 47 | } 48 | } 49 | }; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /LearningKit/Scripts/countryStateSelector.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // Executes whenever a country is selected 5 | $('.js-country-selector').change(function () { 6 | var $countrySelector = $(this), 7 | $countryStateSelector = $countrySelector.parent('.js-country-state-selector'), 8 | $stateSelector = $countryStateSelector.find('.js-state-selector'), 9 | $stateSelectorContainer = $countryStateSelector.find('.js-state-selector-container'), 10 | selectedStateId = $countryStateSelector.data('stateselectedid'), 11 | url = $countryStateSelector.data('statelistaction'), 12 | postData = { 13 | countryId: $countrySelector.val() 14 | }; 15 | 16 | $stateSelectorContainer.hide(); 17 | 18 | if (!postData.countryId) { 19 | return; 20 | } 21 | 22 | // Sends a POST request to the 'CountryStates' endpoint of the 'CheckoutController' 23 | $.post(url, postData, function (data) { 24 | $countryStateSelector.data('stateselectedid', 0); 25 | $stateSelector.val(null); 26 | 27 | if (!data.length) { 28 | return; 29 | } 30 | 31 | // Fills and shows the state selector element 32 | fillStateSelector($stateSelector, data); 33 | $stateSelectorContainer.show(); 34 | 35 | if (selectedStateId > 0) { 36 | $stateSelector.val(selectedStateId); 37 | } 38 | }); 39 | }); 40 | 41 | 42 | // Sets the default option for the state selector 43 | $('.js-country-state-selector').each(function () { 44 | var $selector = $(this), 45 | $countrySelector = $selector.find('.js-country-selector'), 46 | countryId = $selector.data('countryselectedid'); 47 | 48 | if (countryId > 0) { 49 | $countrySelector.val(countryId); 50 | } 51 | 52 | $countrySelector.change(); 53 | $selector.data('countryselectedid', 0); 54 | }); 55 | 56 | // Fills the state selector with retrieved states 57 | function fillStateSelector($stateSelector, data) { 58 | var items = ''; 59 | 60 | $.each(data, function (i, state) { 61 | items += ''; 62 | }); 63 | 64 | $stateSelector.html(items); 65 | } 66 | }()); -------------------------------------------------------------------------------- /LearningKit/Views/Account/SignIn.cshtml: -------------------------------------------------------------------------------- 1 | @model SignInViewModel 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Sign in"; 6 | } 7 | 8 |

Sign in

9 | 10 | @using (Html.BeginForm()) 11 | { 12 | @Html.AntiForgeryToken() 13 | 14 |
15 | @if (ViewData.ModelState.Any(x => x.Value.Errors.Any())) 16 | { 17 | @Html.ValidationSummary(true, "") 18 | } 19 | 20 |
21 | @Html.LabelFor(model => model.UserName) 22 |
23 |
24 | @Html.EditorFor(model => model.UserName) 25 | @Html.ValidationMessageFor(model => model.UserName, "") 26 |
27 | 28 |
29 | @Html.LabelFor(model => model.Password) 30 |
31 |
32 | @Html.PasswordFor(model => model.Password) 33 | @Html.ValidationMessageFor(model => model.Password, "") 34 |
35 |
36 | @Html.EditorFor(model => model.SignInIsPersistent) @Html.LabelFor(model => model.SignInIsPersistent) 37 | 38 | } 39 |
40 | @Html.ActionLink("Forgotten password?", "RequestPasswordReset", "PasswordReset") 41 |
42 |

Sign in using an external authentication service:

43 | 44 | @*DocSection:ExternalAuthView*@ 45 | @using Microsoft.Owin.Security 46 | 47 | @{ 48 | @* Gets a collection of the authentication services registered in your application's startup class *@ 49 | var authServices = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes(); 50 | 51 | @* Generates a form with buttons targeting the RequestSignIn action. Optionally pass a redirect URL as a parameter. *@ 52 | using (Html.BeginForm("RequestSignIn", "ExternalAuthentication")) 53 | { 54 | @Html.AntiForgeryToken() 55 |

56 | @foreach (AuthenticationDescription p in authServices) 57 | { 58 | 59 | } 60 |

61 | } 62 | } 63 | @*EndDocSection:ExternalAuthView*@ 64 |

65 | @Html.ActionLink("> Back to the home index", "Index", "Home") 66 |

-------------------------------------------------------------------------------- /LearningKit/Views/Shared/FormComponents/_RgbInputComponent.cshtml: -------------------------------------------------------------------------------- 1 | @*DocSection:RgbViewModel*@ 2 | @using Kentico.Forms.Web.Mvc 3 | @using LearningKit.FormBuilder 4 | 5 | 6 | @model LearningKit.FormBuilder.FormComponents.RgbInputComponent 7 | 8 | @* Gets a collection of system HTML attributes necessary for the correct functionality of the component input fields *@ 9 | @{ 10 | IDictionary htmlAttributes = ViewData.Kentico().GetEditorHtmlAttributes(); 11 | } 12 | 13 | @{ 14 | @* Specifies additional HTML attributes for the input fields *@ 15 | if (htmlAttributes.ContainsKey("style")) 16 | { 17 | htmlAttributes["style"] += " width:50px;"; 18 | } 19 | else 20 | { 21 | htmlAttributes["style"] = "width:50px;"; 22 | } 23 | 24 | @* Sets the partial color inputs to read-only, ensuring users can only specify the color intensities via the color selector *@ 25 | htmlAttributes["readonly"] = ""; 26 | } 27 | 28 | @* Renders basic text input fields to store the partial color intensity values *@ 29 | @Html.Raw("#") 30 | 31 | @Html.TextBoxFor(m => m.RedComponent, htmlAttributes) 32 | 33 | @Html.TextBoxFor(m => m.GreenComponent, htmlAttributes) 34 | 35 | @Html.TextBoxFor(m => m.BlueComponent, htmlAttributes) 36 | 37 | @* Specifies additional attributes for the color selector *@ 38 | @{ 39 | htmlAttributes.Remove("readonly"); 40 | 41 | // The data attributes are used by the accompanying JavaScript logic to assign values to the input fields represented by 42 | // the corresponding identifiers whenever a different color is selected using the selector 43 | htmlAttributes["data-red-id"] = Html.IdFor(m => Model.RedComponent); 44 | htmlAttributes["data-green-id"] = Html.IdFor(m => Model.GreenComponent); 45 | htmlAttributes["data-blue-id"] = Html.IdFor(m => Model.BlueComponent); 46 | 47 | // The window.kentico.updatableFormHelper.updateForm(this.form) ensures any visibility conditions depending 48 | // on fields based on this component only evaluate after a color has been selected using the color selector 49 | htmlAttributes["onchange"] = "parseColorSelector(this); window.kentico.updatableFormHelper.updateForm(this.form)"; 50 | } 51 | 52 | @* Renders the color selector using a custom HtmlHelper extension method *@ 53 | @Html.CustomInput("color", "colorSelector", Model.GetValue(), htmlAttributes) 54 | 55 | (Color selector) 56 | @*EndDocSection:RgbViewModel*@ -------------------------------------------------------------------------------- /LearningKit/Views/EmailRegister/RegisterWithEmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model RegisterViewModel 2 | 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewBag.Title = "Register with email confirmation"; 6 | } 7 | 8 |

Register with email confirmation

9 | 10 | @using (Html.BeginForm("RegisterWithEmailConfirmation", "EmailRegister", FormMethod.Post)) 11 | { 12 | @Html.AntiForgeryToken() 13 | 14 |
15 | @if (!Html.ViewData.ModelState.IsValid) 16 | { 17 | @Html.ValidationSummary(true, "") 18 | } 19 |
20 | @Html.LabelFor(model => model.FirstName) 21 |
22 |
23 | @Html.EditorFor(model => model.FirstName) 24 | @Html.ValidationMessageFor(model => model.FirstName, "") 25 |
26 | 27 |
28 | @Html.LabelFor(model => model.LastName) 29 |
30 |
31 | @Html.EditorFor(model => model.LastName) 32 | @Html.ValidationMessageFor(model => model.LastName) 33 |
34 | 35 |
36 | @Html.LabelFor(model => model.UserName) 37 |
38 |
39 | @Html.EditorFor(model => model.UserName) 40 | @Html.ValidationMessageFor(model => model.UserName, "") 41 |
42 | 43 |
44 | @Html.LabelFor(model => model.Email) 45 |
46 |
47 | @Html.EditorFor(model => model.Email) 48 | @Html.ValidationMessageFor(model => model.Email) 49 |
50 | 51 |
52 | @Html.LabelFor(model => model.Password) 53 |
54 |
55 | @Html.PasswordFor(model => model.Password) 56 | @Html.ValidationMessageFor(model => model.Password, "") 57 |
58 | 59 |
60 | @Html.LabelFor(model => model.PasswordConfirmation) 61 |
62 |
63 | @Html.PasswordFor(model => model.PasswordConfirmation) 64 | @Html.ValidationMessageFor(model => model.PasswordConfirmation) 65 |
66 | 67 | 68 |

69 | Note: To send confirmation emails, the connected Xperience application must have a valid SMTP server configured. 70 |

71 |
72 | } 73 |

74 | @Html.ActionLink("> Back to the home index", "Index", "Home") 75 |

-------------------------------------------------------------------------------- /LearningKit/Views/AzureSearch/AzureSearch.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Azure Search"; 3 | } 4 | @*DocSection:AzureSearchView*@ 5 | @model LearningKit.Models.Search.AzureSearch.AzureSearchViewModel 6 | 7 |

Azure Search

8 | 9 |
10 | @* Renders the search interface *@ 11 | @using (Html.BeginForm("Search", "AzureSearch", FormMethod.Post)) 12 | { 13 |
14 | @Html.TextBoxFor(m => m.SearchString, new { placeholder = "Search..." }) 15 | 16 |
17 | 18 |

Country

19 | 20 | for (int i = 0; i < Model.FilterCountry.Count; i++) 21 | { 22 | @Html.HiddenFor(m => m.FilterCountry[i].Name) 23 | @Html.HiddenFor(m => m.FilterCountry[i].Value) 24 | @Html.EditorFor(m => m.FilterCountry[i].Selected) 25 | @Html.LabelFor(m => m.FilterCountry[i].Selected, Model.FilterCountry[i].Name) 26 | } 27 | 28 |

Farm

29 | 30 | for (int i = 0; i < Model.FilterFarm.Count; i++) 31 | { 32 | @Html.HiddenFor(m => m.FilterFarm[i].Name) 33 | @Html.HiddenFor(m => m.FilterFarm[i].Value) 34 | @Html.EditorFor(m => m.FilterFarm[i].Selected) 35 | @Html.LabelFor(m => m.FilterFarm[i].Selected, Model.FilterFarm[i].Name) 36 | } 37 | } 38 |
39 | 40 |

Search Results

41 | 42 |
43 | @* Renders the search results *@ 44 | @if (Model.SearchResults == null) 45 | { 46 | @Html.Raw($"No results found for {Model.SearchString}.") 47 | } 48 | else 49 | { 50 | for (int i = 0; i < Model.SearchResults.Count; i++) 51 | { 52 | if (Model.SearchResults[i].Highlights == null) 53 | { 54 |

@Html.Raw(Model.SearchResults[i].DocumentTitle)

55 | @Html.Raw(Model.SearchResults[i].DocumentShortDescription) 56 | } 57 | else 58 | { 59 | string highlightedFragments = String.Join(" || ", Model.SearchResults[i].Highlights.Values.Select(value => String.Join(" ", value))); 60 |

@Html.Raw(Model.SearchResults[i].DocumentTitle)

61 | @Html.Raw(highlightedFragments) 62 | } 63 | } 64 | } 65 |
66 | @*EndDocSection:AzureSearchView*@ 67 | 68 |

69 | @Html.ActionLink("> Back to the home index", "Index", "Home") 70 |

-------------------------------------------------------------------------------- /LearningKit/Models/Checkout/ShoppingCartViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | using CMS.Ecommerce; 5 | 6 | using Kentico.Content.Web.Mvc; 7 | 8 | namespace LearningKit.Models.Checkout 9 | { 10 | //DocSection:ShoppingCartViewModel 11 | public class ShoppingCartViewModel 12 | { 13 | public IEnumerable CartItems { get; set; } 14 | 15 | public string CurrencyFormatString { get; set; } 16 | 17 | public IEnumerable CouponCodes { get; set; } 18 | 19 | public decimal TotalTax { get; set;} 20 | 21 | public decimal TotalShipping { get; set; } 22 | 23 | public decimal GrandTotal { get; set; } 24 | 25 | public decimal RemainingAmountForFreeShipping { get; set; } 26 | 27 | public bool IsEmpty { get; set; } 28 | 29 | /// 30 | /// Constructor for the ShoppingCartViewModel. 31 | /// 32 | /// A shopping cart object. 33 | public ShoppingCartViewModel(ShoppingCartInfo cart) 34 | { 35 | // Creates a collection containing all lines from the given shopping cart 36 | CartItems = cart.CartProducts.Select((cartItemInfo) => 37 | { 38 | return new ShoppingCartItemViewModel() 39 | { 40 | CartItemUnits = cartItemInfo.CartItemUnits, 41 | SKUName = cartItemInfo.SKU.SKUName, 42 | TotalPrice = cartItemInfo.TotalPrice, 43 | CartItemID = cartItemInfo.CartItemID, 44 | SKUID = cartItemInfo.SKUID, 45 | SKUImageUrl = string.IsNullOrEmpty(cartItemInfo.SKU.SKUImagePath) ? null : new FileUrl(cartItemInfo.SKU.SKUImagePath, true) 46 | .WithSizeConstraint(SizeConstraint.MaxWidthOrHeight(100)) 47 | .RelativePath 48 | }; 49 | }); 50 | CurrencyFormatString = cart.Currency.CurrencyFormatString; 51 | CouponCodes = cart.CouponCodes.AllAppliedCodes.Select(x => x.Code); 52 | TotalTax = cart.TotalTax; 53 | TotalShipping = cart.TotalShipping; 54 | GrandTotal = cart.GrandTotal; 55 | RemainingAmountForFreeShipping = cart.CalculateRemainingAmountForFreeShipping(); 56 | IsEmpty = cart.IsEmpty; 57 | } 58 | 59 | } 60 | //EndDocSection:ShoppingCartViewModel 61 | } -------------------------------------------------------------------------------- /LearningKit/App_Start/Startup.Auth.Basic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | using System.Web.Mvc; 4 | 5 | using Microsoft.Owin; 6 | using Microsoft.Owin.Security.Cookies; 7 | using Microsoft.AspNet.Identity; 8 | using Owin; 9 | 10 | using CMS.Helpers; 11 | using CMS.SiteProvider; 12 | 13 | using Kentico.Membership; 14 | 15 | // Assembly attribute that sets the OWIN startup class 16 | // This example sets the Startup class from the 'LearningKit.App_Start' namespace, not 'LearningKit.App_Start.Basic' used below 17 | // The active Startup class is defined in Startup.Auth.cs and additionally demonstrates registration of external authentication services 18 | [assembly: OwinStartup(typeof(LearningKit.App_Start.Startup))] 19 | 20 | namespace LearningKit.App_Start.Basic 21 | { 22 | public partial class Startup 23 | { 24 | // Cookie name prefix used by OWIN when creating authentication cookies 25 | private const string OWIN_COOKIE_PREFIX = ".AspNet."; 26 | 27 | 28 | public void Configuration(IAppBuilder app) 29 | { 30 | // Registers the Kentico.Membership identity implementation 31 | app.CreatePerOwinContext(() => KenticoUserManager.Initialize(app, new KenticoUserManager(new KenticoUserStore(SiteContext.CurrentSiteName)))); 32 | app.CreatePerOwinContext(KenticoSignInManager.Create); 33 | 34 | // Configures the authentication cookie 35 | UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); 36 | app.UseCookieAuthentication(new CookieAuthenticationOptions 37 | { 38 | AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 39 | // Fill in the name of your sign-in action and controller 40 | LoginPath = new PathString(urlHelper.Action("SignIn", "Account")), 41 | Provider = new CookieAuthenticationProvider 42 | { 43 | // Sets the return URL for the sign-in page redirect (fill in the name of your sign-in action and controller) 44 | OnApplyRedirect = context => context.Response.Redirect(urlHelper.Action("SignIn", "Account") 45 | + new Uri(context.RedirectUri).Query) 46 | } 47 | }); 48 | 49 | // Registers the authentication cookie with the 'Essential' cookie level 50 | // Ensures that the cookie is preserved when changing a visitor's allowed cookie level below 'Visitor' 51 | CookieHelper.RegisterCookie(OWIN_COOKIE_PREFIX + DefaultAuthenticationTypes.ApplicationCookie, CookieLevel.Essential); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /LearningKit/Personalization/ConditionTypes/HasGivenConsentConditionTypeCustom.cs: -------------------------------------------------------------------------------- 1 | using CMS.ContactManagement; 2 | using CMS.Core; 3 | using CMS.DataProtection; 4 | 5 | 6 | 7 | using LearningKit.Personalization.ConditionTypes; 8 | 9 | //DocSection:ConditionTypeRegistration 10 | using Kentico.PageBuilder.Web.Mvc.Personalization; 11 | 12 | [assembly: RegisterPersonalizationConditionType("LearningKit.Personalization.HasGivenConsentConditionTypeCustom", 13 | typeof(HasGivenConsentConditionTypeCustom), "Has given consent agreement (custom)", 14 | ControllerType = typeof(HasGivenConsentController), 15 | Description = "Evaluates whether the contact has given an agreement with a specified consent declaration.", 16 | IconClass = "icon-clipboard-checklist", 17 | Hint = "Enter the code name of a consent. The condition is fulfilled for visitors who have given an agreement with the given consent.")] 18 | //EndDocSection:ConditionTypeRegistration 19 | 20 | namespace LearningKit.Personalization.ConditionTypes 21 | { 22 | public class HasGivenConsentConditionTypeCustom : ConditionType 23 | { 24 | // Parameter: Code name that identifies the consent for which visitors need to give an agreement to fulfill the condition 25 | public string ConsentCodeName { get; set; } 26 | 27 | /// 28 | /// Default property representing the name of the personalization variant. 29 | /// 30 | public override string VariantName 31 | { 32 | get 33 | { 34 | // Uses the specified consent code name as the name of the variant 35 | return ConsentCodeName; 36 | } 37 | set 38 | { 39 | // No need to set the variant name property 40 | } 41 | } 42 | 43 | public override bool Evaluate() 44 | { 45 | // Gets the contact object of the current visitor 46 | ContactInfo currentContact = ContactManagementContext.GetCurrentContact(false); 47 | 48 | // Creates an instance of the consent agreement service 49 | // For real-world projects, we recommend using a dependency injection container to initialize service instances 50 | var consentAgreementService = Service.Resolve(); 51 | 52 | // Gets the consent object based on its code name 53 | ConsentInfo consent = ConsentInfo.Provider.Get(ConsentCodeName); 54 | if (consent == null || currentContact == null) 55 | { 56 | return false; 57 | } 58 | 59 | // Checks if the contact has given a consent agreement 60 | return consentAgreementService.IsAgreed(currentContact, consent); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /LearningKit/Scripts/addressSelector.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // Fills address form fields with the selected address whenever 5 | // an address is selected from the address selector drop-down element 6 | $('.js-address-selector-div').change(function () { 7 | var $selectorDiv = $(this), 8 | $addressDiv = $selectorDiv.parent(), 9 | $selector = $selectorDiv.find('.js-address-selector'), 10 | url = $selectorDiv.data('statelistaction'), 11 | postData = { 12 | addressId: $selector.val() 13 | }; 14 | 15 | // If the new address option is selected, clears all address form fields 16 | if (!postData.addressId) { 17 | eraseFields($addressDiv); 18 | return; 19 | } 20 | 21 | // Sends a POST request to the 'CustomerAddress' endpoint of the 'CheckoutController' 22 | $.post(url, postData, function (data) { 23 | fillFields($addressDiv, data); 24 | }); 25 | }); 26 | 27 | function fillFields($addressDiv, data) { 28 | fillBasicFields($addressDiv, data); 29 | fillCountryStateFields($addressDiv, data); 30 | } 31 | 32 | // Fills the 'Address line 1' and '2', 'City', and 'Postal code' 33 | // address form fields from the selected address 34 | function fillBasicFields($addressDiv, data) { 35 | var basicFields = $addressDiv.data('fields'), 36 | addressType = $addressDiv.data('addresstype'); 37 | 38 | $.each(basicFields, function (i, val) { 39 | var fieldId = '#' + addressType + '_' + val, 40 | fieldVal = data[val]; 41 | 42 | $(fieldId).val(fieldVal); 43 | }); 44 | } 45 | 46 | // Fills the Country and State drop-down selectors from selected address data 47 | function fillCountryStateFields($addressDiv, data) { 48 | var $countryStateSelector = $addressDiv.find('.js-country-state-selector'), 49 | countryField = $countryStateSelector.data('countryfield'), 50 | stateField = $countryStateSelector.data('statefield'), 51 | $countrySelector = $countryStateSelector.find('.js-country-selector'); 52 | 53 | // Sets id of the country's state for the 'countryStateSelector' script 54 | $countryStateSelector.data('stateselectedid', data[stateField]); 55 | 56 | // Raises the change event on the country selector drop-down element, 57 | // This invokes the 'coutryStateSelector' script that shows the 58 | // states drop-down selector with the selected state 59 | $countrySelector.val(data[countryField]).change(); 60 | } 61 | 62 | // Clears all address form fields 63 | function eraseFields($addressDiv) { 64 | var data = {}; 65 | fillFields($addressDiv, data); 66 | } 67 | }()); 68 | -------------------------------------------------------------------------------- /LearningKit/Personalization/ConditionTypes/HasGivenConsentConditionType.cs: -------------------------------------------------------------------------------- 1 | using CMS.ContactManagement; 2 | using CMS.Core; 3 | using CMS.DataProtection; 4 | 5 | using Kentico.Forms.Web.Mvc; 6 | using Kentico.PageBuilder.Web.Mvc.Personalization; 7 | 8 | using LearningKit.Personalization.ConditionTypes; 9 | 10 | [assembly: RegisterPersonalizationConditionType("LearningKit.Personalization.HasGivenConsentConditionType", 11 | typeof(HasGivenConsentConditionType), "Has given consent agreement", 12 | Description = "Evaluates whether the contact has given an agreement with a specified consent declaration.", 13 | IconClass = "icon-clipboard-checklist", 14 | Hint = "Enter the code name of a consent. The condition is fulfilled for visitors who have given an agreement with the given consent.")] 15 | 16 | namespace LearningKit.Personalization.ConditionTypes 17 | { 18 | public class HasGivenConsentConditionType : ConditionType 19 | { 20 | // Parameter: Code name that identifies the consent for which visitors need to give an agreement to fulfill the condition 21 | // Assigns the default Xperience text input component to the property, which allows users to enter a text value in the configuration dialog 22 | [EditingComponent(TextInputComponent.IDENTIFIER, Order = 0, Label = "Consent code name")] 23 | public string ConsentCodeName { get; set; } 24 | 25 | /// 26 | /// Default property representing the name of the personalization variant. 27 | /// 28 | public override string VariantName 29 | { 30 | get 31 | { 32 | // Uses the specified consent code name as the name of the variant 33 | return ConsentCodeName; 34 | } 35 | set 36 | { 37 | // No need to set the variant name property 38 | } 39 | } 40 | 41 | public override bool Evaluate() 42 | { 43 | // Gets the contact object of the current visitor 44 | ContactInfo currentContact = ContactManagementContext.GetCurrentContact(false); 45 | 46 | // Creates an instance of the consent agreement service 47 | // For real-world projects, we recommend using a dependency injection container to initialize service instances 48 | var consentAgreementService = Service.Resolve(); 49 | 50 | // Gets the consent object based on its code name 51 | ConsentInfo consent = ConsentInfo.Provider.Get(ConsentCodeName); 52 | if (consent == null || currentContact == null) 53 | { 54 | return false; 55 | } 56 | 57 | // Checks if the contact has given a consent agreement 58 | return consentAgreementService.IsAgreed(currentContact, consent); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /LearningKit/Views/Checkout/PreviewAndPay.cshtml: -------------------------------------------------------------------------------- 1 | @model LearningKit.Models.Checkout.PreviewAndPayViewModel 2 | @using LearningKit.Models.Checkout 3 | 4 | @{ 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | ViewBag.Title = "Preview step"; 7 | } 8 | 9 | @*DocSection:DeliveryDetailsView*@ 10 | @* Displays the customer details. *@ 11 |
12 |

Customer Details

13 |
14 | @Html.LabelFor(m => m.DeliveryDetails.Customer.FirstName): 15 | @Html.DisplayFor(m => m.DeliveryDetails.Customer.FirstName) 16 |
17 |
18 | @Html.LabelFor(m => m.DeliveryDetails.Customer.LastName): 19 | @Html.DisplayFor(m => m.DeliveryDetails.Customer.LastName) 20 |
21 |
22 | @Html.LabelFor(m => m.DeliveryDetails.Customer.Company): 23 | @if (!(String.IsNullOrEmpty((string)Model.DeliveryDetails.Customer.Company))) { 24 | Html.DisplayFor(m => m.DeliveryDetails.Customer.Company); 25 | } else { Html.Raw("none"); } 26 |
27 |
28 | 29 | @* Lists all products from the shopping cart. *@ 30 |
31 |

Ordered products

32 |
    33 | @* Loops through all shopping cart items. *@ 34 | @foreach (ShoppingCartItemViewModel cartItem in Model.Cart.CartItems) 35 | { 36 | @* Displays the shopping cart item's properties. *@ 37 |
  • 38 | @cartItem.CartItemUnits× @cartItem.SKUName ... @String.Format(Model.Cart.CurrencyFormatString, cartItem.TotalPrice) 39 |
  • 40 | } 41 |
42 |
43 | 44 | @* Displays additional order details. *@ 45 |
46 |

Selected shipping method: @Model.DeliveryDetails.ShippingOption.ShippingOptionDisplayName

47 |

Total tax: @String.Format(Model.Cart.CurrencyFormatString, Model.Cart.TotalTax)

48 |

Total shipping: @String.Format(Model.Cart.CurrencyFormatString, Model.Cart.TotalShipping)

49 |

Total (incl. tax): @String.Format(Model.Cart.CurrencyFormatString, Model.Cart.GrandTotal)

50 |
51 | 52 | @* Invokes a POST action finalizing order creation. *@ 53 | @using (Html.BeginForm("PreviewAndPay", "Checkout", FormMethod.Post)) 54 | { 55 |
56 | @Html.LabelFor(m => m.PaymentMethod.PaymentMethodID): 57 | @Html.DropDownListFor(m => m.PaymentMethod.PaymentMethodID, Model.PaymentMethod.PaymentMethods) 58 |
59 | 60 | 61 | } 62 | @*EndDocSection:DeliveryDetailsView*@ 63 | 64 |

65 | @Html.ActionLink("> Back to the customer details step", "DeliveryDetails")
66 | @Html.ActionLink("> Back to the customer details step with pre-filling addresses", "DeliveryDetailsAddressSelector")
67 | @Html.ActionLink("> Back to the home index", "Index", "Home") 68 |

-------------------------------------------------------------------------------- /LearningKit/Models/FormComponents/RgbInputComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Kentico.Forms.Web.Mvc; 4 | 5 | using LearningKit.FormBuilder.FormComponents; 6 | using LearningKit.FormBuilder.FormComponentProperties; 7 | 8 | //DocSection:ComponentRegistration 9 | [assembly: RegisterFormComponent(RgbInputComponent.IDENTIFIER, typeof(RgbInputComponent), "RGB color input", Description = "Allows users to specify a color in the RGB hexadecimal format either manually, or by using a color selector", IconClass = "icon-palette")] 10 | //EndDocSection:ComponentRegistration 11 | 12 | namespace LearningKit.FormBuilder.FormComponents 13 | { 14 | //DocSection:RgbInputComponentImplementation 15 | public class RgbInputComponent : FormComponent 16 | { 17 | public const string IDENTIFIER = "RgbInputComponent"; 18 | 19 | 20 | // Specifies that the property carries data for binding by the form builder 21 | [BindableProperty] 22 | // Used to store the value of the input field of the component 23 | public string RedComponent { get; set; } = "ff"; 24 | 25 | 26 | [BindableProperty] 27 | public string GreenComponent { get; set; } = "00"; 28 | 29 | 30 | [BindableProperty] 31 | public string BlueComponent { get; set; } = "00"; 32 | 33 | 34 | // Disables automatic server-side evaluation for the component 35 | public override bool CustomAutopostHandling => true; 36 | 37 | 38 | // Gets the submitted values of the three properties and normalizes them to form a hexadecimal string of length 7, 39 | // e.g., in case a color was submitted in the #363 shorthand (representing #336633) 40 | // The returned value is subsequently saved to the corresponding column in the form's database table 41 | public override string GetValue() 42 | { 43 | return $"#{NormalizeReceivedValue(RedComponent)}{NormalizeReceivedValue(GreenComponent)}{NormalizeReceivedValue(BlueComponent)}"; 44 | } 45 | 46 | 47 | // Normalizes individual submitted color components to 2 characters, e.g., F -> FF, 5 -> 55 48 | private string NormalizeReceivedValue(string value) 49 | { 50 | return value.Length == 1 ? value + value : value; 51 | } 52 | 53 | 54 | // Sets values of the properties (represented by individual 'input' elements) 55 | public override void SetValue(string value) 56 | { 57 | if (!String.IsNullOrEmpty(value)) 58 | { 59 | RedComponent = value.Substring(1, 2); 60 | GreenComponent = value.Substring(3, 2); 61 | BlueComponent = value.Substring(5, 2); 62 | } 63 | else 64 | { 65 | SetValue("#ff0000"); 66 | } 67 | } 68 | } 69 | //EndDocSection:RgbInputComponentImplementation 70 | } --------------------------------------------------------------------------------