Merge pull request #1661 from normanjaeckel/CKEditor

Added CKEditor to custom slide create and edit form.
This commit is contained in:
Oskar Hahn 2015-11-18 12:21:39 +01:00
commit 0cce7f967b
8 changed files with 97 additions and 68 deletions

View File

@ -26,6 +26,8 @@
"font-awesome-bower": "~4.4.0", "font-awesome-bower": "~4.4.0",
"js-data": "~2.8.1", "js-data": "~2.8.1",
"js-data-angular": "~3.1.0", "js-data-angular": "~3.1.0",
"ng-file-upload": "~9.1.2" "ng-file-upload": "~9.1.2",
"ckeditor": "~4.5.4",
"angular-ckeditor": "~1.0.0"
} }
} }

View File

@ -33,7 +33,7 @@ var output_directory = path.join('openslides', 'static');
// Catches all JavaScript files from all bower components and concats them to // Catches all JavaScript files from all bower components and concats them to
// one file js/openslides-libs.js. In production mode the file is uglified. // one file js/openslides-libs.js. In production mode the file is uglified.
gulp.task('js-libs', function() { gulp.task('js-libs', function () {
return gulp.src(mainBowerFiles({ return gulp.src(mainBowerFiles({
filter: /\.js$/ filter: /\.js$/
})) }))
@ -44,7 +44,7 @@ gulp.task('js-libs', function() {
// Catches all CSS files from all bower components and concats them to one file // Catches all CSS files from all bower components and concats them to one file
// css/openslides-libs.css. In production mode the file is uglified. // css/openslides-libs.css. In production mode the file is uglified.
gulp.task('css-libs', function() { gulp.task('css-libs', function () {
return gulp.src(mainBowerFiles({ return gulp.src(mainBowerFiles({
filter: /\.css$/ filter: /\.css$/
})) }))
@ -61,8 +61,14 @@ gulp.task('fonts-libs', function() {
.pipe(gulp.dest(path.join(output_directory, 'fonts'))); .pipe(gulp.dest(path.join(output_directory, 'fonts')));
}); });
// Extra task only for CKEditor
gulp.task('ckeditor', function () {
return gulp.src(path.join('bower_components', 'ckeditor', '**'))
.pipe(gulp.dest(path.join(output_directory, 'ckeditor')));
});
// Gulp default task. Runs all other tasks before. // Gulp default task. Runs all other tasks before.
gulp.task('default', ['js-libs', 'css-libs', 'fonts-libs'], function() {}); gulp.task('default', ['js-libs', 'css-libs', 'fonts-libs', 'ckeditor'], function () {});
/** /**
@ -90,7 +96,7 @@ gulp.task('translations', function () {
}); });
// Checks JavaScript using JSHint // Checks JavaScript using JSHint
gulp.task( 'jshint', function() { gulp.task( 'jshint', function () {
return gulp.src([ 'gulpfile.js', path.join( 'openslides', '*', 'static', '**', '*.js' ) ]) return gulp.src([ 'gulpfile.js', path.join( 'openslides', '*', 'static', '**', '*.js' ) ])
.pipe(jshint()) .pipe(jshint())
.pipe(jshint.reporter('default')); .pipe(jshint.reporter('default'));

View File

@ -227,6 +227,36 @@ angular.module('OpenSlidesApp.core', [
}; };
} }
]) ])
/* Options for CKEditor used in various create and edit views. */
.value('CKEditorOptions', {
allowedContent:
'h1 h2 h3 p pre b i u strike strong em blockquote;' +
'a[!href];' +
'img[!src,alt]{width,height,float};' +
'table tr th td caption;' +
'li ol ul{list-style};' +
'span{color,background-color};',
extraPlugins: 'colorbutton',
toolbarGroups: [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker', 'editing' ] },
{ name: 'forms', groups: [ 'forms' ] },
{ name: 'tools', groups: [ 'tools' ] },
{ name: 'about', groups: [ 'about' ] },
{ name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
{ name: 'others', groups: [ 'others' ] },
'/',
{ name: 'styles', groups: [ 'styles' ] },
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi', 'paragraph' ] },
{ name: 'links', groups: [ 'links' ] },
{ name: 'insert', groups: [ 'insert' ] },
{ name: 'colors', groups: [ 'colors' ] }
],
removeButtons: 'Anchor,SpecialChar,Subscript,Superscript,Styles,RemoveFormat,HorizontalRule'
})
// Make sure that the DS factories are loaded by making them a dependency // Make sure that the DS factories are loaded by making them a dependency
.run(['Projector', 'Config', 'Tag', 'Customslide', function(Projector, Config, Tag, Customslide){}]); .run(['Projector', 'Config', 'Tag', 'Customslide', function(Projector, Config, Tag, Customslide){}]);

View File

@ -14,6 +14,7 @@ angular.module('OpenSlidesApp.core.site', [
'ngSanitize', // TODO: only use this in functions that need it. 'ngSanitize', // TODO: only use this in functions that need it.
'ui.select', 'ui.select',
'xeditable', 'xeditable',
'ckeditor',
]) ])
// Provider to register entries for the main menu. // Provider to register entries for the main menu.
@ -663,27 +664,42 @@ angular.module('OpenSlidesApp.core.site', [
Customslide.loadRelations(customslide, 'agenda_item'); Customslide.loadRelations(customslide, 'agenda_item');
}) })
.controller('CustomslideCreateCtrl', function($scope, $state, Customslide) { .controller('CustomslideCreateCtrl', [
$scope.customslide = {}; '$scope',
$scope.save = function (customslide) { '$state',
Customslide.create(customslide).then( 'CKEditorOptions',
function(success) { 'Customslide',
$state.go('core.customslide.list'); function($scope, $state, CKEditorOptions, Customslide) {
} $scope.customslide = {};
); $scope.CKEditorOptions = CKEditorOptions;
}; $scope.save = function (customslide) {
}) Customslide.create(customslide).then(
function(success) {
$state.go('core.customslide.list');
}
);
};
}
])
.controller('CustomslideUpdateCtrl', function($scope, $state, Customslide, customslide) { .controller('CustomslideUpdateCtrl', [
$scope.customslide = customslide; '$scope',
$scope.save = function (customslide) { '$state',
Customslide.save(customslide).then( 'CKEditorOptions',
function(success) { 'Customslide',
$state.go('core.customslide.list'); 'customslide',
} function($scope, $state, CKEditorOptions, Customslide, customslide) {
); $scope.customslide = customslide;
}; $scope.CKEditorOptions = CKEditorOptions;
}) $scope.save = function (customslide) {
Customslide.save(customslide).then(
function(success) {
$state.go('core.customslide.list');
}
);
};
}
])
// Tag Controller // Tag Controller
.controller('TagListCtrl', function($scope, Tag) { .controller('TagListCtrl', function($scope, Tag) {

View File

@ -14,8 +14,8 @@
<input type="text" ng-model="customslide.title" class="form-control" name="inputTitle" required> <input type="text" ng-model="customslide.title" class="form-control" name="inputTitle" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="textareaDesciption" translate>Text</label> <label for="customSlideTextCKEditor" translate>Text</label>
<textarea ng-model="customslide.text" class="form-control" name="textareaText" /> <div id="customSlideTextCKEditor" ckeditor="CKEditorOptions" ng-model="customslide.text"></div>
</div> </div>
<button type="submit" ng-click="save(customslide)" class="btn btn-primary" translate> <button type="submit" ng-click="save(customslide)" class="btn btn-primary" translate>

View File

@ -9,6 +9,7 @@
<link rel="stylesheet" href="static/css/openslides-libs.css"> <link rel="stylesheet" href="static/css/openslides-libs.css">
<link rel="stylesheet" href="static/css/app.css"> <link rel="stylesheet" href="static/css/app.css">
<script src="static/js/openslides-libs.js"></script> <script src="static/js/openslides-libs.js"></script>
<script src="static/ckeditor/ckeditor.js"></script>
<!-- Navbar --> <!-- Navbar -->
<nav id="header" class="navbar navbar-inverse"> <nav id="header" class="navbar navbar-inverse">

View File

@ -1,4 +1,3 @@
import copy
import os import os
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
@ -115,45 +114,6 @@ HAYSTACK_SIGNAL_PROCESSOR = 'openslides.utils.haystack_processor.OpenSlidesProce
# Adds all automaticly collected plugins # Adds all automaticly collected plugins
INSTALLED_PLUGINS = collect_plugins() INSTALLED_PLUGINS = collect_plugins()
# CKeditor settings
CKEDITOR_DEFAULT_CONFIG = {'toolbar': 'Full',
'bodyClass': 'ckeditor_html',
'allowedContent':
'h1 h2 h3 pre b i u strike em; '
# A workaround for the problem described in http://dev.ckeditor.com/ticket/10192
# Hopefully, the problem will be solved in the final version of CKEditor 4.1
# If so, then {margin-left} can be removed
'p{margin-left}; '
'a[!href]; '
'ol ul{list-style}; '
'li; '
'pre; '
'span{color,background-color}; ',
'removePlugins': 'save, print, preview, pagebreak, templates, showblocks, magicline',
'extraPlugins': 'insertpre', # see http://ckeditor.com/addon/insertpre
'toolbar_Full': [
{'name': 'document', 'items': ['Source', '-', 'Save', 'DocProps', 'Preview', 'Print', '-', 'Templates']},
{'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
{'name': 'editing', 'items': ['Find', 'Replace', '-', 'SpellChecker', 'Scayt']},
{'name': 'basicstyles', 'items': ['Bold', 'Italic', 'Underline', 'Strike', '-', 'RemoveFormat']},
{'name': 'paragraph', 'items': ['NumberedList', 'BulletedList', '-', 'InsertPre']},
{'name': 'links', 'items': ['Link', 'Unlink']},
{'name': 'styles', 'items': ['Format', 'TextColor', 'BGColor']},
{'name': 'tools', 'items': ['Maximize', 'ShowBlocks', '-', 'About']}
]}
CKEDITOR_IMG_CONFIG = copy.deepcopy(CKEDITOR_DEFAULT_CONFIG)
CKEDITOR_IMG_CONFIG['allowedContent'] += 'img; '
CKEDITOR_IMG_CONFIG['toolbar_Full'].append({'name': 'images', 'items': ['Image']})
CKEDITOR_UPLOAD_PATH = 'ckeditor'
CKEDITOR_CONFIGS = {
'default': CKEDITOR_DEFAULT_CONFIG,
'images': CKEDITOR_IMG_CONFIG,
}
# Set this True to use tornado as single wsgi server. Set this False to use # Set this True to use tornado as single wsgi server. Set this False to use
# other webserver like Apache or Nginx as wsgi server. # other webserver like Apache or Nginx as wsgi server.
USE_TORNADO_AS_WSGI_SERVER = True USE_TORNADO_AS_WSGI_SERVER = True

View File

@ -1,7 +1,8 @@
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch
from openslides.core.config import ConfigVariable from openslides.core.config import ConfigHandler, ConfigVariable, config
from openslides.core.exceptions import ConfigNotFound
class TestConfigVariable(TestCase): class TestConfigVariable(TestCase):
@ -22,3 +23,16 @@ class TestConfigVariable(TestCase):
'test_default_value', 'test_default_value',
"The value of config_variable.data['default_value'] should be the same " "The value of config_variable.data['default_value'] should be the same "
"as set as second argument of ConfigVariable()") "as set as second argument of ConfigVariable()")
class TestConfigHandler(TestCase):
def test_get_from_cache(self):
ConfigHandler._cache = {'key_eeshah4Sho6zailee4ko': 'value_chies7aCohZoo9umieph'}
self.assertEqual(config['key_eeshah4Sho6zailee4ko'], 'value_chies7aCohZoo9umieph')
def test_get_not_found(self):
ConfigHandler._cache = {}
self.assertRaises(
ConfigNotFound,
config.__getitem__,
'key_leehah4Sho4ee7aCohbn')