### Install and Create Serene Project Source: https://github.com/serenity-is/serenity/blob/master/README.md Use the dotnet CLI to install the Serene.Templates package, create a new Serene project, navigate into its directory, install NPM packages, and run the project. ```bash # Install/Update Serene.Templates package. > dotnet new install Serene.Templates # Create a new Serene project. > dotnet new serene -n MySereneApp # Navigate into your project folder. > cd MySereneApp/MySereneApp.Web # Install NPM packages. > npm i # Build and run the project. > dotnet run ``` -------------------------------- ### Install SignaturePad Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Editors-Signature-Editor-using-SignaturePad Install the SignaturePad library using npm. This command adds the library as a project dependency. ```bash npm install --save signature_pad ``` -------------------------------- ### Grid Initialization and Data Setup Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example04-highlighting.html Initializes the SleekGrid with dynamic columns and sample data. Configures cell highlighting and flashing CSS classes. ```javascript const columns = [ { id: "server", name: "Server", field: "server", width: 180 } ]; for (var i = 0; i < 4; i++) { columns.push({ name: "CPU" + i, field: i, width: 80, format: cpuUtilizationFormatter }); } const data = []; for (var i = 0; i < 500; i++) { var d = (data[i] = {}); d.server = "Server " + i; for (var j = 0; j < columns.length; j++) { d[j] = Math.round(Math.random() * 100); } } const grid = new Slick.Grid("#myGrid", data, columns, { editable: false, enableAddRow: false, enableCellNavigation: true, cellHighlightCssClass: "changed", cellFlashingCssClass: "current-server" }); ``` -------------------------------- ### Start Drawing Stroke Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/SignaturePad-100%-Pure-Beef-TypeScript-Implementation Initializes a new stroke by creating a new point group and recording the starting point. It also triggers the `onBegin` callback if defined. ```typescript private _strokeBegin(event: MouseEvent | Touch): void { const newPointGroup = { color: this.penColor, points: [], }; this._data.push(newPointGroup); this._reset(); this._strokeUpdate(event); if (typeof this.onBegin === 'function') { this.onBegin(event); } } ``` -------------------------------- ### Usage of PercentageFormatter in Columns Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Grid-SlickGrid-Formatters Demonstrates how to apply the PercentageFormatter to a column definition in C#. This example shows setting custom decimal places for the percentage display. ```csharp [BasedOnRow(typeof(Entities.ProductExampleRow))] public class ProductExampleColumns { [PercentageFormatter(DecimalPlaces = 3)] public Decimal Discount { get; set; } } ``` -------------------------------- ### Start and Manage Email Sending Background Thread (C#) Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Bulk-Email-Sending This snippet shows how to start a background thread for sending emails. Ensure the `EnableEmailService` configuration is set to true to activate this functionality. The thread runs with the lowest priority and checks for emails every minute. ```C# public static class EmailThread { private static Thread _SendEmailThread = null; public static void StartEmailThread() { if (_SendEmailThread == null) { _SendEmailThread = new Thread(new ThreadStart(SendEmails)); _SendEmailThread.Priority = ThreadPriority.Lowest; _SendEmailThread.Start(); } } public static void EndEmailThread() { if (_SendEmailThread != null) { _SendEmailThread.Abort(); } } private static void SendEmails() { bool enableEmailService = Convert.ToBoolean(ConfigurationManager.AppSettings["EnableEmailService"]); if (enableEmailService) { while (true) { Thread.Sleep(60000); // 10 sec var connection = SqlConnections.NewFor(); var request = new ListRequest(); ListResponse rows = new MyRepository().List(connection, request); if (rows.TotalCount == 0) break; foreach (MyRow row in rows.Entities) { try { MailMessage mailMessage = new MailMessage(); mailMessage.From = new MailAddress(row.From, row.FromName); foreach (var to in row.To.Split(',')) mailMessage.To.Add(new MailAddress(to)); if (!string.IsNullOrEmpty(row.Cc)) foreach (var cc in row.Cc.Split(',')) mailMessage.CC.Add(new MailAddress(cc)); if (!string.IsNullOrEmpty(row.Bcc)) foreach (var bcc in row.Bcc.Split(',')) mailMessage.Bcc.Add(new MailAddress(bcc)); mailMessage.Subject = row.Subject; mailMessage.Body = row.Body; mailMessage.IsBodyHtml = true; EmailAccountsRow emailAccount = connection.TryFirst(EmailAccountsRow.Fields.Id == (int)row.EmailAccountId.Value); SmtpClient smtp = new SmtpClient { Host = emailAccount.Host, Port = (int)emailAccount.Port, EnableSsl = (bool)emailAccount.EnableSsl, DeliveryMethod = SmtpDeliveryMethod.Network, Credentials = new System.Net.NetworkCredential(emailAccount.Username, emailAccount.Password), Timeout = 30000, }; smtp.Send(mailMessage); row.HasError = false; row.SentTries = 1; row.SentOnUtc = DateTime.UtcNow; } catch (Exception ex) { row.HasError = true; row.Result = ex.Message; row.SentTries = 1; } connection.UpdateById(row); } } } } } ``` -------------------------------- ### Add CsvHelper NuGet Package Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Reporting-Adding-CSV-Export Install the CsvHelper NuGet package to enable CSV processing capabilities. ```bash CsvHelper ``` -------------------------------- ### Create a Simple Hangfire Job Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Using-Hangfire-Background-Jobs Implement a basic background job class. This example shows how to log a message and includes commented-out code for using SQL connections. ```csharp using Serenity; using System; namespace YourAwesomeProject.Common.Jobs { public class SimpleJob { // If you want to run SQL with a connection, add this // private readonly ISqlConnections Connections; // public SimpleJob(ISqlConnections connections) // { // this.Connections = connections ?? throw new ArgumentNullException(nameof(connections)); // } public void Run() { new Exception("Hello Serenity from Hangfire!").Log(); // using (var connection = Connections.NewFor()) // { // do stuff // } } } } ``` -------------------------------- ### Install Serenity Core Library via NPM Source: https://github.com/serenity-is/serenity/blob/master/packages/corelib/README.md Add the Serenity Core Library as a dependency in your package.json. Ensure the version aligns with your Serenity NuGet package versions. ```json { "dependencies": { // ... "@serenity-is/corelib": "./node_modules/.dotnet/serenity.corelib" } } ``` -------------------------------- ### Enqueue and Schedule Hangfire Jobs Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Using-Hangfire-Background-Jobs Demonstrates how to enqueue a one-time job and schedule recurring jobs using Hangfire. Includes examples for hourly and cron-based scheduling. ```csharp // Setting up some example jobs BackgroundJob.Enqueue(job => job.Run()); RecurringJob.AddOrUpdate(job => job.Run(), Cron.Hourly); RecurringJob.AddOrUpdate(job => job.Run(), "0 * * * *"); ``` -------------------------------- ### Usage Example of IsInRole Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Additional-Feature-Implementing-server-side-IsInRole-and-client-side-hasRole Shows how to obtain the current user's definition and use the IsInRole extension method to check for specific roles by name or ID. ```csharp var user = (UserDefinition)Authorization.UserDefinition. var hasRole = user.IsInRole("RoleName"); // Or var hasRole = user.IsInRole(1); //RoleId ``` -------------------------------- ### Add Product Categories as Dynamic Navigation Items (C#) Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Dynamic-navigation-items Implement INavigationItemSource to dynamically add product categories as navigation items. This example fetches categories from the database and creates a navigation link for each. ```csharp using System.Collections.Generic; using Serenity.Navigation; using Serenity.Data; using Serenity.Services; using Serene.Northwind.Repositories; using Serene.Northwind.Pages; [assembly: NavigationMenu(7900, "Basic Samples", icon: "icon-magic-wand")] public class DynamicNavigationSample : INavigationItemSource { public List GetItems() { var items = new List { new NavigationMenuAttribute(7970, "Basic Samples/Dynamic Navigation", "icon-paper-plane") }; // Add product categories as dynamic navigation items for demo purpose using (var connection = SqlConnections.NewByKey("Northwind")) { var categories = connection.List(); foreach (var category in categories) items.Add(new NavigationLinkAttribute(7970, path: "Basic Samples/Dynamic Navigation/" + category.CategoryName.Replace("/", "//"), url: "~/Northwind/Product?cat=" + category.CategoryID, permission: Northwind.PermissionKeys.General, icon: "icon-folder-alt")); } return items; } } ``` -------------------------------- ### Add Organization Structure to Navigation (C#) Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Dynamic-navigation-items Implement INavigationItemSource to create a dynamic, hierarchical navigation structure based on an organization's business units. This example recursively builds paths for units and adds them as links or menus. ```csharp using System.Collections.Generic; using Serenity.Navigation; using Serenity.Services; using Organization = Serene.Organization.Pages; using Serene.Organization.Repositories; [assembly: NavigationMenu(8000, "Organization", icon: "fa-sitemap")] [assembly: NavigationLink(8000, "Organization/Business Units", typeof(Organization.BusinessUnitController), icon: "fa-sitemap")] [assembly: NavigationMenu(8000, "Organization/Contacts", icon: "fa-address-book")] [assembly: NavigationLink(8000, "Organization/Contacts/All Contacts", typeof(Organization.ContactController))] public class ContactsByBusinessUnit : INavigationItemSource { public List GetItems() { var items = new List(); using (var connection = Serenity.Data.SqlConnections.NewByKey("Default")) { var businessUnits = new BusinessUnitRepository().List(connection, new ListRequest()).Entities; foreach (var unit in businessUnits) { string path = unit.Name; // Recursively get path var currentUnit = unit; while (currentUnit.ParentUnitId != null) { currentUnit = businessUnits.Find(y => y.UnitId == currentUnit.ParentUnitId); path = currentUnit.Name + "/" + path; } path = "Organization/Contacts/" + path; // If has no child, add navigation link, else add navigation menu if (businessUnits.FindAll(x => x.ParentUnitId == unit.UnitId).Count == 0) items.Add(new NavigationLinkAttribute(8000, path, "Organization/Contact/Index?UnitId=" + unit.UnitId, null)); else items.Add(new NavigationMenuAttribute(8000, path, icon: "icon-folder-alt")); } } return items; } } ``` -------------------------------- ### Active Directory Service Implementation Source: https://github.com/serenity-is/serenity/wiki/Authenticate-with-Active-Directory-(.Net-Core) Implement the Active Directory service to validate user credentials against the domain. Ensure the System.DirectoryServices.AccountManagement NuGet package is installed if needed. This class requires the server to be connected to the domain. ```csharp using Serenity; using Serenity.ComponentModel; using System; using System.DirectoryServices.AccountManagement; namespace PROJECTNAMESPACE.Administration { public class ActiveDirectoryService : IDirectoryService { public DirectoryEntry Validate(string username, string password) { var config = Config.Get(); using (var context = new PrincipalContext(ContextType.Domain, config.Domain)) { bool isValid; try { isValid = context.ValidateCredentials(username, password); } catch (Exception ex) { Log.Error("Error authenticating user", ex, this.GetType()); return null; } if (!isValid) return null; var identity = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username); return new DirectoryEntry { Username = identity.SamAccountName, Email = identity.EmailAddress.TrimToNull(), FirstName = identity.GivenName, LastName = identity.Surname, }; } } [Hidden, SettingScope("Application"), SettingKey("ActiveDirectory")] private class Settings { public string Domain { get; set; } } } } ``` -------------------------------- ### Configure Report Connection String in appsettings.json Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Reporting-Integrating-Telerik-Reporting Defines the connection string for Telerik Reporting. This example uses a specific name for the connection string, which should match the report's data source configuration. ```json { "Data": { "Default": { "ConnectionString": "Server=(localdb)\\MsSqlLocalDB;Database=Serene_Default_v1;Integrated Security=true", "ProviderName": "System.Data.SqlClient" } }, "ConnectionStrings": { "Telerik.Reporting.Examples.CSharp.Properties.Settings.TelerikConnectionString": { "ConnectionString": "Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=SSPI", "ProviderName": "System.Data.SqlClient" } }, "Logging": "..." } ``` -------------------------------- ### Create Temporary Generic Table SQL Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/[BBB]-On-client-side-fetching-a-value-from-server-side-out-of-database SQL script to create a temporary 'Generic' table. This table is used to leverage sergen for auto-creating an endpoint and repository, simplifying the setup process. ```sql USE [] GO /****** Object: Table [dbo].[Generic] Script Date: 29.09.2018 12:22:31 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Generic]( [GenericID] [int] IDENTITY(1,1) NOT NULL, [GenericName] [nvarchar](max) NULL, CONSTRAINT [PK_Generic] PRIMARY KEY CLUSTERED ( [GenericID] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ``` -------------------------------- ### Initialize Basic SleekGrid Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example01-simple.html Use this snippet to set up a simple grid with data and columns. Ensure the target element ID and data/column structures are correctly defined. ```javascript const data = []; for (var i = 0; i < 500; i++) { data[i] = { title: "Task " + i, duration: "5 days", percentComplete: Math.round(Math.random() * 100), start: "01/01/2022", finish: "01/05/2022", effortDriven: (i % 5 == 0) }; } const columns = [ { field: "title" }, { field: "duration" }, { field: "percentComplete", name: "% Complete", width: 90 }, { field: "start", width: 100 }, { field: "finish", width: 100 }, { field: "effortDriven", width: 100 } ]; new Slick.Grid("#myGrid", data, columns, { enableCellNavigation: true }); ``` -------------------------------- ### Configure Startup.cs for Telerik Reporting Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Reporting-Integrating-Telerik-Reporting Adds the Telerik Report Viewer integration to the application's service configuration. Ensure Telerik namespaces are included. ```csharp using YourAwesomeProject.Extensions.DependencyInjection; // Existing code services.AddSingleton(); // ... // Add this line services.AddTelerikReportViewer(Configuration); ``` -------------------------------- ### Use TextAreaColumnFormatter in C# Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Grid-SlickGrid-Formatters Example of how to apply the TextAreaColumnFormatter to a C# property in Serenity. ```csharp [TextAreaColumnFormatter ] public String SomeTextAreaField { get; set; } ``` -------------------------------- ### Applying LookupScriptAttribute to a Row Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Entity-Framework-LookupScriptAttribute-[Extensibility] Example of applying the LookupScriptAttribute to a row class, specifying the lookup key. ```csharp [LookupScript("Northwind.Employee")] public sealed class EmployeeRow : Row, IIdRow, INameRow { [...] } ``` -------------------------------- ### Register Active Directory Service in Startup.cs Source: https://github.com/serenity-is/serenity/wiki/Authenticate-with-Active-Directory-(.Net-Core) Register the ActiveDirectoryService as a singleton with the IDirectoryService interface in the ConfigureServices method of Startup.cs. Ensure necessary usings are included. ```csharp services.AddSingleton(); ``` -------------------------------- ### Configure SignalR Services in Startup.cs Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/A-simple-notification-with-SignalR-in-Serene-StartSharp-(.NET-Core-version) Add SignalR services and the custom User ID provider to the dependency injection container in the application's startup configuration. ```csharp services.AddLogging(loggingBuilder => { loggingBuilder.AddConfiguration(Configuration.GetSection("Logging")); loggingBuilder.AddConsole(); loggingBuilder.AddDebug(); }); // add below 2 lines services.AddSignalR(); services.AddSingleton(); services.AddSingleton(); ``` -------------------------------- ### Usage Example for hasRole Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Additional-Feature-Implementing-server-side-IsInRole-and-client-side-hasRole Demonstrates how to call the `hasRole` function in TypeScript to check for a specific role. ```typescript Authorization.hasRole('RoleName'); ``` -------------------------------- ### Instantiate and Load Help Dialog Source: https://github.com/serenity-is/serenity/wiki/A-proposal-for-"dogfooding"-Serenity-documentation This code snippet demonstrates how to instantiate a HelpDialog and load content by its title. The 'Title' field from the v_Help entity is used as the identifier for readability. ```javascript var dlg = new HelpDialog(); dlg.loadByIdAndOpenDialog('Job-PipeClass Requirement Set'); ``` -------------------------------- ### Add Hangfire NuGet Packages for ASP.NET Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Using-Hangfire-Background-Jobs Install these packages for Hangfire integration in traditional ASP.NET applications. ```csharp Hangfire.AspNet Hangfire.Core Hangfire.SqlServer Microsoft.Owin Microsoft.Owin.Host.SystemWeb Owin ``` -------------------------------- ### Grid Initialization and Data Generation Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example08-modal-editor-form.html Initializes the SleekGrid with sample data and configuration. Includes event handlers for adding new rows and validation errors. ```javascript var data = []; for (var i = 0; i < 500; i++) { var d = (data[i] = {}); d["title"] = "Task " + i; d["description"] = "This is a sample task description.\n It can be multiline"; d["duration"] = "5 days"; d["percentComplete"] = Math.round(Math.random() * 100); d["start"] = "01/01/2009"; d["finish"] = "01/05/2009"; d["effortDriven"] = (i % 5 == 0); } grid = new Slick.Grid("#myGrid", data, columns, { editable: true, enableAddRow: true, enableCellNavigation: true, asyncEditorLoading: false, autoEdit: false }); grid.onAddNewRow.subscribe(function(e, args) { var item = args.item; var column = args.column; grid.invalidateRow(data.length); data.push(item); grid.updateRowCount(); grid.render(); }); grid.onValidationError.subscribe(function(e, args) { // handle validation errors originating from the CompositeEditor if (args.editor && (args.editor instanceof Slick.CompositeEditor)) { var err; var idx = args.validationResults.errors.length; while (idx--) { err = args.validationResults.errors[idx]; err.container.classList.add('highlight-red'); } } }); grid.setActiveCell(0, 0); ``` -------------------------------- ### UpdatableAttribute Constructor and Property Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Entity-Framework-UpdatableAttribute-[Common] Defines the UpdatableAttribute with a constructor that accepts a boolean and a Value property to get its state. ```csharp public class UpdatableAttribute : Attribute { public UpdatableAttribute(bool updatable = true); public bool Value { get; } } ``` -------------------------------- ### Grid Initialization and Event Handling Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example03-events.html Initializes the SleekGrid with data and columns, then sets up event listeners for context menu and click events. The context menu is displayed on right-click, and clicking the 'priority' cell toggles its value. ```javascript var data = []; for (var i = 0; i < 100; i++) { data.push({ title: "Task " + i, priority: "Medium" }); } var columns = [ { field: "title", width: 200, cssClass: "cell-title", editor: Slick.TextEditor }, { field: "priority", width: 80, selectable: false, resizable: false } ]; var grid = new Slick.Grid("#myGrid", data, columns, { editable: true, enableAddRow: false, enableCellNavigation: true, asyncEditorLoading: false, rowHeight: 30 }); const contextMenu = document.getElementById('contextMenu'); function hideContextMenu() { contextMenu.hidden = true; document.body.removeEventListener('click', hideContextMenu); } grid.onContextMenu.subscribe(function (e) { e.preventDefault(); var cell = grid.getCellFromEvent(e); contextMenu.dataset.row = cell.row; contextMenu.style.top = e.pageY + 'px'; contextMenu.style.left = e.pageX + 'px'; contextMenu.hidden = false; document.body.addEventListener('click', hideContextMenu); }); grid.onClick.subscribe(function (e) { var cell = grid.getCellFromEvent(e); if (grid.getColumns()[cell.cell].id == "priority") { if (!grid.getEditorLock().commitCurrentEdit()) { return; } var states = { "Low": "Medium", "Medium": "High", "High": "Low" }; data[cell.row].priority = states[data[cell.row].priority]; grid.updateRow(cell.row); e.stopPropagation(); } }); contextMenu.addEventListener('click', function(e) { if (!e.target.matches('[data-priority]') || !grid.getEditorLock().commitCurrentEdit()) return; var row = parseInt(this.dataset.row); data[row].priority = e.target.dataset.priority; grid.updateRow(row); }); ``` -------------------------------- ### Add Hangfire NuGet Packages for .NET Core Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Using-Hangfire-Background-Jobs Install these packages when using Hangfire with .NET Core applications. ```csharp Hangfire.AspNetCore Hangfire.Core Hangfire.SqlServer ``` -------------------------------- ### SQL Administration Topics Recommendations Source: https://github.com/serenity-is/serenity/wiki/A-proposal-for-"dogfooding"-Serenity-documentation These comments suggest additional administration topics and role-based restrictions for help content. ```sql -- Additional Administration topics you might add would be instructions on editing user permissions. -- These might or might not be system-specific, but Serenity could provide a startup set. -- Finally, Administrative topics supplied in the base can be restricted to a Role or Roles if one or more are set up for this purpose. -- There aren't any roles currently supplied with the template, so I won't do that. ``` -------------------------------- ### Applying InsertableAttribute to a Form Field Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Entity-Framework-InsertableAttribute-[Common] Example of using the InsertableAttribute on a form field to prevent insertion of the 'IdCode' field. ```csharp [BasedOnRow(typeof(Entities.XYZRow))] public class XYZForm { [Insertable(false)] public Int32 IdCode { get; set; } [...] } ``` -------------------------------- ### Apply BSSwitchEditor to a Model Property Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Bootstrap-Switch-plugin-(by-marcobisio) Example of how to use the custom BSSwitchEditor in a Serenity entity's property definition, configuring its text labels. ```csharp [DisplayName("Discontinued"), NotNull] [BSSwitchEditor(OnText = "Yes", OffText = "No")] public Boolean? Discontinued { get { return Fields.Discontinued[this]; } set { Fields.Discontinued[this] = value; } } ``` -------------------------------- ### Initialize SleekGrid with ES6 and Plugins Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example96-es6-demo.html Initializes the SleekGrid instance with data, columns, and options. It also sets up a row selection model and registers a checkbox selector plugin. Uses ES6 arrow functions and `debounce` for efficient filtering. ```javascript const grid = this.grid = new Slick.Grid(this.gridEl, view, columns, options); columns[7].format = columns[7].format.bind(this.grid) grid.setSelectionModel(new Slick.RowSelectionModel({ selectActiveRow: false })); grid.registerPlugin(checkboxSelector); const changeFilter = debounce(value => { healthValue = value view.refresh() }, 500) ``` -------------------------------- ### Initialize Grid with AutoTooltips Plugin Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example95-plugin-autotooltips.html This snippet shows how to initialize a SlickGrid with the AutoTooltips plugin enabled for both header cells and data cells. Ensure the slick.autotooltips.js script is included. ```javascript const data = []; for (var i = 0; i < 500; i++) { data[i] = { title: "Task " + i + " with some long text", duration: "5 days", percentComplete: Math.round(Math.random() * 100), start: "01/01/2022", finish: "01/05/2022", effortDriven: (i % 5 == 0) }; } const columns = [ { field: "title" }, { field: "duration" }, { field: "percentComplete", name: "% Complete", width: 90 }, { field: "start", width: 100 }, { field: "finish", width: 100 }, { field: "effortDriven", width: 100 } ]; var grid = new Slick.Grid("#myGrid", data, columns, {}); grid.registerPlugin(new Slick.AutoTooltips({ enableForHeaderCells: true })); ``` -------------------------------- ### LookupFilteringAttribute Usage Example Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Grid-LookupFilteringAttribute-[Columns] Demonstrates the usage of LookupFilteringAttribute on a 'Country' display name field. When IdField is not provided, it defaults to matching the current field. ```csharp [DisplayName("Country"), Size(15), LookupFiltering("Northwind.CustomerCountry")] public String Country { get { return Fields.Country[this]; } set { Fields.Country[this] = value; } } ``` -------------------------------- ### Get Signature Data Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/SignaturePad-100%-Pure-Beef-TypeScript-Implementation Retrieves the current drawing data from the SignaturePad as an array of IPointGroup objects. This data can be used for saving or re-rendering the signature. ```typescript public toData(): IPointGroup[] { return this._data; } ``` -------------------------------- ### Get Current Pager Mode Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Grid---Mixin---Custom-grid-pager-without-counting-total-records This TypeScript method returns the current pager mode ('full' or 'next-previous-only') by checking the state of a switch button. ```typescript public getCurrentPagerMode(): ('full' | 'next-previous-only') { return $(this._btnSwitch).is(":checked") ? 'full' : 'next-previous-only'; } ``` -------------------------------- ### Basic Grid with Frozen Layout Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example97-frozen-columns.html This snippet shows how to initialize a SleekGrid with frozen columns and rows. Ensure the necessary scripts for formatters and the frozen layout are included. ```javascript var grid; var columns = [ { field: "title", frozen: true }, { field: "duration", frozen: true }, { field: "description", width: 350 }, { field: "percentComplete", name: "% Complete", width: 200, format: Slick.PercentCompleteBarFormatter }, { field: "start", width: 150 }, { field: "finish", width: 150 }, { field: "effortDriven", width: 150 } ]; var data = []; for (var i = 0; i < 500; i++) { data[i] = { title: "Task " + i, duration: "5 days", description: "Some description for task " + i, percentComplete: Math.round(Math.random() * 100), start: "01/01/2022", finish: "01/05/2022", effortDriven: (i % 5 == 0) }; } grid = new Slick.Grid("#myGrid", data, columns, { enableCellNavigation: true, enableColumnReorder: false, layoutEngine: new Slick.FrozenLayout(), frozenRows: 2 }); ``` -------------------------------- ### Employee Lookup Editor - Value Handling Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Editors-Autocomplete-Editor Handles setting and getting values for the Employee Lookup Editor. Includes formatting and validation logic. ```typescript var jsonData = input.data('empkey'); var fields = JSON.parse(jsonData); $('[name=' + fields.en + ']').val(value.EmpNum); $('[name=' + fields.ln + ']').val(value.LastName); $('[name=' + fields.fn + ']').val(value.FirstName); $('[name=' + fields.un + ']').val(value.UserName); $('[name=' + fields.fullname + ']').val(value.LastName + ", " + value.FirstName); $('[name=' + fields.title + ']').val(value.JobTitle); } return false; })// ``` -------------------------------- ### Register SignalR Hub with OWIN Startup Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Group-notifications-with-SignalR-(by-marcobisio) Configure the OWIN startup class to register the SignalR hub. This is essential for enabling SignalR functionality in your application. ```csharp using Owin; using Microsoft.Owin; using Microsoft.AspNet.SignalR; [assembly: OwinStartup(typeof(myApp.Notifications.Startup))] namespace myApp.Notifications { public class Startup { public void Configuration(IAppBuilder app) { var idProvider = new CustomUserIdProvider(); // to send notifications to single users by UserId instead of by Username //GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider); // Any connection or hub wire up and configuration should go here app.MapSignalR(); } } } ``` -------------------------------- ### Add End-User Help Navigation Link Source: https://github.com/serenity-is/serenity/wiki/A-proposal-for-"dogfooding"-Serenity-documentation Include this common navigation link to provide access to the end-user help functionality. It's typically placed in a 'CommonNavigation.cs' file or similar. ```csharp [assembly: NavigationLink(int.MaxValue, "Help", typeof(HelpController), icon: "fa-info-circle")] ``` -------------------------------- ### Draw Dot in SignaturePad Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/SignaturePad-100%-Pure-Beef-TypeScript-Implementation Draws a single dot on the canvas, typically used for single-point signatures or starting points of strokes. Uses a specified color and size. ```typescript private _drawDot({ color, point }: { color: string, point: IBasicPoint }): void { const ctx = this._ctx; const width = typeof this.dotSize === 'function' ? this.dotSize() : this.dotSize; ctx.beginPath(); this._drawCurveSegment(point.x, point.y, width); ctx.closePath(); ctx.fillStyle = color; ctx.fill(); } ``` -------------------------------- ### Web.config with Customer-Specific Connection Strings Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Database-One-application-with-different-database-per-customer This Web.config file defines connection strings for different customers using IIS location tags. Each location path corresponds to a virtual directory that maps to a specific customer's database. ```xml ``` -------------------------------- ### Handle Mouse Down Event Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/SignaturePad-100%-Pure-Beef-TypeScript-Implementation Initiates a new stroke when the primary mouse button (left-click) is pressed down on the canvas. It records the starting point and prepares for stroke updates. ```typescript private _handleMouseDown = (event: MouseEvent): void => { if (event.which === 1) { this._mouseButtonDown = true; this._strokeBegin(event); } } ``` -------------------------------- ### Retrieve User and Roles from Database Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Additional-Feature-Implementing-server-side-IsInRole-and-client-side-hasRole Demonstrates fetching a UserDefinition from the database and retrieving associated roles using GetUserRoles. Requires an IDbConnection and BaseCriteria. ```csharp private UserDefinition GetFirst(IDbConnection connection, BaseCriteria criteria) { var user = connection.TrySingle(criteria); if (user != null) return new UserDefinition { UserId = user.UserId.Value, Username = user.Username, Email = user.Email, UserImage = user.UserImage, DisplayName = user.DisplayName, IsActive = user.IsActive.Value, Source = user.Source, PasswordHash = user.PasswordHash, PasswordSalt = user.PasswordSalt, UpdateDate = user.UpdateDate, LastDirectoryUpdate = user.LastDirectoryUpdate, Roles = GetUserRoles(connection, user.UserId.Value) //<===---- }; return null; } private List GetUserRoles(IDbConnection connection, int userId) //<===---- { var fld = Entities.UserRoleRow.Fields; var userRoles = connection.List((query) => { query.SelectTableFields() .Select(fld.RoleName) .Where(new Criteria(fld.UserId) == userId); }); var roles = new List(); userRoles.ForEach(r => { roles.Add(new RoleDefinition() { RoleId = r.RoleId.Value, RoleName = r.RoleName }); }); return roles; } ``` -------------------------------- ### C# Usage Example for BooleanSwitchEditor Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Bootstrap-Material-Design-Switch This C# code demonstrates how to apply the BooleanSwitchEditor in a Serenity form. Use the BooleanSwitchEditor attribute with the desired CSS class for styling. ```csharp [BooleanSwitchEditor(Css = "label-success")] public bool? IsTechnicianArrived { get; set; } ``` -------------------------------- ### Create Settings Table SQL Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/[BBB]-On-client-side-fetching-a-value-from-server-side-out-of-database SQL script to create a 'Settings' table in the database for storing application-wide settings. This table includes columns for ID, name, value, and description. ```sql USE [] GO /****** Object: Table [dbo].[Settings] Script Date: 29.09.2018 12:13:25 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Settings]( [SettingId] [int] IDENTITY(1,1) NOT NULL, [SettingName] [nvarchar](max) NOT NULL, [SettingValue] [nvarchar](max) NOT NULL, [SettingDescription] [nvarchar](max) NULL, CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ( [SettingId] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ``` -------------------------------- ### List Field Definition in RowFields Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/LinkingSetRelation-Explanation Defines a list field in the RowFields class, used for multi-selection scenarios. This example shows the specific definition for the 'Representatives' field. ```csharp public ListField Representatives; ``` -------------------------------- ### Implement DatabaseUploadStorage Service Source: https://github.com/serenity-is/serenity/wiki/DatabaseUploadStorage This C# class implements the `IUploadStorage` interface to manage file uploads directly to a database. It handles writing, opening, deleting, and checking for the existence of files using SQL queries. ```csharp using Serenity.Web; using System.Collections.Generic; using System.IO; using System; using Newtonsoft.Json; using Serenity.Data; using System.Linq; namespace SereneExtensions.Web.Services { public class DatabaseUploadStorage : IUploadStorage { private readonly ISqlConnections _sqlConnections; public DatabaseUploadStorage(ISqlConnections sqlConnections) { _sqlConnections=sqlConnections; } public string WriteFile(string path, Stream source, OverwriteOption overwrite) { byte[] fileBytes; using (var ms = new MemoryStream()) { source.CopyTo(ms); fileBytes = ms.ToArray(); } using (var connection = _sqlConnections.NewByKey("Default")) { var result = connection.Execute( "INSERT INTO FilesStore (Path, Content) VALUES (@Path, @Content)", new { Path = path, Content = fileBytes }); } return path; } public Stream OpenFile(string path) { byte[] fileBytes; using (var connection = _sqlConnections.NewByKey("Default")) { fileBytes = connection.Query( "SELECT Content FROM FilesStore WHERE Path = @Path", new { Path = path }).FirstOrDefault(); } return fileBytes != null ? new MemoryStream(fileBytes) : null; } public void DeleteFile(string path) { using (var connection = _sqlConnections.NewByKey("Default")) { connection.Execute( "DELETE FROM FilesStore WHERE Path = @Path", new { Path = path }); if (IsImage(path)) { string tempThum = $"{path.Substring(0, path.Length - 4)}_t.jpg"; connection.Execute( "DELETE FROM FilesStore WHERE Path = @Path", new { Path = tempThum }); } } } public bool FileExists(string path) { bool fileExits = false; using (var connection = _sqlConnections.NewByKey("Default")) { fileExits = Dapper.SqlMapper.ExecuteScalar(connection, "SELECT COUNT(*) FROM FilesStore WHERE Path = @Path", new { Path = path }); } return fileExits; } public string ArchiveFile(string path) { // Implement based on your archiving strategy return path; } public string CopyFrom(IUploadStorage source, string path, string targetPath, OverwriteOption overwrite) { byte[] fileBytes; using (var connection = _sqlConnections.NewByKey("Default")) { fileBytes = connection.Query( "SELECT Content FROM FilesStore WHERE Path = @Path", new { Path = path }).FirstOrDefault(); var tempfileName = Path.GetFileNameWithoutExtension(path); var ext = Path.GetExtension(path); string tempThum = $"{path.Substring(0, path.Length - 4)}_t.jpg"; string targetPathThum = $"{targetPath.Substring(0, targetPath.Length - 4)}_t.jpg"; var result = connection.Execute( "INSERT INTO FilesStore (Path, Content) VALUES (@Path, @Content)", new { Path = targetPath, Content = fileBytes }); if (IsImage(path)) { connection.Execute( "Update FilesStore Set Path = @targetPathThum where Path =@tempThum ", new { targetPathThum = targetPathThum, tempThum = tempThum }); } ``` -------------------------------- ### Change Endpoints to Use AdvancedServiceEndpoint Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Database-Multi-Database-Serenity This example shows how to change a controller's base class from the default ServiceEndpoint to AdvancedServiceEndpoint to inherit the dynamic connection logic. ```csharp public class LanguageController : AdvancedServiceEndpoint ``` -------------------------------- ### Initialize SleekGrid with Undo Handler Source: https://github.com/serenity-is/serenity/blob/master/packages/sleekgrid/docs/examples/classic/example06-editing-with-undo.html Initializes the SleekGrid instance, enabling editing and setting the `editCommandHandler` to `queueAndExecuteCommand` for undo support. ```javascript var grid = new Slick.Grid("#myGrid", data, columns, { editable: true, enableAddRow: false, enableCellNavigation: true, asyncEditorLoading: false, autoEdit: true, editCommandHandler: queueAndExecuteCommand }); ``` -------------------------------- ### Login Panel Widget Initialization Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Customizing-the-Login-Screen This script initializes the LoginPanel widget on the specified div. Ensure the MyProject.Membership.LoginPanel is correctly defined. ```javascript jQuery(function() { new MyProject.Membership.LoginPanel($('#LoginPanel')).init(); // ... }); ``` -------------------------------- ### Add Navigation Link for Exception Dashboard Source: https://github.com/serenity-is/serenity/wiki/Exception-log-for-non-premium-users-(ElmahCore) Example of how to add a navigation link to the Elmah exception dashboard in an administration menu. This requires defining a NavigationLink attribute. ```csharp // for example in in AdministrationNavigation.cs: [assembly: NavigationLink(9500,"Administration/Exceptions",url:"Exceptions", permission:PermissionKeys.Security,icon: "fa-bug",Target ="_blank")] ``` -------------------------------- ### Get File Size from Database Source: https://github.com/serenity-is/serenity/wiki/DatabaseUploadStorage Retrieves the size of a file stored in the database by querying the DATALENGTH of the Content column. Requires a SQL connection to the 'Default' database. ```csharp public long GetFileSize(string path) { long fileSize = 0; using (var connection = _sqlConnections.NewByKey("Default")) { fileSize = Dapper.SqlMapper.ExecuteScalar(connection, "SELECT DATALENGTH(Content) FROM FilesStore WHERE Path = @Path", new { Path = path }); } return fileSize; } ``` -------------------------------- ### Configure Hangfire in .NET Core Startup.cs Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Using-Hangfire-Background-Jobs Configures Hangfire services, including SQL Server storage options and the background server, within the ConfigureServices method of Startup.cs. It also sets up the Hangfire dashboard with authorization. ```csharp using Hangfire; using Hangfire.SqlServer; using Hangfire.Dashboard; using Hangfire.Annotations; // ....... public void ConfigureServices(IServiceCollection services) { // ....... services.AddHangfire(configuration => configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() // Reference the Default connection. If you want to add a new connection to // Hangfire's database then remember to add this connection in your appsettings.json .UseSqlServerStorage(Configuration.GetValue("Data:Default:ConnectionString"), new SqlServerStorageOptions { CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), QueuePollInterval = TimeSpan.Zero, UseRecommendedIsolationLevel = true, UsePageLocksOnDequeue = true, DisableGlobalLocks = true }) ); // Add the processing server as IHostedService services.AddHangfireServer(); } // end of ConfigureServices // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IAntiforgery antiforgery) { // ....... app.UseHangfireDashboard("/jobs", new DashboardOptions() { Authorization = new[] { new HangfireAuthorizeFilter() } }); // Setting up some example jobs // BackgroundJob.Enqueue(job => job.Run()); // RecurringJob.AddOrUpdate(job => job.Run(), Cron.Hourly); // RecurringJob.AddOrUpdate(job => job.Run(), "0 * * * *"); } // end of Configure public class HangfireAuthorizeFilter : IDashboardAuthorizationFilter { public bool Authorize([NotNull] DashboardContext context) { return Authorization.HasPermission(Administration.PermissionKeys.BackgroundJob); } } ``` -------------------------------- ### Basic Slider Dialog Initialization Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Slider-dialog-(poor-mans-EntityGridDialog) Initializes the slider dialog for editing an item. Ensure 'MySliderReveal.ts' is referenced. This setup is for transforming a grid edit link into a slider. ```typescript /// ``` ```typescript private slider: Common.MySliderReveal; protected editItem(entityOrId) { this.slider = new Common.MySliderReveal($("
"), { initDialog: () => new ProductDialog(), onDataChangeCallback: () => { this.refresh(); }, entityOrId: entityOrId, }); this.slider.handleEditItem(); } ``` -------------------------------- ### Applying WidthAttribute to Columns Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Grid-WidthAttribute-[Columns] Demonstrates how to apply the WidthAttribute to properties within a column definition class to set specific widths, including min/max constraints. ```csharp public class ProductColumns { [Width(150)] public String ProductID { get; set; } [Width(250, Max = 400, Min = 150)] public String ProductName { get; set; } [...] ``` -------------------------------- ### Append Navigation Buttons to Dialog Toolbar Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/UI-Slider-dialog-(poor-mans-EntityGridDialog) Appends the previous and next record buttons to the dialog's toolbar. This is part of the UI setup for the slider dialog. ```typescript nextPreOuter.append(this.btnPreviousRecord); nextPreOuter.append(this.btnNextRecord); dlgToolbar.find(".buttons-outer").append(nextPreOuter); ``` -------------------------------- ### Configure Hangfire in ASP.NET Startup.cs Source: https://github.com/serenity-is/serenity/wiki/wiki-archive/Background-Tasks-Using-Hangfire-Background-Jobs Sets up Hangfire for an ASP.NET application using OWIN. It configures the SQL Server storage and the dashboard with custom authorization rules. ```csharp using Hangfire; using Hangfire.SqlServer; using Microsoft.Owin; using Owin; using Serenity; using Serenity.Data; using System; using System.Collections.Generic; using System.Diagnostics; using System.Web; [assembly: OwinStartup(typeof(YourAwesomeProject.Startup))] namespace YourAwesomeProject { public class Startup { private IEnumerable GetHangfireServers() { GlobalConfiguration.Configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() // Reference the Default connection. If you want to add a new connection to // Hangfire's database then remember to add this connection in your Web.config .UseSqlServerStorage(SqlConnections.GetConnectionString("Default").ConnectionString, new SqlServerStorageOptions { CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), QueuePollInterval = TimeSpan.Zero, UseRecommendedIsolationLevel = true, UsePageLocksOnDequeue = true, DisableGlobalLocks = true }); yield return new BackgroundJobServer(); } public void Configuration(IAppBuilder app) { var options = new DashboardOptions { // You can add your own rules here, feedback welcome Authorization = new[] { new AuthorizationFilter() { Users = "admin" } }, AppPath = VirtualPathUtility.ToAbsolute("~") }; app.UseHangfireAspNet(GetHangfireServers); app.UseHangfireDashboard("/jobs", options); // Setting up some example jobs // BackgroundJob.Enqueue(job => job.Run()); // RecurringJob.AddOrUpdate(job => job.Run(), Cron.Hourly); // RecurringJob.AddOrUpdate(job => job.Run(), "0 * * * *"); } } } ```