We have developed a portal using the code from the customer portal. But we haven't used the customer portal solution on the CRM server. Everything is working fine except caching prevents updates to show on the portal.
In CRM 4 I used this solution http://pogo69.wordpress.com/2010/11/05/caching-revisited-crm-4-0-sdk-advanced-developer-extensions/. But this doesn't work in CRM 2011 because Microsoft.Xrm.Client.Caching is different. How do I clear the cache for 2011?
Any help or thoughts would be greatly appreciated.
Thanks!
Make sure that you have a cache invalidation URL configured. The PRM portal has a service that invalidates the cache with any update/create event (managed by a plugin registered on all entities).
Go To Settings -> Web Notification URL, either update the URL to the Cache.axd file or create a new entry.
If this is all in place, I would make sure that the plugin that takes care of calling the cache invalidation is working/registered as it should be.
Hope that helps
See the following article for more details on the Web Notification URL.
Set Up Cache Invalidation to Refresh Changes on the Website
I found that removing the preloadcache parameter & value from URLs bypasses CRM's cache. Not ideal but it worked. Your mileage may vary...
<script type="text/javascript">
bypassCrmPreloadCache(); // DE_WR_15706 Bypass 30sec cache to get latest version #
function bypassCrmPreloadCache() {
var ParentURL = window.parent.location.href;
var nStartPreloadcache = ParentURL.indexOf("preloadcache");
if (nStartPreloadcache > 0) {
// Parent URL is cached
var nEnd = ParentURL.indexOf("&", nStartPreloadcache);
if (nEnd == -1) { // Special case: no ampersand => preloadcache is last argument.
nEnd = ParentURL.length; // End of URL is end of preloadcache's value.
}
var strPreloadCacheParamAndValue = ParentURL.substr(nStartPreloadcache, 1 + nEnd - nStartPreloadcache);
// Remove preloadcache-parameter from URL
ParentURL = ParentURL.replace(strPreloadCacheParamAndValue, "");
if (ParentURL.charAt(ParentURL.length-1) == '&')
ParentURL = ParentURL.slice(0, -1); // Ensure URL not terminated by an unnecessary '&'.
// Load URL in parent Window, bypassing the cache
window.open(ParentURL, "_parent");
}
}
</script>
Related
I have a C# web application that uses a component (Progress Telerik Sitefinity CMS) that takes a long time (2 minutes) to initialize. A user who visits the site while in this stage, will be redirected to a page that polls the state every second, until initialization is complete. (This is built-in Sitefinity behavior).
I'm hosting my application within an Azure App Service. If I increase the number of instances (scale up), some of my users end up on the new node while it's still initializing. Problem is, due to the affinity cookie Azure adds, they stay on this node.
I want affinity, except when the site is initializing. In that case, I want to drop the cookie and poll. In that case I get assigned a random node, so an initialzed node is found within seconds.
Question is: how do I achieve this? Much of what happens is handled within Sitefinity, so I resorted to altering content in my global.asax. It doesn't work. I tried to put this in my global.asax.cs:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
var path = HttpContext.Current.Request.Url.AbsolutePath;
// "/sitefinity/status" is the page the client is redirected to
// "/appstatus" is used to poll initialization status
if (path == "/appstatus" || path == "/sitefinity/status")
{
// "ARRAffinity" is the Azure affinity cookie
Response.Cookies.Remove("ARRAffinity");
// just removing the cookie didn't work so i tried to override it
Response.Cookies.Add(new HttpCookie("ARRAffinity", "-") { HttpOnly = true });
// reportedly, this suppresses cookie adding by Azure
Response.Headers.Add("ARR-Disable-Session-Affinity", "true");
};
}
How do I force my client to a different node?
EDIT
I think I found (part of) the problem here.
First, "/" is requested. This returns a 302 redirect, but also the ARRAffinity cookie.
Then, "/sitefinity/status" is requested. Both the ARR-Disable-Session-Affinity and the cookie are stripped. This means, the cookie is not cleared on the client.
While polling, the client already has the cookie. So the user is never redirected to another node.
So that's might be the problem. Now to solve it...
EDIT
I followed Vesselin Vassilevs suggestion and added this to my sites configuration file:
<appSettings>
<add key="sf:AppStatusPageResponseCode" value="503" />
</appSettings>
But because I still incidentally reach the initializing node, I also suppressed the affinity cookie , by altering my global.asax.cs:
protected void Application_EndRequest(object sender, EventArgs e)
{
var httpCode = Response.StatusCode;
var isRedirectBackFromStatusPage = httpCode == 302 && Request.Url.AbsolutePath == "/sitefinity/status";
var isFinalSitefinityStatusPoll = httpCode == 404 && Request.Url.AbsolutePath == "/appstatus";
if (isRedirectBackFromStatusPage || isFinalSitefinityStatusPoll)
{
var cookie = Request.Cookies["ARRAffinity"];
if (cookie != null) Response.Cookies.Add(cookie);
return;
}
if (httpCode != 200 || !Response.ContentType.StartsWith("text/html"))
{
Response.Headers.Add("ARR-Disable-Session-Affinity", "true");
};
}
Why not disable the arr affinity cookie altogether?
Sitefinity backend works fine with no arr cookie and with multiple instances.
EDIT: We need to tell Azure that the site is not yet ready during the Sitefinity initialization. The problem with that is, that the appStatus page (shown by Sitefinity during init) returns status code 302 and even 200, which makes Azure to believe the site is running ok. I've wrote about this here: https://sitefinitydevelopment.com/blog/sitefinity's-application-status-page-can-cause-you-big-problems.html
Depending on your Sitefinity version, you can either implement the custom solution there (manually returning http code 503 during system restart) or set the following setting in the web.config (Sitefinity 9+)
<add key="sf:AppStatusPageResponseCode" value="503" />
We have a Single Page App (SPA) that uses Azure Active Directory "Easy Auth", e.g., the code-less solution. This seems to work ok when users first open the the application. They are redirected to the Microsoft login page and they can authenticate and then access the application.
Then, because its an SPA, users will navigate around and only fire Ajax requests. The problems come approximately 24 hours later when the session cookie expires. Users likely still have the same browser tab open and do not perform a full page refresh. Then they may be working on a record and at some point their next Ajax PUT request fails with a Redirect HTTP status and they loose their work.
So they key question is:
How can we make SPA Ajax requests extend a current user's session so that their session will not expire when they are actively using the application?
It seems like the Azure AD Easy Auth service does not "honor" activity on the part of the user, which leads us to believe that the session cookie never gets updated.
Note: We've recently done some testing with the /.auth/refresh endpoint and this does not solve the problem either.
There are several ways you can possibly solve this. Here are a few that I can think of:
Use local storage: The problem you mentioned is that user's lose their work due to the redirects. The problem of losing work can be solved if you persist the in-progress state in local storage so that it's available when they are redirected back to the page.
Switch to using tokens: The /.auth/refresh endpoint doesn't refresh the AppServiceAuthSession when using AAD because AAD doesn't support refreshing the user information. What you can do instead is authenticate with your backend using the x-zumo-auth tokens. The /.auth/refresh endpoint will correctly refresh these tokens. If you're explicitly logging in users using /.auth/login/aad, then you can add the session_mode=token as a query string parameter. This is done for you if you use the Mobile Apps JavaScript SDK. If login is automatic, then you'll need to add session_mode=token in the additionalLoginParams setting of your auth config. You can then parse the authentication token from the #token fragment which is added to the URL after the login completes.
Use hidden iframes: I haven't tried this myself, but if you can get it working it might require the least amount of code change. The idea is that you use a hidden iframe to re-login the user periodically when you detect they are active. The iframe would need to point to something like ./auth/login/aad?prompt=none&domain_hint={userdomain.com} where {userdomain.com} is the last part of the user's email address - e.g. contoso.com. These parameters get passed to the AAD login page, and the login should complete automatically without any user interaction. Test it manually a few times in a browser window to make sure it works correctly. The result should be an updated auth cookie with a fresh expiration.
Let me know in the comments if you have any questions or issues with any of these options.
Expanding on Chris Gillum's answer with implementation example:
Scenario: Single Page Application (SPA) with Progressive Web App (PWA) capabilities, hosted in Azure Web App. Added authentication using Azure Web Authentication/EasyAuth.
Ran into similar/same issue: Initial loads of the SPA worked fine, but after period of hour(s) (token expires) the app "breaks" - in SPA on iOS tablet that manifested for me with endless whitescreen and seemingly no practical fix (force killing did NOT resolve). Error messages thrown ranged from 401 (understandable) to service-worker refusing to process scripts/handle 302 redirects/etc (less obvious where problem may be).
SPA + Azure Web Authentication/EasyAuth tweaks:
If using MDM, disable "Block Safari navigation menu bar" feature in the MDM for this app. This appears to allow the app to work as expected after force kill (it would reload the page, see expired token, redirect to login and then back to the app). I'm not sure if this behavior is controllable in manifest.json, may be iOS specific capability.
Hidden iframe refreshing of token + Timer/check token periodically (in ajax calls, etc):
Note: As of ~2021-04, Chromium based browser worked with hidden iframe method. For other browsers the AAD page would experience errors and fail - current solution suggested would be storing app state -> navigate to AAD login page with redirect param -> User logs in and redirected back to the app -> App state restored w/ refreshed token.
refreshAuthToken() {
//Chrome based browsers work with silent iFrame based token reAuth
if (this.browserChromium()) {
let domainHint = "contoso.com"; //Domain of your organization users (e.g. me#contoso.com)
//Remove existing iframe (if exists), to minimize history/back button entries
let existingFrame = document.getElementById("authIFrame");
if (existingFrame) {
existingFrame.remove();
}
//Inject iFrame that will call endpoint to refresh token/cookie
console.log("Refreshing auth token (quietly)...");
let iframe = document.createElement("iframe");
iframe.id = "authIFrame";
iframe.style =
"width: 0; height: 0; border: 0; border: none; position: absolute; visibility: hidden;";
iframe.src = `/.auth/login/aad?prompt=none&domain_hint=${domainHint}`;
document.body.appendChild(iframe);
new Promise(r => setTimeout(r, 2000)).finally(() => resolve()); //Hacky method of "waiting" for iframe to finish
} else {
console.log("Refreshing auth token (via page reload)...");
window.location.replace("/.auth/login/aad?post_login_redirect_url=/?restoreData=true");
}
},
//
// Timer example:
//
setInterval(() => {this.refreshAuthToken()}, 1000 * 60 * 5); //Fire every 5 minutes
//
// And/or periodically call this function maintain token freshness
//
checkAuthToken() {
//this.authEnd = JWT from /.auth/me "exp" claim
let now = new Date() / 1000;
let expirationWindow = this.authEnd - 600; // Consider token expiring if 10 minutes or less remaining
if (now >= expirationWindow) {
console.log("Auth Token expired - Refreshing...")
this.refreshAuthToken();
} else {
// console.log("Auth token still healthy.");
}
}
Nicety: Enable anonymous access to PWA icons (if possible). iOS requires icons be publicly accessible when saving PWA to homescreen, otherwise uses screenshot of app rather than formal icon: https://stackoverflow.com/a/67116374/7650275
I suspect this is a total newbie question, but I seem to be missing the basics here. I am NOT new to coding and have a lifetime of experience (27 years) with various languages, but the plugin process is eluding me.
I have developed custom bookmarking system in php & js, it works great and I've been using it for months as I develop it.
I simply want to get the url of the page in the active tab and pass it to my php handler. I want my web site script return the html form into the popup. I can think of a thousand ways that "should" work.
ALL the code examples I am finding seem to over-complicate what should be a simple task.
In short I just want:
<script type="text/javascript">
<!--
var loadurl = "http://my.site.com?theUrl=" + window.location;
location.href = loadurl;
//-->
</script>
And have that page show in the popup. So far I'm at a loss. Even tried ajax calls etc.
Can somebody clue me in on how to achieve this simple task? Maybe I can get started writing extensions with the info.
For the record, most of the examples I have found are deprecated under manifest 2.0
Manifest 2.0 introduces a new feature contentSecurityPolicy. All external resources are blocked by default. For the best practice, you should include all needed asset files in the extension. The communication between your extension and your service (php side) is only data using XHR2.
So, In order to make bookmark extension work, I guess you need to something like this:
Add your service's domain to permissions array
{
...
permissions: ['*://my.site.com/*', 'tabs']
}
Move all javascript from popup.html to popup.js. In popup.js, You create a ajax request to your bookmarking service. More document here
function addBookmark(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://my.site.com/new_bookmark.php?url=" + encodeURIComponent(url), true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var resp = xhr.responseText;
// handle service result here
}
}
xhr.send();
}
chrome.tabs.getSelected(null,function(tab) {
addBookmark(tab.url);
});
I have a SharePoint 2010 MySites set up on its own web application. There is the standard site collection at the base level, http://site:80/.
The personal sites for each user is at the managed URL /personal/.
I have a working event handler which add items to the Newsfeed when a user adds something to a picture library.
THE PROBLEM:
The problem is, this only works if they add to a picture library at the base site collection, http://site:80/, and does NOT work if they add to http://site:80/personal/last first/.
Does anyone know why? The event handler feature is site scoped and my understanding is that it should work on all subsites.
The problem is that personal sites are not subsites of My Site host. In fact each user's personal site is a site collection on its own. So basically you need to register your event receiver not only for My SIte host, but also for each user's personal site.
Ok. Because you can only 'staple' features to site definitions which will be provisioned in the future, you need a way to activate new features on existing sites.
So, the fix I discovered and used follows:
The default page for the newsfeed is http://site:80/default.aspx. If you create an event receiver and scope it for 'site' and deploy it globally or to that web application, then it will work on the base site collection. Each personal site is a site collection and has the feature but it needs to be activated on each personal site collection.
So, in the default.aspx page, you place the following which will activate the feature if it has not yet been activated.
<script runat="server" type="text/c#">
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
String sAccount = (((SPWeb)((SPSite)SPContext.Current.Site).OpenWeb()).CurrentUser.LoginName).Split('\\')[1];
String basePersonalURL = "http://site:80/personal/";
String eventReceiverFeatureId = "12345678-1234-1234-1234-1234567890ab";
using(SPSite site = new SPSite(basePersonalURL + sAccount)) {
site.AllowUnsafeUpdates = true;
using(SPWeb web = site.RootWeb) {
web.AllowUnsafeUpdates = true;
try { site.Features.Add(new Guid(eventReceiverFeatureId)); } catch {}
web.AllowUnsafeUpdates = false;
}
site.AllowUnsafeUpdates = false;
}
}
</script>
You also need to edit the web.config file in order to allow inline code to run for this page. Hope this helps.
Our ASP.NET application pages are deployed as a feature into a MOSS 2007 farm.
When a user logs on to the site the user is directed to a default page.
In the top right corner there is an option "Log on as a different user".
If the user selects this option and enters the credentials of another user, an "Access Denied" message is displayed.
This message does not make sense since all the users have access to this default page. Furthermore, if the user now goes to the address bar and manually changes the URL to the default page, the page loads. I can see in the source part of the "Access Denied" URL that the encoded URL of the target page is the expected default page URL.
Previously, I made a change to the INIT.JS file to redirect the user in the case where the login change is done from a page that is not the default page.
function LoginAsAnother(url, bUseSource)
{
document.cookie="loginAsDifferentAttemptCount=0";
if (bUseSource=="1")
{
GoToPage(url);
}
else
{
var ch=url.indexOf("?") >=0 ? "&" : "?";
//url+=ch+"Source="+escapeProperly(window.location.href);
url+=ch+"Source="+escapeProperly(getSspLocation(window.location.href));
STSNavigate(url);
}
}
The original line is commented out.
The function getSspLocation is just a function I wrote to get the default page URL from any other URL.
function getSspLocation(url) {
var parts = url.split('/');
var result = "";
for (var i = 0; i < parts.length; ++i) {
result += parts[i] + "/";
if (parts[i].toLowerCase() == "ssp")
return result + "default.aspx";
}
return url;
}
Any ideas?
This will not answer your question, but I would advice you against modifying the init.js file. It is both against good practice and against Microsoft's recommendation, you will lose support from them. Furthermore, it will most certainly be overwritten when applying service packs and/or hot fixes.
If you have the need to customize the functionality of OOB script files, do so by deploying a custom master page that incorporates your custom scripts instead.