123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text;
- using System.Text.RegularExpressions;
- using Comal.Classes;
- using InABox.Clients;
- using InABox.Core;
- using PRSClasses;
- using PRSServer.Properties;
- using RazorEngine.Templating;
- using RazorEngine.Text;
- using Syncfusion.Pdf.Parsing;
- using Color = System.Windows.Media.Color;
- using ColorConverter = System.Windows.Media.ColorConverter;
- using Encoder = System.Drawing.Imaging.Encoder;
- namespace PRSServer
- {
- /// <summary>
- /// Provides utility functions for writing Razor template pages
- /// </summary>
- public class WebUtils
- {
- #region Entity Editor
- /// <summary>
- /// Builds an editor for a given entity.
- /// </summary>
- /// <remarks>To get reasonable formatting, import <see cref="WebUtils.DigitalFormViewerStyle" /> in a <style> tag</remarks>
- /// <typeparam name="T">The type of <paramref name="entity" /></typeparam>
- /// <param name="entity">The entity which contains the data to be pre-inserted into the editor</param>
- /// <param name="id">An optional HTML id to add to root <div> of the generated item.</param>
- /// <param name="classList">An optional list of HTML classes to add to root <div> of the generated item.</param>
- /// <returns>A string containing raw HTML markup</returns>
- public static string BuildEntityEditor<T>(T entity, string? id = null, string[]? classList = null) where T : Entity
- {
- var builder = new StringBuilder();
- builder.AppendFormat(
- @"<div class=""prs-web_utils-entity_editor{0}""{1}>",
- classList != null ? " " + string.Join(" ", classList) : "",
- id != null ? " id=\"" + id + "\"" : ""
- );
- var layout = DFLayout.GenerateEntityLayout<T>();
- var data = WebHandler.SerializeEntityForEditing(typeof(T), entity);
- var markup = BuildDigitalFormViewer(layout, data, false, submitLabel: "Save", saveLabel: null);
- builder.Append(markup).Append("</div>");
- return builder.ToString();
- }
- #endregion
- #region CoreTable Viewer
- /// <summary>
- /// Used for determining which columns to show in an HTML representation of a CoreTable.
- /// EntityTableColumns.ALL means to show every column
- /// EntityTableColumns.VISIBLE means only show columns which do not have their visiblity set to hidden
- /// EntityTableColumns.REQUIRED means only show columns which have their visibility set to visible.
- /// EntityTableColumns.LOOK_UP means to use the Lookup associated with the entity to generate the columns.
- /// </summary>
- public enum EntityTableColumns
- {
- ALL,
- VISIBLE,
- LOOKUP,
- REQUIRED
- }
- private static List<string> GetColumnsForTable<T>(EntityTableColumns showType = EntityTableColumns.ALL) where T : Entity
- {
- if (showType == EntityTableColumns.LOOKUP) return LookupFactory.DefineColumns(typeof(T)).ColumnNames().ToList<string>();
- var columns = new List<string>();
- var properties = CoreUtils.PropertyInfoList(
- typeof(T),
- x => x.GetCustomAttribute<DoNotPersist>() == null &&
- x.GetCustomAttribute<DoNotSerialize>() == null &&
- x.PropertyType != typeof(UserProperties), true);
- switch (showType)
- {
- case EntityTableColumns.VISIBLE:
- foreach (var property in properties)
- {
- var editor = property.Value.GetEditor();
- if (editor != null && editor.Visible != Visible.Hidden) columns.Add(property.Key);
- }
- break;
- case EntityTableColumns.REQUIRED:
- foreach (var property in properties)
- {
- var editor = property.Value.GetEditor();
- if (editor != null && editor.Visible == Visible.Default) columns.Add(property.Key);
- }
- break;
- case EntityTableColumns.ALL:
- foreach (var property in properties.Keys) columns.Add(property);
- break;
- }
- return columns;
- }
- /// <summary>
- /// Returns a string containing an HTML table representing the data present in a CoreTable.
- /// </summary>
- /// <typeparam name="T">The type of the entity that the table represents.</typeparam>
- /// <param name="table">The table to be converted to HTML</param>
- /// <param name="showType">A flag to determine which columns to show.</param>
- /// <returns>The HTML string</returns>
- public static string BuildHTMLTableFromCoreTable<T>(CoreTable table, EntityTableColumns showType = EntityTableColumns.ALL) where T : Entity
- {
- var columns = GetColumnsForTable<T>(showType);
- var html = new List<string>();
- html.Add("<table class=\"prs-web_utils-entity_table entity-" + typeof(T).Name + "\"><caption>");
- html.Add(typeof(T).Name);
- html.Add("</caption><thead><tr>");
- foreach (var column in columns)
- {
- html.Add("<th>");
- html.Add(column);
- html.Add("</th>");
- }
- html.Add("</tr></thead>");
- foreach (var row in table.Rows)
- {
- html.Add("<tr>");
- foreach (var column in columns)
- {
- html.Add("<td>");
- html.Add(row.Get<object?>(column)?.ToString() ?? "NULL");
- html.Add("</td>");
- }
- html.Add("</tr>");
- }
- html.Add("</table>");
- html.Add("</table>");
- return string.Concat(html);
- }
- /// <summary>
- /// Returns a string containing an HTML table representing the data present in a CoreTable.
- /// </summary>
- /// <param name="entityType">The type of the entity that the table represents</param>
- /// <param name="table">The table to be converted to HTML</param>
- /// <param name="showType">A flag to determine which columns to show.</param>
- /// <returns>The HTML string</returns>
- public static string BuildHTMLTableFromCoreTable(Type entityType, CoreTable table, EntityTableColumns showType = EntityTableColumns.ALL)
- {
- var fnc = typeof(WebUtils).GetMethod(nameof(BuildHTMLTableFromCoreTable), 1, new[] { typeof(CoreTable), typeof(EntityTableColumns) })!
- .MakeGenericMethod(entityType);
- return (fnc.Invoke(null, new object[] { table, showType }) as string)!;
- }
- #endregion
- #region Extra Utilities
- public static string ConvertStringToPlainHTML(string text)
- {
- return text.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("\"", """).ReplaceLineEndings("<br/>");
- }
- public enum ImageEncoding
- {
- JPEG
- }
- private static ImageCodecInfo? GetEncoder(ImageFormat format)
- {
- ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
- foreach (ImageCodecInfo codec in codecs)
- {
- if (codec.FormatID == format.Guid)
- {
- return codec;
- }
- }
- return null;
- }
- public static List<byte[]> RenderPDFToImages(byte[] pdfData, ImageEncoding encoding = ImageEncoding.JPEG)
- {
- var rendered = new List<byte[]>();
- PdfLoadedDocument loadeddoc = new PdfLoadedDocument(pdfData);
- Bitmap[] images = loadeddoc.ExportAsImage(0, loadeddoc.Pages.Count - 1);
- var jpgEncoder = GetEncoder(ImageFormat.Jpeg)!;
- var quality = Encoder.Quality;
- var encodeParams = new EncoderParameters(1);
- encodeParams.Param[0] = new EncoderParameter(quality, 100L);
- if (images != null)
- foreach (var image in images)
- {
- using(var data = new MemoryStream())
- {
- image.Save(data, jpgEncoder, encodeParams);
- rendered.Add(data.ToArray());
- }
- }
- return rendered;
- }
- private static readonly ImageConverter converter = new();
- public static string WebDocumentToDataURL(string code)
- {
- var document = new Client<Document>().Query(
- new Filter<Document>(x => x.ID)
- .InQuery(new SubQuery<WebDocument>(
- new Filter<WebDocument>(x => x.Code).IsEqualTo(code),
- new Column<WebDocument>(x => x.Document.ID))),
- new Columns<Document>(x => x.Data));
- var data = (byte[]?)document.Rows.FirstOrDefault()?["Data"];
- return "data:;base64," + (data != null ? Convert.ToBase64String(data) : "");
- }
- public static Guid? WebDocumentID(string code)
- {
- var document = new Client<Document>().Query(
- new Filter<Document>(x => x.ID)
- .InQuery(new SubQuery<WebDocument>(
- new Filter<WebDocument>(x => x.Code).IsEqualTo(code),
- new Column<WebDocument>(x => x.Document.ID))),
- new Columns<Document>(x => x.Data));
- return (Guid?)document.Rows.FirstOrDefault()?["ID"];
- }
- public static string ResourceToDataURL(Bitmap resource)
- {
- return "data:;base64," + Convert.ToBase64String((byte[])converter.ConvertTo(resource, typeof(byte[]))!);
- }
- #endregion
- #region Security
- public static bool UserCanAccess<T>(IDataModel model) where T : ISecurityDescriptor, new()
- {
- var userTable = model.GetTable<User>();
- if (userTable.Rows.Count == 0) return false;
- var user = userTable.Rows[0].ToObject<User>();
- return Security.IsAllowed<T>(user.ID, user.SecurityGroup.ID);
- }
- public static bool IsLoggedIn(IDataModel model)
- {
- var userTable = model.GetTable<User>();
- return userTable.Rows.Count > 0;
- }
- public static User? GetUser(IDataModel model)
- {
- var userTable = model.GetTable<User>();
- if (userTable.Rows.Count == 0) return null;
- return userTable.Rows[0].ToObject<User>();
- }
- #endregion
- #region Directory Viewer
- public static string DirectoryViewerStyle { get; } = @"<style>
- .prs-web_utils-file_manager {
- width: 30em;
- height: 40em;
- padding: 0.5em;
- text-align: left;
- overflow: auto;
- border: 1px solid black;
- background-color: white;
- box-sizing: border-box;
- }
- .prs-web_utils-file_manager * {
- box-sizing: border-box;
- }
- .prs-web_utils-file_manager-file,.prs-web_utils-file_manager-directory_head {
- font-size: 1em;
- color: black;
- width: 100%;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
-
- text-align: left;
-
- padding-left: 1.5em;
- background-position: left;
- background-repeat: no-repeat;
- background-size: contain;
- }
- .prs-web_utils-file_manager-file {
- border: #0000 solid 1px;
- background-image: url(" + ResourceToDataURL(Resources.file) + @");
- }
- .prs-web_utils-file_manager-directory_head {
- background-color: white;
- background-image: url(" + ResourceToDataURL(Resources.folder) + @");
- }
- .prs-web_utils-file_manager-pdf_type {
- background-image: url(" + ResourceToDataURL(Resources.pdf) + @");
- }
- .prs-web_utils-file_manager-png_type {
- background-image: url(" + ResourceToDataURL(Resources.png) + @");
- }
- .prs-web_utils-file_manager-jpg_type {
- background-image: url(" + ResourceToDataURL(Resources.jpg) + @");
- }
- .prs-web_utils-file_manager-directory_head:active {
- cursor: pointer;
- background-color: #CCCCCC
- }
- @media (hover:hover) {
- .prs-web_utils-file_manager-directory_head:hover {
- cursor: pointer;
- background-color: #DDDDDD
- }
- .prs-web_utils-file_manager-file:hover {
- cursor: pointer;
- background-color: #b8d9e7;
- border: #79c4e5 solid 1px;
- }
- }
- .prs-web_utils-file_manager-download {
- display: inline-block;
- width: 1.5em;
- height: 1.5em;
- padding: 0.5em;
- float: right;
- border: solid 1px black;
- background-image: url(" + ResourceToDataURL(Resources.download) + @");
- background-size: 100% 100%;
- }
- .prs-web_utils-file_manager-directory {
- width: 100%;
- padding-left: 0.5em;
- border-left: dashed 1px #AAA;
- border-bottom: dashed 1px #AAA;
- }
- .prs-web_utils-file_manager-collapsed {
- display: none;
- }
- </style>";
- private interface IFileItem
- {
- public string FilePath { get; }
- public string FileName { get; }
- }
- private class FileItem : IFileItem
- {
- public FileItem(string filePath, string fileName)
- {
- FilePath = filePath;
- FileName = fileName;
- }
- public string FilePath { get; }
- public string FileName { get; }
- }
- private class DirectoryItem : IFileItem
- {
- public readonly List<DirectoryItem> Directories = new();
- public readonly List<FileItem> Files = new();
- public DirectoryItem(string filePath, string fileName)
- {
- FilePath = filePath;
- FileName = fileName;
- }
- public string FilePath { get; }
- public string FileName { get; }
- }
- private static DirectoryItem? GetDirectoryStructure(string path, Func<string, bool>? fileFilter, Func<string, bool>? directoryFilter,
- string? name = null, int depth = 2)
- {
- var fileName = name ?? path;
- if (depth == 0) return new DirectoryItem(path, fileName);
- if (!Directory.Exists(path)) return null;
- var directory = new DirectoryItem(path, fileName);
- foreach (var item in Directory.GetDirectories(path))
- if (directoryFilter?.Invoke(item) != false)
- try
- {
- var subDirectory = GetDirectoryStructure(item, fileFilter, directoryFilter, Path.GetFileName(item), depth - 1);
- if (subDirectory != null) directory.Directories.Add(subDirectory);
- }
- catch (UnauthorizedAccessException)
- {
- }
- foreach (var item in Directory.GetFiles(path))
- if (fileFilter?.Invoke(item) != false)
- directory.Files.Add(new FileItem(item, Path.GetFileName(item)));
- return directory;
- }
- private static DirectoryItem? GetDirectoryStructure(string path, string? fileFilter, string? directoryFilter, string? name = null,
- int depth = 2)
- {
- var fileFilterRgx = fileFilter != null ? new Regex(fileFilter) : null;
- var dirFilterRgx = directoryFilter != null ? new Regex(directoryFilter) : null;
- return GetDirectoryStructure(
- path,
- fileFilterRgx != null ? x => fileFilterRgx.IsMatch(x) : null,
- dirFilterRgx != null ? x => dirFilterRgx.IsMatch(x) : null,
- name, depth
- );
- }
- private static readonly string DirectoryViewerScript = @"<script>
- function headClickEventListener(e){
- var next = e.target.nextSibling;
- next.classList.toggle(""prs-web_utils-file_manager-collapsed"");
- }
- var heads = document.getElementsByClassName(""prs-web_utils-file_manager-directory_head"");
- for(var head of heads){
- head.addEventListener(""click"", headClickEventListener);
- }
- </script>";
- private static void BuildDirectory(StringBuilder builder, DirectoryItem directory)
- {
- builder.Append(@"<div class=""prs-web_utils-file_manager-directory_head"">");
- builder.Append(directory.FileName);
- builder.Append(@"</div>");
- builder.Append(@"<div class=""prs-web_utils-file_manager-directory prs-web_utils-file_manager-collapsed""/>");
- if (directory.Directories.Count == 0 && directory.Files.Count == 0)
- {
- builder.Append(@"Folder is empty");
- }
- else
- {
- foreach (var subDirectory in directory.Directories) BuildDirectory(builder, subDirectory);
- foreach (var file in directory.Files)
- {
- var typeClass = Path.GetExtension(file.FileName) switch
- {
- ".png" => "prs-web_utils-file_manager-png_type",
- ".pdf" => "prs-web_utils-file_manager-pdf_type",
- ".jpg" => "prs-web_utils-file_manager-jpg_type",
- ".jpeg" => "prs-web_utils-file_manager-jpg_type",
- _ => null
- };
- builder.Append(@"<div class=""prs-web_utils-file_manager-file");
- if (typeClass != null) builder.Append(" " + typeClass);
- builder.Append(string.Format(@""" data-file=""{0}"">", file.FilePath));
- builder.Append(@"<div class=""prs-web_utils-file_manager-download""></div>");
- builder.Append(file.FileName);
- builder.Append(@"</div>");
- }
- }
- builder.Append(@"</div>");
- }
- /// <summary>
- /// </summary>
- /// <param name="path">The path to the folder in which to initialise the directory viewer</param>
- /// <param name="fileFilter">A file filter which is compiled to a Regex. Regex.IsMatch is used to filter files</param>
- /// <param name="directoryFilter">
- /// A directory filter which is compiled to a Regex. Regex.IsMatch is used to filter
- /// directories
- /// </param>
- /// <param name="alias">The name to call the root folder. If null, defaults to the directory path.</param>
- /// <param name="id">An HTML id attribute for the viewer</param>
- /// <param name="classList">An HTML class list for the viewer</param>
- /// <param name="depth">The depth of directories to search</param>
- /// <returns></returns>
- public static string BuildDirectoryViewer(string path, Func<string, bool> fileFilter, Func<string, bool>? directoryFilter = null,
- string? alias = null, string? id = null, string[]? classList = null, int depth = 2)
- {
- var stringBuilder = new StringBuilder();
- stringBuilder.Append(@"<div ");
- if (id != null) stringBuilder.Append("id=\"" + id + "\" ");
- stringBuilder.Append(@"class=""prs-web_utils-file_manager");
- if (classList != null)
- foreach (var className in classList)
- stringBuilder.Append(" " + className);
- stringBuilder.Append(@""">");
- var structure = GetDirectoryStructure(path, fileFilter, directoryFilter, alias ?? path, depth);
- if (structure != null) BuildDirectory(stringBuilder, structure);
- stringBuilder.Append(@"</div>");
- stringBuilder.Append(DirectoryViewerScript);
- return stringBuilder.ToString();
- }
- public static string BuildDirectoryViewer(string path, string fileFilter, string? directoryFilter = null, string? alias = null,
- string? id = null,
- string[]? classList = null, int depth = 2)
- {
- var fileFilterRgx = new Regex(fileFilter);
- var dirFilterRgx = directoryFilter != null ? new Regex(directoryFilter) : null;
- return BuildDirectoryViewer(
- path,
- x => fileFilterRgx.IsMatch(x),
- dirFilterRgx != null ? x => dirFilterRgx.IsMatch(x) : null,
- alias, id, classList, depth
- );
- }
- #endregion
- #region JSON Entities
- public static Dictionary<string, object> SerializeEntity(Type entityType, Entity entity)
- {
- var data = new Dictionary<string, object>();
- foreach (var property in DatabaseSchema.Properties(entityType))
- {
- data[property.Name] = CoreUtils.GetPropertyValue(entity, property.Name);
- }
- return data;
- }
- /// <summary>
- /// Generates a JSON object for javascript
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="entity"></param>
- /// <returns></returns>
- public static string EntityToJSON<T>(T entity) where T : Entity
- {
- var serializedEntity = SerializeEntity(typeof(T), entity);
- var serializedJSON = Serialization.Serialize(serializedEntity);
- return serializedJSON;
- }
- public static string CreateJSONEntity<T>() where T : Entity, new()
- {
- var serializedEntity = SerializeEntity(typeof(T), new T());
- var serializedJSON = Serialization.Serialize(serializedEntity);
- return serializedJSON;
- }
- /// <summary>
- /// Generate code for setting a property of a JSON entity
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="entityExpr">A Javascript expression that evaluates to the entity JSON</param>
- /// <param name="expression"></param>
- /// <returns></returns>
- public static string JSONSetProperty<T>(string entityExpr, Expression<Func<T, object>> expression, string value) where T : Entity
- {
- var propName = CoreUtils.GetFullPropertyName(expression, ".");
- return $"({entityExpr})[\"{propName}\"]={value}";
- }
- /// <summary>
- /// Generate code for getting a property of a JSON entity
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="entityExpr">A Javascript expression that evaluates to the entity JSON</param>
- /// <param name="expression"></param>
- /// <returns></returns>
- public static string JSONGetProperty<T>(string entityExpr, Expression<Func<T, object>> expression) where T : Entity
- {
- var propName = CoreUtils.GetFullPropertyName(expression, ".");
- return $"({entityExpr})[\"{propName}\"]";
- }
- #endregion
- #region Digital Form Viewer
- private const string _multiImageStyle = @"
- .prs-web_utils-digital_form-multi_image {
-
- }
- .prs-web_utils-digital_form-multi_image_cont {
- overflow: auto;
- position: relative;
- height: 10em;
- background: #999;
- box-shadow: black inset 0 0 4px;
- }
- .prs-web_utils-digital_form-multi_image_cont > div {
- position: absolute;
- top: 0;
- bottom: 0;
- display: flex;
- gap: 1em;
- padding: 0.4em;
- }
- .prs-web_utils-digital_form-multi_image_cont img {
- max-height: 100%;
- }
- .prs-web_utils-digital_form-multi_image_cont img.selected {
- border: dashed 2px black;
- }
- .prs-web_utils-digital_form-multi_image_btns {
- display: flex;
- gap: 0.2em;
- }
- .prs-web_utils-digital_form-multi_image_btns .prs-button {
- flex: 1;
- height: 4em;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- ";
- private static string _digitalFormViewerStyle = @"
- .prs-web_utils-digital_form {
- display: grid;
- column-gap: 0.4em;
- row-gap: 0.4em;
- }
- .prs-web_utils-digital_form-field {
- min-height: 2em;
- }
- @media (pointer:coarse), (pointer:none) {
- .prs-web_utils-digital_form-field {
- min-height: 4em;
- }
- }
- .prs-web_utils-digital_form * {
- box-sizing: border-box;
- }
- .prs-web_utils-digital_form-label {
- text-align: left;
- padding: 1.5em 0.3em;
- align-self: center;
- overflow-wrap: anywhere;
- }
- .prs-web_utils-digital_form-image {
- max-width: 100%;
- max-height: 100%;
- }
- .prs-web_utils-digital_form-color,
- .prs-web_utils-digital_form-integer,
- .prs-web_utils-digital_form-text,
- .prs-web_utils-digital_form-string,
- .prs-web_utils-digital_form-url,
- .prs-web_utils-digital_form-password,
- .prs-web_utils-digital_form-date,
- .prs-web_utils-digital_form-datetime {
- background-color: white;
- }
- .prs-web_utils-digital_form-time {
- display: flex;
- align-items: center;
- width: min-content;
-
- background: white;
- border: solid 1px gray;
- }
- .prs-web_utils-digital_form-time_hours {
- text-align: right;
- }
- .prs-web_utils-digital_form-time_mins {
- text-align: left;
- }
- .prs-web_utils-digital_form-time span {
- margin: 0.1em;
- }
- .prs-web_utils-digital_form-time input {
- height: 100%;
- width: 3em;
- font-family: monospace;
- border: none;
- background: none;
- }
- .prs-web_utils-digital_form-time input:active {
- background: #ddd;
- }
- .prs-web_utils-digital_form-timestamp {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 0.4em;
- }
- .prs-web_utils-digital_form-timestamp input {
- flex: 1;
- height: 100%;
- }
- .prs-web_utils-digital_form-timestamp .prs-button {
- height: 100%;
- padding: 1em;
- display: flex;
- align-items: center;
- }
- .prs-web_utils-digital_form-pin {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 0.4em;
- }
- .prs-web_utils-digital_form-pin input {
- flex: 1;
- height: 100%;
- }
- .prs-web_utils-digital_form-pin .prs-button {
- height: 100%;
- padding: 1em;
- display: flex;
- align-items: center;
- }
- .prs-web_utils-digital_form-document {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 0.4em;
- }
- .prs-web_utils-digital_form-document input {
- flex: 1;
- height: 100%;
- }
- .prs-web_utils-digital_form-document .prs-button {
- height: 100%;
- padding: 1em;
- display: flex;
- align-items: center;
- }
- .prs-web_utils-digital_form-notes {
- display: flex;
- gap: 0.2em;
- }
- .prs-web_utils-digital_form-note_cont {
- flex: 1;
-
- display: flex;
- flex-direction: column;
- gap: 0.2em;
- }
- .prs-web_utils-digital_form-note {
- resize: none;
- width: 100%;
- min-height: 6em;
-
- background-color: #cdcdcd;
- }
- .prs-web_utils-digital_form-note.active {
- background-color: lightgoldenrodyellow;
- }
- .prs-web_utils-digital_form-note_error {
- color: black;
- text-align: left;
- }
- .prs-web_utils-digital_form-note_add {
- display: flex;
- justify-content: center;
- align-items: center;
- text-align: center;
- background-color: #dedede;
- border: solid 1px black;
- border-radius: 3px;
- user-select: none;
-
- width: 2em;
- }
- @media (hover:hover) {
- .prs-web_utils-digital_form-note_add:hover {
- cursor: pointer;
- background-color: #cacaca;
- }
- }
- .prs-web_utils-digital_form-note_add:active {
- background-color: #ccc;
- }
- .prs-web_utils-digital_form-embedded_image, .prs-web_utils-digital_form-signature {
- max-width: 100%;
- border-radius: 3px;
- min-height: 2em;
- display: flex;
- flex-direction: column;
- }
- .prs-web_utils-digital_form-img_cont {
- flex: 1;
- overflow: hidden;
- }
- .prs-web_utils-digital_form-clear_img {
- height: 4em;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .prs-web_utils-digital_form-img {
- max-width: 100%;
- max-height: 100%;
- }
- div.prs-web_utils-digital_form-boolean {
- display: flex;
- align-items: center;
- gap: 0.2em;
- }
- .prs-web_utils-digital_form-boolean label {
- flex: 1;
- text-align: left;
- }
- input.prs-web_utils-digital_form-boolean {
- width: min-content;
- height: min-content;
- margin: auto;
- }
- .prs-web_utils-digital_form-lookup, .prs-web_utils-digital_form-option {
- min-width: 0;
- min-height: 2em;
- background-color: white;
- }
- .prs-web_utils-digital_form-option_fake {
- visibility:hidden;
- margin-left:1em;
- overflow:hidden;
- min-height: 2em;
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button {
- display: inline-block;
- flex: 1;
- height: 100%;
- position: relative;
- height: 2em;
-
- background: none;
- border: none;
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button input {
- appearance: none;
- background-color: #a7a7a7;
- border-radius: 3px;
- border: dashed 1px black;
- margin: 0;
-
- width: 100%;
- height: 100%;
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button input:checked {
- background-color: #e7e7e7;
- border: solid 1px black;
- }
- @media (hover:hover) {
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button input:hover {
- cursor:pointer;
- background-color: #a3a3a3;
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button input:checked:hover {
- background-color: #e0e0e0;
- }
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button input:active {
- background-color: #979797;
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button input:checked:active {
- background-color: #c6c6c6;
- }
- .prs-web_utils-digital_form-boolean .prs-web_utils-digital_form-button span {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- pointer-events: none;
-
- line-height: 2em;
- }
- .prs-web_utils-digital_form-radio_cont {
- display: flex;
- justify-content: stretch;
- align-items: center;
- }
- .prs-web_utils-digital_form-radio_cont label {
- flex: 1;
- text-align: left;
- }
- .prs-web_utils-digital_form-save_cont {
- margin-top: 2em;
- display: flex;
- gap: 0.2em;
- }
- .prs-web_utils-digital_form-save_cont .prs-button {
- height: 4em;
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .prs-web_utils-digital_form-add_task {
- display: flex;
- gap: 0.4em;
- }
- .prs-web_utils-digital_form-add_task .prs-button {
- height: 4em;
- padding: 0 1em;
-
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .prs-web_utils-digital_form-add_task input {
- flex: 1;
- }
- .prs-web_utils-digital_form input:invalid {
- background-color: #FFDDDD;
- }
- .prs-web_utils-digital_form-field:required {
- border: solid 1px #edaf58;
- }
- .prs-web_utils-digital_form-validate {
- position: absolute;
- top: 1em;
- left: 1em;
- height: 3em;
- line-height: 3em;
- padding: 0 1em;
- background: white;
- border: black solid 1px;
- border-radius: 3px;
- box-shadow: grey 0 0 10px;
- transition: opacity 1s linear 0s;
- }
- " + _multiImageStyle;
- /// <summary>
- /// A string which contains a stylesheet used for formatting digital forms. This is not necessary, but can be useful as
- /// a base.<br />
- /// Include within the <style> tag in your document head.
- /// </summary>
- public static string DigitalFormViewerStyle => _digitalFormViewerStyle;
- private const string _submitScript = @"
- var submitButtons = document.getElementsByClassName(""prs-web_utils-digital_form-submit"");
- var saveButtons = document.getElementsByClassName(""prs-web_utils-digital_form-save"");
- function getData(element){
- if(element.classList.contains(""prs-web_utils-digital_form-double"")
- || element.classList.contains(""prs-web_utils-digital_form-integer"")){
- var res = element.valueAsNumber;
- if(Number.isNaN(res)){
- return null;
- }
- return res;
- } else if(element.classList.contains(""prs-web_utils-digital_form-color"")){
- return ""#FF"" + element.value.substring(1).toUpperCase();
- } else if(element.classList.contains(""prs-web_utils-digital_form-multi_image"")){
- let imgCont = element.getElementsByClassName(""prs-web_utils-digital_form-multi_image_cont"")[0];
- let imgContDiv = imgCont.children[0];
-
- let imgs = [];
- for(let img of imgContDiv.children){
- imgs.push(img.src.replace(/^data:(.*,)?/, ''));
- }
- return imgs;
- } else if(element.classList.contains(""prs-web_utils-digital_form-notes"")){
- let cont = element.getElementsByClassName(""prs-web_utils-digital_form-note_cont"")[0];
- let retVal = [];
- for(let note of cont.children){
- retVal.push(note.value);
- }
- if(!(/\S/.test(retVal[retVal.length - 1]))){
- retVal.pop();
- }
- return retVal;
- } else if(element.classList.contains(""prs-web_utils-digital_form-time"")){
- let hours = element.getElementsByClassName(""prs-web_utils-digital_form-time_hours"")[0];
- let mins = element.getElementsByClassName(""prs-web_utils-digital_form-time_mins"")[0];
- return hours.value + "":"" + mins.value;
- } else if(element.classList.contains(""prs-web_utils-digital_form-date"")
- || element.classList.contains(""prs-web_utils-digital_form-datetime"")){
- return element.value;
- } else if(element.classList.contains(""prs-web_utils-digital_form-pin"")){
- let pinText = element.getElementsByClassName(""prs-web_utils-digital_form-pin_text"")[0];
- return pinText.value;
- } else if(element.classList.contains(""prs-web_utils-digital_form-timestamp"")){
- let dateText = element.getElementsByClassName(""prs-web_utils-digital_form-timestamp_date"")[0];
- return dateText.value;
- } else if(element.classList.contains(""prs-web_utils-digital_form-text"")
- || element.classList.contains(""prs-web_utils-digital_form-string"")
- || element.classList.contains(""prs-web_utils-digital_form-password"")
- || element.classList.contains(""prs-web_utils-digital_form-url"")
- || element.classList.contains(""prs-web_utils-digital_form-code"")
- || element.classList.contains(""prs-web_utils-digital_form-option"")){
- return element.value;
- } else if(element.classList.contains(""prs-web_utils-digital_form-radio_cont"")){
- var options = element.getElementsByTagName(""input"");
- for(let option of options){
- if(option.checked){
- return option.value;
- }
- }
- return null;
- } else if(element.classList.contains(""prs-web_utils-digital_form-lookup"")){
- return element.value;
- } else if(element.classList.contains(""prs-web_utils-digital_form-embedded_image"")
- || element.classList.contains(""prs-web_utils-digital_form-signature"")){
- var imgs = element.getElementsByClassName(""prs-web_utils-digital_form-img"");
- if(imgs.length == 0){
- return null;
- } else {
- var img = imgs[0];
- var src = img.getAttribute(""src"");
- var match = /data:[^;]*;base64,/d.exec(src);
- if(match){
- return src.substring(match.indices[0][1]);
- }
- return null;
- }
- } else if(element.classList.contains(""prs-web_utils-digital_form-document"")){
- return element.getAttribute(""data-id"");
- } else if(element.classList.contains(""prs-web_utils-digital_form-boolean"")){
- if(element.tagName == ""INPUT""){
- return element.checked;
- } else if(element.tagName == ""SELECT""){
- if(element.selectedOptions.length > 0){
- return element.selectedOptions[0].hasAttribute(""data-true"");
- }
- return null;
- } else if(element.tagName == ""DIV""){
- for(var element of element.getElementsByTagName(""input"")){
- if(element.checked){
- return element.hasAttribute(""data-true"");
- }
- }
- return null;
- }
- } else if(element.classList.contains(""prs-web_utils-digital_form-add_task"")){
- var input = element.getElementsByTagName(""input"")[0];
- return input.value;
- }
- }
- function validate(element){
- let valid = true;
- let err = """";
- if(element.classList.contains(""prs-web_utils-digital_form-embedded_image"")
- || element.classList.contains(""prs-web_utils-digital_form-signature"")){
- let imgCont = element.getElementsByClassName(""prs-web_utils-digital_form-img_cont"")[0];
- let imgs = imgCont.getElementsByTagName(""img"");
- if(imgs.length == 0){
- valid = false;
- err = ""Please provide an image"";
- }
- } else if(element.classList.contains(""prs-web_utils-digital_form-multi_image"")){
- let imgCont = element.getElementsByClassName(""prs-web_utils-digital_form-multi_image_cont"")[0];
- let imgContDiv = imgCont.children[0];
- if(imgContDiv.children.length == 0){
- valid = false;
- err = ""Please provide at least one image"";
- }
- }
- return [valid, err];
- }
- function doValidationCheck(form){
- var validations = form.getElementsByClassName(""prs-web_utils-digital_form-validate"");
- for(let validation of validations){
- validation.remove();
- }
- if(!form.reportValidity()){ return false; }
-
- let valid = true;
- var elements = form.getElementsByClassName(""prs-web_utils-digital_form-field"");
- for(var element of elements){
- if(element.hasAttribute(""required"")){
- var [elValid, err] = validate(element);
- valid = elValid && valid;
- if(!elValid){
- element.scrollIntoView();
- let validation = document.createElement(""div"");
- validation.classList.add(""prs-web_utils-digital_form-validate"");
- validation.textContent = err;
- element.style.position = ""relative"";
- element.appendChild(validation);
- setTimeout(() => {validation.style.opacity = 0;}, 5000);
- }
- }
- }
- return valid;
- }
- function submitForm(form, save, validate = true){
- if(!validate || doValidationCheck(form)){
- var formData = {};
-
- var elements = form.getElementsByClassName(""prs-web_utils-digital_form-field"");
- for(var element of elements){
- formData[element.getAttribute(""name"")] = getData(element);
- if(element.classList.contains(""prs-web_utils-digital_form-lookup"")){
- for(var option of element.getElementsByTagName(""option"")){
- if(option.value == element.value){
- formData[element.getAttribute(""name"") + ""$ID""] = option.getAttribute(""data-id"");
- break;
- }
- }
- }
- }
- let eventName = save ? ""prs-web_utils-df_save"" : ""prs-web_utils-df_submit"";
- const submitFormEvent = new CustomEvent(eventName, { detail: { formData: formData } });
- form.dispatchEvent(submitFormEvent);
- }
- }
- for(var btn of submitButtons){
- btn.addEventListener(""click"", function(e) {
- var form = e.target.parentNode.parentNode;
- submitForm(form, false, true);
- });
- }
- for(var btn of saveButtons){
- btn.addEventListener(""click"", function(e) {
- var form = e.target.parentNode.parentNode;
- submitForm(form, true, false);
- });
- }
- ";
- private static string GetDigitalFormViewerScript()
- {
- return @"<script>
- function checkDisabled(target, fnc){
- target.addEventListener(""click"", function(e){
- return !target.hasAttribute(""disabled"") ? fnc(e) : null;
- });
- }
- var integerFields = document.getElementsByClassName(""prs-web_utils-digital_form-integer"");
- function integerValidator(e)
- {
- e.target.value = Math.floor(e.target.value);
- }
- for(var field of integerFields){
- field.addEventListener(""change"", integerValidator);
- }
- function changeSelect(e){
- var parent = e.target.parentNode;
- var label = parent.getElementsByTagName(""div"")[0];
- label.textContent = e.target.value;
- }
- var optionCont = document.getElementsByClassName(""prs-web_utils-digital_form-option_cont"");
- for(var option of optionCont){
- var select = option.getElementsByTagName(""select"")[0];
- select.setAttribute(""style"", ""position: absolute;top:0;left:0;right:0;bottom:0;"");
- select.addEventListener(""change"", changeSelect);
- }
- function saveDocument(filename, data, onResponse){
- var xhr = new XMLHttpRequest();
- xhr.open(""POST"", ""/save/Document"");
- xhr.setRequestHeader(""Content-Type"", ""application/json"");
- xhr.responseType = ""json"";
- xhr.onreadystatechange = function(){
- if(xhr.readyState === 4){
- onResponse(xhr.response);
- }
- }
- xhr.send(JSON.stringify({
- ""FileName"": filename,
- ""Data"": data
- }));
- }
- function selectDocument(e){
- var editor = e.target.parentNode;
- var filename = editor.getElementsByClassName(""prs-web_utils-digital_form-filename"")[0];
- var accept = filename.getAttribute(""accept"");
-
- var input = document.createElement(""input"");
- input.type = ""file"";
- input.setAttribute(""accept"", accept);
- input.onchange = e => {
- var file = e.target.files[0];
- var reader = new FileReader();
- reader.onloadend = (re) => {
- saveDocument(file.name, reader.result.replace(/^data:(.*,)?/, ''), function(data){
- filename.value = data[""FileName""];
- editor.setAttribute(""data-id"", data[""ID""]);
- });
- };
- reader.readAsDataURL(file);
- };
- input.click();
- }
- function clearDocument(e){
- var editor = e.target.parentNode;
- editor.removeAttribute(""data-id"");
- var filenameEl = editor.getElementsByClassName(""prs-web_utils-digital_form-filename"")[0];
- filenameEl.value = """";
- }
- function viewDocument(e){
- var editor = e.target.parentNode;
- if(editor.hasAttribute(""data-id"")){
- var id = editor.getAttribute(""data-id"");
- var filenameEl = editor.getElementsByClassName(""prs-web_utils-digital_form-filename"")[0];
- console.log(filenameEl.value);
- const viewDocEvent = new CustomEvent(""prs-web_utils-df_view_doc"", { detail: { id: id, filename: filenameEl.value } });
- editor.dispatchEvent(viewDocEvent);
- }
- }
- var docSelectBtns = document.getElementsByClassName(""prs-web_utils-digital_form-select_doc"");
- var docClearBtns = document.getElementsByClassName(""prs-web_utils-digital_form-clear_doc"");
- var docViewBtns = document.getElementsByClassName(""prs-web_utils-digital_form-view_doc"");
- for(var docSelect of docSelectBtns){
- checkDisabled(docSelect, selectDocument);
- }
- for(var docClear of docClearBtns){
- checkDisabled(docClear, clearDocument);
- }
- for(var docView of docViewBtns){
- checkDisabled(docView, viewDocument);
- }
- function createPIN(e){
- var editor = e.target.parentNode;
-
- var length = parseInt(editor.getAttribute(""data-length""), 10);
- var pinEl = editor.getElementsByClassName(""prs-web_utils-digital_form-pin_text"")[0];
- pinEl.value = """";
- for(let i = 0; i < length; i++){
- pinEl.value += Math.floor(Math.random() * 10).toString();
- }
- }
- function clearPIN(e){
- var editor = e.target.parentNode;
- var pinEl = editor.getElementsByClassName(""prs-web_utils-digital_form-pin_text"")[0];
- pinEl.value = """";
- }
- var pinCreateBtns = document.getElementsByClassName(""prs-web_utils-digital_form-create_pin"");
- var pinClearBtns = document.getElementsByClassName(""prs-web_utils-digital_form-clear_pin"");
- for(var pinCreate of pinCreateBtns){
- checkDisabled(pinCreate, createPIN);
- }
- for(var pinClear of pinClearBtns){
- checkDisabled(pinClear, clearPIN);
- }
- function toggleStamp(e){
- var editor = e.target.parentNode;
- var stampEl = editor.getElementsByClassName(""prs-web_utils-digital_form-timestamp_date"")[0];
- if(/\S/.test(stampEl.value)){
- stampEl.value = """";
- e.target.textContent = ""Set"";
- } else {
- var dateString = (new Date()).toISOString();
- stampEl.value = dateString.substring(0, dateString.length - 1);
- e.target.textContent = ""Clear"";
- }
- }
- var pinToggleBtns = document.getElementsByClassName(""prs-web_utils-digital_form-toggle_stamp"");
- for(var pinToggle of pinToggleBtns){
- checkDisabled(pinToggle, toggleStamp);
- }
- var embedImageFields = document.getElementsByClassName(""prs-web_utils-digital_form-embedded_image"");
- var signatureFields = document.getElementsByClassName(""prs-web_utils-digital_form-signature"");
- function fileSelector(e)
- {
- var selectedFile = e.target.files[0];
- var target = e.target;
- var parent = e.target.parentNode;
- let clearButton = parent.nextSibling;
-
- var img = document.createElement(""img"");
- img.classList.add(""prs-web_utils-digital_form-img"");
- const reader = new FileReader();
- reader.onload = (e) => {
- img.src = e.target.result;
- clearButton.removeAttribute(""disabled"");
- }
- reader.readAsDataURL(selectedFile);
-
- parent.replaceChildren(img, target);
- }
- function fileButton(field, fileElement){
- field.addEventListener(""click"", (e) => {
- fileElement.click();
- });
- }
- function clearEmbeddedImage(e){
- let imgField = e.target.parentNode;
- let imgCont = imgField.children[0];
- let span = document.createElement(""span"");
- span.textContent = ""Choose a file"";
- imgCont.replaceChildren(span, imgCont.children[1]);
- e.target.setAttribute(""disabled"", """");
- }
- var clearButtons = [];
- for(var field of embedImageFields){
- var imgElement = field.getElementsByClassName(""prs-web_utils-digital_form-img_cont"")[0];
- var fileElement = field.getElementsByTagName(""input"")[0];
- fileButton(imgElement, fileElement);
- fileElement.addEventListener(""change"", fileSelector);
- clearButtons.push(field.getElementsByClassName(""prs-web_utils-digital_form-clear_img"")[0]);
- }
- for(var field of signatureFields){
- var imgElement = field.getElementsByClassName(""prs-web_utils-digital_form-img_cont"")[0];
- var fileElement = field.getElementsByTagName(""input"")[0];
- fileButton(imgElement, fileElement);
- fileElement.addEventListener(""change"", fileSelector);
- clearButtons.push(field.getElementsByClassName(""prs-web_utils-digital_form-clear_img"")[0]);
- }
- for(let clearButton of clearButtons){
- checkDisabled(clearButton, clearEmbeddedImage);
- }
- function enableMultiImageRem(multiImage, enabled = true){
- if(!multiImage.hasAttribute(""disabled"")){
- var btn = multiImage.getElementsByClassName(""prs-web_utils-digital_form-multi_image_remove"")[0];
- if(enabled){
- btn.removeAttribute(""disabled"");
- } else {
- btn.setAttribute(""disabled"", """");
- }
- }
- }
- function multiImageClick(e){
- var multiImage = e.target.parentNode.parentNode.parentNode;
- for(let img of e.target.parentNode.children){
- img.classList.remove(""selected"");
- }
- e.target.classList.add(""selected"");
- enableMultiImageRem(multiImage);
- }
- var multiImages = document.getElementsByClassName(""prs-web_utils-digital_form-multi_image"");
- for(let multiImg of multiImages){
- var imgs = multiImg.getElementsByClassName(""prs-web_utils-digital_form-multi_image_cont"")[0].getElementsByTagName(""img"");
- for(let img of imgs){
- img.addEventListener(""click"", multiImageClick);
- }
- }
- function multiImageRemove(e){
- let multiImage = e.target.parentNode.parentNode;
- let imgList = multiImage.getElementsByClassName(""prs-web_utils-digital_form-multi_image_cont"")[0].children[0];
- for(let img of imgList.children){
- if(img.classList.contains(""selected"")){
- imgList.removeChild(img);
- }
- }
- enableMultiImageRem(multiImage, false);
- }
- function multiImageAdd(e){
- let multiImage = e.target.parentNode.parentNode;
- let imgCont = multiImage.getElementsByClassName(""prs-web_utils-digital_form-multi_image_cont"")[0];
- let imgContDiv = imgCont.children[0];
- var input = document.createElement(""input"");
- input.type = ""file"";
- input.setAttribute(""accept"", ""image/*"");
- input.onchange = e => {
- var file = e.target.files[0];
- var reader = new FileReader();
- reader.onloadend = (re) => {
- var img = document.createElement(""img"");
- img.src = reader.result;
- img.addEventListener(""click"", multiImageClick);
- imgContDiv.appendChild(img);
- };
- reader.readAsDataURL(file);
- };
- input.click();
- }
- var multiImageAddBtns = document.getElementsByClassName(""prs-web_utils-digital_form-multi_image_add"");
- var multiImageRemBtns = document.getElementsByClassName(""prs-web_utils-digital_form-multi_image_remove"");
- for(var multiImageBtn of multiImageAddBtns){
- checkDisabled(multiImageBtn, multiImageAdd);
- }
- for(var multiImageBtn of multiImageRemBtns){
- checkDisabled(multiImageBtn, multiImageRemove);
- }
- function addNote(e){
- let notesCont = e.target.parentNode.getElementsByClassName(""prs-web_utils-digital_form-note_cont"")[0];
- let noteChildren = notesCont.getElementsByClassName(""prs-web_utils-digital_form-note"");
- let lastChild = noteChildren[noteChildren.length - 1];
- let error = notesCont.getElementsByClassName(""prs-web_utils-digital_form-note_error"")[0];
- if(/\S/.test(lastChild.value)){
- lastChild.classList.remove(""active"");
- lastChild.setAttribute(""disabled"", """");
-
- let newNotes = document.createElement(""textarea"");
- newNotes.classList.add(""prs-web_utils-digital_form-note"");
- newNotes.classList.add(""active"");
- notesCont.insertBefore(newNotes, error);
-
- error.textContent = """";
- } else {
- error.textContent = ""Please enter a note!"";
- }
- }
- var notesAddButtons = document.getElementsByClassName(""prs-web_utils-digital_form-note_add"");
- for(let noteAdd of notesAddButtons){
- checkDisabled(noteAdd, addNote);
- }
- function addTask(e){
- let field = e.target.parentNode;
- let kanbanType = field.getAttribute(""data-tasktype"");
- let kanban = " + WebUtils.CreateJSONEntity<Kanban>() + ";" +
- WebUtils.JSONSetProperty<Kanban>("kanban", x => x.Type.ID, "kanbanType") + @";
- let eventName = ""prs-web_utils-df_add_task"";
- const addTaskEvent = new CustomEvent(eventName, { detail: {
- kanban: kanban,
- setTaskNumber: function(number){" +
- WebUtils.JSONSetProperty<Kanban>("kanban", x => x.Number, "number") + @";
- var input = field.getElementsByTagName(""input"")[0];
- input.value = number.toString();
- var btn = field.getElementsByClassName(""prs-button"")[0];
- btn.setAttribute(""disabled"", """");
- submitForm(field.closest("".prs-web_utils-digital_form""), true, false);
- }
- }});
- form.dispatchEvent(addTaskEvent);
- }
- var addTaskFields = document.getElementsByClassName(""prs-web_utils-digital_form-add_task"");
- for(let addTaskField of addTaskFields){
- let addTaskBtn = addTaskField.getElementsByClassName(""prs-button"")[0];
- checkDisabled(addTaskBtn, addTask);
- }
- " + _submitScript + "</script>";
- }
- public static string DigitalFormViewerScript => GetDigitalFormViewerScript();
- private static string BuildLookupField(
- IEnumerable<Tuple<string, string?>> options,
- string? selectedString,
- string style,
- string className,
- bool readOnly,
- bool required,
- string name
- )
- {
- var element = new StringBuilder(
- string.Format(@"<div class=""prs-web_utils-digital_form-option_cont"" style=""{0}position: relative;font-size: 1.2em;"">", style));
- var childOptions = new StringBuilder();
- var display = selectedString ?? "";
- foreach (var option in options)
- {
- var idString = option.Item2 != null ? $" data-id=\"{option.Item2}\"" : "";
- childOptions.Append($"<option{(option.Item1 == selectedString ? " selected" : "")} value=\"{option.Item1}\"{idString}>{option.Item1}</option>");
- }
- element.AppendFormat(
- @"<div class=""prs-web_utils-digital_form-option_fake"">{0}</div><select name=""{1}"" class=""{2}""{3}{4}><option></option>",
- display,
- name,
- className,
- readOnly ? " disabled" : "",
- required ? " required" : ""
- ).Append(childOptions).Append("</select></div>");
- return element.ToString();
- }
- private static string BuildRadioButtons(
- IEnumerable<string> options,
- string? selectedString,
- string style,
- string className,
- bool readOnly,
- bool required,
- string name
- )
- {
- var element = new StringBuilder(
- string.Format(@"<div name=""{0}"" class=""prs-web_utils-digital_form-radio_cont {1}"" style=""{2}position: relative;font-size: 1.2em;"">", name, className, style));
- var childOptions = new StringBuilder();
- var radioOptions = string.Format(
- @"name=""{0}"" type=""radio""{1}{2}",
- name,
- required ? " required" : "",
- readOnly ? " disabled" : ""
- );
- foreach (var option in options)
- {
- var selected = option == selectedString;
- var valueStr = $" value=\"{option}\"";
- var optionID = Guid.NewGuid();
- childOptions.Append($"<input id=\"{optionID}\"{valueStr} {radioOptions} {(selected ? " checked" : "")}/>"
- + $"<label for=\"{optionID}\">{option}</label>");
- }
- element.Append(childOptions).Append("</div>");
- return element.ToString();
- }
- public static string BuildDigitalFormViewer(
- DFLayout layout,
- Dictionary<string, object>? data,
- bool readOnly,
- string? id = null, string[]? classList = null,
- string submitLabel = "Submit Form", string? saveLabel = "Save Form"
- )
- {
- StringBuilder stringBuilder = new();
- stringBuilder.Append("<form ");
- if (id != null) stringBuilder.Append("id=\"" + id + "\" ");
- stringBuilder.Append(@"class=""prs-web_utils-digital_form");
- if (classList != null)
- foreach (var className in classList)
- stringBuilder.Append(" " + className);
- stringBuilder.Append(@""" style=""grid-template-columns:");
- foreach (var width in layout.ColumnWidths)
- if (width == "Auto")
- {
- stringBuilder.Append("auto ");
- }
- else if (width == "*")
- {
- stringBuilder.Append("1fr ");
- }
- else
- {
- stringBuilder.Append(width);
- stringBuilder.Append("px ");
- }
- stringBuilder.Append(@";grid-template-rows:");
- foreach (var height in layout.RowHeights)
- if (height == "Auto")
- {
- stringBuilder.Append("auto ");
- }
- else if (height == "*")
- {
- stringBuilder.Append("1fr ");
- }
- else
- {
- stringBuilder.Append(height);
- stringBuilder.Append("px ");
- }
- stringBuilder.Append(@"min-content;"">");
- var lastRowNum = layout.RowHeights.Count + 1;
- foreach (var element in layout.Elements)
- {
- var style = string.Format("grid-column: {0} / span {1};grid-row: {2} / span {3};", element.Column, element.ColumnSpan, element.Row,
- element.RowSpan);
- if (element is DFLayoutField fieldElement)
- {
- var required = fieldElement.GetPropertyValue<bool>("Required");
- var fieldReadOnly = readOnly || fieldElement.GetPropertyValue<bool>("Secure");
- object? fieldData = null;
- data?.TryGetValue(fieldElement.Name, out fieldData);
- var tagType = "input";
- var classes = "";
- string? value = null;
- var properties = "";
- string? children = null;
- // TODO: DocumentField, MultiSignaturePad
- if(element is DFLayoutAddTaskField addTaskField)
- {
- var kanbanType = addTaskField.Properties.TaskType;
- var kanbanNumber = (int?)fieldData;
- tagType = "div";
- classes = "prs-web_utils-digital_form-add_task";
- properties = $"data-tasktype=\"{kanbanType.ID}\"";
- children = $"<input type=\"number\" step=\"1\" disabled{(kanbanNumber != null ? $" value=\"{kanbanNumber}\"" : "")}/>" +
- $"<div class=\"prs-button\"{(fieldReadOnly || kanbanNumber != null ? " disabled" : "")}>Create Task</div>";
- }
- else if (element is DFLayoutBooleanField)
- {
- var fieldType = fieldElement.GetPropertyValue<DesignBooleanFieldType>("Type");
- var trueValue = fieldElement.GetPropertyValue<string>("TrueValue");
- var falseValue = fieldElement.GetPropertyValue<string>("FalseValue");
- bool checkTrue = (bool?)fieldData == true;
- bool checkFalse = (bool?)fieldData == false;
- if (fieldType == DesignBooleanFieldType.Checkbox)
- {
- tagType = "div";
- classes = "prs-web_utils-digital_form-boolean";
- var options = string.Format(
- @"name=""{0}"" type=""radio""{1}{2}",
- fieldElement.Name,
- required ? " required" : "",
- fieldReadOnly ? " disabled" : ""
- );
- var disabled = fieldReadOnly ? " disabled" : "";
- var idTrue = Guid.NewGuid();
- var idFalse = Guid.NewGuid();
- children =
- $"<input value=\"{trueValue}\" id=\"{idTrue}\" {(checkTrue ? " checked" : "")} data-true {options}/>" +
- $"<label for=\"{idTrue}\">{trueValue}</label>" +
- $"<input value=\"{falseValue}\" id=\"{idFalse}\" \"{(checkFalse ? " checked" : "")} data-false {options}/>" +
- $"<label for=\"{idFalse}\">{falseValue}</label>";
- }
- else if (fieldType == DesignBooleanFieldType.ComboBox)
- {
- classes = "prs-web_utils-digital_form-boolean";
- tagType = "select";
- children = string.Format(
- @"<option{0} data-true>{1}</option><option{2} data-false>{3}</option>",
- checkTrue ? " selected" : "",
- trueValue,
- checkFalse ? " selected" : "",
- falseValue
- );
- }
- else if (fieldType == DesignBooleanFieldType.Buttons)
- {
- classes = "prs-web_utils-digital_form-boolean";
- tagType = "div";
- var options = string.Format(
- @"name=""{0}"" type=""radio""{1}{2}",
- fieldElement.Name,
- required ? " required" : "",
- fieldReadOnly ? " disabled" : ""
- );
- children = string.Format(
- @"<div class=""prs-web_utils-digital_form-button""><input {0} value=""{1}""{3} data-true></input><span>{1}</span></div>" +
- @"<div class=""prs-web_utils-digital_form-button""><input {0} value=""{2}""{4} data-false></input><span>{2}</span></div>",
- options,
- trueValue,
- falseValue,
- checkTrue ? " checked" : "",
- checkFalse ? " checked" : ""
- );
- }
- }
- else if (element is DFLayoutCodeField)
- {
- properties = @"type=""text"" pattern=""[^a-z]+""";
- classes = "prs-web_utils-digital_form-code";
- value = fieldData?.ToString() ?? "";
- }
- else if (element is DFLayoutColorField)
- {
- properties = @"type=""color""";
- classes = "prs-web_utils-digital_form-color";
- if (fieldData != null)
- try
- {
- var color =
- (Color)ColorConverter.ConvertFromString((string)fieldData);
- value = string.Format("#{0:x}{1:x}{2:x}", color.R, color.G, color.B);
- }
- catch (FormatException)
- {
- }
- }
- else if (element is DFLayoutDateField)
- {
- properties = @"type=""date""";
- classes = "prs-web_utils-digital_form-date";
- value = ((DateTime?)fieldData)?.ToString("yyyy-MM-dd") ?? "";
- }
- else if (element is DFLayoutDateTimeField)
- {
- properties = @"type=""datetime-local""";
- classes = "prs-web_utils-digital_form-datetime";
- value = fieldData != null ? string.Format("{0:yyyy-MM-dd}T{0:HH:mm}", fieldData) : "";
- }
- else if (element is DFLayoutDoubleField)
- {
- properties = @"type=""number""";
- classes = "prs-web_utils-digital_form-double";
- value = fieldData?.ToString() ?? "";
- }
- else if (element is DFLayoutIntegerField)
- {
- properties = @"type=""number"" step=""1""";
- classes = "prs-web_utils-digital_form-integer";
- value = fieldData?.ToString() ?? "";
- }
- else if (element is DFLayoutLookupField)
- {
- var type = CoreUtils.GetEntityOrNull((element as DFLayoutLookupField).Properties.LookupType);
- if(type is not null)
- {
- var table = ClientFactory.CreateClient(type).Query(
- LookupFactory.DefineFilter(type),
- LookupFactory.DefineColumns(type),
- LookupFactory.DefineSort(type)
- );
- data.TryGetValue(fieldElement.Name + "$ID", out var fieldID);
- data.TryGetValue(fieldElement.Name, out var fieldFormat);
- var options = table.Rows.Select(x => new Tuple<string, string?>(
- LookupFactory.FormatLookup(type, x.ToDictionary(new[] { "ID" }), Array.Empty<string>()),
- x["ID"].ToString())).ToList();
- fieldFormat ??= options.Where(x => x.Item2 == fieldID?.ToString()).FirstOrDefault()?.Item1;
- tagType = null;
- stringBuilder.Append(BuildLookupField(
- options,
- fieldFormat?.ToString(),
- style,
- "prs-web_utils-digital_form-lookup prs-web_utils-digital_form-field",
- fieldReadOnly,
- required,
- fieldElement.Name
- ));
- }
- }
- else if (element is DFLayoutMultiImage)
- {
- tagType = "div";
- classes = "prs-web_utils-digital_form-multi_image";
- var childBuild = new StringBuilder();
- childBuild.Append(@"<div class=""prs-web_utils-digital_form-multi_image_cont""><div>");
- if (fieldData is List<byte[]> images)
- foreach (var image in images)
- {
- var imgStr = Convert.ToBase64String(image);
- childBuild.AppendFormat(@"<img src=""data:;base64,{0}""/>", imgStr);
- }
- childBuild.AppendFormat(@"</div></div><div class=""prs-web_utils-digital_form-multi_image_btns"">" +
- @"<div class=""prs-button prs-web_utils-digital_form-multi_image_add""{0}>Add Image</div>" +
- @"<div class=""prs-button prs-web_utils-digital_form-multi_image_remove"" disabled>Remove Image</div>" +
- @"</div>",
- fieldReadOnly ? " disabled" : ""
- );
- children = childBuild.ToString();
- }
- else if (element is DFLayoutNotesField)
- {
- tagType = "div";
- classes = "prs-web_utils-digital_form-notes";
- var notes = (string[]?)fieldData ?? Array.Empty<string>();
- var childrenBuilder = new StringBuilder(@"<div class=""prs-web_utils-digital_form-note_cont"">");
- foreach (var note in notes)
- childrenBuilder.AppendFormat(@"<textarea class=""prs-web_utils-digital_form-note"" disabled>{0}</textarea>", note);
- var options = fieldReadOnly ? "disabled" : "";
- childrenBuilder.Append(
- @"<textarea class=""prs-web_utils-digital_form-note active""></textarea><div class=""prs-web_utils-digital_form-note_error""></div></div>"
- + $"<div class=\"prs-web_utils-digital_form-note_add prs-button\" {options}>+</div>");
- children = childrenBuilder.ToString();
- }
- else if (element is DFLayoutOptionField optionField)
- {
- var optionType = optionField.Properties.OptionType;
- tagType = null;
- if (optionType == DFLayoutOptionType.Radio)
- {
- stringBuilder.Append(BuildRadioButtons(
- optionField.Properties.Options.Split(','),
- fieldData?.ToString(),
- style,
- "prs-web_utils-digital_form-field",
- fieldReadOnly,
- required,
- fieldElement.Name
- ));
- }
- else
- {
- stringBuilder.Append(BuildLookupField(
- optionField.Properties.Options.Split(',').Select(x => new Tuple<string, string?>(x, null)),
- fieldData?.ToString(),
- style,
- "prs-web_utils-digital_form-option prs-web_utils-digital_form-field",
- fieldReadOnly,
- required,
- fieldElement.Name
- ));
- }
- }
- else if (element is DFLayoutPasswordField)
- {
- properties = @"type=""password""";
- classes = "prs-web_utils-digital_form-password";
- value = fieldData?.ToString() ?? "";
- }
- else if (element is DFLayoutPINField)
- {
- tagType = "div";
- classes = "prs-web_utils-digital_form-pin";
- var pinLength = fieldElement.GetPropertyValue<int>("Length");
- properties += $" data-length=\"{pinLength}\"";
- var options = fieldReadOnly ? "disabled" : "";
- children +=
- $"<input type=\"text\" disabled value=\"{fieldData?.ToString() ?? ""}\" class=\"prs-web_utils-digital_form-pin_text\"{(required ? " required" : "")}/>"
- + $"<div class=\"prs-button prs-web_utils-digital_form-create_pin\" {options}>Create</div>"
- + $"<div class=\"prs-button prs-web_utils-digital_form-clear_pin\" {options}>Clear</div>";
- }
- else if (element is DFLayoutStringField)
- {
- properties = @"type=""text""";
- classes = "prs-web_utils-digital_form-string";
- value = fieldData?.ToString() ?? "";
- }
- else if (element is DFLayoutTextField)
- {
- tagType = "textarea";
- classes = "prs-web_utils-digital_form-text";
- style += "resize:none;";
- children += fieldData?.ToString();
- }
- else if (element is DFLayoutTimeField)
- {
- tagType = "div";
- classes = "prs-web_utils-digital_form-time";
- var timeSpan = (TimeSpan?)fieldData;
- var hours = Math.Truncate(timeSpan?.TotalHours ?? 0.0);
- var mins = timeSpan?.Minutes ?? 0.0;
- var requiredStr = required ? " required" : "";
- children = $"<input class=\"prs-web_utils-digital_form-time_hours\" type=\"number\" min=\"0\" step=\"1\" value=\"{hours}\"{requiredStr}/>" +
- @"<span>:</span>"
- + $"<input class=\"prs-web_utils-digital_form-time_mins\" type=\"number\" min=\"0\" step=\"1\" max=\"60\" value=\"{mins}\"{requiredStr}/>";
- }
- else if (element is DFLayoutTimeStampField)
- {
- tagType = "div";
- classes = "prs-web_utils-digital_form-timestamp";
- var exists = fieldData != null && (DateTime)fieldData != DateTime.MinValue;
- var valueStr = exists ? string.Format("{0:yyyy-MM-dd}T{0:HH:mm}", fieldData) : "";
- var btnStr = exists ? "Clear" : "Set";
- var disabled = fieldReadOnly ? " disabled" : "";
- children +=
- $"<input type=\"datetime-local\" disabled value=\"{valueStr}\" class=\"prs-web_utils-digital_form-timestamp_date\"{(required ? " required" : "")}/>"
- + $"<div class=\"prs-button prs-web_utils-digital_form-toggle_stamp\"{disabled}>{btnStr}</div>";
- }
- else if (element is DFLayoutURLField)
- {
- properties = @"type=""url""";
- classes = "prs-web_utils-digital_form-url";
- value = fieldData?.ToString();
- }
- else if (element is DFLayoutEmbeddedImage || element is DFLayoutSignaturePad)
- {
- // TODO: Perform validation for images
- tagType = "div";
- classes = element is DFLayoutEmbeddedImage
- ? "prs-web_utils-digital_form-embedded_image"
- : "prs-web_utils-digital_form-signature";
- var content = "";
- var hasContent = fieldData != null && ((byte[])fieldData).Length > 0;
- if (hasContent)
- {
- content = string.Format(
- @"<img class=""prs-web_utils-digital_form-img"" src=""data:;base64,{0}"">",
- Convert.ToBase64String((byte[])fieldData)
- );
- }
- else
- {
- if (fieldReadOnly)
- content = "<span>No image</span>";
- else
- content = "<span>Choose a file</span>";
- }
- children = String.Format(@"<div class=""prs-button prs-web_utils-digital_form-img_cont""{1}>{0}<input type=""file"" accept=""image/*"" style=""display: none"" {1}></div><div class=""prs-button prs-web_utils-digital_form-clear_img""{2}>Clear Image</div>",
- content,
- fieldReadOnly ? " disabled" : "",
- (hasContent && !fieldReadOnly) ? "" : " disabled"
- );
- }
-
- if (tagType != null)
- stringBuilder.Append(string.Format(
- @"<{0} style=""{1}"" class=""{2} prs-web_utils-digital_form-field""{3}{4}{5} name=""{6}"" {7}>{8}</{0}>",
- tagType,
- style,
- classes,
- required ? " required" : "",
- fieldReadOnly ? " disabled" : "",
- value != null ? " value=\"" + value + "\"" : "",
- fieldElement.Name,
- properties,
- children ?? ""
- ));
- }
- else if (element is DFLayoutLabel)
- {
- stringBuilder.Append(string.Format(@"<label class=""prs-web_utils-digital_form-label"" style=""{0}"">{1}</label>", style,
- (element as DFLayoutLabel).Caption));
- }
- else if (element is DFLayoutImage)
- {
- stringBuilder.Append(string.Format(@"<img src=""/document?id={0}"" class=""prs-web_utils-digital_form-image"" style=""{1}"">",
- (element as DFLayoutImage).Image.ID, style));
- }
- }
- if (!readOnly)
- {
- stringBuilder.Append($"<div class=\"prs-web_utils-digital_form-save_cont\" style=\"grid-column: 1 / -1; grid-row: {lastRowNum}\">");
- if (saveLabel != null) stringBuilder.Append($"<div class=\"prs-button prs-web_utils-digital_form-save\">{saveLabel}</div>");
- stringBuilder.Append($"<div class=\"prs-button prs-web_utils-digital_form-submit\">{submitLabel}</div>");
- stringBuilder.Append(@"</div>");
- }
- stringBuilder.Append("</form>");
- stringBuilder.Append(DigitalFormViewerScript);
- return stringBuilder.ToString();
- }
- /// <summary>
- /// Builds a layout for viewing and optionally editing digital forms.
- /// </summary>
- /// <note>
- /// <para>
- /// In addition to outputting an HTML representation of the form, a script tag is outputted, which provides form
- /// submission functionality and some extra layouting functionality.
- /// The submit form button has the class of "prs-web_utils-digital_form-submit", and when clicked calls an event
- /// named "prs-web_utils-df_submit" on the root form tag itself.
- /// This event is a JavaScript CustomEvent object with detail.formData set to a JSON object containing data which
- /// can be sent directly to www.domain.com/form_submission/XXXXForm?id=ID, allowing for the form to be submitted.
- /// </para>
- /// </note>
- /// <param name="form">The instance of the form</param>
- /// <param name="entity">The entity associated with the form</param>
- /// <param name="readOnly">If set to true, causes all elements to be disabled and the Submit Form button to be omitted</param>
- /// <param name="id">An optional HTML id to add to root <div> of the generated item.</param>
- /// <param name="classList">An optional list of HTML classes to add to root <div> of the generated item.</param>
- /// <returns>A string containing raw HTML markup</returns>
- public static string BuildDigitalFormViewer(IBaseDigitalFormInstance form, Entity entity, bool readOnly, string? id = null,
- string[]? classList = null)
- {
- var formLayoutTable = new Client<DigitalFormLayout>().Query(
- new Filter<DigitalFormLayout>(x => x.Form.ID).IsEqualTo(form.Form.ID),
- new Columns<DigitalFormLayout>(x => x.Type).Add(x => x.Layout)
- );
- var variables = new Client<DigitalFormVariable>().Load(new Filter<DigitalFormVariable>(x => x.Form.ID).IsEqualTo(form.Form.ID));
- var digitalFormLayout =
- (formLayoutTable.Rows.FirstOrDefault(x => (DFLayoutType)x["Type"] == DFLayoutType.Mobile)
- ?? formLayoutTable.Rows.FirstOrDefault(x => (DFLayoutType)x["Type"] == DFLayoutType.Desktop))?.ToObject<DigitalFormLayout>();
- var layout = digitalFormLayout != null
- ? DFLayout.FromLayoutString(digitalFormLayout.Layout)
- : DFLayout.GenerateAutoMobileLayout(variables);
- layout.LoadVariables(variables);
- var data = DigitalForm.ParseFormData(form.FormData, variables, entity);
- return BuildDigitalFormViewer(layout, data, readOnly, id, classList);
- }
- #endregion
- }
- public abstract class WebTemplateBase<T> : TemplateBase<T>
- {
- public IEncodedString JSON<U>() where U : Entity, new()
- {
- return new RawString(WebUtils.CreateJSONEntity<U>());
- }
- public IEncodedString JSON<U>(U entity) where U : Entity
- {
- return new RawString(WebUtils.EntityToJSON(entity));
- }
- public IEncodedString Get<U>(string entity, Expression<Func<U, object>> expression) where U : Entity
- {
- return new RawString(WebUtils.JSONGetProperty(entity, expression));
- }
- public IEncodedString Set<U>(string entity, Expression<Func<U, object>> expression, string value) where U : Entity
- {
- return new RawString(WebUtils.JSONSetProperty(entity, expression, value));
- }
- }
- }
|