Mark of the Web on Xpages - javascript

So I need to send an attachment to a document, but I have to validate if it is larger than 15mb , for so I am using this code in javascript to get the file :
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
var filePath = document.getElementById(fileid).value;
var objFile = objFSO.getFile(filePath);
var fileSize = objFile.size; //size in kb
I'm having an error when I try to create ActiveXObject because my site is not "trusted " by not having a Mark of the Web
<!doctype html>
<!-- saved from url=(0023)http://www.contoso.com/ -->
<html>
<head>
<title>A Mark of the Web Example.</title>
</head>
<body>
<p>Hello, World</p>
</body>
</html>
so I wonder if it is possible to have a mark of the web in a XPage and how I could put it the body of the XPage.
My client does not want to manually place the security option , but want to use IE , please help me haha.
If there is another way to check the file size when selecting a file using javascript would be interesting.

Try this code to check file size in HTML5 should work in all modern browsers
var fileSize=0
if (typeof FileReader !== "undefined") {
var filePath = document.getElementById(fileid);
fileSize= filePath.files[0].size;
}
Check the filesize var for the max limit of you file.
Use this code if the browser is IE10 or newer and your old code if the browser is older.

You can create a Java validator for old browsers, but if the Javascript API is available (modern browsers), use it.
public class Attachment implements Validator {
private final static long BYTES_IN_1_MB = 1048576;
private final static long MAX_MB_ALLOWED = 10;
private final static String MSG_ERROR_SIZE = "File size cannot be bigger than {0} MBs";
public void validate(FacesContext fc, UIComponent uiComp, Object attach)
throws ValidatorException {
FacesMessage msg;
UploadedFile upFile = (UploadedFile) attach;
long max_bytes = BYTES_IN_1_MB * MAX_MB_ALLOWED;
// SIZE:
if (upFile.getContentLength() > max_bytes) {
String msgError = MSG_ERROR_SIZE.replace("{0}", String.valueOf(MAX_MB_ALLOWED));
msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msgError, msgError);
throw new ValidatorException(msg);
}
}
}
These validators need to be added into the faces-config.xml
<validator>
<validator-id>attachmentValidator</validator-id>
<validator-class>com.faces.validator.Attachment</validator-class>
</validator>
Then you can add the validator into the fileUpload field:
<xp:this.validators>
<!-- Validator for Attachments -->
<xp:validator validatorId="attachmentValidator">
</xp:validator>
</xp:this.validators>

Related

Getting text from file using FileReader on Load

So, I've been working on a page that uses only local files (server is not an option, unfortunately. Not even a localhost. The struggle is real.) and I've come to a situation where I need to grab text from a .csv file and populate it to the page. I have this bit of code that works, but I need to have a file set within the function when a button is pressed. Looking up the file manually isn't an option (to visualize what I'm doing, I'm making a mock database file in the most annoying way possible (because I have to, not because I want to)).
In the page I would have something like:
<button id="myButton" onclick="getText()"></button>
<script>
var myFile = "dataset.csv";
...
</script>
The following bit of code works (in regards to having it pull the data from the csv file), but, as I said, I need to pull the text from the file when a button is pressed and just have the file name set in the script, not pulling it up manually.
<!DOCTYPE html>
<html>
<body>
<input type="file" id="fileinput" />
<div id="outputdiv"></div>
<script type="text/javascript">
function readSingleFile(evt) {
var f = evt.target.files[0];
if (f) {
var r = new FileReader();
r.onload = function(e) {
var contents = e.target.result;
var splited = contents.split(/\r\n|\n|\r|,/g);
for (i=0; i<splited.length; i++){
document.getElementById("outputdiv").innerHTML = document.getElementById("outputdiv").innerHTML + splited[i] + "<br>";
}
}
r.readAsText(f);
} else {
alert("Failed to load file");
}
}
document.getElementById('fileinput').addEventListener('change', readSingleFile, false);
</script>
</body>
</html>
From what I can tell from the API, I would need to set the file attributes to a blob in order to pass it to FileReader. How I can do this without using an input box, I have no idea. There's also a 50% chance that I am completely wrong about this since I obviously don't know how to get this done.
If someone could show me how to achieve this with regards to what I'm looking for, it would be very much appreciated. I'm absolutely stumped.
Thank you.
Note: CORS restrictons will prevent this from working in most browsers. You can use FireFox Developer Edition, which disables CORS validation.
You can use an XMLHttpRequest to load a local file:
<!DOCTYPE html>
<html>
<body>
<button onclick="readSingleFile()">Click Me</button>
<div id="outputdiv"></div>
<script type="text/javascript">
function readSingleFile() {
let xhr = new XMLHttpRequest();
let url = "relative/path/to/file.txt;
if (!url) return;
xhr.onload = dataLoaded;
xhr.onerror = _ => "There was an error loading the file.";
xhr.overrideMimeType("text/plain");
xhr.open("GET",url);
xhr.send();
}
function dataLoaded(e){
var contents = e.target.responseText;
var splited = contents.split(/\r\n|\n|\r|,/g);
for (i=0; i<splited.length; i++){
document.getElementById("outputdiv").innerHTML = document.getElementById("outputdiv").innerHTML + splited[i] + "<br>";
}
</script>
</body>
</html>

(Apache Wicket) Set java atrribute from a js function

I am brand new on Apache Wicket and I need to set value on a Java attribute. This value comes from a var on JS filled by a specific function from a specific GIS lib (https://leaflet.github.io/Leaflet.draw/docs/leaflet-draw-latest.html). This setting must be triggered by some component behavior.
Here is a simplified example code:
Wicket web page:
public class MapPage extends WebPage {
private static final long serialVersionUID = 1L;
private Integer coordinates;
// getters and setters
}
Wicket html:
<html xmlns:wicket="http://wicket.apache.org">
<head>
<!-- metas, scripts, and css imports -->
</head>
<body>
<script>
// component declarations
var coordinates = ''
map.on('draw:edited', function (e) {
e.layers.eachLayer(function(layer) {
coordinates = toWKT(layer);
// send coordinates to coordinates java attribute ??? how??
});
});
</script>
</body>
Thanks a lot!
This is a piece of code from one of my projects, where I want to handle a click on a (HighCharts) chart. It passes data to Wicket and Wicket then updates another panel to display details related to the click.
The relevant javascript part, where interactionurl is actually the callbackScript that is generated by the behavior later on:
interactionurl(JSON.stringify(myDataToPass));
The behaviour:
this.add( this.interactionbehavior = new AbstractDefaultAjaxBehavior()
{
#Override
protected void respond( final AjaxRequestTarget target )
{
RequestCycle cycle = RequestCycle.get();
WebRequest webRequest = (WebRequest) cycle.getRequest();
String param1 = webRequest.getQueryParameters().getParameterValue( "mydict" ).toString( "" );
//param1 contains the JSON map passed from javascript.
//you can also do stuff now, like replacing components using ajax
}
#Override
protected void updateAjaxAttributes( AjaxRequestAttributes attributes )
{
super.updateAjaxAttributes( attributes );
attributes.getExtraParameters().put( "mydict", "__PLACEHOLDER__" );
}
#Override
public CharSequence getCallbackScript()
{
String script = super.getCallbackScript().toString().replace( "\"__PLACEHOLDER__\"", "data" );
return script;
}
} );
You only need to pass the interaction url to the page on some moment. For this you can use renderHead in the component that has the behaviour:
#Override
public void renderHead( final IHeaderResponse response )
{
...
//use the `setupCallback` to store the callback script somewhere.., I store it in 'interactionurl'
String script = String.format( " setupCallback(this.interactionbehavior.getCallbackScript()); ");
response.render( OnDomReadyHeaderItem.forScript( script )
}

How do I add a line of code to help debug Javascript?

I have a code that is supposed to show an image and refresh that image every 1 second. I have two .aspx pages, one saves the new image to a file, the other is to display the new image. It is not refreshing with the javascript code. it will display the new image if I hit F5 or refresh the page from the address bar, but not with the code. I cannot debug because I do not have administrative privileges on this computer (at work) so I want to try to add something like "num = num+1" and then display num in a textbox. I want to do this to see if my code is even entering the javascript at all.
Here is the code for the viewer page with the javascript:
<body>
<form id="form1" runat="server">
<div style="height: 60px">
<%--<asp:Image ID="Image1" runat="server" /> --%>
<img src="/video.aspx" id="the_image" alt="" />
<script type="text/javascript" language="javascript">
function refreshImage() {
objIMG = document.getElementById('the_image');
objIMG.src = objIMG.src.substr(0, objIMG.src.indexOf('&nocache=')); +'&nocache=' + Math.random();
}
$(document).ready(function () {
setInterval(refreshImage(), 1000);
})
</script>
<br />
</div>
</form>
</body>
If you are interested here is the code to save the new image to a file:
namespace PlayVideo
{
public partial class Video : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//This is where I originally had the function that saves the new image.
//string saveTo = #"location to save new image";
//FileStream writeStream = new FileStream(saveTo, FileMode.Create, FileAccess.ReadWrite);
using(FileStream fs = File.Open(#"C:Location of file", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
ReadWriteStream(fs, writeStream);
}
Response.Clear();
Response.TransmitFile("~/images/test.jpg");
}
// readStream is the stream you need to read
// writeStream is the stream you want to write to
private void ReadWriteStream(Stream readStream, Stream writeStream)
{
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
}
I don't really no javascrict, I got the code I'm using online. I want to just add that debug line. Can someone help? I'm using Visual Studio 2010, if you're wondering.
console.log(yourData);
and before running it press F12 and go to Console tab. Using Firefox with Firebug plugin would help too.
Depending on the browser you are using to view the page, it probably has a developer toolbar or a set of dev tools built in. You can use this to debug your javascript without needing any elevated privileges.
You can access them in IE but hitting F12.

Play Framework 2.1 websockets in Chrome

I can't seem to get websocket communication to work in the Play Framework version 2.1.
I created a simple test that does nothing but send messages back and forth with a push of a button. All the code for it is below. But nothing shows up except for the button.
Has anybody seen this problem or can someone tell me what I may be doing wrong in the code below?
I am using the latest version of Chrome.
Here is my simple setup.
In Application.java
public static Result index() {
return ok(index.render());
}
public static WebSocket<String> sockHandler() {
return new WebSocket<String>() {
// called when the websocket is established
public void onReady(WebSocket.In<String> in,
WebSocket.Out<String> out) {
// register a callback for processing instream events
in.onMessage(new Callback<String>() {
public void invoke(String event) {
System.out.println(event);
}
});
// write out a greeting
out.write("I'm contacting you regarding your recent websocket.");
}
};
}
In Routes File
GET / controllers.Application.index()
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
GET /greeter controllers.Application.sockHandler()
In Index.Scala.html
#main(null) {
<div class="greeting"></div>
<button class="send">Send</button>
<script type="text/javascript" charset="utf-8">
$(function() {
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var sock = new WS("#routes.Application.sockHandler()")
sock.onmessage = function(event) {
$('.greeting').append(event.data)
}
$('button.send').click(function() {
sock.send("I'm sending a message now.")
});
})
</script>
}
In Main.scala.html
#(title: String)(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>#title</title>
<link rel="stylesheet" media="screen" href="#routes.Assets.at("stylesheets/main.css")">
<link rel="shortcut icon" type="image/png" href="#routes.Assets.at("images/favicon.png")">
<script src="#routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
</head>
<body>
#content
</body>
The problem is in
var sock = new WS("#routes.Application.sockHandler()")
you have to specify the protocol and the complete url in the format: ws://localhost:9000/greeter.
Check this question to do it in javascript: How to construct a WebSocket URI relative to the page URI?
you can use a Route's webSocketURL() method to retrieve a url that can be passed to a WebSocket's constructor. Here's an example from Play's websocket-chat sample code:
$(function() {
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var chatSocket = new WS("#routes.Application.chat(username).webSocketURL()")
var sendMessage = function() {
chatSocket.send(JSON.stringify(
{text: $("#talk").val()}
))
$("#talk").val('')
}
// ...
So in your code you can use something like
var sock = new WS("#routes.Application.sockHandler().webSocketURL()");
Personally I don't like intermingling interpolated code with JS, since I think that any code executing on the client should only be concerned with the state of the client, and not the server (not to mention it makes refactoring the script out into an external file impossible), so I tend to do something like this:
<div class="container app-container"
data-ws-uri="#routes.Application.WSUri.webSocketURL()">
.......
</div>
Then in my JS I can just do something like
var sock = new WS(document.querySelector(".app-container").dataset.wsUri);
// ....

force browsers to get latest js and css files in asp.net application

Some browsers cache js and css files, failing to refresh them unless you force them to. What's the easiest way.
I just implemented this solution that seems to work.
Declare a version variable on your page
public string version { get; set; }
Get the version number from web.config key
version = ConfigurationManager.AppSettings["versionNumber"];
In your aspx page make the calls to javascript and stylesheets like so
<script src="scripts/myjavascript.js?v=<%=version %>" type="text/javascript"></script>
<link href="styles/mystyle.css?v=<%=version %>" rel="stylesheet" type="text/css" />
So if you set the version = 1.1 from 1.0 in your web.config your browser will download the latest files which will hopefully save you and your users some frustration.
Is there another solution that works better, or will this cause any unforeseen issues for a website?
I solved this by tacking a last modified timestamp as a query parameter to the scripts.
I did this with an extension method, and using it in my CSHTML files. Note: this implementation caches the timestamp for 1 minute so we don't thrash the disk quite so much.
Here is the extension method:
public static class JavascriptExtension {
public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename) {
string version = GetVersion(helper, filename);
return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>");
}
private static string GetVersion(this HtmlHelper helper, string filename)
{
var context = helper.ViewContext.RequestContext.HttpContext;
if (context.Cache[filename] == null)
{
var physicalPath = context.Server.MapPath(filename);
var version = $"?v={new System.IO.FileInfo(physicalPath).LastWriteTime.ToString("MMddHHmmss")}";
context.Cache.Add(filename, version, null,
DateTime.Now.AddMinutes(5), TimeSpan.Zero,
CacheItemPriority.Normal, null);
return version;
}
else
{
return context.Cache[filename] as string;
}
}
}
And then in the CSHTML page:
#Html.IncludeVersionedJs("/MyJavascriptFile.js")
In the rendered HTML, this appears as:
<script type='text/javascript' src='/MyJavascriptFile.js?20111129120000'></script>
In ASP.NET Core (MVC 6) this works out of the box via the asp-append-version tag helper:
<script src="scripts/myjavascript.js" asp-append-version="true"></script>
<link href="styles/mystyle.css rel="stylesheet" asp-append-version="true" />
Your solution works. It is quite popular in fact.
Even Stack Overflow uses a similar method:
<link rel="stylesheet" href="http://sstatic.net/so/all.css?v=6184">
Where v=6184 is probably the SVN revision number.
ASP.NET MVC will handle this for you if you use bundles for your JS/CSS. It will automatically append a version number in the form of a GUID to your bundles and only update this GUID when the bundle is updated (aka any of the source files have changes).
This also helps if you have a ton of JS/CSS files as it can greatly improve content load times!
See Here
There are a built-in way in asp.net for this: bundling. Just use it. Each new version will have unique suffix "?v=XXXXXXX". In debug mode bundling is off, for switching on make setting in web.config:
<system.web>
<compilation debug="false" />
</system.web>
Or add to the method RegisterBundles(BundleCollection bundles) :
BundleTable.EnableOptimizations = true;
For example:
BundleConfig.cs :
bundles.Add(new ScriptBundle("~/Scripts/myjavascript.js")
.Include("~/Scripts/myjavascript.js"));
bundles.Add(new StyleBundle("~/Content/mystyle.css")
.Include("~/Content/mystyle.css"));
_Layout.cshtml :
#Scripts.Render("~/Scripts/myjavascript.js")
#Styles.Render("~/Content/mystyle.css")
I wanted a simple one liner to make the path unique to bust the cache. This worked for me:
<script src="scripts/main.js?bust_js_cache=<%=System.IO.File.GetLastWriteTime(Server.MapPath("scripts/main.js")).ToString("HH:mm:ss")%>" type="text/javascript"></script>
If the file has been modified since the last time it was loaded on the page the browser will pull the updated file.
It generates the last modified stamp from the .js file and chucks it in there instead of the version which may not be easy to gain access to.
<script src="scripts/main.js?bust_js_cache=10:18:38" type="text/javascript"></script>
Another option could be to get the checksum of the file.
There is a simpler answer to this than the answer given by the op in the question (the approach is the same):
Define the key in the web.config:
<add key="VersionNumber" value="06032014"/>
Make the call to appsettings directly from the aspx page:
<link href="styles/navigation.css?v=<%=ConfigurationManager.AppSettings["VersionNumber"]%>" rel="stylesheet" type="text/css" />
Based on Adam Tegan's answer, modified for use in a web forms application.
In the .cs class code:
public static class FileUtility
{
public static string SetJsVersion(HttpContext context, string filename) {
string version = GetJsFileVersion(context, filename);
return filename + version;
}
private static string GetJsFileVersion(HttpContext context, string filename)
{
if (context.Cache[filename] == null)
{
string filePhysicalPath = context.Server.MapPath(filename);
string version = "?v=" + GetFileLastModifiedDateTime(context, filePhysicalPath, "yyyyMMddhhmmss");
return version;
}
else
{
return string.Empty;
}
}
public static string GetFileLastModifiedDateTime(HttpContext context, string filePath, string dateFormat)
{
return new System.IO.FileInfo(filePath).LastWriteTime.ToString(dateFormat);
}
}
In the aspx markup:
<script type="text/javascript" src='<%= FileUtility.SetJsVersion(Context,"/js/exampleJavaScriptFile.js") %>'></script>
And in the rendered HTML, it appears as
<script type="text/javascript" src='/js/exampleJavaScriptFile.js?v=20150402021544'></script>
Here's an approach that works with ASP.NET 5 / MVC 6 / vNext.
Step 1: Create a class to return the last write time of the file, similar to other answers in this thread. Note, this requires ASP.NET 5 (or other) dependency injection.
public class FileVersionService
{
private IHostingEnvironment _hostingEnvironment;
public FileVersionService(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public string GetFileVersion(string filename)
{
var path = string.Format("{0}{1}", _hostingEnvironment.WebRootPath, filename);
var fileInfo = new FileInfo(path);
var version = fileInfo.LastWriteTimeUtc.ToString("yyyyMMddhhmmssfff");
return version;
}
}
Step 2: Register the service to be injected inside startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<FileVersionService>();
...
}
Step 3: Then, in ASP.NET 5, it is possible to inject the service directly into a layout view such as _Layout.cshtml like this:
#inject Namespace.Here.FileVersionService fileVersionService
<!DOCTYPE html>
<html lang="en" class="#ViewBag.HtmlClass">
<head>
...
<link href="/css/styles.css?v=#fileVersionService.GetFileVersion("\\css\\styles.css")" rel="stylesheet" />
...
</head>
<body>
...
</body>
There are some finishing touches that could be done to combine physical paths better and handle the file name in a style more consistent with the syntax, but this is a starting point. Hope it helps people moving to ASP.NET 5.
Interestingly, this very site has issues with the approach you describe in connection with some proxy setups, even though it should be fail-safe.
Check this Meta Stack Overflow discussion.
So in light of that, it might make sense not to use a GET parameter to update, but the actual file name:
href="/css/scriptname/versionNumber.css"
even though this is more work to do, as you'll have to actually create the file, or build a URL rewrite for it.
I have employed a slightly different technique in my aspnet MVC 4 site:
_ViewStart.cshtml:
#using System.Web.Caching
#using System.Web.Hosting
#{
Layout = "~/Views/Shared/_Layout.cshtml";
PageData.Add("scriptFormat", string.Format("<script src=\"{{0}}?_={0}\"></script>", GetDeployTicks()));
}
#functions
{
private static string GetDeployTicks()
{
const string cacheKey = "DeployTicks";
var returnValue = HttpRuntime.Cache[cacheKey] as string;
if (null == returnValue)
{
var absolute = HostingEnvironment.MapPath("~/Web.config");
returnValue = File.GetLastWriteTime(absolute).Ticks.ToString();
HttpRuntime.Cache.Insert(cacheKey, returnValue, new CacheDependency(absolute));
}
return returnValue;
}
}
Then in the actual views:
#Scripts.RenderFormat(PageData["scriptFormat"], "~/Scripts/Search/javascriptFile.min.js")
Starting from the above answer I modified a little bit the code to make the helper work with CSS files too and add a version every time when you do some change in the files and not only when you do the build
public static class HtmlHelperExtensions
{
public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename)
{
string version = GetVersion(helper, filename);
return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>");
}
public static MvcHtmlString IncludeVersionedCss(this HtmlHelper helper, string filename)
{
string version = GetVersion(helper, filename);
return MvcHtmlString.Create("<link href='" + filename + version + "' type ='text/css' rel='stylesheet'/>");
}
private static string GetVersion(this HtmlHelper helper, string filename)
{
var context = helper.ViewContext.RequestContext.HttpContext;
var physicalPath = context.Server.MapPath(filename);
var version = "?v=" +
new System.IO.FileInfo(physicalPath).LastWriteTime
.ToString("yyyyMMddHHmmss");
context.Cache.Add(physicalPath, version, null,
DateTime.Now.AddMinutes(1), TimeSpan.Zero,
CacheItemPriority.Normal, null);
if (context.Cache[filename] == null)
{
context.Cache[filename] = version;
return version;
}
else
{
if (version != context.Cache[filename].ToString())
{
context.Cache[filename] = version;
return version;
}
return context.Cache[filename] as string;
}
}
}
<?php $rand_no = rand(10000000, 99999999)?>
<script src="scripts/myjavascript.js?v=<?=$rand_no"></script>
This works for me in all browsers. Here I have used PHP to generate random no. You can use your own server side language.`
Get file modified time, as shown below
private static string GetLastWriteTimeForFile(string pathVal)
{
return System.IO.File.GetLastWriteTime(HostingEnvironment.MapPath(pathVal)).ToFileTime().ToString();
}
Append this with input as querystring
public static string AppendDateInFile(string pathVal)
{
var patheWithDate = new StringBuilder(pathVal);
patheWithDate.AppendFormat("{0}x={1}",
pathVal.IndexOf('?') >= 0 ? '&' : '?',
GetLastWriteTimeForFile(pathVal));
return patheWithDate.ToString();
}
Call this from markup.
MVC Extension Helper Approach
Add an extension method
namespace TNS.Portal.Helpers
{
public static class ScriptExtensions
{
public static HtmlString QueryStringScript<T>(this HtmlHelper<T> html, string path)
{
var file = html.ViewContext.HttpContext.Server.MapPath(path);
DateTime lastModified = File.GetLastWriteTime(file);
TagBuilder builder = new TagBuilder("script");
builder.Attributes["src"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss");
return new HtmlString(builder.ToString());
}
public static HtmlString QueryStringStylesheet<T>(this HtmlHelper<T> html, string path)
{
var file = html.ViewContext.HttpContext.Server.MapPath(path);
DateTime lastModified = File.GetLastWriteTime(file);
TagBuilder builder = new TagBuilder("link");
builder.Attributes["href"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss");
builder.Attributes["rel"] = "stylesheet";
return new HtmlString(builder.ToString());
}
}
}
Add this namespace in web.config
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="TNS.Portal" />
<add namespace="TNS.Portal.Helpers" />
</namespaces>
</pages>
</system.web.webPages.razor>
Use it in view as
#Html.QueryStringScript("/Scripts/NPIAjaxCalls.js")
#Html.QueryStringStylesheet("/Content/StyledRadio.css")
Simplified prior suggestions and providing code for .NET Web Forms developers.
This will accept both relative ("~/") and absolute urls in the file path to the resource.
Put in a static extensions class file, the following:
public static string VersionedContent(this HttpContext httpContext, string virtualFilePath)
{
var physicalFilePath = httpContext.Server.MapPath(virtualFilePath);
if (httpContext.Cache[physicalFilePath] == null)
{
httpContext.Cache[physicalFilePath] = ((Page)httpContext.CurrentHandler).ResolveUrl(virtualFilePath) + (virtualFilePath.Contains("?") ? "&" : "?") + "v=" + File.GetLastWriteTime(physicalFilePath).ToString("yyyyMMddHHmmss");
}
return (string)httpContext.Cache[physicalFilePath];
}
And then call it in your Master Page as such:
<link type="text/css" rel="stylesheet" href="<%= Context.VersionedContent("~/styles/mystyle.css") %>" />
<script type="text/javascript" src="<%= Context.VersionedContent("~/scripts/myjavascript.js") %>"></script>
The main problem with doing it this way is mainly that you will need to remember to update your version number in your code every time you make any change to your css or js files.
A possibly better way to do it is to set a guaranteed unique parameter with each of your css or js files, like so:
<script src="scripts/myjavascript.js?_=<%=DateTime.Now.Ticks%>" type="text/javascript"></script>
<link href="styles/mystyle.css?_=<%=DateTime.Now.Ticks%>" rel="stylesheet" type="text/css" />
This forces the files to be requested from the server every single time, which also means that your site will not be as performant upon page load, since those files will never be cached, and will use unneeded bandwidth each time.
Essentially, if you can remember to update the version number every time a change is made, you can get away with how you're doing it.
Based on the above answer I've written a small extension class to work with CSS and JS files:
public static class TimestampedContentExtensions
{
public static string VersionedContent(this UrlHelper helper, string contentPath)
{
var context = helper.RequestContext.HttpContext;
if (context.Cache[contentPath] == null)
{
var physicalPath = context.Server.MapPath(contentPath);
var version = #"v=" + new FileInfo(physicalPath).LastWriteTime.ToString(#"yyyyMMddHHmmss");
var translatedContentPath = helper.Content(contentPath);
var versionedContentPath =
contentPath.Contains(#"?")
? translatedContentPath + #"&" + version
: translatedContentPath + #"?" + version;
context.Cache.Add(physicalPath, version, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero,
CacheItemPriority.Normal, null);
context.Cache[contentPath] = versionedContentPath;
return versionedContentPath;
}
else
{
return context.Cache[contentPath] as string;
}
}
}
Instead of writing something like:
<link href="#Url.Content(#"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content(#"~/Scripts/bootstrap.min.js")"></script>
You can now write:
<link href="#Url.VersionedContent(#"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<script src="#Url.VersionedContent(#"~/Scripts/bootstrap.min.js")"></script>
I.e. simply replace Url.Content with Url.VersionedContent.
Generated URLs look something like:
<link href="/Content/bootstrap.min.css?v=20151104105858" rel="stylesheet" type="text/css" />
<script src="/Scripts/bootstrap.min.js?v=20151029213517"></script>
If you use the extension class you might want to add error handling in case the MapPath call doesn't work, since contentPath isn't a physical file.
I use a similar way to do the same you are doing without modifying each page. Added a PreRender event is master file. It keeps my logic at one place and applicable to both js and css files.
protected void Page_PreRender(object sender, EventArgs e)
{
HtmlLink link = null;
LiteralControl script = null;
foreach (Control c in Header.Controls)
{
//StyleSheet add version
if (c is HtmlLink)
{
link = c as HtmlLink;
if (link.Href.EndsWith(".css", StringComparison.InvariantCultureIgnoreCase))
{
link.Href += string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]);
}
}
//Js add version
if (c is LiteralControl)
{
script = c as LiteralControl;
if (script.Text.Contains(".js"))
{
var foundIndexes = new List<int>();
for (int i = script.Text.IndexOf(".js\""); i > -1; i = script.Text.IndexOf(".js\"", i + 1))
{
foundIndexes.Add(i);
}
for (int i = foundIndexes.Count - 1; i >= 0; i--)
{
script.Text = script.Text.Insert(foundIndexes[i] + 3, string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]));
}
}
}
}
}
You can override the DefaultTagFormat property of Scripts or Styles.
Scripts.DefaultTagFormat = #"<script src=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + #"""></script>";
Styles.DefaultTagFormat = #"<link href=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + #""" rel=""stylesheet""/>";
For resolving this issue in my ASP.Net Ajax application, I created an extension and then called in the master page.
For more details, you can go through the link.
Easy and smart way to implement css versioning in .net application by below concept.. no need to write back-end code.
<link href="<%="../../App_Themes/Base/css/main.css?v="+ DateTime.Now.ToString("yyyyMMddhhmmss") +""%>" rel="stylesheet" />
For ASP.NET pages I am using the following
BEFORE
<script src="/Scripts/pages/common.js" type="text/javascript"></script>
AFTER (force reload)
<script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>
Adding the DateTime.Now.Ticks works very well.
just put this inside system.webserver in web.config
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".html" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange"/>
<add extension=".css" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange"/>
<add extension=".js" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange"/>
</profiles>
</caching>

Categories