iText 7 PDF accessibility: "Table header cell has no associated subcells" - html

I am converting HTML to a PDF using iText 7. I need the PDF to be accessible (508 compliant with appropriate tags, etc), but, no matter what markup I put on a table, accessibility checkers give the same error: "Table header cell has no associated subcells". I've tried setting scope, headers, etc... nothing seems to work. Here is an example of one of the tables but all of them have the same issue:
<table class="problems" summary="Patient's diagnosed problems and associated ICD codes.">
<thead>
<tr>
<th scope="col" id="problem-header">
Problem
</th>
<th scope="col" id="icd-code-header">
Code
</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="problem-header">Some Problem</td>
<td headers="icd-code-header">Some ICD Code</td>
</tr>
</tbody>
</table>
Any help would be appreciated. Thank you so much.
EDIT: I forgot to mention, I am using the .NET version of iText 7.
EDIT 2: Here is the code that converts the HTML to PDF:
public class AccessiblePdfService : IAccessiblePdfService
{
private static readonly string[] FontPaths = ConfigurationManager.AppSettings["FontPaths"].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
public void createPdf(string html, string dest, PdfTypes type = PdfTypes.RefDoc) //string resources
{
FileStream outputStream = new FileStream(dest, FileMode.Create, FileAccess.Write);
WriterProperties writerProperties = new WriterProperties();
//Add metadata
writerProperties.AddXmpMetadata();
PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
pdfDoc.GetCatalog().SetLang(new PdfString("en-US"));
//Set the document to be tagged
pdfDoc.SetTagged();
pdfDoc.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
//Set meta tags
PdfDocumentInfo pdfMetaData = pdfDoc.GetDocumentInfo();
pdfMetaData.SetAuthor("SOME STRING");
pdfMetaData.AddCreationDate();
pdfMetaData.GetProducer();
pdfMetaData.SetCreator("SOME STRING");
switch (type)
{
case PdfTypes.RefDoc:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
case PdfTypes.PatientRoi:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
case PdfTypes.RoiAdmin:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
default:
break;
}
//Title is derived from html
// pdf conversion
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.AddStandardPdfFonts();
foreach (var path in FontPaths)
{
fp.AddFont(path);
}
props.SetFontProvider(fp);
DefaultTagWorkerFactory tagWorkerFactory = new AccessibilityTagWorkerFactory();
props.SetTagWorkerFactory(tagWorkerFactory);
HtmlConverter.ConvertToPdf(html, pdfDoc, props);
pdfDoc.Close();
}
}
EDIT 3:
Here is the AccessibilityTagWorkerFactory (keep in mind, the tables that I want to act like tables are not marked with the class, "make-table-div" and there shouldn't be affected by the customizations in this class:
public class AccessibilityTagWorkerFactory : DefaultTagWorkerFactory
{
public override ITagWorker GetCustomTagWorker(IElementNode tag, ProcessorContext context)
{
bool hasClass = false;
foreach (var attribute in tag.GetAttributes())
{
if (attribute.GetKey() == "class")
{
hasClass = true;
}
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-h1"))
{
return new HRoleSpanTagWorker(tag, context, StandardRoles.H1);
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-h2"))
{
return new HRoleSpanTagWorker(tag, context, StandardRoles.H2);
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-table-div"))
{
return new DivRoleTableTagWorker(tag, context);
}
return base.GetCustomTagWorker(tag, context);
}
}

After working with Jon Reilly on the iText team, this was the final solution that worked for me (column IDs and associated headers aren't needed... just Scope)
public class ThWithScopeTagWorker : ThTagWorker
{
public ThWithScopeTagWorker(IElementNode element, ProcessorContext context) : base(element, context)
{
}
public override void ProcessEnd(IElementNode element, ProcessorContext context)
{
base.ProcessEnd(element, context);
IPropertyContainer elementResult = base.GetElementResult();
if (elementResult is IAccessibleElement)
{
((IAccessibleElement)elementResult).GetAccessibilityProperties().SetRole(StandardRoles.TH);
//Can use this in the future in case we have th elements with different scope than "col"
string htmlScope = element.GetAttribute("scope"); //This is the scope="XXX" in your HTML
AccessibilityProperties properties = ((IAccessibleElement)elementResult).GetAccessibilityProperties();
//Could add "Row" if needed based on htmlScope string above.
//For my purposes, all th elements were scope="col"
properties.AddAttributes(new PdfStructureAttributes("Table").AddEnumAttribute("Scope", "Column"));
}
}
}
and this:
public class AccessibilityTagWorkerFactory : DefaultTagWorkerFactory
{
public override ITagWorker GetCustomTagWorker(IElementNode tag, ProcessorContext context)
{
//...
if (tag.Name() == "th")
{
return new ThWithScopeTagWorker(tag, context);
}
return base.GetCustomTagWorker(tag, context);
}
}

Related

How to include CSS style inside HTML style (code generated via ASP.Net MVC Razor page)

I'm still at my early beginning with ASP.Net MVC. So, I'm not sure if I'm following good practices or not.
I want to generate HTML code based on CSS style
Razor page:
#foreach (p in ...)
{
<td style="#Helper.CustomBackground(p) #Helper.CustomComplexStyle(p)">...</td>
}
CSS classes:
.complexStyleA {
// ...
}
.complexStyleB { }
C# helper method:
// None of the styling method has the "style" attribute, so I can concatenate several custom-styles
public static class Helper
{
// Basic direct styling
public static string CustomBackground(object p) => "{ background: red; }";
// More complex styling based on CSS ones
public static string CustomComplexStyle(object p) => "{ // complexStyleA, complexStyleB, etc. based on p; }"
}
I don't really mind generated all the code inside the HTML tags, but generate a table with several thousand lines and can obviously reduce the file size!
Thanks for any insights :-)
UPDATE
I think that I will do something as follow :
<td #Html.Raw(#Hepers.ConcatenateCSSClasses(#CustomComplexStyle(p),
#OtherCustomComplexStyle(p),
...))>
...
</td>
where #concatenateCSSClasses() removes all " class=" ans #...Syle(p) returns "class=\"...StyleXYZ\"", unless I can have duplicated tag attributes.. Any better ideas?
UPDATE with cleaner methods
public static string HTMLTagAttribute(FieldType fieldType, Field field = null)
{
string output = "";
// Makes distinction between hearder and body
if (field == null)
{
output += HTMLHeaderTagClassAttributes(fieldType);
output += HTMLHeaderTagStyleAttributes(fieldType);
}
else
{
output += HTMLBodyTagClassAttributes(fieldType, field);
output += HTMLBodyTagStyleAttributes(fieldType, field);
}
return output;
}
Body only shown with clear distinction between classes and (inline styles). I don't detail the samll method HTMLWidth(), HTMLBackground(), etc. which are self explanatory.
#region HTML Body Tag Attribute Methods
private static string HTMLBodyTagStyleAttributes(FieldType fieldType, Field field)
{
AttributeValues style = new AttributeValues("style");
style += HTMLWidth(fieldType);
style += HTMLBackground(fieldType, field);
style += HTMLAlign(fieldType);
return style.ToString();
}
private static string HTMLBodyTagClassAttributes(FieldType fieldType, Field field)
{
AttributeValues cls = new AttributeValues("class");
cls += HTMLTopBorder(fieldType, field);
cls += HTMLSideBorder(fieldType);
return cls.ToString();
}
#endregion
Class which helps concatenate class and style attribute values (with no extra space).
/// <summary>Class to help format HMTL class and style attribute values</summary>
class AttributeValues
{
private readonly string _attribute = "";
private readonly List<string> _values = new List<string>();
// TODO - Make distinction bewteen class and styles
#region Constructors
public AttributeValues(string attribute)
{
_attribute = attribute;
}
#endregion
public static AttributeValues operator +(AttributeValues values, string value)
{
if (value != "") values._values.Add(value);
return values;
}
public static bool operator ==(AttributeValues values, string value) => values == value;
public static bool operator !=(AttributeValues values, string value) => values != value;
#region Public Overridden Methods
public override string ToString()
{
string values = "";
foreach (string value in _values)
{
if (values != "") values += " ";
values += value;
}
return values != "" ? $"{ _attribute }=\"{ values }\"" :"" ;
}
public override bool Equals(object obj) => base.Equals(obj);
public override int GetHashCode() => base.GetHashCode();
#endregion
}
And finally in the Razor page (apparently, I have to wrap it in HTML.Raw() because of the " (I agree that it could be refractor within the th, but doesn't make the code easier):
<td Html.Raw(Helpers.HTMLTagAttribute(Enums.FieldType.Account))>..</td>

How to use a if statement in a div using thymeleaf?

If I remove the div tag from the source code below my application runs with no error. But it displays an empty cell (which is correct). I just want to hide this if the cells are empty.
Thymeleaf html
<div th:object="${AppPortModel.Status}" th:if="${AppPortModel.Status} == 'CRITICAL'">
<h3>
MONITORING
</h3>
<table id ="apCritTable">
<thead>
<tr>
<th> Status </th>
<th> HostName </th>
<th> Port Name</th>
<th> Port Listening Count </th>
</tr>
</thead>
<tbody>
<tr th:each="AppPortModel, iterStat : ${showap}" th:if="${AppPortModel.Status == 'CRITICAL'}">
<td th:text ="${AppPortModel.Status}"></td>
<td th:text="${AppPortModel.host}">${AppPortModel.host}</td>
<td th:text="${AppPortModel.portOwner}"></td>
<td th:text="${AppPortModel.count}"></td>
</tr>
</tbody>
</table>
</div>
AppPortModel
public class AppPortModel implements Comparable {
private String Status;
private String host;
private String portName;
private String plCount;
//getters and setters
#Override int compareTo(Object o) {
return //stuff
}
Controller
#Controller
public class IndexController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView getdata() throws IOException {
ModelAndView model = new ModelAndView("index");
model.addObject("showap", apList);
return model;
}
AppPortList
#Component
public class AppPortList {
#Value("#{'$APP_SERVERS_PORT}'.split('#!')}")
private String[] apServerArray;
#Value("#{'${APP_SERVER_MONITORING_LIST}'.split('#!')}")
private String[] appServerPortsList;
#PostConstruct
public List<AppPortModel> getAppPortList() {
final int MYTHREADS = 80;
ExecutorService executor = Executors.newFixedThreadPool(MYTHREADS);
ApplicationPort.resetData();
try {
for (int z = 0; z < apServerArray.length; z++) {
String apServer = apServerArray[z];
String[] portListArray=appServerPortsList[z].split(",");
ApplicationPort apWorker = new ApplicationPort(apServer, portListArray);
executor.execute(apWorker);
}
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException in AppPortList");
}
finally {
executor.shutdown();
while (!executor.isTerminated()) {
}
logger.info("\nFinished all threads in App Port. ");
}
return ApplicationPort.getData();
}
Snippet of Class App
static List<AppPortModel> apData = new ArrayList<AppPortModel>();
public static List<AppPortModel> getData() {
return apData;
}
public static void setData(List<AppPortModel> apData) {
ApplicationPort.apData = apData;
}
public static void resetData(){
apData = new ArrayList<AppPortModel>();
}
public ApplicationPort(String apServer, String[] portListArray) {
this.apServer = apServer;
this.portListArray = portListArray;
}
This table will populate if AppPortModel.Status is CRITICAL. I am trying to hide this table if there is no values in this table. If I just have a regular div tag my code will run but I will have a awkward head and table row heads on my page with empty cells.
Attempt
I tried adding some th logic into my div tag, but I receive an null error.
<div th:object="${AppPortModel.Status}" th:if="${AppPortModel.Status == 'CRITICAL'}">
Attempt 2
<div th:if="${Status} == 'CRITICAL'">
This script would hide my div tag. Even if I have Status = to CRITICAL it would still hide the table.
You can check whether the list is empty using the following condition expression. Assuming the object showap is a List type,
<div th:if="${not #lists.isEmpty(showap)}">
--content--
</div>
Your h3 tag and table goes inside this div.
#lists is a thymeleaf utility class. Refer http://www.thymeleaf.org/apidocs/thymeleaf/2.0.2/org/thymeleaf/expression/Lists.html for more options. You can alternatively use size() method and check for list length.

Extending Entity Framework 6 - adding custom properties to entities in designer

The problem:
I want to be able to extend EF6 in a way that enables me to define Caching policy, Transactional policy, Multi-tenancy , etc on a single entity in an EF edmx file. I am willing doing this with designer.
I've managed to make an extension like suggested in http://www.databinding.net/blog/post/2010/12/02/entity-framework-4-benutzerdefinierte-eigenschaften-mit-dem-adonet-entity-data-model-designer-ext.html
but after installing the vsix the properties are not there. Restarted VS etc but not working. Is something I have to do more in VS 2013?
Has somebody done something similar?
It turns out that EF6 extensions are quiet simple to implement. First you have to follow the MSDN docs for your EF version. Then you have to Make 2 project one for the EF extension and the second is the VS extension project (VSIX).
In the EF extension project add two classes:
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Data.Entity.Design.Extensibility;
namespace SaopEdmxExtenstions
{
[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(IEntityDesignerExtendedProperty))]
[EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)]
public class EfDesignerCustomPropertiesFactory : IEntityDesignerExtendedProperty
{
public object CreateProperty(XElement element, PropertyExtensionContext context)
{
return new EfDesignerCustomProperties(element, context);
}
}
public class EfDesignerCustomProperties
{
internal static readonly string Namespace = "http://saop.si";
internal static XName NsBaseDiagram = XName.Get("CachingType", Namespace);
internal const string Category = "Saop Edmx Extensions";
private readonly XElement _parent;
private readonly PropertyExtensionContext _context;
public EfDesignerCustomProperties(XElement parent, PropertyExtensionContext context)
{
_context = context;
_parent = parent;
}
[DisplayName("Caching Type")]
[Description("Specifies how to handle entity Caching")]
[Category(EfDesignerCustomProperties.Category)]
[DefaultValue(CachingType.None)]
public CachingType CustomCachingType
{
get
{
var propertyValue = CachingType.None;
if (!_parent.HasElements) return propertyValue;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
var bv = element.Value.Trim();
switch (bv)
{
case "None": propertyValue = CachingType.None; break;
case "Application": propertyValue = CachingType.Application; break;
case "Request": propertyValue = CachingType.Request; break;
case "Session": propertyValue = CachingType.Session; break;
}
}
return propertyValue;
}
set
{
CachingType propertyValue = value;
using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set Entity Caching"))
{
if (_parent.HasElements)
{
var updated = false;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
element.SetValue(propertyValue.ToString().Trim());
updated = true;
break;
}
if (!updated)
{
var lastChild = _parent.Elements().Last();
lastChild.AddAfterSelf(new XElement(NsBaseDiagram, propertyValue));
}
}
else
{
_parent.Add(new XElement(NsBaseDiagram, propertyValue.ToString().Trim()));
}
scope.Complete();
}
}
}
[DisplayName("Is Multi-Tenant")]
[Description("Specifies if the entity is multi-tenant")]
[Category(EfDesignerCustomProperties.Category)]
[DefaultValue(false)]
public bool IsMultitTenant
{
get
{
var propertyValue = false;
if (!_parent.HasElements) return propertyValue;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
var bv = element.Value.Trim();
switch (bv.ToLower())
{
case "false": propertyValue = false; break;
case "true": propertyValue = true; break;
}
}
return propertyValue;
}
set
{
var propertyValue = value;
using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set MultiTenancy"))
{
if (_parent.HasElements)
{
var updated = false;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
element.SetValue(propertyValue.ToString().Trim());
updated = true;
break;
}
if (!updated)
{
var lastChild = _parent.Elements().Last();
lastChild.AddAfterSelf(new XElement(NsBaseDiagram, propertyValue));
}
}
else
{
_parent.Add(new XElement(NsBaseDiagram, propertyValue.ToString().Trim()));
}
scope.Complete();
}
}
}
public enum CachingType
{
None,
Request,
Session,
Application
}
}
}
see MSDN why is that way. You can define where to hook by setting different value for [EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)] , again see MSDN.
I've decided to hook on Entity diagram select.
Then In the VSIX project add an Asset of type Microsoft.VisualStudio.MefComponent and select the EF extension project. Build the solution. Go to bin's (debug or release) and copy all files to a new folder. Add a new xml file named [Content_Types].xml with the sample content of:
<?xml version="1.0" encoding="utf-8" ?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="vsixmanifest" ContentType="text/xml" />
<Default Extension="dll" ContentType="application/octet-stream" />
<Default Extension="png" ContentType="application/octet-stream" />
<Default Extension="txt" ContentType="text/plain" />
<Default Extension="pkgdef" ContentType="text/plain" />
</Types>
Then compress all as a zip and rename it so xxxx.vsix. Install it to VS (defined as Install Target in the VSIX project).
Restart VS and go to an edmx of your choice. You will se the results like this

Including static html file from ~/Content into ASP.NET MVC view

I've got master page in my project, which contains some information about site copyright and some contact info in it. I'd like to take it out of master page and place it in a static files (for some reason, these files must be placed in ~/Content folder). Is there a way that I can tell in my view something like
<% Html.Include("~/Content/snippet.html") %> // not a real code
?
You are better off using a partial view (even if it only contains static text) and include it with the Html.Partial helper. But if you insist:
<%= File.ReadAllText(Server.MapPath("~/Content/snippet.html")) %>
If you are in .Net MVC 5 and want to include a HTML file a partial file without having Razor that render it:
#Html.Raw(File.ReadAllText(Server.MapPath("~/Views/Shared/ICanHaz.html")))
Use your own view engine
using
#Html.Partial("_CommonHtmlHead")
as
/Views/Shared/_CommonHtmlHead.html
or
/Views/MyArea/Shared/_CommonHtmlHead.htm
is the best for me.
Creating your own view engine for this by using the System.Web.Mvc.VirtualPathProviderViewEngine ascending class seems to be relatively easy:
/// <summary>
/// Simple render engine to load static HTML files, supposing that view files has the html/htm extension, supporting replacing tilda paths (~/MyRelativePath) in the content
/// </summary>
public class HtmlStaticViewEngine : VirtualPathProviderViewEngine
{
private static readonly ILog _log = LogManager.GetLogger(typeof (HtmlStaticViewEngine));
protected readonly DateTime? AbsoluteTimeout;
protected readonly TimeSpan? SlidingTimeout;
protected readonly CacheItemPriority? Priority;
private readonly bool _useCache;
public HtmlStaticViewEngine(TimeSpan? slidingTimeout = null, DateTime? absoluteTimeout = null, CacheItemPriority? priority = null)
{
_useCache = absoluteTimeout.HasValue || slidingTimeout.HasValue || priority.HasValue;
SlidingTimeout = slidingTimeout;
AbsoluteTimeout = absoluteTimeout;
Priority = priority;
AreaViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.html",
"~/Areas/{2}/Views/{1}/{0}.htm",
"~/Areas/{2}/Views/Shared/{0}.html",
"~/Areas/{2}/Views/Shared/{0}.htm"
};
AreaMasterLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.html",
"~/Areas/{2}/Views/{1}/{0}.htm",
"~/Areas/{2}/Views/Shared/{0}.html",
"~/Areas/{2}/Views/Shared/{0}.htm"
};
AreaPartialViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.html",
"~/Areas/{2}/Views/{1}/{0}.htm",
"~/Areas/{2}/Views/Shared/{0}.html",
"~/Areas/{2}/Views/Shared/{0}.htm"
};
ViewLocationFormats = new[]
{
"~/Views/{1}/{0}.html",
"~/Views/{1}/{0}.htm",
"~/Views/Shared/{0}.html",
"~/Views/Shared/{0}.htm"
};
MasterLocationFormats = new[]
{
"~/Views/{1}/{0}.html",
"~/Views/{1}/{0}.htm",
"~/Views/Shared/{0}.html",
"~/Views/Shared/{0}.htm"
};
PartialViewLocationFormats = new[]
{
"~/Views/{1}/{0}.html",
"~/Views/{1}/{0}.htm",
"~/Views/Shared/{0}.html",
"~/Views/Shared/{0}.htm"
};
FileExtensions = new[]
{
"html",
"htm",
};
}
protected virtual string GetContent(string viewFilePath)
{
string result = null;
if (!string.IsNullOrWhiteSpace(viewFilePath))
{
if (_useCache)
{
result = TryCache(viewFilePath);
}
if (result == null)
{
using (StreamReader streamReader = File.OpenText(viewFilePath))
{
result = streamReader.ReadToEnd();
}
result = ParseContent(result);
if (_useCache)
{
CacheIt(viewFilePath, result);
}
}
}
return result;
}
static readonly Regex TildaRegularExpression = new Regex(#"~/", RegexOptions.Compiled);
/// <summary>
/// Finds all tilda paths in the content and replace it for current path
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
protected virtual string ParseContent(string content)
{
if (String.IsNullOrWhiteSpace(content))
{
return content;
}
string absolutePath = VirtualPathUtility.ToAbsolute("~/");
string result = TildaRegularExpression.Replace(content, absolutePath);
return result;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
HttpContextBase httpContextBase = controllerContext.RequestContext.HttpContext;
string filePath = httpContextBase.Server.MapPath(partialPath);
string content = GetContent(filePath);
return new StaticView(content);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
HttpContextBase httpContextBase = controllerContext.RequestContext.HttpContext;
string result = null;
if (!string.IsNullOrWhiteSpace(masterPath))
{
string filePath = httpContextBase.Server.MapPath(masterPath);
result = GetContent(filePath);
}
string physicalViewPath = httpContextBase.Server.MapPath(viewPath);
result += GetContent(physicalViewPath);
return new StaticView(result);
}
protected virtual string TryCache(string filePath)
{
HttpContext httpContext = HttpContext.Current;
if (httpContext != null && httpContext.Cache != null)
{
string cacheKey = CacheKey(filePath);
return (string)httpContext.Cache[cacheKey];
}
return null;
}
protected virtual bool CacheIt(string filePath, string content)
{
HttpContext httpContext = HttpContext.Current;
if (httpContext != null && httpContext.Cache != null)
{
string cacheKey = CacheKey(filePath);
httpContext.Cache.Add(cacheKey, content, new CacheDependency(filePath), AbsoluteTimeout.GetValueOrDefault(Cache.NoAbsoluteExpiration), SlidingTimeout.GetValueOrDefault(Cache.NoSlidingExpiration), Priority.GetValueOrDefault(CacheItemPriority.AboveNormal), CacheItemRemovedCallback);
return true;
}
return false;
}
protected virtual string CacheKey(string serverPath)
{
return serverPath;
}
protected virtual void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
_log.InfoFormat("CacheItemRemovedCallback(string key='{0}', object value = ..., {1} reason={2})", key, reason.GetType().Name, reason);
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ViewEngines.Engines.Add(new HtmlStaticViewEngine(new TimeSpan(12,0,0,0)));
}
}
public class StaticView : IView
{
private readonly string _text;
public StaticView(string text)
{
_text = text;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
if (! string.IsNullOrEmpty(_text))
{
writer.Write(_text);
}
}
}
NOTE:
This code is tested only with simple usage for rendering
partial views
Is there a reason you are holding the content in an HTML file rather than a partial view?
If you create a file called snippet.ascx in your Views/Shared folder you can import the content of that snippet.ascx file into any view by using <% Html.RenderPartial("snippet"); %>
To include static html file into a MVC View goes like this:
<!-- #include virtual="~\Content\snippet.htm" -->
For ASP .NET Core 3.1 Razor page you can do it like this:
#Html.Raw(System.IO.File.ReadAllText("wwwroot/Content/snippet.html"));
See documentation regarding static files here:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-3.1

What's your most reused class?

Every programmer ends up with a set of utility classes after a while. Some of them are true programming pearls and they are reused in several of your projects. For example, in java:
class Separator {
private String separator;
private boolean called;
public Separator(String aSeparator) {
separator = aSeparator;
called = false;
}
#Override
public String toString() {
if (!called) {
called = true;
return "";
} else {
return separator;
}
}
}
and:
public class JoinHelper {
public static <T> String join(T... elements) {
return joinArray(" ", elements);
}
public static <T> String join(String separator, T... elements) {
return joinArray(separator, elements);
}
private static <T> String joinArray(String sep, T[] elements) {
StringBuilder stringBuilder = new StringBuilder();
Separator separator = new Separator(sep);
for (T element : elements) {
stringBuilder.append(separator).append(element);
}
return stringBuilder.toString();
}
}
What is your most reused class?
System.Object - almost all my types extend it.
A utility class that has logging and email functionality. An extensions class that contains extension methods. A reporting class that basically harness the reporting services web service and makes it easy to stream reports as excel, pdf, etc.
Examples...
1.) Utility Class (static)
public static void LogError(Exception ex)
{
EventLog log = new EventLog();
if (ex != null)
{
log.Source = ConfigurationManager.AppSettings["EventLog"].ToString();
StringBuilder sErrorMessage = new StringBuilder();
if (HttpContext.Current.Request != null && HttpContext.Current.Request.Url != null)
{
sErrorMessage.Append(HttpContext.Current.Request.Url.ToString() + System.Environment.NewLine);
}
sErrorMessage.Append(ex.ToString());
log.WriteEntry(sErrorMessage.ToString(), EventLogEntryType.Error);
}
}
2.) Extensions Class
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
public static short getLastDayOfMonth(short givenMonth, short givenYear)
{
short lastDay = 31;
switch (givenMonth)
{
case 4:
case 6:
case 9:
case 11:
lastDay = 30;
break;
case 2:
if ((int)givenYear % 4 == 0)
{
lastDay = 29;
}
else
{
lastDay = 28;
}
break;
}
return lastDay;
}
Most reused but boring:
public static void handleException(Exception e) throws RuntimeException {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e); //NOPMD
}
Less boring (also methods for building lists and sets):
/**
* Builds a Map that is based on the Bean List.
*
* #param items Bean List items
* #param keyField Bean Field that will be key of Map elements (not null)
* #return a Map that is based on the Bean List
*/
#SuppressWarnings("unchecked")
public static <T, K> Map<K, T> buildMapFromCollection(final Collection<T> items,
boolean linkedMap,
final String keyField,
final Class<K> keyType) {
if (items == null) {
return Collections.emptyMap();
}
if (keyField == null) {
throw new IllegalArgumentException("KeyField is null");
}
final Map<K, T> result;
if (linkedMap) {
result = new LinkedHashMap<K, T>();
} else {
result = new HashMap<K, T>();
}
BeanMapper mapper = null;
for (final T item : items) {
if (mapper == null) {
mapper = new BeanMapper(item.getClass());
}
final K key = (K) mapper.getFieldValue(item, keyField);
result.put(key, item);
}
return result;
}
Logger class: Which logs the flow of control in a log file.
Configuration Reader/Setter: which reads the configuration from ini/xml file and sets the environment of the application
Most reused? Hmmm...
boost::shared_ptr<> with boost::weak_ptr<>
probably most reused (also probably most bang-for-buck ratio)
Globals
Just a simple class with static DBConnString, and a few other app wide settings.
Have reused the simple file in about 2 dozen projects since working with .Net
A ConcurrentDictionary I wrote, which I now seem to use everywhere (I write lots of parallel programs)