From dfb40684ee00efdc7830b2f27a583d3f4d76a21d Mon Sep 17 00:00:00 2001 From: FinnStutzenstein Date: Tue, 30 Jan 2018 16:12:02 +0100 Subject: [PATCH] Custom fonts for pdf and projector --- CHANGELOG | 1 + gulpfile.js | 4 +- openslides/core/config.py | 6 +- openslides/core/config_variables.py | 68 +++++++- .../migrations/0007_auto_20180130_1400.py | 62 ++++++++ openslides/core/models.py | 2 +- openslides/core/static/css/_fonts.scss | 24 +-- openslides/core/static/css/_variables.scss | 9 +- openslides/core/static/css/projector.scss | 1 - openslides/core/static/css/site.scss | 1 - openslides/core/static/js/core/base.js | 74 +++++++-- openslides/core/static/js/core/pdf-worker.js | 19 ++- openslides/core/static/js/core/pdf.js | 150 ++++++++++++++---- openslides/core/static/js/core/projector.js | 17 +- .../static/templates/projector-container.html | 2 +- .../core/static/templates/projector.html | 30 +++- openslides/core/views.py | 9 +- .../mediafiles/static/js/mediafiles/list.js | 19 +-- .../static/js/mediafiles/resources.js | 61 ++++++- .../templates/mediafiles/mediafile-list.html | 49 +++++- openslides/users/signals.py | 4 +- 21 files changed, 493 insertions(+), 119 deletions(-) create mode 100644 openslides/core/migrations/0007_auto_20180130_1400.py diff --git a/CHANGELOG b/CHANGELOG index c09196ef0..7f2089666 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -112,6 +112,7 @@ Core: - Set default of projector resolution to 1220x915 [#2549]. - Preparations for the SAML plugin; Fixed caching of main views [#3535]. - Removed unnecessary OPTIONS request in config [#3541]. +- Added possibility to upload custom fonts for projector and pdf [#3568]. Mediafiles: - Fixed reloading of PDF on page change [#3274]. diff --git a/gulpfile.js b/gulpfile.js index 4f87b0666..12432640a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -80,9 +80,9 @@ gulp.task('pdf-worker', function () { gulp.task('pdf-worker-libs', function () { return gulp.src([ path.join('bower_components', 'pdfmake', 'build', 'pdfmake.min.js'), - path.join('bower_components', 'pdfmake', 'build', 'vfs_fonts.js'), ]) - .pipe(concat('pdf-worker-libs.js')) + .pipe(gulpif(argv.production, uglify())) + .pipe(rename('pdf-worker-libs.js')) .pipe(gulp.dest(path.join(output_directory, 'js', 'workers'))); }); diff --git a/openslides/core/config.py b/openslides/core/config.py index 3d29ebf6e..ab1d6921c 100644 --- a/openslides/core/config.py +++ b/openslides/core/config.py @@ -19,7 +19,7 @@ INPUT_TYPE_MAPPING = { 'colorpicker': str, 'datetimepicker': int, 'majorityMethod': str, - 'logo': dict, + 'static': dict, 'translations': list, } @@ -125,9 +125,9 @@ class ConfigHandler: valuecopy[id] = commentsfield value = valuecopy - if config_variable.input_type == 'logo': + if config_variable.input_type == 'static': if not isinstance(value, dict): - raise ConfigError(_('logo has to be a dict.')) + raise ConfigError(_('This has to be a dict.')) whitelist = ( 'path', 'display_name', diff --git a/openslides/core/config_variables.py b/openslides/core/config_variables.py index 8d97ef8f2..843ed6b2b 100644 --- a/openslides/core/config_variables.py +++ b/openslides/core/config_variables.py @@ -259,7 +259,7 @@ def get_config_variables(): default_value={ 'display_name': 'Projector logo', 'path': ''}, - input_type='logo', + input_type='static', weight=301, group='Logo', hidden=True) @@ -269,7 +269,7 @@ def get_config_variables(): default_value={ 'display_name': 'Projector header image', 'path': ''}, - input_type='logo', + input_type='static', weight=302, group='Logo', hidden=True) @@ -279,7 +279,7 @@ def get_config_variables(): default_value={ 'display_name': 'Web interface header logo', 'path': ''}, - input_type='logo', + input_type='static', weight=303, group='Logo', hidden=True) @@ -290,7 +290,7 @@ def get_config_variables(): default_value={ 'display_name': 'PDF header logo', 'path': ''}, - input_type='logo', + input_type='static', weight=310, group='Logo', hidden=True) @@ -300,7 +300,7 @@ def get_config_variables(): default_value={ 'display_name': 'PDF footer logo', 'path': ''}, - input_type='logo', + input_type='static', weight=311, group='Logo', hidden=True) @@ -310,11 +310,67 @@ def get_config_variables(): default_value={ 'display_name': 'PDF ballot paper logo', 'path': ''}, - input_type='logo', + input_type='static', weight=312, group='Logo', hidden=True) + # Fonts + yield ConfigVariable( + name='fonts_available', + default_value=[ + 'font_regular', + 'font_italic', + 'font_bold', + 'font_bold_italic'], + weight=320, + group='Font', + hidden=True) + + yield ConfigVariable( + name='font_regular', + default_value={ + 'display_name': 'Font regular', + 'default': 'static/fonts/Roboto-Regular.woff', + 'path': ''}, + input_type='static', + weight=321, + group='Font', + hidden=True) + + yield ConfigVariable( + name='font_italic', + default_value={ + 'display_name': 'Font italic', + 'default': 'static/fonts/Roboto-Medium.woff', + 'path': ''}, + input_type='static', + weight=321, + group='Font', + hidden=True) + + yield ConfigVariable( + name='font_bold', + default_value={ + 'display_name': 'Font bold', + 'default': 'static/fonts/Roboto-Condensed-Regular.woff', + 'path': ''}, + input_type='static', + weight=321, + group='Font', + hidden=True) + + yield ConfigVariable( + name='font_bold_italic', + default_value={ + 'display_name': 'Font bold italic', + 'default': 'static/fonts/Roboto-Condensed-Light.woff', + 'path': ''}, + input_type='static', + weight=321, + group='Font', + hidden=True) + # Custom translations yield ConfigVariable( name='translations', diff --git a/openslides/core/migrations/0007_auto_20180130_1400.py b/openslides/core/migrations/0007_auto_20180130_1400.py new file mode 100644 index 000000000..f685beed4 --- /dev/null +++ b/openslides/core/migrations/0007_auto_20180130_1400.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-01-30 13:00 +from __future__ import unicode_literals + +from django.contrib.auth.models import Permission +from django.db import migrations + + +def delete_old_logo_permission(apps, schema_editor): + """ + Deletes the old 'can_manage_logo' permission which is replaced with + 'can_manage_logos_and_fonts'. If this is a fresh database, no permission + will be deleted, in fact the old permission does not exist. Django creates + the permission after all migration and the old one is not generated. + If this is an old database, the new permission will be created and the old + one deleted. Also it will be assigned to the groups, which had the old permission. + """ + perm = Permission.objects.filter(codename='can_manage_logos') + + if len(perm): + perm = perm.get() + # Save content_type for manual creation of new permissions. + content_type = perm.content_type + + # Save groups. list() is necessary to evaluate the database query right now. + groups = list(perm.group_set.all()) + + # Delete permission + perm.delete() + + # Create new permission + perm = Permission.objects.create( + codename='can_manage_logos_and_fonts', + name='Can manage logos and fonts', + content_type=content_type) + + for group in groups: + group.permissions.add(perm) + group.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0006_auto_20180123_0903'), + ] + + operations = [ + migrations.AlterModelOptions( + name='configstore', + options={ + 'default_permissions': (), + 'permissions': ( + ('can_manage_config', 'Can manage configuration'), + ('can_manage_logos_and_fonts', 'Can manage logos and fonts') + ) + }, + ), + migrations.RunPython( + delete_old_logo_permission + ), + ] diff --git a/openslides/core/models.py b/openslides/core/models.py index abb0c09c1..a186962a1 100644 --- a/openslides/core/models.py +++ b/openslides/core/models.py @@ -281,7 +281,7 @@ class ConfigStore(RESTModelMixin, models.Model): default_permissions = () permissions = ( ('can_manage_config', 'Can manage configuration'), - ('can_manage_logos', 'Can manage logos')) + ('can_manage_logos_and_fonts', 'Can manage logos and fonts')) @classmethod def get_collection_string(cls): diff --git a/openslides/core/static/css/_fonts.scss b/openslides/core/static/css/_fonts.scss index 57c7f95cf..12ab0c028 100644 --- a/openslides/core/static/css/_fonts.scss +++ b/openslides/core/static/css/_fonts.scss @@ -3,22 +3,22 @@ */ @font-face { - font-family: $font; - src: $font-src; - font-weight: 400; - font-style: normal; + font-family: $font; + src: $font-src; + font-weight: 400; + font-style: normal; } @font-face { - font-family: $font-medium; - src: $font-medium-src; - font-weight: 400; - font-style: normal; + font-family: $font-medium; + src: $font-medium-src; + font-weight: 400; + font-style: normal; } @font-face { - font-family: $font-condensed; - src: $font-condensed-src; - font-weight: 100; - font-style: normal; + font-family: $font-condensed; + src: $font-condensed-src; + font-weight: 100; + font-style: normal; } @font-face { font-family: $font-condensed-light; diff --git a/openslides/core/static/css/_variables.scss b/openslides/core/static/css/_variables.scss index f52882b66..8894c57e4 100644 --- a/openslides/core/static/css/_variables.scss +++ b/openslides/core/static/css/_variables.scss @@ -1,9 +1,10 @@ /** Fonts **/ -$font: 'Roboto'; +/* Note: The font naming has to be consistent to the projector.html */ +$font: 'OSFont'; $font-src: url('../fonts/Roboto-Regular.woff') format('woff'); -$font-medium: 'Roboto Medium'; +$font-medium: 'OSFont Medium'; $font-medium-src: url('../fonts/Roboto-Medium.woff') format('woff'); -$font-condensed: 'Roboto Condensed'; +$font-condensed: 'OSFont Condensed'; $font-condensed-src: url('../fonts/Roboto-Condensed-Regular.woff') format('woff'); -$font-condensed-light: 'Roboto Condensed Light'; +$font-condensed-light: 'OSFont Condensed Light'; $font-condensed-light-src: url('../fonts/Roboto-Condensed-Light.woff') format('woff'); diff --git a/openslides/core/static/css/projector.scss b/openslides/core/static/css/projector.scss index 3b555410a..82fc04756 100644 --- a/openslides/core/static/css/projector.scss +++ b/openslides/core/static/css/projector.scss @@ -2,7 +2,6 @@ /* General */ @import "variables"; -@import "fonts"; @import "helper"; @import "ui-override"; diff --git a/openslides/core/static/css/site.scss b/openslides/core/static/css/site.scss index b0f101633..824a03fc9 100644 --- a/openslides/core/static/css/site.scss +++ b/openslides/core/static/css/site.scss @@ -15,4 +15,3 @@ @import "../../../motions/static/css/motions/site"; @import "../../../users/static/css/users/site"; @import "../../../mediafiles/static/css/mediafiles/site"; - diff --git a/openslides/core/static/js/core/base.js b/openslides/core/static/js/core/base.js index b9046c86d..b8ef87cb8 100644 --- a/openslides/core/static/js/core/base.js +++ b/openslides/core/static/js/core/base.js @@ -826,36 +826,78 @@ angular.module('OpenSlidesApp.core', [ getAll: function () { var self = this; return _.map(this.getKeys(), function (key) { - return self.getFromKey(key); + return self.get(key); }); }, - getFromKey: function (key) { + get: function (key) { var config = Config.get(key); if (config) { config.value.key = key; return config.value; } }, - isMediafileUsedAsLogo: function (mediafile) { - return _.find(this.getAll(), function (logoPlaceholder) { - return logoPlaceholder.path === mediafile.mediafileUrl; - }); - }, - canMediafileBeUsedAsLogo: function (mediafile) { - return mediafile.is_image; - }, - setMediafile: function (key, mediafile) { + set: function (key, path) { var config = Config.get(key); - if (!mediafile || mediafile.canBeUsedAsLogo()) { - config.value.path = mediafile ? mediafile.mediafileUrl : ''; + if (config) { + config.value.path = path;// ? mediafile.mediafileUrl : ''; Config.save(key); } }, - getLogosForMediafile: function (mediafile) { - return _.filter(this.getAll(), function (logoPlaceholder) { - return logoPlaceholder.path === mediafile.mediafileUrl; + }; + } +]) + +.factory('Fonts', [ + 'Config', + 'gettext', + function (Config, gettext) { + var extensionFormatMap = { + 'ttf': 'truetype', + 'woff': 'woff', + }; + + return { + getKeys: function () { + return Config.get('fonts_available').value; + }, + getAll: function () { + var self = this; + return _.map(this.getKeys(), function (key) { + return self.get(key); }); }, + get: function (key) { + var config = Config.get(key); + if (config) { + config.value.key = key; + return config.value; + } + }, + getUrl: function (key) { + var font = this.get(key); + if (font) { + var path = font.path; + if (!path) { + return font.default; + } + return path; + } + }, + getForCss: function (key) { + var url = this.getUrl(key); + if (url) { + var ext = _.last(url.split('.')); + return "url('" + url + "') format('" + + extensionFormatMap[ext] + "')"; + } + }, + set: function (key, path) { + var config = Config.get(key); + if (config) { + config.value.path = path; + Config.save(key); + } + }, }; } ]) diff --git a/openslides/core/static/js/core/pdf-worker.js b/openslides/core/static/js/core/pdf-worker.js index 074035ed1..449645e82 100644 --- a/openslides/core/static/js/core/pdf-worker.js +++ b/openslides/core/static/js/core/pdf-worker.js @@ -10,20 +10,18 @@ var document = { }; var window = this; -// PdfMake and Fonts +// PdfMake importScripts('/static/js/workers/pdf-worker-libs.js'); // Set default font family. -// To use custom ttf font files you have to replace the vfs_fonts.js file. -// See https://github.com/pdfmake/pdfmake/wiki/Custom-Fonts---client-side -// "PdfFont" is used as generic name in core/pdf.js. Adjust the four -// font style names only. +// "PdfFont" and "OSFont-*" are generic names used here and in core/pdf.js. The +// suffix after "OSFont-" has to be the same as the config value. pdfMake.fonts = { PdfFont: { - normal: 'Roboto-Regular.ttf', - bold: 'Roboto-Medium.ttf', - italics: 'Roboto-Italic.ttf', - bolditalics: 'Roboto-Italic.ttf' + normal: 'OSFont-regular.ttf', + bold: 'OSFont-bold.ttf', + italics: 'OSFont-italic.ttf', + bolditalics: 'OSFont-bold_italic.ttf' } }; @@ -106,8 +104,9 @@ var replaceFooter = function (doc) { // Create PDF on message and return the base64 decoded document self.addEventListener('message', function(e) { var data = JSON.parse(e.data); - var doc = data.pdfDocument; + pdfMake.vfs = data.vfs; // Set custom fonts. + var doc = data.pdfDocument; replaceFooter(doc); replacePlaceholder(doc.content); diff --git a/openslides/core/static/js/core/pdf.js b/openslides/core/static/js/core/pdf.js index 234df7ed2..03deb869c 100644 --- a/openslides/core/static/js/core/pdf.js +++ b/openslides/core/static/js/core/pdf.js @@ -1061,13 +1061,99 @@ angular.module('OpenSlidesApp.core.pdf', []) } ]) +// Creates the virtual filesystem for PdfMake. +.factory('PdfVfs', [ + '$q', + '$http', + 'Fonts', + 'Config', + function ($q, $http, Fonts, Config) { + var urlCache = {}; // Caches the get request. Maps urls to base64 data ready to use. + + var loadFont = function (url) { + return $q(function (resolve, reject) { + // Get font + return $http.get(url, {responseType: 'blob'}).then(function (success) { + // Convert to base64 + var reader = new FileReader(); + reader.readAsDataURL(success.data); + reader.onloadend = function() { + resolve(reader.result.split(',')[1]); + }; + }, function (error) { + reject(error); + }); + }); + }; + + /* + * Returns a map from urls to arrays of font types used by PdfMake. + * E.g. if the font "regular" and bold" have the urls "fonts/myFont.ttf", + * the map fould be "fonts/myFont.ttf": ["OSFont-regular.ttf", "OSFont-bold.ttf"] + */ + var getUrlMapping = function () { + var urlMap = {}; + var fonts = ['regular', 'italic', 'bold', 'bold_italic']; + _.forEach(fonts, function (font) { + var url = Fonts.getUrl('font_' + font); + if (!urlMap[url]) { + urlMap[url] = []; + } + urlMap[url].push('OSFont-' + font + '.ttf'); + }); + return urlMap; + }; + + /* + * Create the virtual filesystem needed by PdfMake for the fonts. Gets the url + * mapping and loads all fonts via get requests or the urlCache. + */ + var getVfs = function () { + return $q(function (resolve, reject) { + var vfs = {}; + var urls = getUrlMapping(); + var promises = _.chain(urls) + .map(function (filenames, url) { + if (urlCache[url]) { + // Just save the cache data into vfs. + _.forEach(filenames, function (filename) { + vfs[filename] = urlCache[url]; + }); + return false; // No promise here, it was all cached. + } else { + // Not in the cache, get the font and save the data into vfs. + return loadFont(url).then(function (data) { + urlCache[url] = data; + _.forEach(filenames, function (filename) { + vfs[filename] = data; + }); + }); + } + }) + .filter(function (promise) { + return promise; + }) + .value(); + $q.all(promises).then(function () { + resolve(vfs); + }); + }); + }; + + return { + get: getVfs, + }; + } +]) + .factory('PdfCreate', [ '$timeout', '$q', 'gettextCatalog', 'FileSaver', + 'PdfVfs', 'Messaging', - function ($timeout, $q, gettextCatalog, FileSaver, Messaging) { + function ($timeout, $q, gettextCatalog, FileSaver, PdfVfs, Messaging) { var filenameMessageMap = {}; var b64toBlob = function(b64Data) { var byteCharacters = atob(b64Data); @@ -1105,40 +1191,46 @@ angular.module('OpenSlidesApp.core.pdf', []) return { getBase64FromDocument: function (pdfDocument) { return $q(function (resolve, reject) { - var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); - pdfWorker.addEventListener('message', function (event) { - resolve(event.data); + PdfVfs.get().then(function (vfs) { + var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); + pdfWorker.addEventListener('message', function (event) { + resolve(event.data); + }); + pdfWorker.addEventListener('error', function (event) { + reject(event); + }); + pdfWorker.postMessage(JSON.stringify({ + pdfDocument: pdfDocument, + vfs: vfs, + })); }); - pdfWorker.addEventListener('error', function (event) { - reject(event); - }); - pdfWorker.postMessage(JSON.stringify({ - pdfDocument: pdfDocument - })); }); }, // Struckture of pdfDocuments: { filname1: doc, filename2: doc, ...} getBase64FromMultipleDocuments: function (pdfDocuments) { return $q(function (resolve, reject) { - var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); - var resultCount = 0; - var base64Map = {}; // Maps filename to base64 - pdfWorker.addEventListener('message', function (event) { - resultCount++; - var data = JSON.parse(event.data); - base64Map[data.filename] = data.base64; - if (resultCount === _.keys(pdfDocuments).length) { - resolve(base64Map); - } - }); - pdfWorker.addEventListener('error', function (event) { - reject(event); - }); - _.forEach(pdfDocuments, function (doc, filename) { - pdfWorker.postMessage(JSON.stringify({ - filename: filename, - pdfDocument: doc - })); + PdfVfs.get().then(function (vfs) { + var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); + var resultCount = 0; + var base64Map = {}; // Maps filename to base64 + pdfWorker.addEventListener('message', function (event) { + resultCount++; + var data = JSON.parse(event.data); + base64Map[data.filename] = data.base64; + if (resultCount === _.keys(pdfDocuments).length) { + resolve(base64Map); + } + }); + pdfWorker.addEventListener('error', function (event) { + reject(event); + }); + _.forEach(pdfDocuments, function (doc, filename) { + pdfWorker.postMessage(JSON.stringify({ + filename: filename, + pdfDocument: doc, + vfs: vfs, + })); + }); }); }); }, diff --git a/openslides/core/static/js/core/projector.js b/openslides/core/static/js/core/projector.js index b31fcbb7c..71b59baa7 100644 --- a/openslides/core/static/js/core/projector.js +++ b/openslides/core/static/js/core/projector.js @@ -78,12 +78,13 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core']) } ]) -.controller('LanguageCtrl', [ +.controller('LanguageAndFontCtrl', [ '$scope', 'Languages', 'Config', 'ProjectorID', - function ($scope, Languages, Config, ProjectorID) { + 'Fonts', + function ($scope, Languages, Config, ProjectorID, Fonts) { // for the dynamic title $scope.projectorId = ProjectorID(); @@ -98,6 +99,18 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core']) } Languages.setCurrentLanguage($scope.selectedLanguage); }); + + $scope.$watch(function () { + return Config.lastModified('font_regular') + + Config.lastModified('font_italic') + + Config.lastModified('font_bold') + + Config.lastModified('font_bold_italic'); + }, function () { + $scope.font = Fonts.getForCss('font_regular'); + $scope.font_medium = Fonts.getForCss('font_italic'); + $scope.font_condensed = Fonts.getForCss('font_bold'); + $scope.font_condensed_light = Fonts.getForCss('font_bold_italic'); + }); } ]) diff --git a/openslides/core/static/templates/projector-container.html b/openslides/core/static/templates/projector-container.html index 78296c5e3..1b83c8532 100644 --- a/openslides/core/static/templates/projector-container.html +++ b/openslides/core/static/templates/projector-container.html @@ -1,5 +1,5 @@ - + diff --git a/openslides/core/static/templates/projector.html b/openslides/core/static/templates/projector.html index cf9c771ec..c28cf4f52 100644 --- a/openslides/core/static/templates/projector.html +++ b/openslides/core/static/templates/projector.html @@ -1,5 +1,5 @@ - + @@ -7,6 +7,34 @@ + + + diff --git a/openslides/core/views.py b/openslides/core/views.py index b22d961a8..2df11bae4 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -600,11 +600,12 @@ class ConfigViewSet(ModelViewSet): # enabled. result = self.request.user.is_authenticated() or anonymous_is_enabled() elif self.action in ('partial_update', 'update'): - # The user needs 'core.can_manage_logos' for all config values - # starting with 'logo'. For all other config values th euser needs + # The user needs 'core.can_manage_logos_and_fonts' for all config values + # starting with 'logo' and 'font'. For all other config values th euser needs # the default permissions 'core.can_manage_config'. - if self.kwargs['pk'].startswith('logo'): - result = has_perm(self.request.user, 'core.can_manage_logos') + pk = self.kwargs['pk'] + if pk.startswith('logo') or pk.startswith('font'): + result = has_perm(self.request.user, 'core.can_manage_logos_and_fonts') else: result = has_perm(self.request.user, 'core.can_manage_config') else: diff --git a/openslides/mediafiles/static/js/mediafiles/list.js b/openslides/mediafiles/static/js/mediafiles/list.js index e3b12f795..5af001994 100644 --- a/openslides/mediafiles/static/js/mediafiles/list.js +++ b/openslides/mediafiles/static/js/mediafiles/list.js @@ -24,8 +24,9 @@ angular.module('OpenSlidesApp.mediafiles.list', [ 'Mediafile', 'MediafileForm', 'Logos', + 'Fonts', function ($http, $scope, gettext, ngDialog, osTableFilter, osTableSort, osTablePagination, - ProjectionDefault, Projector, User, Mediafile, MediafileForm, Logos) { + ProjectionDefault, Projector, User, Mediafile, MediafileForm, Logos, Fonts) { $scope.$watch(function () { return Mediafile.lastModified(); }, function () { @@ -259,21 +260,9 @@ angular.module('OpenSlidesApp.mediafiles.list', [ ); }; - /** Logos **/ + /** Logos and fonts **/ $scope.logos = Logos.getAll(); - $scope.toggleLogo = function (mediafile, logo) { - if (!$scope.hasLogo(mediafile, logo)) { - Logos.setMediafile(logo.key, mediafile); - } else { - Logos.setMediafile(logo.key); - } - }; - $scope.hasLogo = function (mediafile, logo) { - var allUrls = _.map(mediafile.getLogos(), function (logo) { - return logo.path; - }); - return _.includes(allUrls, logo.path); - }; + $scope.fonts = Fonts.getAll(); $scope.hasProjectorHeaderLogo = function (mediafile) { return _.some(mediafile.getLogos(), function (logo) { return logo.key === 'logo_projector_header'; diff --git a/openslides/mediafiles/static/js/mediafiles/resources.js b/openslides/mediafiles/static/js/mediafiles/resources.js index 9579a554a..4c7a8b3e9 100644 --- a/openslides/mediafiles/static/js/mediafiles/resources.js +++ b/openslides/mediafiles/static/js/mediafiles/resources.js @@ -13,7 +13,8 @@ angular.module('OpenSlidesApp.mediafiles.resources', [ 'gettext', 'jsDataModel', 'Logos', - function (DS, gettext, jsDataModel, Logos) { + 'Fonts', + function (DS, gettext, jsDataModel, Logos, Fonts) { var name = 'mediafiles/mediafile'; return DS.defineResource({ name: name, @@ -50,13 +51,60 @@ angular.module('OpenSlidesApp.mediafiles.resources', [ }); }, isUsedAsLogo: function () { - return Logos.isMediafileUsedAsLogo(this); + var mediafile = this; + return _.find(Logos.getAll(), function (logoPlaceholder) { + return logoPlaceholder.path === mediafile.mediafileUrl; + }); }, canBeUsedAsLogo: function () { - return Logos.canMediafileBeUsedAsLogo(this); + return this.is_image; }, getLogos: function () { - return Logos.getLogosForMediafile(this); + var mediafile = this; + return _.filter(Logos.getAll(), function (logoPlaceholder) { + return logoPlaceholder.path === mediafile.mediafileUrl; + }); + }, + hasLogo: function (logo) { + var allUrls = _.map(this.getLogos(), function (logo) { + return logo.path; + }); + return _.includes(allUrls, logo.path); + }, + toggleLogo: function (logo) { + if (this.hasLogo(logo)) { + Logos.set(logo.key); + } else { + Logos.set(logo.key, this.mediafileUrl); + } + }, + isUsedAsFont: function () { + var mediafile = this; + return _.find(Fonts.getAll(), function (font) { + return font.path === mediafile.mediafileUrl; + }); + }, + canBeUsedAsFont: function () { + return this.is_font; + }, + getFonts: function () { + var mediafile = this; + return _.filter(Fonts.getAll(), function (font) { + return font.path === mediafile.mediafileUrl; + }); + }, + hasFont: function (font) { + var allUrls = _.map(this.getFonts(), function (font) { + return font.path; + }); + return _.includes(allUrls, font.path); + }, + toggleFont: function (font) { + if (this.hasFont(font)) { + Fonts.set(font.key); + } else { + Fonts.set(font.key, this.mediafileUrl); + } }, }, computed: { @@ -77,6 +125,11 @@ angular.module('OpenSlidesApp.mediafiles.resources', [ is_presentable: ['is_pdf', 'is_image', 'is_video', function (is_pdf, is_image, is_video) { return (is_pdf && !this.mediafile.encrypted) || is_image || is_video; }], + is_font: [function () { + var FONT_FILE_EXTENSIONS = ['ttf', 'woff']; + var ext = _.last(this.mediafile.name.split('.')); + return _.includes(FONT_FILE_EXTENSIONS, ext); + }], mediafileUrl: [function () { return this.media_url_prefix + this.mediafile.name; }], diff --git a/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html b/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html index a819d0ffa..314fb59f5 100644 --- a/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html +++ b/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html @@ -291,8 +291,12 @@
- + + + +
@@ -325,7 +329,7 @@
-
@@ -354,14 +358,49 @@
+ +
+ + + + + + + + + + + + {{ font.display_name | translate }},
+
+
+
+
+ +
+ +
+
+
diff --git a/openslides/users/signals.py b/openslides/users/signals.py index 836a0bd24..483ac1c1e 100644 --- a/openslides/users/signals.py +++ b/openslides/users/signals.py @@ -39,7 +39,7 @@ def create_builtin_groups_and_admin(**kwargs): 'assignments.can_nominate_self', 'assignments.can_see', 'core.can_manage_config', - 'core.can_manage_logos', + 'core.can_manage_logos_and_fonts', 'core.can_manage_projector', 'core.can_manage_tags', 'core.can_manage_chat', @@ -116,7 +116,7 @@ def create_builtin_groups_and_admin(**kwargs): permission_dict['core.can_see_frontpage'], permission_dict['core.can_see_projector'], permission_dict['core.can_manage_config'], - permission_dict['core.can_manage_logos'], + permission_dict['core.can_manage_logos_and_fonts'], permission_dict['core.can_manage_projector'], permission_dict['core.can_manage_tags'], permission_dict['core.can_use_chat'],