Refer to this post for a description of the feature.
My HTML cache is enabled, and I have turned on "Clear on Index Update" for one of my Renderings.
However, the entry for my Rendering is NOT marked with any special token in the cache - I verified the cache content before and after using the rendering. This, of course, leads to the Clear() method in Sitecore.ContentSearch.Maintenance.IndexDependentHtmlCacheManager to not pick it up, making this feature useless.
I am on Sitecore 7.2 - is this a known bug?
You can override GenerateCacheKey processor (or add your own processor after that one) and update the key to include _#index.
Code below is for MVC. As #jammykam says, in WebForms key is added to cacheKey OOTB.
public class GenerateCustomCacheKey : GenerateCacheKey
{
protected override string GenerateKey(Rendering rendering, RenderRenderingArgs args)
{
Item renderingItem = rendering.RenderingItem.InnerItem;
var key = base.GenerateKey(rendering, args);
if (renderingItem["ClearOnIndexUpdate"].ToBool())
key += "_#index";
return key;
}
}
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<mvc.renderRendering>
<processor
type="My.Assembly.Namespace.GenerateCustomCacheKey, My.Assembly"
patch:instead="*[#type='Sitecore.Mvc.Pipelines.Response.RenderRendering.GenerateCacheKey, Sitecore.Mvc']" />
</mvc.renderRendering>
</pipelines>
</sitecore>
</configuration>
Ok, it turns out that this features is currently bugged on MVC, at least as of 16th of March 2015:
http://www.tarasalenin.com/caching-sitecore-mvc-renderings/
That's about it. There are workarounds, but in the meanwhile I sorted my problem in a different way. Posting this in case someone else happens to have the same problem.
Related
I am attempting to add primefaces to an existing Spring/JSF (myfaces) project. I have the actual timeline component working (or at least loading with the correct data), but the event listeners don't seem to work correctly. I have tried a number of configurations but can't seem to resolve this issue.
<p:timeline id="timeline1"
value="#{timelineView.getModel(searchFacade.dataModel)}"
editable="true" eventMargin="10" eventMarginAxis="0"
start="#{timelineView.start}"
end="#{timelineView.end}"
showNavigation="true" showButtonNew="true"
axisOnTop="true" stackEvents="false"
oncomplete="styleEvents();">
<p:ajax event="changed" listener="#{timelineView.onEdit()}" oncomplete="restyleTimeline()" />
</p:timeline>
In the above code the changed event is triggered and there is an AJAX call made. In the network pane of the browser I can see that the AJAX call is always to the URL of the current page (so like /mypage.jsf?conversationCode=a - I'm also using Apache Orchestra obviously)
The actual form data being sent for the changed event looks like this:
javax.faces.partial.ajax:true
javax.faces.source:timeline1
javax.faces.partial.execute:timeline1
javax.faces.behavior.event:changed
javax.faces.partial.event:changed
timeline1_eventIdx:0
timeline1_startDate:1498881600000
timeline1_endDate:1510894800000
timeline1_group:<div class='timeline-group'><div class='timeline-row timeline-row-group'>Group One/div><div class='timeline-row timeline-row-sub'>Group One subtitle</div><div class='timeline-row timeline-row-details'>Group One details</div></div>
mainForm:j_id_mt_SUBMIT:1
javax.faces.ViewState: Big hash as usual
The bizarrely long group name is because I am formatting the output group names into a more detailed title for the row btw.
The bean in the back (imports and package omitted):
public class TimelineView<T> implements Serializable {
private static final long serialVersionUID = 1L;
TimelineView<T> timelineView;
public TimelineModel getModel() {
return timelineView.getModel();
}
public TimelineModel getModel(ListDataModel<? extends Row<T>> results) {
return timelineView.getModel(results);
}
public void setTimelineView(TimelineView<T> timelineView) {
this.timelineView = timelineView;
}
public Date getStart() {
return timelineView.getStart();
}
public Date getEnd() {
return timelineView.getEnd();
}
public void onEdit(TimelineModificationEvent e) {
timelineView.onEdit(e);
}
public void onSelect(TimelineSelectEvent e) {
timelineView.onSelect(e);
}
}
The functionality and code inside these methods doesn't matter because when I set a breakpoint I can see that the onEdit method is never called and neither was the onSelect method when I had a select event in the timeline.
Since I have Spring handling all beans, my configuration was just this, located in the ApplicationContext.xml file:
<bean id="timelineView" class="com.mycompany.projectone.view.timeline.TimelineView" />
I have tried adding a form around the timeline, which made no difference, and have also tried adding process=":form_id", which caused the error "Cannot find component for expression ":form_id"".
As an alternative that would also suit my needs, does anyone know how to send primefaces events to a javascript function to be handled? For example, if the user moves the start and end points of the item in the timeline I would like to update the displayed start and end dates.
I would also like to change or intercept the delete behavior and modify what occurs when the delete link is clicked.
Any help would be hugely appreciated. Thanks.
EDIT
The suggested potential duplicate does not address the symptoms nor the actual solution - see answer below.
Okay, so the answer turns out to be that the Primfaces timeline component must have a widgetVar set in order for the p:ajax and pe:javascript calls to work. Incredibly simple solution but so infuriatingly difficult to find.
I was using "NLog.Extensions.Logging" for logging and need to log user identity and found that it is possible with "NLog.Web.AspNetCore". "nlog.config" file is configured to log the "aspnet-user-identity". However, when I look at logs, user identity part is always empty string, the other columns are look pretty good. Am I missing something?
A part of my configuration file is here:
<extensions>
<assembly="NLog.Web.AspNetCore" />
</extensions>
<parameter name="#identity" layout="${aspnet-user-identity}"/>
<logger name="*" minlevel="Trace" appendTo="database"/>
And an insert command insert a log to db with "#identity" parameter, but it is always empty like I said.
Accepted answer didn't worked for my case (using JwtSecurityToken on ASP.NET Core 3.1),
Later realized that I just forgot to add the ClaimTypes.Name on my JwtSecurityToken Claims.
Now its working and no need to register IHttpContextAccessor.
I think I have found the issue,
There was an breaking change, The IHttpContextAccessor service is not registered by default anymore. (See announcement)
So add in your startup.cs:
public void ConfigureServices(IServiceCollection Services)
{
//call this in case you need aspnet-user-authtype/aspnet-user-identity
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
I found this question while searching for "asp.net core nlog username". Eventually I found the solution and I am logging it here for posterity.
Problem
How do I include the currently logged in user's username in my NLog log messages?
Solution
I am using NLog inside of my ASP.NET Core web app. I tried using the ${aspnet-user-identity} layout renderer, but determined that the value was empty because we were using custom authentication.
After doing some digging, I figured out that I could access values stored as a ASP.NET Session variable from NLog. So, after I authenticated the user, I stuffed their username into a session variable and voila!
Here is the code I call after authenticating the user:
// Store username in session so we can access it from NLog logs.
httpContext.Session.SetString("NlogUser", contact.Username);
Here is what the layout renderer line in the nlog.config looks like
<parameter name="#identity" layout="${aspnet-session:variable=NlogUser}"/>
Here is the corresponding NLog documenation for the AspNetSession layout renderer.
If you recently upgraded ASP.NET Core you may have to configure NLog differently in program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.CaptureStartupErrors(true)
.UseIISIntegration()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
//logging.AddNLog(); //<--- Can remove (NLog.Extensions.Logging)
})
;
})
.UseNLog(); // <--- Call UseNLog off of IHostBuilder (NLog.Web)
}
see https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3
I am using XML Documentation for my ASP.NET Web API Help Page as shown here.
I would like to know if there is a way to include html in the comments such that it will be rendered on the web page, instead of it being removed/ignored/escaped.
Specifically, I am looking for a way to create a newline, but being able to create bulleted lists, etc. would be great!
Ex/ I would like to be able to do something like this:
/// <summary>
/// CRUD operations for SalesDocument<br/>
/// This is a new line
/// </summary>
[RoutePrefix("api/SalesDocument")]
public partial class SalesDocumentController : ApiController
And have it show on the help page like this:
CRUD operations for SalesDocument
This is a new line.
Instead of this: (in this case, <br/> gets removed somehow - if I try using <p> tags, they are just escaped)
CRUD operations for SalesDocument This is a new line.
*I have already tried the <para> tag as suggested by multiple posts for tooltips, but this does not work on my help page.
Any suggestions are greatly appreciated!
In the installed XmlDocumentationProvider.cs file at Areas\HelpPage, you can look for a method called GetTagValue. Here modify the return value from node.Value.Trim() to node.InnerXml.
private static string GetTagValue(XPathNavigator parentNode, string tagName)
{
if (parentNode != null)
{
XPathNavigator node = parentNode.SelectSingleNode(tagName);
if (node != null)
{
return node.InnerXml;
}
}
return null;
}
Now open the installed file Areas\HelpPage\Views\Help\DisplayTemplates\ApiGroup.cshtml and modify the following line from:
<p>#controllerDocumentation</p>
to
<p>#Html.Raw(controllerDocumentation)</p>
#controllerDocumentation does not work for me, but changing the line to#api.Documentation works. i.e. #html.raw(api.Documentation).
I am using a SelectMethod for GridView, I use ModelBnding. The simplified view:
<asp:GridView AllowPaging="True"
AllowSorting="True"
runat="server"
PageSize="10"
SelectMethod="GetRedirections"
ID="rediretionGrid" AutoGenerateColumns="False"
ItemType="TTC.TT.Entities.ViewModels.Redirections.RedirectionView"
DataKeyNames="Id"
>
...
</asp:GridView>
The code behind:
public IQueryable<RedirectionView> GetRedirections2([Control] string filter)
{
var mappings = RedirectionService.GetRedirections().Where(x => x.Source.Contains(filter)).AsQueryable();
// Setting PageIndex to 0 doesn't solve the problem.
rediretionGrid.PageIndex = 0;
return mappings;
}
When filter is set to something popular multiple results are returned (for example 2000) user is able to go to different pagination page (for example he will go to page 30) unfortunately when he changes the filter to something not popular and not as many results are returned (for example 3 results) DataGrid will display information that 'No items were found' because DataGrid preserves the information that the user was on a far away page (30) and he is trying to display that page with a respect to new filtering. Everything is working if a user is on a first page. What is a work around? How can I either send a user to a first page (or any other page that will display results, typically it is last page) when a filter is changed, or do something to avoid a situation when DataGrid is not showing anything even when multiple records were found.
My experiments with PageIndex to 0 don't solve the problem (they introduce more issues, but I just want to mention it, in case anyone suggests this answer). When I set PageIndex to 0, still DataGrid is not showing any result.
To make it clear, I know how can I resolve this issue, but the implementation is long, and I can't believe that M$ could potentially not see this happening. Because if I am forced to implement additional event listeners and add a custom logic to handle this situation for me using ModelBinding with SelectMethod is pointless, it is not worth it, because writing this custom logic takes more time, then not using SelectMethod and ModelBinding.
Update:
Simple experiment with return type for SelectMethod showed that when I change a definition of my GetRedirection2 method to:
public IEnumerable<RedirectionView> GetRedirections2(int maximumRows, int startRowIndex, out int totalRowCount, string sortByExpression, [Control] string filter)
And I do pagination and sorting on my own, and I try to set PageIndex to 0 like:
if (startRowIndex > totalRowCount)
{
rediretionGrid.PageIndex = 0;
}
Setting it now doesn't resolves the issue because it is already too late to do it. I believe that DataGrid still stores it's startRowIndex and uses it to handle data after it was returned. And at the very end it uses PageIndex and pretends to display page 0, when really it displayed different page. So the thing I would like to have is a method to change startRowIndex like having a parameter marked as out just like totalRowCount is handled.
The simplest and quickest to implement way I was able to come up, but also a terrible way in terms of what I would like to see is to do a full page reload. So, I returned to my original GetRedirections2 method definition. Code below is simplified for visibility reasons:
public IQueryable<RedirectionView> GetRedirections2([Control] string filter)
{
var mappings = RedirectionService.GetRedirections().Where(x => x.Source.Contains(filter));
// This is a terrible hack to redirect user to a first page.
if (rediretionGrid.PageIndex * rediretionGrid.PageSize > mappings.Count)
{
Response.Redirect(Paths.Redirections.Index_aspx + "?filterValue=" + filter);
}
return mappings.AsQueryable();
}
And in Page_Load:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack) return;
var filterValue = Request.QueryString["filterValue"];
filter.Text = filterValue;
}
I wish there was a proper way of doing it, w/o a need of implementing hacks like this :(
I have a combined authorization and menustructure system on our backend.
For performance reasons EntLib caching is used in the frontend client (MVC rel 1.0 website, IIS 5.1 local, IIS 6.0 server, no cluster).
Sometimes 'Cache.Contains' will return true, but the contents of the cache is NULL. I know for certain that I filled it correctly, so what can be the problem here?
EDIT: when I set the cache to 1 minute and add the cacheKey 'A_Key', I will see the key coming back when inspecting the CurrentCacheState. When I view CurrentCacheState after 2 minutes, the key is still there. When I execute 'contains', true is returned. When I execute 'contains' again, the cacheKey is gone!
Synchronization problem??
Regards,
Michel
Excerpt:
if (IntranetCaching.Cache.Contains(cacheKey))
{
menuItems = (List<BoMenuItem>)IntranetCaching.Cache[cacheKey];
}
else
{
using (AuthorizationServiceProxyHelper authorizationServiceProxyHelper = new AuthorizationServiceProxyHelper())
{
menuItems = authorizationServiceProxyHelper.Proxy.SelectMenuByUserAndApplication(APPNAME, userName, AuthorizationType.ENUM_LOGIN);
IntranetCaching.Add(cacheKey, menuItems);
}
}
And the cachehelper:
public static class IntranetCaching
{
public static ICacheManager Cache { get; private set; }
static IntranetCaching()
{
Cache = CacheFactory.GetCacheManager();
}
public static void Add(string key, object value)
{
Cache.Add(
key
, value
, CacheItemPriority.Normal
, null
, new Microsoft.Practices.EnterpriseLibrary.Caching.Expirations.AbsoluteTime(TimeSpan.FromMinutes(10)));
}
}
Thanks Michael for following up your own issue, I have been stuck with this all day identifying that if I try and retrieve an item from Cache this is due to expire (+ 0 up to 25 seconds) I will get a NULL value. Codeplex have posted a workaround (of sorts)as it is in their FAQ:
a. How do I avoid getting a null value from the CacheManager when the item is being refreshed? - Intermittently, you may encounter this situation. To work around this, create your own implementation of an ICacheItemExpiration. In the HasExpired()method, implement a logic that will check whether the item has already expired and will update the item's value if it has expired. This method should always return false for the item to not be tagged as expired. As a result of returning false in the HasExpired() method, the item will not be refreshed and will contain the updated value as implemented in the method.
REF: link
I got the following response from Avanade (creators of Entlib):
Most likely, the BackgroundScheduler
hasn't performed its sweeping yet. If
you're going to examine the source
code, the Contains method only checks
if the specific key is present in the
inmemory cache hashtable while on the
GetData method, the code first checks
if the item has expired, if it has,
the item is removed from the cache.
Sarah Urmeneta Global Technology &
Solutions Avanade, Inc.
entlib.support#avanade.com
That solution is working for me.
Still the question remains why you can use 'Contains' when its outcome cannot be used in a sensible way.
Regards,
M.