This one is quiet interresting - a while ago, we had a customer which wanted to use the Send Email save action in WFFM.
They had designed the form with several fields, including a field called Mail, in which the user should enter their email address.
In the save action, they had selected the Mail field in the CC section, so the user would be CC on the mail that got send out.
The way this works, is when you insert a field into here, it is wrapped with square brackets, so it becomes [Mail] .
Now, when they clicked OK to close the save action, and opened it again, Sitecore had changed it to [[Mail]], which made it fail with an exception when the user filled out the form.
Changing it back and saving made not difference, since it kept changing it to [[Mail]].
We contacted Sitecore Support, and got a fix for it - so if you run in to this issue, please contact them, and say you want the fix from issue #436119.
The version we had the issue with is 2.3 rev.140617, so if you are using another version, it might work for you without the fix.
torsdag den 18. juni 2015
Insert Media Link in General Link breaks if parentheses is used
In Sitecore 6.6 (the issue is fixed from 7.0 and forward), there is an annoying bug, that causes the Insert Media Link dialog, if parentheses is used in the link description or the alternative text.
Here is how to reproduce it:
It is a small DLL and a zip file, so nothing fancy - but none the less, it works.
Here is how to reproduce it:
- Install Sitecore 6.6 rev. 140410
- Create a template with a general link field.
- Create an item based on this template.
- Click Insert Media Link, and select a media item.
- Enter a link description and alternative text containing parantheses, and click OK to close the dialog.
- Click Insert Media Link again, and watch the "Value cannot be null" exception.
It is a small DLL and a zip file, so nothing fancy - but none the less, it works.
The case of the test server with 500MB/s reads from the web database MDF file
First, let me describe the environment.
We have these servers:
SERVER-CM, which is the sitecore content management server.
SERVER-CD, which is the sitecore content delivery server.
SERVER-SQL, which is the MSSQL server containing the databases.
SERVER-TEST, which is the test server, containing both the test databases and the test sitecore instance.
Now, when the site was developed, it was deployed to both the production and the test environment, and changes was applied both places when we make changes.
Time went by, and we had to upgrade the site, so we thought it would be a good idea to upgrade the production environment, make a copy of it, and overwrite the test environment with this copy, since it still contained the original test data, which made it difficult to test some changes, since there was no real data.
So, we upgraded the production environment, and made the copy.
The production environment performed fine, but the test environment all the sudden has a constant load of several hundred MB/s reads from the web database.
After alot of debugging, profiling and staring into the screen, we found the cause.
It turns out, when staging is enabled, everything that needs to get done on all servers is written as events into the EventQueue table in the web database - and also which instances of Sitecore that has run these events. (The EventQueue table is meant to be trimmed every 4 hours, but for some reason that does not happen, but thats another issue).
So, when all the sudden the test instance sees a copy of the web database from production, it sees 1.3 million event records that it needs to go through and replay, since it thinks it never has run them (even though it has, since it is a copy of an instance that has).
After we truncated the EventQueue table in the web database and restarted the IIS process, the database I/O went normal instantly, and performance seems to have improved.
So, if you clone an production environment into a test environment, and staging is enabled, make sure to truncate the EventQueue tables.
We have these servers:
SERVER-CM, which is the sitecore content management server.
SERVER-CD, which is the sitecore content delivery server.
SERVER-SQL, which is the MSSQL server containing the databases.
SERVER-TEST, which is the test server, containing both the test databases and the test sitecore instance.
Now, when the site was developed, it was deployed to both the production and the test environment, and changes was applied both places when we make changes.
Time went by, and we had to upgrade the site, so we thought it would be a good idea to upgrade the production environment, make a copy of it, and overwrite the test environment with this copy, since it still contained the original test data, which made it difficult to test some changes, since there was no real data.
So, we upgraded the production environment, and made the copy.
The production environment performed fine, but the test environment all the sudden has a constant load of several hundred MB/s reads from the web database.
After alot of debugging, profiling and staring into the screen, we found the cause.
It turns out, when staging is enabled, everything that needs to get done on all servers is written as events into the EventQueue table in the web database - and also which instances of Sitecore that has run these events. (The EventQueue table is meant to be trimmed every 4 hours, but for some reason that does not happen, but thats another issue).
So, when all the sudden the test instance sees a copy of the web database from production, it sees 1.3 million event records that it needs to go through and replay, since it thinks it never has run them (even though it has, since it is a copy of an instance that has).
After we truncated the EventQueue table in the web database and restarted the IIS process, the database I/O went normal instantly, and performance seems to have improved.
So, if you clone an production environment into a test environment, and staging is enabled, make sure to truncate the EventQueue tables.
onsdag den 27. maj 2015
A simple usercontrol to make sure resources is refreshed when changed
Often in a project, the things evolve over time, which means both stylesheets and scripts needs to be updated.
A problem you can often run into, is that the client keeps the old version cached, which can break things in really weird ways.
This is a simple way to fix this - all you need to do is replace your script and link tags with this usercontrol.
That way, it ensures the url to the files gets a unique querystring added, which makes sure the client refreshes the file if it gets changed.
To do this, create a new class in a C# project, and call it HashedResourceFile, and paste the following code into it:
Then, when you create your project, add this to the top of the aspx/ascx file:
Then, add this instead of your css/js file references:
When the page is rendered, it will add a querystring to the file, containing a hash of the file, that changes when the file changes.
Really simple solution, to a really annoying problem :-)
A problem you can often run into, is that the client keeps the old version cached, which can break things in really weird ways.
This is a simple way to fix this - all you need to do is replace your script and link tags with this usercontrol.
That way, it ensures the url to the files gets a unique querystring added, which makes sure the client refreshes the file if it gets changed.
To do this, create a new class in a C# project, and call it HashedResourceFile, and paste the following code into it:
public class HashedResourceFile : WebControl
{
public Uri ResourceUrl { get; set; }
private string FinalUrl
{
get
{
string url = ResourceUrl.ToString();
// Handle external URL's with not-defined protocol.
if (url.StartsWith("//"))
{
return url;
}
// If the URL does not start with leading slash, it is external, and we don't do anything with it.
if (!url.StartsWith("/"))
{
return url;
}
FileInfo fileInfo = new FileInfo(HttpContext.Current.Server.MapPath(url));
if (!fileInfo.Exists)
{
throw new FileNotFoundException("File in resource not found: " + ResourceUrl);
}
string hash;
using (MD5 md5 = MD5.Create())
{
using (FileStream stream = fileInfo.OpenRead())
{
hash = BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", string.Empty);
}
}
return ResourceUrl + "?filehash=" + hash.Substring(0, 8);
}
}
private ResourceLinkType LinkType
{
get
{
if (ResourceUrl.ToString().EndsWith(".css", StringComparison.InvariantCultureIgnoreCase))
{
return ResourceLinkType.Css;
}
if (ResourceUrl.ToString().EndsWith(".js", StringComparison.InvariantCultureIgnoreCase))
{
return ResourceLinkType.Javascript;
}
return ResourceLinkType.Invalid;
}
}
protected override void Render(HtmlTextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer", "writer must not be null");
}
if (ResourceUrl == null || string.IsNullOrEmpty(ResourceUrl.ToString()))
{
return;
}
switch (LinkType)
{
case ResourceLinkType.Css:
writer.Write("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\" ", FinalUrl);
foreach (string key in Attributes.Keys.Cast<string>().Where(key => key != "rel" && key != "type" && key != "href"))
{
writer.Write("{0}=\"{1}\" ", key, Attributes[key]);
}
writer.Write("/>");
break;
case ResourceLinkType.Javascript:
writer.Write("<script type=\"text/javascript\" src=\"{0}\" ", FinalUrl);
foreach (string key in Attributes.Keys.Cast<string>().Where(key => key != "type" && key != "src"))
{
writer.Write("{0}=\"{1}\" ", key, Attributes[key]);
}
writer.Write("></script>");
break;
default:
return;
}
}
private enum ResourceLinkType
{
Invalid,
Css,
Javascript
}
}
Then, when you create your project, add this to the top of the aspx/ascx file:
<%@ Register TagPrefix="test" Namespace="YourNamespace" Assembly="YourDLL" %>
Then, add this instead of your css/js file references:
<test:HashedResourceFile ResourceUrl="/static/css/style.css" runat="server" />
When the page is rendered, it will add a querystring to the file, containing a hash of the file, that changes when the file changes.
Really simple solution, to a really annoying problem :-)
onsdag den 4. marts 2015
Small little trick to validate if an email address is valid, and getting them as MailAddress objects, even with IDN domain names
The world today is pretty international, which means that we can no longer assume that domain names is only ASCII.
If you try and create a MailAddress object in .NET with an IDN domain, it will fail with a FormatException.
To fix this, I have created two small helper functions.
One that gets a MailAddress object that works both with non-IDN and IDN domain names.
The other uses that one to validate if an email address is valid.
This is pretty smart, since people have a tendency to validate email addresses using different regular expressions, and none of them really works in all cases.
So first - here is how to get an email address for both types of domains:
And here is how to use it to validate email addresses:
This is smart, since we don't use any regular expressions for it, but just uses .NET's own implementation to validate addresses.
That is really it - and it works everywhere, not just in Sitecore :-)
If you try and create a MailAddress object in .NET with an IDN domain, it will fail with a FormatException.
To fix this, I have created two small helper functions.
One that gets a MailAddress object that works both with non-IDN and IDN domain names.
The other uses that one to validate if an email address is valid.
This is pretty smart, since people have a tendency to validate email addresses using different regular expressions, and none of them really works in all cases.
So first - here is how to get an email address for both types of domains:
public static MailAddress GetMailAddress(string email, string name)
{
if (string.IsNullOrEmpty(email) || email.Contains(' ') || email.Count(c => c == '@') > 1)
{
return null;
}
try
{
return string.IsNullOrEmpty(name) ? new MailAddress(email) : new MailAddress(email, name);
}
catch (FormatException)
{
if (!email.Contains("@") || email.Count(c => c == '@') != 1)
{
return null;
}
string[] parts = email.Split('@');
try
{
IdnMapping mapping = new IdnMapping();
return string.IsNullOrEmpty(name) ?
new MailAddress(string.Concat(mapping.GetAscii(parts[0]), "@", mapping.GetAscii(parts[1]))) :
new MailAddress(string.Concat(mapping.GetAscii(parts[0]), "@", mapping.GetAscii(parts[1])), name);
}
catch (ArgumentException)
{
return null;
}
catch (FormatException)
{
return null;
}
}
}
And here is how to use it to validate email addresses:
public static bool ValidateEmailAddress(string email)
{
return GetMailAddress(email, string.Empty) != null;
}
This is smart, since we don't use any regular expressions for it, but just uses .NET's own implementation to validate addresses.
That is really it - and it works everywhere, not just in Sitecore :-)
Bug in WFFM prevents number validation on text fields
There seems to be a bug in WFFM, that causes it to fail to validate entered text as valid numbers.
If you configure it to validate between 1 and 2,147484E+09, it seems to think that as between 1 and 2.
There is a solution, but you have to contact Sitecore Support to get it - if you do, mention you want the solution from the support issue #430157 .
Once the javascript file they mention has been updated, it will once again validate the values correct.
If you configure it to validate between 1 and 2,147484E+09, it seems to think that as between 1 and 2.
There is a solution, but you have to contact Sitecore Support to get it - if you do, mention you want the solution from the support issue #430157 .
Once the javascript file they mention has been updated, it will once again validate the values correct.
Enabling the Sitecore license ID on the logon screen for Sitecore 8
Since Sitecore 8, for some reason, it seems like the license ID is considered something confidential.
Now, it is no longer rendered by default - however, if you want to have it back at the login screen, change the following setting to false in an include file: Login.DisableLicenseInfo
Once you have done that, it can be accessed from the login screen again.
Now, it is no longer rendered by default - however, if you want to have it back at the login screen, change the following setting to false in an include file: Login.DisableLicenseInfo
Once you have done that, it can be accessed from the login screen again.
Abonner på:
Opslag (Atom)