Support logos
This commit is contained in:
parent
34c7b51159
commit
6c932aa77a
@ -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.
|
||||||
|
|
||||||
|
|
||||||
Version 2.1.1 (2017-04-05)
|
Version 2.1.1 (2017-04-05)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
25
openslides/core/migrations/0005_auto_20170412_1258.py
Normal file
25
openslides/core/migrations/0005_auto_20170412_1258.py
Normal 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')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -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):
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -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>
|
||||||
|
@ -632,7 +632,13 @@ 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'):
|
||||||
result = has_perm(self.request.user, 'core.can_manage_config')
|
# 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')
|
||||||
else:
|
else:
|
||||||
result = False
|
result = False
|
||||||
return result
|
return result
|
||||||
|
@ -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);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 -->
|
||||||
|
@ -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'],
|
||||||
|
Loading…
Reference in New Issue
Block a user