/// <summary> /// Renders the base control. /// </summary> /// <param name="writer">The writer.</param> public void RenderBaseControl(HtmlTextWriter writer) { int editorHeight = EditorHeight.AsIntegerOrNull() ?? 200; // Add merge field help if (MergeFields.Any()) { writer.Write("<div class='codeeditor-header margin-b-md clearfix'>"); _mfpMergeFields.RenderControl(writer); writer.Write("</div>"); editorHeight = editorHeight - 40; } // add editor div string customDiv = @"<div class='code-editor-container' style='position:relative; height: {0}px'><pre id='codeeditor-div-{1}'>{2}</pre></div>"; writer.Write(string.Format(customDiv, editorHeight, this.ClientID, HttpUtility.HtmlEncode(this.Text))); // write custom css for the code editor string customStyle = @" <style type='text/css' media='screen'> #codeeditor-div-{0} {{ position: absolute; top: 0; right: 0; bottom: 0; left: 0; }} </style> "; string cssCode = string.Format(customStyle, this.ClientID); writer.Write(cssCode); // make textbox hidden ((WebControl)this).Style.Add(HtmlTextWriterStyle.Display, "none"); // add ace.js on demand only when there will be a codeeditor rendered if (ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack) { ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "ace-include", ResolveUrl("~/Scripts/ace/ace.js")); } string scriptFormat = @" var ce_{0} = ace.edit('codeeditor-div-{0}'); ce_{0}.setTheme('ace/theme/{1}'); ce_{0}.getSession().setMode('ace/mode/{2}'); ce_{0}.setShowPrintMargin(false); document.getElementById('{0}').value = $('<div/>').text( ce_{0}.getValue() ).html().replace(/'/g,""&apos""); ce_{0}.getSession().on('change', function(e) {{ document.getElementById('{0}').value = $('<div/>').text( ce_{0}.getValue() ).html().replace(/'/g,""&apos""); {3} }}); "; string script = string.Format(scriptFormat, this.ClientID, EditorThemeAsString(this.EditorTheme), EditorModeAsString(this.EditorMode), this.OnChangeScript); ScriptManager.RegisterStartupScript(this, this.GetType(), "codeeditor_" + this.ClientID, script, true); base.RenderControl(writer); }
/// <summary> /// Renders the base control. /// </summary> /// <param name="writer">The writer.</param> public void RenderBaseControl(HtmlTextWriter writer) { bool rockMergeFieldEnabled = MergeFields.Any(); bool rockFileBrowserEnabled = false; bool rockAssetManagerEnabled = false; var currentPerson = this.RockBlock().CurrentPerson; // only show the File/Image plugin if they have Auth to the file browser page var fileBrowserPage = new Rock.Model.PageService(new RockContext()).Get(Rock.SystemGuid.Page.HTMLEDITOR_ROCKFILEBROWSER_PLUGIN_FRAME.AsGuid()); if (fileBrowserPage != null && currentPerson != null) { rockFileBrowserEnabled = fileBrowserPage.IsAuthorized(Authorization.VIEW, currentPerson); } var assetManagerPage = new Rock.Model.PageService(new RockContext()).Get(Rock.SystemGuid.Page.HTMLEDITOR_ROCKASSETMANAGER_PLUGIN_FRAME.AsGuid()); if (assetManagerPage != null && currentPerson != null) { rockAssetManagerEnabled = assetManagerPage.IsAuthorized(Authorization.VIEW, currentPerson); } string documentFolderRoot = this.DocumentFolderRoot; string imageFolderRoot = this.ImageFolderRoot; if (this.UserSpecificRoot) { var currentUser = this.RockBlock().CurrentUser; if (currentUser != null) { documentFolderRoot = System.Web.VirtualPathUtility.Combine(documentFolderRoot.EnsureTrailingBackslash(), currentUser.UserName.ToString()); imageFolderRoot = System.Web.VirtualPathUtility.Combine(imageFolderRoot.EnsureTrailingBackslash(), currentUser.UserName.ToString()); } } string callbacksOption = string.Empty; if (!string.IsNullOrEmpty(this.CallbackOnKeyupScript) || !string.IsNullOrEmpty(this.CallbackOnChangeScript)) { callbacksOption = $@" onKeyup: function(e) {{ {this.CallbackOnKeyupScript} }}, onChange: function(contents, $editable) {{ {this.CallbackOnChangeScript} }} "; } string summernoteInitScript = $@" function pageLoad() {{ // remove any leftover popovers that summernote might have created and orphaned $('.note-popover.popover').hide(); }} $(document).ready( function() {{ var summerNoteEditor_{this.ClientID} = $('#{this.ClientID}').summernote({{ height: '{this.Height}', //set editable area's height toolbar: Rock.htmlEditor.toolbar_RockCustomConfig{this.Toolbar.ConvertToString()}, popover: {{ image: [ ['custom1', ['rockimagelink']], ['imagesize', ['resizeFull', 'resizeHalf', 'resizeQuarter']], ['custom2', ['rockimagebrowser', 'rockassetmanager']], ['float', ['floatLeft', 'floatRight', 'floatNone']], ['remove', ['removeMedia']] ], link: [ ['link', ['linkDialogShow', 'unlink']] ], air: [ ['color', ['color']], ['font', ['bold', 'underline', 'clear']], ['para', ['ul', 'paragraph']], ['table', ['table']], ['insert', ['link', 'picture']] ] }}, callbacks: {{ onInit: function() {{ $(this).parent().removeClass('loading').css('min-height', ''); }}, {callbacksOption} }}, buttons: {{ rockfilebrowser: RockFileBrowser, rockimagebrowser: RockImageBrowser, rockimagelink: RockImageLink, rockassetmanager: RockAssetManager, rockmergefield: RockMergeField, rockcodeeditor: RockCodeEditor, rockpastetext: RockPasteText, rockpastefromword: RockPasteFromWord }}, rockFileBrowserOptions: {{ enabled: {rockFileBrowserEnabled.ToTrueFalse().ToLower()}, documentFolderRoot: '{Rock.Security.Encryption.EncryptString( documentFolderRoot )}', imageFolderRoot: '{Rock.Security.Encryption.EncryptString( imageFolderRoot )}' }}, rockAssetManagerOptions: {{ enabled: { rockAssetManagerEnabled.ToTrueFalse().ToLower() } }}, rockMergeFieldOptions: {{ enabled: {rockMergeFieldEnabled.ToTrueFalse().ToLower()}, mergeFields: '{this.MergeFields.AsDelimited( "," )}' }}, rockTheme: '{( ( RockPage ) this.Page ).Site.Theme}', codeEditorOptions: {{ controlId: '{_ceEditor.ClientID}', inCodeEditorModeHiddenFieldId: '{_hfInCodeEditorMode.ClientID}' }}, disableDragAndDrop: true, }}); if ({StartInCodeEditorMode.ToTrueFalse().ToLower()} && RockCodeEditor) {{ RockCodeEditor(summerNoteEditor_{this.ClientID}.data('summernote'), true).trigger('click'); }} }}); "; ScriptManager.RegisterStartupScript(this, this.GetType(), "summernote_init_script_" + this.ClientID, summernoteInitScript, true); // add script on demand only when there will be an htmleditor rendered if (ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack) { ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "summernote-lib", ((RockPage)this.Page).ResolveRockUrl("~/Scripts/summernote/summernote.js", true)); var bundleUrl = System.Web.Optimization.BundleResolver.Current.GetBundleUrl("~/Scripts/Bundles/RockHtmlEditorPlugins"); ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "summernote-plugins", bundleUrl); } // set this textbox hidden until we can run the js to attach summernote to it this.Style[HtmlTextWriterStyle.Display] = "none"; writer.AddAttribute("class", "html-editor-container loading"); writer.AddStyleAttribute("min-height", this.Height.ToString()); writer.RenderBeginTag(HtmlTextWriterTag.Div); base.RenderControl(writer); writer.RenderEndTag(); }
/// <summary> /// Renders the base control. /// </summary> /// <param name="writer">The writer.</param> public void RenderBaseControl(HtmlTextWriter writer) { int editorHeight = EditorHeight.AsIntegerOrNull() ?? 200; // Add merge field help if (MergeFields.Any()) { writer.Write("<div class='codeeditor-header margin-b-md clearfix'>"); _mfpMergeFields.RenderControl(writer); writer.Write("</div>"); editorHeight = editorHeight - 40; } // add editor div var encodedText = HttpUtility.HtmlEncode(this.Text); string customDiv = $@"<div class='code-editor-container {this.CssClass}' style='position:relative; height: {editorHeight}px'><pre id='codeeditor-div-{this.ClientID}'>{encodedText}</pre></div>"; writer.Write(customDiv); // write custom css for the code editor string styleTag = $@" <style type='text/css' media='screen'> #codeeditor-div-{this.ClientID} {{ position: absolute; top: 0; right: 0; bottom: 0; left: 0; }} </style> "; writer.Write(styleTag); // make textbox hidden ((WebControl)this).Style.Add(HtmlTextWriterStyle.Display, "none"); // add ace.js on demand only when there will be a codeeditor rendered if (ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack) { ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "ace-include", ResolveUrl("~/Scripts/ace/ace.js")); } string scriptFormat = @" var ce_{0} = ace.edit('codeeditor-div-{0}'); ce_{0}.setTheme('ace/theme/{1}'); ce_{0}.getSession().setMode('ace/mode/{2}'); ce_{0}.getSession().setUseWrapMode({7}); ce_{0}.setShowPrintMargin(false); ce_{0}.commands.addCommand({{ name: 'Toggle Fullscreen', bindKey: 'F11', exec: function(editor) {{ Rock.controls.fullScreen.toggleFullscreen(editor.container); editor.resize() }} }}); $('#codeeditor-div-{0}').data('aceEditor', ce_{0}); document.getElementById('{0}').value = $('<div/>').text( ce_{0}.getValue() ).html().replace(/'/g,""&apos""); ce_{0}.getSession().on('change', function(e) {{ // get the raw content from the codeEditor var contents = ce_{0}.getValue(); // set the input element value to the escaped value of contents document.getElementById('{0}').value = $('<div/>').text( contents ).html().replace(/'/g,""&apos""); {3} }}); // disable warning about block scrolling ce_{0}.$blockScrolling = Infinity; ce_{0}.setReadOnly({4}); ce_{0}.on('blur', function(e) {{ {5} }}); // make sure the editor is sized correctly (fixes an issue when editor is used in a modal) setTimeout(function () {{ ce_{0}.resize(); }}, 0); {6} "; string script = string.Format( scriptFormat, this.ClientID, // {0} EditorThemeAsString(this.EditorTheme), // {1} EditorModeAsString(this.EditorMode), // {2} this.OnChangeScript, // {3} this.ReadOnly.ToTrueFalse().ToLower(), // {4} this.OnBlurScript, // {5} this.OnLoadCompleteScript, // {6} this.LineWrap.ToString().ToLower() // {7} ); ScriptManager.RegisterStartupScript(this, this.GetType(), "codeeditor_" + this.ClientID, script, true); base.RenderControl(writer); }
/// <summary> /// Renders the base control. /// </summary> /// <param name="writer">The writer.</param> public void RenderBaseControl(HtmlTextWriter writer) { // NOTE: Some of the plugins in the Full (72 plugin) build of CKEditor are buggy, so we are just using the Standard edition. // This is why some of the items don't appear in the RockCustomConfiguFull toolbar (like the Justify commands) string ckeditorInitScriptFormat = @" // ensure that ckEditor.js link is added to page if (!$('#ckeditorJsLib').length) {{ // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this $.ajaxSetup({{ cache: true }}); $('head').prepend(""<script id='ckeditorJsLib' src='{12}' />""); }} // allow i tags to be empty (for font awesome) CKEDITOR.dtd.$removeEmpty['i'] = false // In IE, the CKEditor doesn't accept keyboard input when loading again within the same page instance. Destroy fixes it, but destroy throws an exception in Chrome if (CKEDITOR.instances.{0}) {{ try {{ CKEDITOR.instances.{0}.destroy(); }} catch (ex) {{ // ignore error }} }} CKEDITOR.replace('{0}', {{ {11} allowedContent: true, toolbar: Rock.htmlEditor.toolbar_RockCustomConfig{1}, removeButtons: '', baseFloatZIndex: 200000, // set zindex to be 200000 so it will be on top of our modals (100000) entities: false, // stop CKEditor from using HTML entities in the editor output. Prevents single quote from getting escaped, etc htmlEncodeOutput: true, extraPlugins: '{5}', resize_maxWidth: '{3}', rockFileBrowserOptions: {{ documentFolderRoot: '{6}', imageFolderRoot: '{7}', imageFileTypeWhiteList: '{8}', fileTypeBlackList: '{9}' }}, rockMergeFieldOptions: {{ mergeFields: '{10}' }}, rockTheme: '{13}', on : {{ change: function (e) {{ // update the underlying TextElement on every little change (when in WYSIWIG mode) to ensure that Posting and Validation works consistently (doing it OnSubmit or OnBlur misses some cases) e.editor.updateElement(); {4} }}, instanceReady: function (e) {{ CKEDITOR.instances.{0}.updateElement(); // update the underlying TextElement when there is a change event in SOURCE mode $('#cke_{0}').on( 'change paste', '.cke_source', function(e, data) {{ CKEDITOR.instances.{0}.updateElement(); }}); // In IE, clicking the Source button does not cause the .cke_source to lose focus // and fire the onchange event, so also updateElement when source button is clicked $('#cke_{0} .cke_button__source').click( function(e, data) {{ CKEDITOR.instances.{0}.updateElement(); }}); // set the height if ('{2}' != '') {{ var topHeight = $('#' + e.editor.id + '_top').height(); var contentHeight = '{2}'.replace('px','') - topHeight - 40; $('#' + e.editor.id + '_contents').css('height', contentHeight); }} }} }} }} ); "; string customOnChangeScript = null; if (!string.IsNullOrWhiteSpace(this.OnChangeScript)) { customOnChangeScript = @" // custom on change script " + this.OnChangeScript; } List <string> enabledPlugins = new List <string>(); if (MergeFields.Any()) { enabledPlugins.Add("rockmergefield"); } // only show the File/Image plugin if they have Auth to the file browser page var fileBrowserPage = new Rock.Model.PageService(new RockContext()).Get(Rock.SystemGuid.Page.CKEDITOR_ROCKFILEBROWSER_PLUGIN_FRAME.AsGuid()); if (fileBrowserPage != null) { var currentPerson = this.RockBlock().CurrentPerson; if (currentPerson != null) { if (fileBrowserPage.IsAuthorized(Authorization.VIEW, currentPerson)) { enabledPlugins.Add("rockfilebrowser"); } } } var globalAttributesCache = GlobalAttributesCache.Read(); string imageFileTypeWhiteList = globalAttributesCache.GetValue("ContentImageFiletypeWhitelist"); string fileTypeBlackList = globalAttributesCache.GetValue("ContentFiletypeBlacklist"); string documentFolderRoot = this.DocumentFolderRoot; string imageFolderRoot = this.ImageFolderRoot; if (this.UserSpecificRoot) { var currentUser = this.RockBlock().CurrentUser; if (currentUser != null) { documentFolderRoot = System.Web.VirtualPathUtility.Combine(documentFolderRoot.EnsureTrailingBackslash(), currentUser.UserName.ToString()); imageFolderRoot = System.Web.VirtualPathUtility.Combine(imageFolderRoot.EnsureTrailingBackslash(), currentUser.UserName.ToString()); } } // Make sure that if additional configurations are defined, that the string ends in a comma. if (!string.IsNullOrWhiteSpace(this.AdditionalConfigurations) && !this.AdditionalConfigurations.Trim().EndsWith(",")) { this.AdditionalConfigurations = this.AdditionalConfigurations.Trim() + ","; } string ckEditorLib = ((RockPage)this.Page).ResolveRockUrl("~/Scripts/ckeditor/ckeditor.js", true); string ckeditorInitScript = string.Format(ckeditorInitScriptFormat, this.ClientID, // {0} this.Toolbar.ConvertToString(), // {1} this.Height, // {2} this.ResizeMaxWidth ?? 0, // {3} customOnChangeScript, // {4} enabledPlugins.AsDelimited(","), // {5} Rock.Security.Encryption.EncryptString(documentFolderRoot), // {6} encrypt the folders so the folder can only be configured on the server Rock.Security.Encryption.EncryptString(imageFolderRoot), // {7} imageFileTypeWhiteList, // {8} fileTypeBlackList, // {9} this.MergeFields.AsDelimited(","), // {10} this.AdditionalConfigurations, // {11} ckEditorLib, // {12} ((RockPage)this.Page).Site.Theme // {13} ); ScriptManager.RegisterStartupScript(this, this.GetType(), "ckeditor_init_script_" + this.ClientID, ckeditorInitScript, true); base.RenderControl(writer); }
/// <summary> /// Renders the base control. /// </summary> /// <param name="writer">The writer.</param> public void RenderBaseControl(HtmlTextWriter writer) { string summernoteInitScriptFormat = @" $(document).ready( function() {{ var summerNoteEditor_{0} = $('#{0}').summernote({{ height: '{2}', //set editable area's height toolbar: Rock.htmlEditor.toolbar_RockCustomConfig{11}, popover: {{ image: [ ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']], ['custom', ['rockimagebrowser']], ['float', ['floatLeft', 'floatRight', 'floatNone']], ['remove', ['removeMedia']] ], link: [ ['link', ['linkDialogShow', 'unlink']] ], air: [ ['color', ['color']], ['font', ['bold', 'underline', 'clear']], ['para', ['ul', 'paragraph']], ['table', ['table']], ['insert', ['link', 'picture']] ] }}, callbacks: {{ {12} }}, buttons: {{ rockfilebrowser: RockFileBrowser, rockimagebrowser: RockImageBrowser, rockmergefield: RockMergeField, rockcodeeditor: RockCodeEditor, rockpastetext: RockPasteText, rockpastefromword: RockPasteFromWord }}, rockFileBrowserOptions: {{ enabled: {3}, documentFolderRoot: '{4}', imageFolderRoot: '{5}', imageFileTypeWhiteList: '{6}', fileTypeBlackList: '{7}' }}, rockMergeFieldOptions: {{ enabled: {9}, mergeFields: '{8}' }}, rockTheme: '{10}', codeEditorOptions: {{ controlId: '{13}', inCodeEditorModeHiddenFieldId: '{14}' }}, // summernote-cleaner.js plugin options cleaner:{{ el:'#{0}', // Element ID or Class used to Initialise Summernote. notTime:2400, // Time to display Notifications. action:'paste', // both|button|paste 'button' only cleans via toolbar button, 'paste' only clean when pasting content, both does both options. }} }}); if ({15} && RockCodeEditor) {{ RockCodeEditor(summerNoteEditor_{0}.data('summernote'), true).click(); }} }}); "; bool rockMergeFieldEnabled = MergeFields.Any(); bool rockFileBrowserEnabled = false; // only show the File/Image plugin if they have Auth to the file browser page var fileBrowserPage = new Rock.Model.PageService(new RockContext()).Get(Rock.SystemGuid.Page.HTMLEDITOR_ROCKFILEBROWSER_PLUGIN_FRAME.AsGuid()); if (fileBrowserPage != null) { var currentPerson = this.RockBlock().CurrentPerson; if (currentPerson != null) { if (fileBrowserPage.IsAuthorized(Authorization.VIEW, currentPerson)) { rockFileBrowserEnabled = true; } } } var globalAttributesCache = GlobalAttributesCache.Read(); string imageFileTypeWhiteList = globalAttributesCache.GetValue("ContentImageFiletypeWhitelist"); string fileTypeBlackList = globalAttributesCache.GetValue("ContentFiletypeBlacklist"); string documentFolderRoot = this.DocumentFolderRoot; string imageFolderRoot = this.ImageFolderRoot; if (this.UserSpecificRoot) { var currentUser = this.RockBlock().CurrentUser; if (currentUser != null) { documentFolderRoot = System.Web.VirtualPathUtility.Combine(documentFolderRoot.EnsureTrailingBackslash(), currentUser.UserName.ToString()); imageFolderRoot = System.Web.VirtualPathUtility.Combine(imageFolderRoot.EnsureTrailingBackslash(), currentUser.UserName.ToString()); } } string callbacksOption = null; if (!string.IsNullOrEmpty(this.OnChangeScript)) { callbacksOption = string.Format( @" onKeyup: function() {{ {0} }}", this.OnChangeScript); } string summernoteInitScript = string.Format( summernoteInitScriptFormat, this.ClientID, // {0} null, // {1} this.Height, // {2} rockFileBrowserEnabled.ToTrueFalse().ToLower(), // {3} Rock.Security.Encryption.EncryptString(documentFolderRoot), // {4} encrypt the folders so the folder can only be configured on the server Rock.Security.Encryption.EncryptString(imageFolderRoot), // {5} imageFileTypeWhiteList, // {6} fileTypeBlackList, // {7} this.MergeFields.AsDelimited(","), // {8} rockMergeFieldEnabled.ToTrueFalse().ToLower(), // {9} ((RockPage)this.Page).Site.Theme, // {10} this.Toolbar.ConvertToString(), // {11} callbacksOption, // {12} _ceEditor.ClientID, // {13} _hfInCodeEditorMode.ClientID, // {14} StartInCodeEditorMode.ToTrueFalse().ToLower() // {15} ); ScriptManager.RegisterStartupScript(this, this.GetType(), "summernote_init_script_" + this.ClientID, summernoteInitScript, true); // add script on demand only when there will be an htmleditor rendered if (ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack) { ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "summernote-lib", ((RockPage)this.Page).ResolveRockUrl("~/Scripts/summernote/summernote.min.js", true)); var bundleUrl = System.Web.Optimization.BundleResolver.Current.GetBundleUrl("~/Scripts/Bundles/RockHtmlEditorPlugins"); ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "summernote-plugins", bundleUrl); } // set this textbox hidden until we can run the js to attach summernote to it this.Style[HtmlTextWriterStyle.Display] = "none"; base.RenderControl(writer); }