I will try the zip on next saturday, and I will post here the result. Sorry for the delay but I'm working with Umbraco for my new company site, and so I can work on It only in the week-end.
I have changed your uCKEditor.controller.js to load the sylesheets configured in the grid section (just copied a part from built in umbraco.controllers.js).
Perhaps not the cleanest way but not too much dirty. I post the code because I didn't find the way to add an attachment.
With this code we can load the stylesheets you checked in the grid configuration.
Regards
Marco
angular.module("umbraco")
.controller("uCKEditor.uCKEditorController", function ($scope, $q, assetsService, dialogService, $log, $timeout) {
// Check whether the property editor's label should be hidden
if ($scope.model.config.hideLabel == 1) {
$scope.model.hideLabel = true;
}
var await = [];
// Check whether CKEditor is already loaded before loading it
if (typeof CKEDITOR === "undefined") { // Don't reload ckEditor if it is already loaded
await.push(assetsService.loadJs('/App_Plugins/uCKEditor/CKEditor/ckeditor.js', $scope));
}
//StyleSheets of RTE Editor configured in Grid
var stylesheets = [];
//queue rules loading
angular.forEach($scope.model.config.rte.stylesheets, function (val, key) {
stylesheets.push("../css/" + val + ".css?" + new Date().getTime());
});
// Wait for queue to end
$q.all(await).then(function () {
// Textaread ID used by the editor
var editorTextAreaId = 'editorPlaceholder';
// Sleep 2 milliseconds in order to ensure different Ids
sleep(2);
// Assign a UniqueId for the textarea (in case there is more than one editor in the same form)
var date = new Date();
var uniqueID = "" + date.getDate() + date.getHours() + date.getMinutes() + date.getSeconds() + date.getMilliseconds();
$('#' + editorTextAreaId).attr('id', uniqueID);
editorTextAreaId = uniqueID;
// Create the CKEditor control
var editor;
// Loads plugin (UmbracoMedia, UmbracoEmbed, ...)
if (CKEDITOR.config.plugins != null && CKEDITOR.config.plugins != 'undefined' && jQuery.trim(CKEDITOR.config.plugins) != '')
CKEDITOR.config.plugins += ',umbracomedia,umbracoembed'; /*umbracomediatagger*/
else
CKEDITOR.config.plugins = 'umbracomedia,umbracoembed'; /*umbracomediatagger*/
if ($scope.model.config.customConfigurationFile != null && jQuery.trim($scope.model.config.customConfigurationFile) != '') {
// Create the editor using the custom configuration file
editor = CKEDITOR.replace(editorTextAreaId, {
customConfig: $scope.model.config.customConfigurationFile
});
}
else {
// Create the editor using the other setting
CKEDITOR.config.width = $scope.model.config.width;
CKEDITOR.config.height = $scope.model.config.height;
if ($scope.model.config.language != null && jQuery.trim($scope.model.config.language) != '') {
CKEDITOR.config.language = $scope.model.config.language;
}
if ($scope.model.config.font_names != null && jQuery.trim($scope.model.config.font_names) != '') {
CKEDITOR.config.font_names = $scope.model.config.font_names;
}
if ($scope.model.config.font_style != null && jQuery.trim($scope.model.config.font_style) != '') {
CKEDITOR.config.font_style = $scope.model.config.font_style;
}
if ($scope.model.config.format_tags != null && jQuery.trim($scope.model.config.format_tags) != '') {
CKEDITOR.config.format_tags = $scope.model.config.format_tags;
}
if ($scope.model.config.allowedContent != null && jQuery.trim($scope.model.config.allowedContent) != '') {
CKEDITOR.config.allowedContent = $scope.model.config.allowedContent;
}
if ($scope.model.config.extraAllowedContent != null && jQuery.trim($scope.model.config.extraAllowedContent) != '') {
CKEDITOR.config.extraAllowedContent = $scope.model.config.extraAllowedContent;
}
if ($scope.model.config.toolbar != null && jQuery.trim($scope.model.config.toolbar) != '') {
CKEDITOR.config.toolbar = eval("[['umbracomedia,umbracoembed'], " + $scope.model.config.toolbar + ",]"); /*umbracomediatagger*/
}
if ($scope.model.config.toolbarGroups != null && jQuery.trim($scope.model.config.toolbarGroups) != '') {
CKEDITOR.config.toolbarGroups = eval("[{name: 'umbraco', groups: ['umbraco']}, " + $scope.model.config.toolbarGroups + ",]");
}
if ($scope.model.config.removeButtons != null && jQuery.trim($scope.model.config.removeButtons) != '') {
CKEDITOR.config.removeButtons = $scope.model.config.removeButtons;
}
if ($scope.model.config.extraPlugins != null && jQuery.trim($scope.model.config.extraPlugins) != '') {
CKEDITOR.config.extraPlugins = $scope.model.config.extraPlugins;
}
if ($scope.model.config.removePlugins != null && jQuery.trim($scope.model.config.removePlugins) != '') {
CKEDITOR.config.removePlugins = $scope.model.config.removePlugins;
}
if ($scope.model.config.stylesSet != null && jQuery.trim($scope.model.config.stylesSet) != '') {
CKEDITOR.config.stylesSet = $scope.model.config.stylesSet;
}
/* Addition to pass CSS configured in the rte section of the grid */
if (stylesheets.length > 0) {
CKEDITOR.config.contentsCss = stylesheets;
}
editor = CKEDITOR.replace(editorTextAreaId, CKEDITOR.config);
}
// If toolbars haven't been customized then add umbraco toolbar group to the default CKEditor toolbar
if ((CKEDITOR.config.toolbarGroups == null || CKEDITOR.config.toolbarGroups == 'undefined' || jQuery.trim(CKEDITOR.config.toolbarGroups) == '') &&
(CKEDITOR.config.toolbar == null || CKEDITOR.config.toolbar == 'undefined' || jQuery.trim(CKEDITOR.config.toolbar) == '')) {
editor.ui.addToolbarGroup('umbraco', 0);
}
// Get the internal texteditor ID
var editorId = 'cke_' + editorTextAreaId;
// Get UmbracoMedia plugin's button IDs
var editorButtonMediaIdSelector = '#' + editorId + ' .cke_button__umbracomedia';
// Hook the click event for the UmbracoMedia plugin's button
$(document).on('click', editorButtonMediaIdSelector, function () {
// Open Umbraco's media picker dialog
dialogService.mediaPicker({
// Media picker dialog settings
onlyImages: true,
showDetails: true,
// Media picker callback
callback: function (data) {
// Check whether an image has been selected
if (data) {
// Selected image
var selectedImage = {
alt: data.altText,
src: (data.url) ? data.url : '/App_Plugins/uCKEditor/CKEditor/plugins/umbracomedia/images/noimage.png',
rel: data.id
};
// Create an html img tag with the picked image properties to insert into the editor
var htmlImage = editor.document.createElement('img');
htmlImage.setAttribute('src', selectedImage.src);
htmlImage.setAttribute('alt', selectedImage.alt);
editor.insertElement(htmlImage)
};
}
});
});
// Get UmbracoEmbed plugin's button IDs
var editorButtonEmbedIdSelector = '#' + editorId + ' .cke_button__umbracoembed';
// Hook the click event for the UmbracoEmbed plugin's button
$(document).on('click', editorButtonEmbedIdSelector, function () {
// Open Umbraco's Embed dialog
dialogService.embedDialog({
// Embed dialog callback
callback: function (data) {
// Insert embed element
if (data) {
var embedElement = CKEDITOR.dom.element.createFromHtml(data, editor.document);
editor.insertElement(embedElement);
};
}
});
});
// Get UmbracoMediaTagger plugin's button IDs
var editorButtonMediaTaggerIdSelector = '#' + editorId + ' .cke_button__umbracomediatagger';
// Hook the click event for the UmbracoMediaTagger plugin's button
$(document).on('click', editorButtonMediaTaggerIdSelector, function () {
// Open Umbraco's media tagger picker dialog
dialogService.open({
// Dialog
template: '/App_Plugins/MediaTagger/Dialog/_dialog.html',
show: true,
// Media tagger picker callback
callback: function (data) {
// Check whether an image has been selected
if (data) {
// Selected image
var selectedImage = {
alt: '',
src: (data.image) ? data.image : '/App_Plugins/uCKEditor/CKEditor/plugins/umbracomediatagger/images/noimage.png',
rel: data.id
};
// Create an html img tag with the picked image properties to insert into the editor
var htmlImage = editor.document.createElement('img');
htmlImage.setAttribute('src', selectedImage.src);
htmlImage.setAttribute('alt', selectedImage.alt);
editor.insertElement(htmlImage)
};
}
});
});
// Set editor's value (when loading)
editor.setData($scope.control.value);
// Detects changes in the editor and updates the model
editor.on('change', function () {
$scope.control.value = editor.getData();
setTimeout(function () {
$scope.$apply();
}, 0);
});
// Save editor's value (when submitting)
$scope.$on("formSubmitting", function (ev, args) {
$scope.control.value = editor.getData();
});
// Hook the destroy event in order to destroy the CKEditor instances
$scope.$on("$destroy", function () {
try {
// Check wheter uCKeditor is inside an Archetype property in order to not destroy the instance
if (!($scope && $scope.$parent && $scope.$parent.archetypeConfig)) {
// Destroy All CKEditors
for (var instanceName in CKEDITOR.instances) {
var instanceEditor = CKEDITOR.instances[instanceName];
if (instanceEditor) {
instanceEditor.destroy(true);
instanceEditor = null;
}
}
}
// If the editor still exists (which is the case for the editors inside an Archetype property) then try to destroy it
if (editor) {
editor.destroy(false);
editor = null;
}
}
catch (err) {
}
});
});
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
});
Still struggeling to get the editor to work inside the umbraco grid.
I unpacked the uCKEditor.grip.zip inside the App_plugins folder and followed every step Marco and Alain took but the editor won't render on the website (in the backend grid it renders and works).
Here is the error I get displayed on the front-end:
System.Web.HttpCompileException (0x80004005): c:\test-website\App_Plugins\uCKEditor.grid\editor.cshtml(1): error CS0103: Der Name 'model' ist im aktuellen Kontext nicht vorhanden.
at System.Web.Compilation.BuildManager.PostProcessFoundBuildResult(BuildResult result, Boolean keyFromVPP, VirtualPath virtualPath)
at System.Web.Compilation.BuildManager.GetBuildResultFromCacheInternal(String cacheKey, Boolean keyFromVPP, VirtualPath virtualPath, Int64 hashCode, Boolean ensureIsUpToDate)
at System.Web.Compilation.BuildManager.GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, Boolean ensureIsUpToDate)
at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
at System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(VirtualPath virtualPath, HttpContext context, Boolean allowCrossApp, Boolean throwIfNotFound)
at System.Web.Mvc.BuildManagerWrapper.System.Web.Mvc.IBuildManager.FileExists(String virtualPath)
at Microsoft.Web.Mvc.ViewEngineFixWorker`1.GetPathFromSpecificName(ControllerContext controllerContext, String name, String cacheKey, String[]& searchedLocations)
at Microsoft.Web.Mvc.ViewEngineFixWorker`1.GetPath(ControllerContext controllerContext, String[] locations, String[] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String[]& searchedLocations)
at Microsoft.Web.Mvc.ViewEngineFixWorker`1.FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache)
at Umbraco.Core.Profiling.ProfilingViewEngine.FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache)
at System.Web.Mvc.ViewEngineCollection.Find(Func`2 lookup, Boolean trackSearchedPaths)
at System.Web.Mvc.HtmlHelper.FindPartialView(ViewContext viewContext, String partialViewName, ViewEngineCollection viewEngineCollection)
at System.Web.Mvc.HtmlHelper.RenderPartialInternal(String partialViewName, ViewDataDictionary viewData, Object model, TextWriter writer, ViewEngineCollection viewEngineCollection)
at System.Web.Mvc.Html.PartialExtensions.Partial(HtmlHelper htmlHelper, String partialViewName, Object model, ViewDataDictionary viewData)
at ASP._Page_Views_Partials_grid_editors_base_cshtml.Execute() in c:\test-website\Views\Partials\Grid\Editors\Base.cshtml:line 20
Would love to finally get tid of the RTE and fully use the uCKEditor.
'/App_Plugins/uCKEditor/uCKEditorGrid.controller.js' looks like:
(This is the same as previous stated in this discussion.)
angular.module("umbraco")
.controller("uCKEditor.uCKEditorGridController", function ($scope, $q, assetsService, dialogService, $log, $timeout) {
var await = [];
// Check whether CKEditor is already loaded before loading it
if (typeof CKEDITOR === "undefined") { // Don't reload ckEditor if it is already loaded
await.push(assetsService.loadJs('/App_Plugins/uCKEditor/CKEditor/ckeditor.js', $scope));
}
//StyleSheets of RTE Editor configured in Grid
var stylesheets = [];
//queue rules loading
angular.forEach($scope.model.config.rte.stylesheets, function (val, key) {
stylesheets.push("../css/" + val + ".css?" + new Date().getTime());
});
// Wait for queue to end
$q.all(await).then(function () {
// Textaread ID used by the editor
var editorTextAreaId = 'editorPlaceholder';
// Sleep 2 milliseconds in order to ensure different Ids
sleep(2);
// Assign a UniqueId for the textarea (in case there is more than one editor in the same form)
var date = new Date();
var uniqueID = "" + date.getDate() + date.getHours() + date.getMinutes() + date.getSeconds() + date.getMilliseconds();
$('#' + editorTextAreaId).attr('id', uniqueID);
editorTextAreaId = uniqueID;
// Create the CKEditor control
var editor;
// Loads plugin (UmbracoMedia, UmbracoEmbed, ...)
if (CKEDITOR.config.plugins != null && CKEDITOR.config.plugins != 'undefined' && jQuery.trim(CKEDITOR.config.plugins) != '')
CKEDITOR.config.plugins += ',umbracomedia,umbracoembed'; /*umbracomediatagger*/
else
CKEDITOR.config.plugins = 'umbracomedia,umbracoembed'; /*umbracomediatagger*/
/* Addition to pass CSS configured in the rte section of the grid */
if (stylesheets.length > 0) {
CKEDITOR.config.contentsCss = stylesheets;
}
editor = CKEDITOR.replace(editorTextAreaId, {
customConfig: '/config/uCKEditorGridconfig.js'
});
// If toolbars haven't been customized then add umbraco toolbar group to the default CKEditor toolbar
if ((CKEDITOR.config.toolbarGroups == null || CKEDITOR.config.toolbarGroups == 'undefined' || jQuery.trim(CKEDITOR.config.toolbarGroups) == '') &&
(CKEDITOR.config.toolbar == null || CKEDITOR.config.toolbar == 'undefined' || jQuery.trim(CKEDITOR.config.toolbar) == '')) {
editor.ui.addToolbarGroup('umbraco', 0);
}
// Get the internal texteditor ID
var editorId = 'cke_' + editorTextAreaId;
// Get UmbracoMedia plugin's button IDs
var editorButtonMediaIdSelector = '#' + editorId + ' .cke_button__umbracomedia';
// Hook the click event for the UmbracoMedia plugin's button
$(document).on('click', editorButtonMediaIdSelector, function () {
// Open Umbraco's media picker dialog
dialogService.mediaPicker({
// Media picker dialog settings
onlyImages: true,
showDetails: true,
// Media picker callback
callback: function (data) {
// Check whether an image has been selected
if (data) {
// Selected image
var selectedImage = {
alt: data.altText,
src: (data.url) ? data.url : '/App_Plugins/uCKEditor/CKEditor/plugins/umbracomedia/images/noimage.png',
rel: data.id
};
// Create an html img tag with the picked image properties to insert into the editor
var htmlImage = editor.document.createElement('img');
htmlImage.setAttribute('src', selectedImage.src);
htmlImage.setAttribute('alt', selectedImage.alt);
editor.insertElement(htmlImage)
};
}
});
});
// Get UmbracoEmbed plugin's button IDs
var editorButtonEmbedIdSelector = '#' + editorId + ' .cke_button__umbracoembed';
// Hook the click event for the UmbracoEmbed plugin's button
$(document).on('click', editorButtonEmbedIdSelector, function () {
// Open Umbraco's Embed dialog
dialogService.embedDialog({
// Embed dialog callback
callback: function (data) {
// Insert embed element
if (data) {
var embedElement = CKEDITOR.dom.element.createFromHtml(data, editor.document);
editor.insertElement(embedElement);
};
}
});
});
// Get UmbracoMediaTagger plugin's button IDs
var editorButtonMediaTaggerIdSelector = '#' + editorId + ' .cke_button__umbracomediatagger';
// Hook the click event for the UmbracoMediaTagger plugin's button
$(document).on('click', editorButtonMediaTaggerIdSelector, function () {
// Open Umbraco's media tagger picker dialog
dialogService.open({
// Dialog
template: '/App_Plugins/MediaTagger/Dialog/_dialog.html',
show: true,
// Media tagger picker callback
callback: function (data) {
// Check whether an image has been selected
if (data) {
// Selected image
var selectedImage = {
alt: '',
src: (data.image) ? data.image : '/App_Plugins/uCKEditor/CKEditor/plugins/umbracomediatagger/images/noimage.png',
rel: data.id
};
// Create an html img tag with the picked image properties to insert into the editor
var htmlImage = editor.document.createElement('img');
htmlImage.setAttribute('src', selectedImage.src);
htmlImage.setAttribute('alt', selectedImage.alt);
editor.insertElement(htmlImage)
};
}
});
});
// Set editor's value (when loading)
editor.setData($scope.control.value);
// Detects changes in the editor and updates the model
editor.on('change', function () {
$scope.control.value = editor.getData();
setTimeout(function () {
$scope.$apply();
}, 0);
});
// Save editor's value (when submitting)
$scope.$on("formSubmitting", function (ev, args) {
$scope.control.value = editor.getData();
});
// Hook the destroy event in order to destroy the CKEditor instances
$scope.$on("$destroy", function () {
try {
// Check wheter uCKeditor is inside an Archetype property in order to not destroy the instance
if (!($scope && $scope.$parent && $scope.$parent.archetypeConfig)) {
// Destroy All CKEditors
for (var instanceName in CKEDITOR.instances) {
var instanceEditor = CKEDITOR.instances[instanceName];
if (instanceEditor) {
instanceEditor.destroy(true);
instanceEditor = null;
}
}
}
// If the editor still exists (which is the case for the editors inside an Archetype property) then try to destroy it
if (editor) {
editor.destroy(false);
editor = null;
}
}
catch (err) {
}
});
});
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
});
My '/Config/uCKEditorGridconfig.js' looks almost like a normal CKEditor config file. What's allowed in here you can find in the normal documentation of the CKEditor.
What i have her is just an example. Change everything according to CKEditor-possibilities.
By implementing the above i noticed that caching can be very persistent. Browser-caching as well as server-caching. I even sometimes needed to restart the app-pool because IIS was also doing some caching!
Grid Layout Compatibility
Hi, I tried to make uCKEditor working with the new Grid Layout, but I have no luck,
I modified grid.editors.config.js adding
but this is not enough. I can see the editor but at the first key press, it disappear. Do you have any suggestion?
Hi Marco,
I will add this to uCKEditor's roadmap, but cannot specify a release date.
I just have a look at how to create custom property editors for the grid and found the following thread: https://our.umbraco.org/forum/umbraco-7/using-umbraco-7/56496-Custom-editors-in-grid-%28Umbraco-72%29?p=0
and: https://github.com/umbraco/Umbraco4Docs/blob/master/Documentation/Using-Umbraco/Backoffice-Overview/Property-Editors/Built-in-Property-Editors-v7/Grid-Layout.md
It seems it requires some tweaking ... but not too complex.
Can you try if it works?
Alain
Hi Marco,
Download and unzip the following file https://our.umbraco.org/FileDownload?id=13309 into the directory ~/App_plugins/uCKEditor.grid/
Add the settings below to the grid's config file (~/config/grid.editors.config.js):
That's it. It should work.
That is only for the backoffice and you still need to create a .cshtml view file to render the content in the front-end.
Alain
Hi Alain,
Thank you for the quick response.
I will try the zip on next saturday, and I will post here the result. Sorry for the delay but I'm working with Umbraco for my new company site, and so I can work on It only in the week-end.
Marco
Alain, could you post the link to the zip file again please?
I seems to be a broken link, even if copied manually.
Thanks
Marco
I have updated the previous post with the correct link
Alain
Hi Alain, it works fine.
my Partials/Grid/Editors/uCKEditor.cshtml
in grid.editors.config.js
I have changed your uCKEditor.controller.js to load the sylesheets configured in the grid section (just copied a part from built in umbraco.controllers.js). Perhaps not the cleanest way but not too much dirty. I post the code because I didn't find the way to add an attachment. With this code we can load the stylesheets you checked in the grid configuration.
Regards Marco
Hi Marco,
Thanks for letting me know and sharing your changes.
When I have time, I will try to create a version compatible with the new grid property editor.
Alain
Hey,
are there any updates to this?
Still struggeling to get the editor to work inside the umbraco grid.
I unpacked the uCKEditor.grip.zip inside the App_plugins folder and followed every step Marco and Alain took but the editor won't render on the website (in the backend grid it renders and works).
Here is the error I get displayed on the front-end:
Would love to finally get tid of the RTE and fully use the uCKEditor.
Best regards,
Chris
Hi, i have an implementation for the uCKEditor in a Grid working. Maybe someone likes it, so i post it here.
For my solution i did the following: I changed 2 existing files. -- /App_Plugins/uCKEditor/package.manifest -- /Config/grid.editors.config.js
I added 2 files within the uCKEditor-plugin. -- /AppPlugins/uCKEditor/editorGrid.html -- /AppPlugins/uCKEditor/uCKEditorGrid.controller.js
I added 1 file within the /Config/ - folder. -- /Config/uCKEditorGridconfig.js
I added a partial view in the /Views/Partials/Grid/Editors/ - folder. -- /Views/Partials/Grid/Editors/uCKEditorGrid.cshtml
Changes in '/App_Plugins/uCKEditor/package.manifest':
Changes in '/Config/grid.editors.config.js':
'/App_Plugins/uCKEditor/editorGrid.html' looks like:
'/App_Plugins/uCKEditor/uCKEditorGrid.controller.js' looks like: (This is the same as previous stated in this discussion.)
My '/Config/uCKEditorGridconfig.js' looks almost like a normal CKEditor config file. What's allowed in here you can find in the normal documentation of the CKEditor. What i have her is just an example. Change everything according to CKEditor-possibilities.
'/Views/Partials/Grid/Editors/uCKEditorGrid.cshtml' is just a copy of '/Views/Partials/Grid/Editors/Rte'.
This file is needed because the Grid rendering engine expects a view for each grid-editor with the same name as the editor. The view looks like this:
By implementing the above i noticed that caching can be very persistent. Browser-caching as well as server-caching. I even sometimes needed to restart the app-pool because IIS was also doing some caching!
Good luck,
Arthur
is working on a reply...