fixes rte property editor to ensure that data is saved since tinymce events are a bit strange. fixes up selection changes and ensures that macro markup in the editor can never be modified.

This commit is contained in:
Shannon
2013-09-25 16:30:13 +10:00
parent 06400f8db5
commit 55e9a4fbb0
2 changed files with 83 additions and 17 deletions

View File

@@ -194,7 +194,7 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou
}
});
}
/** Adds the button instance */
editor.addButton('umbmacro', {
icon: 'custom icon-settings-alt',
@@ -204,6 +204,57 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou
var ctrl = this;
var isOnMacroElement = false;
/**
if the selection comes from a different element that is not the macro's
we need to check if the selection includes part of the macro, if so we'll force the selection
to clear to the next element since if people can select part of the macro markup they can then modify it.
*/
function handleSelectionChange() {
if (!editor.selection.isCollapsed()) {
var endSelection = tinymce.activeEditor.selection.getEnd();
var startSelection = tinymce.activeEditor.selection.getStart();
//don't proceed if it's an entire element selected
if (endSelection !== startSelection) {
//if the end selection is a macro then move the cursor
//NOTE: we don't have to handle when the selection comes from a previous parent because
// that is automatically taken care of with the normal onNodeChanged logic since the
// evt.element will be the macro once it becomes part of the selection.
var $testForMacro = $(endSelection).closest(".umb-macro-holder");
if ($testForMacro.length > 0) {
//it came from before so move after, if there is no after then select ourselves
var next = $testForMacro.next();
if (next.length > 0) {
editor.selection.setCursorLocation($testForMacro.next().get(0));
}
else {
selectMacroElement($testForMacro.get(0));
}
}
}
}
}
/** helper method to select the macro element */
function selectMacroElement(macroElement) {
// move selection to top element to ensure we can't edit this
editor.selection.select(macroElement);
// check if the current selection *is* the element (ie bug)
var currentSelection = editor.selection.getStart();
if (tinymce.isIE) {
if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) {
while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) {
currentSelection = currentSelection.parentNode;
}
editor.selection.select(currentSelection);
}
}
}
/**
* Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag.
* If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves
@@ -214,7 +265,10 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou
//set our macro button active when on a node of class umb-macro-holder
var $macroElement = $(evt.element).closest(".umb-macro-holder");
handleSelectionChange();
//set the button active
ctrl.active($macroElement.length !== 0);
if ($macroElement.length > 0) {
@@ -222,20 +276,8 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou
//remove the event listener before re-selecting
editor.off('NodeChange', onNodeChanged);
// move selection to top element to ensure we can't edit this
editor.selection.select(macroElement);
// check if the current selection *is* the element (ie bug)
var currentSelection = editor.selection.getStart();
if (tinymce.isIE) {
if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) {
while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) {
currentSelection = currentSelection.parentNode;
}
editor.selection.select(currentSelection);
}
}
selectMacroElement(macroElement);
//set the flag
isOnMacroElement = true;
@@ -257,7 +299,6 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou
loadMacroContent($(this));
});
});
/** This prevents any other commands from executing when the current element is the macro so the content cannot be edited */

View File

@@ -1,6 +1,6 @@
angular.module("umbraco")
.controller("Umbraco.Editors.RTEController",
function ($rootScope, $scope, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService) {
function ($rootScope, $element, $scope, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper) {
//TODO: This should be configurable (i.e. from the config file we have and/or from pre-values)
var validElements = "@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|"
@@ -47,11 +47,36 @@ angular.module("umbraco")
height: 340,
toolbar: toolbar,
setup: function (editor) {
//We need to listen on multiple things here because of the nature of tinymce, it doesn't
//fire events when you think!
//The change event doesn't fire when content changes, only when cursor points are changed and undo points
//are created. the blur event doesn't fire if you insert content into the editor with a button and then
//press save.
//We have a couple of options, one is to do a set timeout and check for isDirty on the editor, or we can
//listen to both change and blur and also on our own 'saving' event. I think this will be best because a
//timer might end up using unwanted cpu and we'd still have to listen to our saving event in case they clicked
//save before the timeout elapsed.
editor.on('change', function (e) {
angularHelper.safeApply($scope, function() {
$scope.model.value = editor.getContent();
});
});
editor.on('blur', function (e) {
$scope.$apply(function () {
angularHelper.safeApply($scope, function () {
$scope.model.value = editor.getContent();
});
});
var unsubscribe = $scope.$on("saving", function() {
$scope.model.value = editor.getContent();
});
//when the element is disposed we need to unsubscribe!
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
// element might still be there even after the modal has been hidden.
$element.bind('$destroy', function () {
unsubscribe();
});
//Create the insert media plugin
tinyMceService.createMediaPicker(editor, $scope);