Merge pull request #3184 from FinnStutzenstein/Logo

Support logos
This commit is contained in:
Emanuel Schütze 2017-04-12 14:49:59 +02:00 committed by GitHub
commit 2be9d45884
13 changed files with 187 additions and 7 deletions

View File

@ -19,6 +19,7 @@ Motions:
Core: Core:
- No reload on logoff. OpenSlides is now a full single page - No reload on logoff. OpenSlides is now a full single page
application [#3172]. application [#3172].
- Adding support for choosing image files as logos.
Users: Users:
- User without permission to see users can now see agenda item speakers, - User without permission to see users can now see agenda item speakers,

View File

@ -15,6 +15,7 @@ INPUT_TYPE_MAPPING = {
'colorpicker': str, 'colorpicker': str,
'datetimepicker': int, 'datetimepicker': int,
'majorityMethod': str, 'majorityMethod': str,
'logo': dict,
} }
@ -104,6 +105,19 @@ class ConfigHandler:
if not isinstance(comment['public'], bool): if not isinstance(comment['public'], bool):
raise ConfigError(_('public property has to be bool.')) raise ConfigError(_('public property has to be bool.'))
if config_variable.input_type == 'logo':
if not isinstance(value, dict):
raise ConfigError(_('logo has to be a dict.'))
whitelist = (
'path',
'display_name',
)
for required_entry in whitelist:
if required_entry not in value:
raise ConfigError(_('{} has to be given.'.format(required_entry)))
if not isinstance(value[required_entry], str):
raise ConfigError(_('{} has to be a string.'.format(required_entry)))
# Save the new value to the database. # Save the new value to the database.
try: try:
db_value = ConfigStore.objects.get(key=key) db_value = ConfigStore.objects.get(key=key)

View File

@ -209,3 +209,21 @@ def get_config_variables():
weight=201, weight=201,
group='Projector', group='Projector',
hidden=True) hidden=True)
# Logos.
yield ConfigVariable(
name='logos_available',
default_value=['logo_projector_main'],
weight=300,
group='Logo',
hidden=True)
yield ConfigVariable(
name='logo_projector_main',
default_value={
'display_name': 'Main projector logo',
'path': ''},
input_type='logo',
weight=301,
group='Logo',
hidden=True)

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-04-12 10:58
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0004_auto_20170215_1624'),
]
operations = [
migrations.AlterModelOptions(
name='configstore',
options={
'default_permissions': (),
'permissions': (
('can_manage_config', 'Can manage configuration'),
('can_manage_logos', 'Can manage logos')
)
},
),
]

View File

@ -257,7 +257,8 @@ class ConfigStore(RESTModelMixin, models.Model):
class Meta: class Meta:
default_permissions = () default_permissions = ()
permissions = ( permissions = (
('can_manage_config', 'Can manage configuration'),) ('can_manage_config', 'Can manage configuration'),
('can_manage_logos', 'Can manage logos'))
@classmethod @classmethod
def get_collection_string(cls): def get_collection_string(cls):

View File

@ -580,6 +580,49 @@ angular.module('OpenSlidesApp.core', [
} }
]) ])
.factory('Logos', [
'Config',
'gettext',
function (Config, gettext) {
return {
getKeys: function () {
return Config.get('logos_available').value;
},
getAll: function () {
var self = this;
return _.map(this.getKeys(), function (key) {
return self.getFromKey(key);
});
},
getFromKey: function (key) {
var config = Config.get(key).value;
config.key = key;
return config;
},
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) {
var config = Config.get(key);
if (!mediafile || mediafile.canBeUsedAsLogo()) {
config.value.path = mediafile ? mediafile.mediafileUrl : '';
Config.save(key);
}
},
getLogosForMediafile: function (mediafile) {
return _.filter(this.getAll(), function (logoPlaceholder) {
return logoPlaceholder.path === mediafile.mediafileUrl;
});
},
};
}
])
.factory('Tag', [ .factory('Tag', [
'DS', 'DS',
function(DS) { function(DS) {

View File

@ -150,7 +150,8 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
'slides', 'slides',
'Config', 'Config',
'ProjectorID', 'ProjectorID',
function($scope, $location, $timeout, Projector, slides, Config, ProjectorID) { 'Logos',
function($scope, $location, $timeout, Projector, slides, Config, ProjectorID, Logos) {
var projectorId = ProjectorID(); var projectorId = ProjectorID();
$scope.broadcast = 0; $scope.broadcast = 0;
@ -243,6 +244,11 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
} }
}); });
$scope.getLogo = function (key) {
var logo = Logos.getFromKey(key);
return logo ? logo.path : void 0;
};
$scope.$on('$destroy', function() { $scope.$on('$destroy', function() {
if ($scope.broadcastDeregister) { if ($scope.broadcastDeregister) {
$scope.broadcastDeregister(); $scope.broadcastDeregister();

View File

@ -33,7 +33,8 @@
<div id="header"> <div id="header">
<img ng-if="config('projector_enable_logo')" id="logo" src="/static/img/logo-projector.png" alt="OpenSlides" /> <img ng-if="config('projector_enable_logo')" id="logo" alt="OpenSlides"
ng-src="{{ getLogo('logo_projector_main') || '/static/img/logo-projector.png' }}"/>
<div ng-if="config('projector_enable_title')" id="eventdata"> <div ng-if="config('projector_enable_title')" id="eventdata">
<div class="title" ng-class="{ 'titleonly': !config('general_event_description') }" <div class="title" ng-class="{ 'titleonly': !config('general_event_description') }"
ng-bind-html="config('general_event_name')"></div> ng-bind-html="config('general_event_name')"></div>

View File

@ -632,6 +632,12 @@ class ConfigViewSet(ViewSet):
# enabled. # enabled.
result = self.request.user.is_authenticated() or anonymous_is_enabled() result = self.request.user.is_authenticated() or anonymous_is_enabled()
elif self.action in ('partial_update', 'update'): 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 default permissions 'core.can_manage_config'.
if self.kwargs['pk'].startswith('logo'):
result = has_perm(self.request.user, 'core.can_manage_logos')
else:
result = has_perm(self.request.user, 'core.can_manage_config') result = has_perm(self.request.user, 'core.can_manage_config')
else: else:
result = False result = False

View File

@ -22,8 +22,9 @@ angular.module('OpenSlidesApp.mediafiles.list', [
'User', 'User',
'Mediafile', 'Mediafile',
'MediafileForm', 'MediafileForm',
'Logos',
function ($http, $scope, gettext, ngDialog, osTableFilter, osTableSort, function ($http, $scope, gettext, ngDialog, osTableFilter, osTableSort,
ProjectionDefault, Projector, User, Mediafile, MediafileForm) { ProjectionDefault, Projector, User, Mediafile, MediafileForm, Logos) {
Mediafile.bindAll({}, $scope, 'mediafiles'); Mediafile.bindAll({}, $scope, 'mediafiles');
User.bindAll({}, $scope, 'users'); User.bindAll({}, $scope, 'users');
$scope.$watch(function() { $scope.$watch(function() {
@ -250,6 +251,22 @@ angular.module('OpenSlidesApp.mediafiles.list', [
{playing: !mediafile.playing} {playing: !mediafile.playing}
); );
}; };
/** Logos **/
$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);
};
} }
]) ])

View File

@ -12,7 +12,8 @@ angular.module('OpenSlidesApp.mediafiles.resources', [
'DS', 'DS',
'gettext', 'gettext',
'jsDataModel', 'jsDataModel',
function (DS, gettext, jsDataModel) { 'Logos',
function (DS, gettext, jsDataModel, Logos) {
var name = 'mediafiles/mediafile'; var name = 'mediafiles/mediafile';
return DS.defineResource({ return DS.defineResource({
name: name, name: name,
@ -48,6 +49,15 @@ angular.module('OpenSlidesApp.mediafiles.resources', [
} }
}); });
}, },
isUsedAsLogo: function () {
return Logos.isMediafileUsedAsLogo(this);
},
canBeUsedAsLogo: function () {
return Logos.canMediafileBeUsedAsLogo(this);
},
getLogos: function () {
return Logos.getLogosForMediafile(this);
},
}, },
computed: { computed: {
is_pdf: ['filetype', function (filetype) { is_pdf: ['filetype', function (filetype) {

View File

@ -282,6 +282,8 @@
<div class="spacer-right"> <!-- horizontal block --> <div class="spacer-right"> <!-- horizontal block -->
<i ng-style="{'visibility': mediafile.hidden ? 'visible' : 'hidden'}" class="fa fa-lock fa-lg" <i ng-style="{'visibility': mediafile.hidden ? 'visible' : 'hidden'}" class="fa fa-lock fa-lg"
title="{{ 'Is hidden' | translate }}"></i> title="{{ 'Is hidden' | translate }}"></i>
<i ng-style="{'visibility': mediafile.isUsedAsLogo() ? 'visible' : 'hidden'}" class="fa fa-picture-o fa-lg spacer-left"
title="{{ 'Is used as a logo' | translate }}" os-perms="core.can_manage_logos"></i>
</div> </div>
<div> <div>
<div> <!-- vertical block --> <div> <!-- vertical block -->
@ -312,7 +314,41 @@
<div><i class="fa fa-upload"></i> {{ mediafile.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}</div> <div><i class="fa fa-upload"></i> {{ mediafile.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}</div>
</small> </small>
</div> </div>
<div style="width: 40%;" class="pull-right optional"></div> <div style="width: 40%;" class="pull-right optional">
<!-- Logo placeholder dropdown for manage user -->
<div os-perms="core.can_manage_logos"
ng-mouseover="mediafile.logoHover=true"
ng-mouseleave="mediafile.logoHover=false"
ng-show="mediafile.canBeUsedAsLogo()">
<span uib-dropdown>
<span id="dropdownLogo{{ mediafile.id }}" class="pointer nobr"
uib-dropdown-toggle uib-tooltip="{{ 'Manage logos' | translate }}"
tooltip-class="nobr">
<span ng-if="!mediafile.isUsedAsLogo()" ng-show="mediafile.hover">
<i class="fa fa-picture-o"></i>
<i class="fa fa-plus"></i>
</span>
<span ng-if="mediafile.isUsedAsLogo()">
<i class="fa fa-picture-o spacer-right"></i>
<span ng-repeat="logo in mediafile.getLogos()">
<small>
{{ logo.display_name }}<span ng-if="!$last">,</span>
</small>
</span>
<i class="fa fa-cog fa-lg spacer-left" ng-show="mediafile.logoHover"></i>
</span>
</span>
<ul class="dropdown-menu" aria-labelledby="dropdownLogos{{ mediafile.id }}">
<li ng-repeat="logo in logos">
<a href ng-click="toggleLogo(mediafile, logo)">
<i class="fa fa-check" ng-if="hasLogo(mediafile, logo)"></i>
{{ logo.display_name }}
</a>
</li>
</ul>
</span>
</div>
</div>
</div> </div>
</div><!-- end data row --> </div><!-- end data row -->

View File

@ -38,6 +38,7 @@ def create_builtin_groups_and_admin(**kwargs):
'assignments.can_nominate_self', 'assignments.can_nominate_self',
'assignments.can_see', 'assignments.can_see',
'core.can_manage_config', 'core.can_manage_config',
'core.can_manage_logos',
'core.can_manage_projector', 'core.can_manage_projector',
'core.can_manage_tags', 'core.can_manage_tags',
'core.can_manage_chat', 'core.can_manage_chat',
@ -113,6 +114,7 @@ def create_builtin_groups_and_admin(**kwargs):
permission_dict['core.can_see_frontpage'], permission_dict['core.can_see_frontpage'],
permission_dict['core.can_see_projector'], permission_dict['core.can_see_projector'],
permission_dict['core.can_manage_config'], permission_dict['core.can_manage_config'],
permission_dict['core.can_manage_logos'],
permission_dict['core.can_manage_projector'], permission_dict['core.can_manage_projector'],
permission_dict['core.can_manage_tags'], permission_dict['core.can_manage_tags'],
permission_dict['core.can_use_chat'], permission_dict['core.can_use_chat'],