Customizing RTE functions according to user/role

In many cases, the customer want that some functionality in RTE are not accessible for some authors and accessible to the other. For safety reasons for example, it’s necessary to allow the Webmaster only the ability to insert links within rich text editor or to add images. In Sitecore it is possible and pretty easy to set access rights for some roles or users to read, write or edit some fields within a component.
In this article we will take a look how we could configure a rich text editor, the way that the role „User/Author“ will not be able to use the button „Insert Link“ while the Webmaster is allowed to use the „Insert Link“ button.

(mehr …)

Custom Dialogs im Rich Text Editor

Sitecore bietet Dank des RadEditors von Telerik standardmässig einen mächtigen Rich Text Editor. Durch die starke Vernetzung mit Sitecore kann dieser sehr einfach erweitert werden. So können beispielsweise in der Core-Datenbank neue Rich Text Profile erstellt werden, welche die Möglichkeit bieten, dem Autor im Editor komplett unterschiedliche Buttons zur Verfügung zu stellen. Ebenfalls können auf einfache Weise Snippets erstellt werden, die das Einfügen von statischem HTML-Code in den Editor per Klick ermöglichen. Dies funktioniert bei wenig komplexen Strukturen, wie Tabellen und Listen, ziemlich gut. Bei komplexeren Strukturen, wie beispielsweise einem Youtube-Iframe, kommt diese Technik jedoch an ihre Grenzen, da der Benutzer hier gezwungen ist, die Youtube-ID manuell in den Code einzufügen. Für Autoren ohne HTML-Kenntnisse ist dies oftmals mit Problemen verbunden. Es wäre daher sehr hilfreich, wenn der Autor die Möglichkeit hat, konfigurierbare Werte zunächst in ein Formular einzutragen und der richtige HTML-Code dann per Knopfdruck erzeugt wird. Für die Lösung dieses Problems bietet Sitecore ebenfalls eine Möglichkeit, die jedoch etwas Programmieraufwand erfordert. Der im Folgenden beschriebene Custom Dialog erzeugt genau den oben angesprochenen Youtube-Iframe.

Schritt 1:

Als erstes muss ein neuer Html Editor Button im gewünschten Rich Text Editor Profil erzeugt werden. Dies kann in der Core-Datenbank unter dem Pfad \sitecore\system\settings\Html Editor Profiles\My Rich Text Profile\Toolbar X bewerkstelligt werden.

Schritt 2:

Nun muss das User Interface des Dialogs erstellt werden. Dies wird mittels XML realisiert und im Ordner /sitecore/shell/Controls/Rich Text Editor/InsertYoutubeVideo erstellt. Im SDN ist mehr zu diesem Thema beschrieben.

<?xml version="1.0" encoding="utf-8" ?>

<control xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">
    <RichText.InsertYoutubeVideo>

        <FormDialog Icon="Multimedia/32x32/film_clip.png" Header="Insert a youtube video"
     Text="You can insert a youtube video as an iframe." OKButton="Insert Video">
           
            <script Type="text/javascript" Language="javascript" Src="Controls/Rich Text Editor/InsertYoutubeVideo/InsertYoutubeVideo.js">.</script>

            <CodeBeside Type="Sitecore.CustomCode.InsertYoutubeVideoDialog,Sitecore.CustomCode"/>

            <GridPanel Width="100%" Columns="2">
                <Border Width="100%" GridPanel.Width="20%" Padding="0px 4px 0px 0px">
                    <Literal Text="YouTube ID:"/>
                </Border>
                <Edit ID="YoutubeId" Width="95%"/>

                <Border Width="100%" GridPanel.Width="20%" Padding="0px 4px 0px 0px">
                    <Literal Text="Video Width:"/>
                </Border>
                <Edit ID="VideoWidth" Width="95%"/>

                <Border Width="100%" GridPanel.Width="20%" Padding="0px 4px 0px 0px">
                    <Literal Text="Video Height:"/>
                </Border>
                <Edit ID="VideoHeight" Width="95%"/>
            </GridPanel>

        </FormDialog>

    </RichText.InsertYoutubeVideo>
</control>

Schritt 3:

Der nächste Schritt ist die Erstellung des Code-Behind für das Dialog Control.

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="InsertYoutubeVideoDialog.cs" company="Namics AG">
//   (c) Namics AG
// </copyright>
// <summary>
//   Dialog form to insert a youtube video.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Sitecore.CustomCode
{
    using System;

    using Sitecore;
    using Sitecore.Web;
    using Sitecore.Web.UI.Pages;
    using Sitecore.Web.UI.Sheer;

    /// <summary>
    /// Dialog form to insert a youtube video.
    /// </summary>
    public class InsertYoutubeVideoDialog : DialogForm
    {
        /// <summary>
        /// The youtube id.
        /// </summary>
        protected Sitecore.Web.UI.HtmlControls.Edit YoutubeId;

        /// <summary>
        /// The video width.
        /// </summary>
        protected Sitecore.Web.UI.HtmlControls.Edit VideoWidth;

        /// <summary>
        /// The video height.
        /// </summary>
        protected Sitecore.Web.UI.HtmlControls.Edit VideoHeight;

        /// <summary>
        /// Gets or sets the mode.
        /// </summary>
        protected string Mode
        {
            get
            {
                string modeString = StringUtil.GetString(ServerProperties["Mode"]);
                if (!string.IsNullOrEmpty(modeString))
                {
                    return modeString;
                }

                return "shell";
            }

            set
            {
                ServerProperties["Mode"] = value;
            }
        }

        /// <summary>
        /// The on load.
        /// </summary>
        /// <param name="pE">
        /// The e.
        /// </param>
        protected override void OnLoad(EventArgs pE)
        {
            base.OnLoad(pE);
            if (Context.ClientPage.IsEvent)
            {
                return;
            }

            Mode = WebUtil.GetQueryString("mo");
            YoutubeId.Value = WebUtil.GetQueryString("id");
            VideoWidth.Value = WebUtil.GetQueryString("width");
            VideoHeight.Value = WebUtil.GetQueryString("height");
        }

        /// <summary>
        /// The ok event.
        /// </summary>
        /// <param name="pSender">
        /// The sender.
        /// </param>
        /// <param name="pArgs">
        /// The args.
        /// </param>
        protected override void OnOK(object pSender, EventArgs pArgs)
        {
            if (string.IsNullOrEmpty(YoutubeId.Value) || string.IsNullOrEmpty(VideoWidth.Value) || string.IsNullOrEmpty(VideoHeight.Value))
            {
                SheerResponse.Alert("Please enter values for the fields YouTube ID, Video Width and Video Height.");
                return;
            }
           
            string outputYoutubeId = StringUtil.EscapeJavascriptString(YoutubeId.Value, true);
            string outputVideoWidth = StringUtil.EscapeJavascriptString(VideoWidth.Value, true);
            string outputVideoHeight = StringUtil.EscapeJavascriptString(VideoHeight.Value, true);
            if (Mode == "webedit")
            {
                SheerResponse.SetDialogValue(outputYoutubeId);
                base.OnOK(pSender, pArgs);
                return;
            }

            SheerResponse.Eval("scClose(" + outputYoutubeId + ", " + outputVideoWidth + ", " + outputVideoHeight + ")");
        }

        /// <summary>
        /// The cancel event.
        /// </summary>
        /// <param name="pSender">
        /// The sender.
        /// </param>
        /// <param name="pArgs">
        /// The args.
        /// </param>
        protected override void OnCancel(object pSender, EventArgs pArgs)
        {
            if (Mode == "webedit")
            {
                base.OnCancel(pSender, pArgs);
                return;
            }

            SheerResponse.Eval("scCancel()");
        }
    }
}

Schritt 4:

Nun wird der Javascript-Controller erstellt, der clientseitig für die Abarbeitung der Click-Events zuständig ist. Dieser muss im bereits erstellten XML-Control referenziert werden. Diese Javascript-Datei wird im selben Ordner angelegt wie die XML-Datei.

function GetDialogArguments() {
    return getRadWindow().ClientParameters;
}

function getRadWindow() {
  if (window.radWindow) {
        return window.radWindow;
  }
   
    if (window.frameElement && window.frameElement.radWindow) {
        return window.frameElement.radWindow;
    }
   
    return null;
}

var isRadWindow = true;

var radWindow = getRadWindow();

if (radWindow) {
  if (window.dialogArguments) {
    radWindow.Window = window;
  }
}

function scClose(youtubeId, videoWidth, videoHeight) {
    var returnValue = {
        youtubeId: youtubeId,
        videoWidth: videoWidth,
        videoHeight: videoHeight
    };

    getRadWindow().close(returnValue);
}

function scCancel() {
  getRadWindow().close();
}

function scCloseWebEdit(url) {
  window.returnValue = url;
  window.close();
}

if (window.focus && Prototype.Browser.Gecko) {
  window.focus();
}

Schritt 5:

Zu guter Letzt wird der erstellte Button in die Command-Liste des RadEditors eingetragen. Diese befindet sich in der Datei /sitecore/shell/Controls/Rich Text Editor/RichText Commands.js

RadEditorCommandList["InsertYoutubeVideo"] = function (commandName, editor, tool) {
    var html = editor.getSelectionHtml();
   
    var attributes = GetYoutubeVideoAttributes(html);

    scEditor = editor;

    editor.showExternalDialog(
            "/sitecore/shell/default.aspx?xmlcontrol=RichText.InsertYoutubeVideo&id=" + attributes.youtubeId + "&width=" + attributes.videoWidth + "&height=" + attributes.videoHeight,
            null, //argument
            500,
            400,
            scInsertYoutubeVideoDialog,
            null,
            "Insert youtube video");
};


// CallBack function for InsertYoutubeVideo window
function scInsertYoutubeVideoDialog(sender, returnValue) {
    if (!returnValue) {
        return;
    }
   
    var htmlCode = "<iframe width=\"" + returnValue.videoWidth + "\" height=\"" + returnValue.videoHeight + "\" src=\"http://www.youtube.com/embed/" + returnValue.youtubeId + "\" frameborder=\"0\" allowfullscreen=\"true\"></iframe>";

    scEditor.pasteHtml(htmlCode, "DocumentManager");
}

GetYoutubeVideoAttributes = function (html) {
    var returnValue = {
        youtubeId: "",
        videoWidth: "",
        videoHeight: ""
    };
   
    var pattern = "<iframe width=\"(.*)\" height=\"(.*)\" src=\"http://www.youtube.com/embed/(.*)\" frameborder=\"0\" allowfullscreen=\"true\"></iframe>";
    var regex = new RegExp(pattern, 'm');
    var match = regex.exec(html);
    if (match && match.length >= 1) {
        if (match[1]) {
            returnValue.videoWidth = match[1];
        }
        if (match[2]) {
            returnValue.videoHeight = match[2];
        }
        if (match[3]) {
            returnValue.youtubeId = match[3];
        }
    }

    return returnValue;
}

Resultat:

Der erstellte Dialog bietet den Autoren eine komfortable Möglichkeit, Youtube-Videos als Iframe in einen Text einzufügen. Wird ein eingefügtes Video selektiert und erneut der Button betätigt, so sind die zuvor eingetragenen Werte im Dialog bereits automatisch eingetragen.