Sharepoint has own default editor for RichTextField. However if you are not satisfied with the functionality offered by this html editor, there are several other implementations available from other vendors. One of the popular editors for Sharepoint is Telerik Rad Editor Lite. Advantage of this control is that it is free (it was developed by Telerik using agreement with MS). However it works only in IE and according to information from forums from Telerik site there are no plans to support other browsers in free version.
The good news is that even if it is not officially supported it is still possible to show Rad Editor in other than IE browsers (there is not guarantee that all features will work properly in this case, but most of basic features work). We will need little reflection and knowledge of how rendering templates work in Sharepoint.
At first lets check from where problem comes. If you investigate code of RadHtmlListField in Reflector (from RadEditorSharePoint.dll), you will find that it has the following hierarchy:
RadEditor > MOSSRadEditor > RadHtmlListField
RadHtmlListField has property IsSupportedBrowser:
1: internal bool IsSupportedBrowser
2: {
3: get
4: {
5: this.CheckBrowserCapabilities();
6: return this._isSupportedBrowser;
7: }
8: }
If you will check another class RadEditorRenderer you will find that this property is used in order to determine will Rad Editor will be rendered as html editor or as text area without any toolbars:
1: private void RenderConditional()
2: {
3: this._editor.Page.VerifyRenderingInServerForm(this._editor);
4: if (!this._editor.HasPermission)
5: {
6: this.RenderNonEditableContent();
7: }
8: else if (!this._editor.Editable)
9: {
10: this.RenderNonEditable();
11: }
12: else if (this._editor.RenderAsTextArea || !this._editor.IsSupportedBrowser)
13: {
14: this.RenderAsTextArea(this._editor.GetPostbackErrorMessage());
15: }
16: else
17: {
18: this.RenderEditable(this._editor.GetPostbackErrorMessage());
19: }
20: }
(see line 12). Let’s check how Rad Editor determines browser capabilities. The logic is implemented in CheckBrowserCapabilities() method which is called from IsSupportedBrowser property (see above):
1: private void CheckBrowserCapabilities()
2: {
3: if (!this._browserCapabilitiesRetrieved)
4: {
5: this._browserCapabilitiesRetrieved = true;
6: if (base.IsDesignMode)
7: {
8: this._isSupportedBrowser = true;
9: this._isIe = true;
10: this._isSafari = false;
11: }
12: else
13: {
14: HttpRequest request = this.Context.Request;
15: HttpBrowserCapabilities browser = request.Browser;
16: if (browser.Browser == "IE")
17: {
18: double num = browser.MinorVersion + browser.MajorVersion;
19: this._isSupportedBrowser = num >= 5.5;
20: this._isIe = true;
21: }
22: else if ((browser.Browser.ToLower() == "opera") &&
23: (browser.MajorVersion == 9))
24: {
25: this._isSupportedBrowser = true;
26: }
27: else
28: {
29: string input =
30: (request.UserAgent != null) ? request.UserAgent.ToLower() : "";
31: if (Regex.Match(input, @"rv:((1\.(3|4|5|6|7|8|9|10))|((2|3|4|5)\.\d))",
32: RegexOptions.Compiled | RegexOptions.IgnoreCase).Success)
33: {
34: this._isSupportedBrowser = true;
35: }
36: else if ((input.IndexOf("safari") > 0) && (input.IndexOf("gecko") > 0))
37: {
38: this._isSafari = true;
39: this._isSupportedBrowser = true;
40: }
41: else
42: {
43: this._isSupportedBrowser = false;
44: }
45: }
46: }
47: }
48: }
This method is the root of all problems. When you use last version of FireFox (I used FF 8) request.Browser contains “Firefox” and user agent contains “2 mozilla/5.0 (windows nt 5.2; rv:8.0) gecko/20100101 firefox/8.0”. In older FF versions (I tested with 3.6) Rad Editor worked – so it passed another values in specified fields (I didn’t check what exact values before to update it to version 8).
Now when we know the problem let’s find solution. We need to check if browser is Firefox and if yes set _isSupportedBrowser field to true. This field is private so we can’t just inherit the RadHtmlListField class and modify it. We need to use reflection. I created the following helper class:
1: public static class ReflectionHelper
2: {
3: public static void SetFieldValue(object obj, string name, object value)
4: {
5: BindingFlags bf = 0 | BindingFlags.Instance | BindingFlags.Static |
6: BindingFlags.Public | BindingFlags.NonPublic;
7: FieldInfo fi = obj.GetType().BaseType.BaseType.BaseType.FindMembers(MemberTypes.Field,
8: bf, Type.FilterName, name)[0] as FieldInfo;
9: fi.SetValue(obj, value);
10: }
11: }
1: public class AllBrowsersHtmlListField : RadHtmlListField
2: {
3: private void checkForFirefox()
4: {
5: HttpRequest request = HttpContext.Current.Request;
6: HttpBrowserCapabilities browser = request.Browser;
7: if ((browser.Browser.ToLower() == "firefox"))
8: {
9: ReflectionHelper.SetFieldValue(this, "_browserCapabilitiesRetrieved", true);
10: ReflectionHelper.SetFieldValue(this, "_isSupportedBrowser", true);
11: }
12: }
13:
14: protected override void OnLoad(EventArgs e)
15: {
16: this.checkForFirefox();
17: base.OnLoad(e);
18: }
19: }
1: <%@ Control Language="C#" AutoEventWireup="false" %>
2: ...
3: <%@ Register TagPrefix="customradeditor" Assembly="CustomTelerikField, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." Namespace="CustomTelerikField" %>
4: <SharePoint:RenderingTemplate ID="RichTextField" runat="server">
5: <Template>
6: <customradeditor:AllBrowsersHtmlListField id="RadTextField" runat="server" FontSizeCoef="7" />
7: ...
8: </Template>
9: </SharePoint:RenderingTemplate>
It is also worth to make RadEditorList.ascx read only so when “Use RadEditor to edit list items” will be activated next time it won’t override your version. Code which copies file is inside try/catch block so it won’t fail feature activation and site creation if it is stapled with this feature.
After you will perform iisreset Sharepoint will use your own control and those will display Rad Editor in FireFox. If you will have similar problems with another browsers you can fix them by the same way.
No comments:
Post a Comment