Improved related agenda items

- Added QuickEdit mode for related agenda items
- show/hide hidden agenda items
- Added ng-dialog for modal create/update dialogs of customslides
- use generic links for list of speakers, edit, delete, project
- Moved projector elements to sidebar of index template (In progress!
  It will be improved with new base template design).
- Fixed error if chat messages is empty.
- Moved ngSanitize to base.js to use ng-bind-html in projector slides.
This commit is contained in:
Emanuel Schuetze 2015-11-21 20:14:19 +01:00
parent 78c7b2497f
commit 968083e9e5
16 changed files with 373 additions and 254 deletions

View File

@ -23,6 +23,7 @@
"angular-xeditable": "~0.1.9", "angular-xeditable": "~0.1.9",
"angular-scroll-glue": "~2.0.6", "angular-scroll-glue": "~2.0.6",
"ngBootbox": "~0.1.2", "ngBootbox": "~0.1.2",
"ng-dialog": "~0.5.6",
"sockjs": "~0.3.4", "sockjs": "~0.3.4",
"font-awesome-bower": "~4.4.0", "font-awesome-bower": "~4.4.0",
"js-data": "~2.8.1", "js-data": "~2.8.1",

View File

@ -92,26 +92,82 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
'$scope', '$scope',
'$http', '$http',
'$state', '$state',
'ngDialog',
'Agenda', 'Agenda',
'AgendaTree', 'AgendaTree',
'Customslide',
'Projector', 'Projector',
function($scope, $http, $state, Agenda, AgendaTree, Projector) { function($scope, $http, $state, ngDialog, Agenda, AgendaTree, Customslide, Projector) {
// Bind agenda tree to the scope // Bind agenda tree to the scope
$scope.$watch(function () { $scope.$watch(function () {
return Agenda.lastModified(); return Agenda.lastModified();
}, function () { }, function () {
$scope.items = AgendaTree.getFlatTree(Agenda.getAll()); $scope.items = AgendaTree.getFlatTree(Agenda.getAll());
}); });
$scope.alert = {};
// open detail view link // project related item (content object)
$scope.openDetail = function (id) { $scope.project = function (item) {
$state.go('agenda.item.detail', {id: id}); item.getContentResource().find(item.content_object.id).then(
function(object) {
object.project();
}
);
}; };
// save changed item // open new customslide dialog
$scope.save = function (item) { $scope.newDialog = function () {
Agenda.save(item); ngDialog.open({
template: 'static/templates/core/customslide-form.html',
controller: 'CustomslideCreateCtrl',
className: 'ngdialog-theme-default wide-form'
});
};
// detail view of related item (content object)
$scope.open = function (item) {
$state.go(item.content_object.collection.replace('/','.')+'.detail',
{id: item.content_object.id});
};
// edit view of related item (content object)
$scope.edit = function (item) {
if (item.content_object.collection == "core/customslide") {
ngDialog.open({
template: 'static/templates/core/customslide-form.html',
controller: 'CustomslideUpdateCtrl',
className: 'ngdialog-theme-default wide-form',
resolve: {
customslide: function(Customslide) {
return Customslide.find(item.content_object.id);
}
}
});
}
else {
$state.go(item.content_object.collection.replace('/','.')+'.detail.update',
{id: item.content_object.id});
}
};
// update changed item
$scope.update = function (item) {
Agenda.save(item).then(
function(success) {
item.quickEdit = false;
$scope.alert.show = false;
},
function(error){
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = { type: 'danger', msg: message, show: true };
});
;
};
// delete related item
$scope.deleteRelatedItem = function (item) {
if (item.content_object.collection == 'core/customslide') {
Customslide.destroy(item.content_object.id);
}
}; };
// *** delete mode functions *** // *** delete mode functions ***
@ -131,11 +187,14 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
}); });
} }
}; };
// delete selected item // delete selected items only if items are customslides
$scope.delete = function () { $scope.delete = function () {
angular.forEach($scope.items, function (item) { angular.forEach($scope.items, function (item) {
if (item.selected) if (item.selected) {
Agenda.destroy(item.id); if (item.content_object.collection == 'core/customslide') {
Customslide.destroy(item.content_object.id);
}
}
}); });
$scope.isDeleteMode = false; $scope.isDeleteMode = false;
$scope.uncheckAll(); $scope.uncheckAll();
@ -292,7 +351,8 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
'$scope', '$scope',
'$state', '$state',
'Agenda', 'Agenda',
function($scope, $state, Agenda) { 'Customslide',
function($scope, $state, Agenda, Customslide) {
// import from textarea // import from textarea
$scope.importByLine = function () { $scope.importByLine = function () {
$scope.items = $scope.itemlist[0].split("\n"); $scope.items = $scope.itemlist[0].split("\n");
@ -300,7 +360,7 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
$scope.items.forEach(function(title) { $scope.items.forEach(function(title) {
var item = {title: title}; var item = {title: title};
// TODO: create all items in bulk mode // TODO: create all items in bulk mode
Agenda.create(item).then( Customslide.create(item).then(
function(success) { function(success) {
$scope.importcounter++; $scope.importcounter++;
} }
@ -324,8 +384,8 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
var item = {}; var item = {};
item.title = obj[i].title; item.title = obj[i].title;
item.text = obj[i].text; item.text = obj[i].text;
item.duration = obj[i].duration; // TODO: save also 'duration' in related agenda item
Agenda.create(item).then( Customslide.create(item).then(
function(success) { function(success) {
$scope.csvimportcounter++; $scope.csvimportcounter++;
} }

View File

@ -1,9 +1,9 @@
<h1>{{ item.get_title }}</h1> <h1>{{ item.title }}</h1>
<div id="submenu"> <div id="submenu">
<a ui-sref="agenda.item.list" class="btn btn-sm btn-default"> <a ui-sref="agenda.item.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i> <i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate> <translate>Back to agenda</translate>
</a> </a>
<!-- project --> <!-- project -->
<a os-perms="core.can_manage_projector" class="btn btn-default btn-sm" <a os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
@ -12,28 +12,11 @@
title="{{ 'Project item' | translate }}"> title="{{ 'Project item' | translate }}">
<i class="fa fa-video-camera"></i> <i class="fa fa-video-camera"></i>
</a> </a>
<!-- edit -->
<a ui-sref="agenda.item.detail.update({id: item.id })" os-perms="agenda.can_manage"
class="btn btn-default btn-sm"
title="{{ 'Edit' | translate}}">
<i class="fa fa-pencil"></i>
</a>
</div>
<div class="white-space-pre-line">{{ item.text }}</div>
<div os-perm="agenda.can_manage">
<h2 os-perm="agenda.can_manage" translate>Duration</h2>
{{ item.duration }}
</div>
<div os-perm="agenda.can_manage">
<h2 os-perm="agenda.can_manage" translate>Comment</h2>
<div class="white-space-pre-line">{{ item.comment }}</div>
</div> </div>
<h2 translate>List of speakers</h2> <h2>
<translate>List of speakers</translate>
<span os-perms="agenda.can_manage"> <span os-perms="agenda.can_manage">
<button ng-if="item.speaker_list_closed" ng-click="closeList(false)" <button ng-if="item.speaker_list_closed" ng-click="closeList(false)"
class="btn btn-sm btn-danger" translate> class="btn btn-sm btn-danger" translate>
@ -44,18 +27,18 @@
Opened Opened
</button> </button>
</span> </span>
<!-- project list --> <!-- TODO: project list
ng-class="{ 'btn-primary': item.isListOfSpeakersProjected() }"-->
<a os-perms="core.can_manage_projector" class="btn btn-default btn-sm" <a os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
ng-class="{ 'btn-primary': item.isProjected() }"
ng-click="projectListOfSpeakers()"> ng-click="projectListOfSpeakers()">
<i class="fa fa-video-camera"></i> Project list <i class="fa fa-video-camera"></i>
<translate>List of speakers</translate>
</a> </a>
</h2> </h2>
<!-- TODO: <!-- TODO:
* show only 'add me' OR 'remove me' button * show only 'add me' OR 'remove me' button
--> -->
<div class="well">
<button class="btn btn-default btn-xs" type="button" <button class="btn btn-default btn-xs" type="button"
data-toggle="collapse" data-target="#old_speakers" data-toggle="collapse" data-target="#old_speakers"
aria-expanded="false" aria-controls="collapseExample"> aria-expanded="false" aria-controls="collapseExample">
@ -133,4 +116,3 @@
<translate>Stop current speaker</translate> <translate>Stop current speaker</translate>
</button> </button>
</div> </div>
</div>

View File

@ -34,7 +34,7 @@ Keep each item in a single line.</p>
<p translate>Please note:</p> <p translate>Please note:</p>
<ul><!--TODO: utf-8 encoding still required with angular-csv? --> <ul><!--TODO: utf-8 encoding still required with angular-csv? -->
<li><translate>Required comma separated values</translate>:<br> <li><translate>Required comma separated values</translate>:<br>
<code translate>'title, text, duration'</code> <code translate>'title, text'</code>
<li translate>Text and duration are optional and may be empty. <li translate>Text and duration are optional and may be empty.
<li translate>The header in first line is required. <li translate>The header in first line is required.
<li translate>Required CSV file encoding is UTF-8. <li translate>Required CSV file encoding is UTF-8.

View File

@ -1,7 +1,7 @@
<h1 translate>Agenda</h1> <h1 translate>Agenda</h1>
<div id="submenu"> <div id="submenu">
<a ui-sref="agenda.item.create" os-perms="agenda.can_manage" class="btn btn-primary btn-sm"> <a ng-click="newDialog()" ng-dialog-class="ngdialog-theme-plain"os-perms="agenda.can_manage" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg"></i> <i class="fa fa-plus fa-lg"></i>
<translate>New</translate> <translate>New</translate>
</a> </a>
@ -51,6 +51,16 @@
<i class="fa fa-trash fa-lg"></i> <i class="fa fa-trash fa-lg"></i>
<translate>Delete selected items</translate> <translate>Delete selected items</translate>
</a> </a>
<!-- hidden item filter -->
<label>
<input type="checkbox" ng-model="filter.noHiddenItems" ng-true-value="1" ng-false-value="">
<translate> Hide internal agenda items</translate>
</label>
<!-- closed filter -->
<label>
<input type="checkbox" ng-model="filter.noClosedItems" ng-true-value="false" ng-false-value="">
<translate> Hide closed items</translate>
</label>
</div> </div>
</form> </form>
</div> </div>
@ -84,32 +94,95 @@
<th class="minimum"> <th class="minimum">
<translate>Done</translate> <translate>Done</translate>
<tbody> <tbody>
<tr ng-repeat="item in items | filter: filter.search" <tr ng-repeat="item in items | filter: filter.search | filter: {type: filter.noHiddenItems}
ng-click="openDetail(item.id)" | filter: {closed: filter.noClosedItems}"
ng-class="{ 'activeline': item.isProjected() }" class="animate-item"
class="pointer"> ng-class="{ 'activeline': item.isProjected(), 'selected': item.selected, 'hiddenrow': item.type == 2}">
<!-- projector column --> <!-- projector column -->
<td ng-show="!isDeleteMode" os-perms="core.can_manage_projector"> <td ng-show="!isDeleteMode" os-perms="core.can_manage_projector">
<a class="btn btn-default btn-sm" <a class="btn btn-default btn-sm"
ng-class="{ 'btn-primary': item.isProjected() }" ng-class="{ 'btn-primary': item.isProjected() }"
ng-click="item.project(); $event.stopPropagation();" ng-click="project(item)"
title="{{ 'Project item' | translate }}"> title="{{ 'Project item' | translate }}">
<i class="fa fa-video-camera"></i> <i class="fa fa-video-camera"></i>
</a> </a>
<!-- delete selection column --> <!-- delete selection column -->
<td ng-show="isDeleteMode" os-perms="agenda.can_manage" class="deleteColumn" <td ng-show="isDeleteMode" os-perms="agenda.can_manage" class="deleteColumn">
ng-click="$event.stopPropagation();">
<input type="checkbox" ng-model="item.selected"> <input type="checkbox" ng-model="item.selected">
<!-- agenda data columns --> <!-- agenda data columns -->
<td> <td ng-if="!item.quickEdit" ng-mouseover="item.hover=true" ng-mouseleave="item.hover=false">
<span ng-repeat="n in [].constructor(item.parentCount) track by $index">&ndash;</span> <span ng-if="item.type == 2" title="'Hidden agenda item'|translate"><i class="fa fa-ban"></i></span>
{{ item.item_number }} {{ item.getTitle() }} <strong>
<a href="" ng-click="open(item)">
<span ng-repeat="n in [].constructor(item.parentCount) track by $index">&ndash;</span>
{{ item.getTitle() }}
</a>
</strong>
<div ng-if="item.comment"> <div ng-if="item.comment">
<small><i class="fa fa-info-circle"></i> {{ item.comment }}</small> <small><i class="fa fa-info-circle"></i> {{ item.comment }}</small>
</div> </div>
<td os-perms="agenda.can_manage" class="optional"> <div os-perms="agenda.can_manage" class="hoverActions" ng-class="{'hiddenDiv': !item.hover}">
<a ui-sref="agenda.item.detail({id: item.id})" translate>List of speakers</a> |
<a href="" ng-click="item.quickEdit=true" translate>QuickEdit</a> |
<a href="" ng-click="edit(item)" translate>Edit</a>
<!-- TODO: translate confirm message -->
<span ng-if="item.content_object.collection == 'core/customslide'"> |
<a href="" class="text-danger"
ng-bootbox-confirm="Are you sure you want to delete <b>{{ item.getTitle() }}</b>?"
ng-bootbox-confirm-action="deleteRelatedItem(item)" translate>Delete</a>
</span>
</div>
<td ng-if="!item.quickEdit" class="optional">
{{ item.duration }} {{ item.duration }}
<span ng-if="item.duration" translate>h</span> <span ng-if="item.duration" translate>h</span>
<td ng-click="$event.stopPropagation();"> <td ng-if="!item.quickEdit">
<input type="checkbox" ng-model="item.closed" ng-change="save(item.id);"> <input type="checkbox" ng-model="item.closed" ng-change="update(item.id);">
<!-- quickEdit columns -->
<td ng-if="item.quickEdit" os-perms-lite="agenda.can_manage" colspan="3">
<form ng-submit="update(item)">
<h4>{{ item.getTitle() }} <span class="text-muted">&ndash; QuickEdit</span></h4>
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
{{alert.msg}}
</alert>
<div class="row">
<div class="col-xs-6">
<label for="inputTitle" translate>Title</label>
<input type="text" ng-model="item.title" class="form-control input-sm" id="inputTitle">
</div>
<div class="col-xs-6">
<label for="inputComment" translate>Comment</label>
<input type="text" ng-model="item.comment" class="form-control input-sm" id="inputComment">
</div>
</div>
<div class="row">
<div class="col-xs-6">
<label for="inputItemNumber" translate>Item number</label>
<input type="text" ng-model="item.item_number" class="form-control input-sm" id="inputItemNumber">
</div>
<div class="col-xs-6">
<label for="inputDuration" translate>Duration</label>
<input type="text" ng-model="item.duration" class="form-control input-sm" id="inputDuration">
</div>
</div>
<div class="row">
<div class="col-xs-6">
<label>
<!-- item type: AGENDA_ITEM = 1, HIDDEN_ITEM = 2 -->
<input type="checkbox" ng-model="item.type" ng-true-value="2" ng-false-value="1">
<translate> Hidden agenda item</translate>
</label>
</div>
<div class="col-xs-6">
</div>
</div>
<div class="spacer">
<button ng-click="item.quickEdit=false" class="btn btn-default pull-left" translate>
Cancel
</button> &nbsp;
<button type="submit" class="btn btn-primary" translate>
Update
</button>
<a href="" ng-click="edit(item)" class="pull-right"><translate>Edit</translate>...</a>
</div>
</form>
</table> </table>

View File

@ -61,8 +61,8 @@ body {
.hiddenDiv { .hiddenDiv {
visibility: hidden; visibility: hidden;
} }
/* override bootstraps's progress bar for poll results*/ /* override bootstraps's progress bar for poll results */
.progress { .pollresults .progress {
height: 12px; height: 12px;
margin-bottom: 0; margin-bottom: 0;
} }
@ -70,6 +70,31 @@ body {
.result_label { .result_label {
margin-top: 5px; margin-top: 5px;
} }
/* background colors for table rows */
tr.offline td, li.offline {
background-color: #EAEAEA !important;
}
tr.hiddenrow td {
background-color: #e5e5e5;
}
tr.activeline td, li.activeline, .projected {
background-color: #bed4de;
}
tr.selected td {
background-color: #ff9999;
}
/* override ngdialog-theme-default */
.ngdialog.ngdialog-theme-default {
padding-top: 50px;
}
.ngdialog.ngdialog-theme-default.wide-form .ngdialog-content {
width: 650px;
}
.ngdialog h2 {
margin-top: 5px;
margin-bottom: 20px;
}
.inline { .inline {
display: inline; display: inline;
@ -253,15 +278,7 @@ div.import > div > input[type="text"] {
} }
tr.offline td, li.offline {
background-color: #EAEAEA !important;
}
tr.activeline td, li.activeline, .projected {
background-color: #bed4de;
}
tr.selected td {
background-color: #ff9999;
}
.nopadding { .nopadding {
padding: 0; padding: 0;
} }

View File

@ -8,6 +8,7 @@ angular.module('OpenSlidesApp.core', [
'js-data', 'js-data',
'gettext', 'gettext',
'ngAnimate', 'ngAnimate',
'ngSanitize', // TODO: only use this in functions that need it.
'ui.bootstrap', 'ui.bootstrap',
'ui.tree', 'ui.tree',
'uiSwitch', 'uiSwitch',

View File

@ -9,9 +9,9 @@ angular.module('OpenSlidesApp.core.site', [
'formly', 'formly',
'formlyBootstrap', 'formlyBootstrap',
'ngBootbox', 'ngBootbox',
'ngDialog',
'ngMessages', 'ngMessages',
'ngCsvImport', 'ngCsvImport',
'ngSanitize', // TODO: only use this in functions that need it.
'ui.select', 'ui.select',
'luegg.directives', 'luegg.directives',
'xeditable', 'xeditable',
@ -220,14 +220,6 @@ angular.module('OpenSlidesApp.core.site', [
abstract: true, abstract: true,
template: "<ui-view/>", template: "<ui-view/>",
}) })
.state('core.customslide.list', {
resolve: {
customslides: function(Customslide) {
return Customslide.findAll();
}
}
})
.state('core.customslide.create', {})
.state('core.customslide.detail', { .state('core.customslide.detail', {
resolve: { resolve: {
customslide: function(Customslide, $stateParams) { customslide: function(Customslide, $stateParams) {
@ -235,11 +227,6 @@ angular.module('OpenSlidesApp.core.site', [
} }
} }
}) })
.state('core.customslide.detail.update', {
views: {
'@core.customslide': {}
}
})
// tag // tag
.state('core.tag', { .state('core.tag', {
url: '/tag', url: '/tag',
@ -369,46 +356,6 @@ angular.module('OpenSlidesApp.core.site', [
}; };
}) })
.controller("LoginFormCtrl", function ($scope, $modal) {
$scope.open = function () {
var modalInstance = $modal.open({
animation: true,
templateUrl: 'LoginForm.html',
controller: 'LoginFormModalCtrl',
size: 'sm',
});
};
})
.controller('LoginFormModalCtrl', [
'$scope',
'$modalInstance',
'$http',
'operator',
function ($scope, $modalInstance, $http, operator) {
$scope.login = function () {
$http.post(
'/users/login/',
{'username': $scope.username, 'password': $scope.password}
).success(function(data) {
if (data.success) {
operator.setUser(data.user_id);
$scope.loginFailed = false;
$modalInstance.close();
} else {
$scope.loginFailed = true;
}
});
};
$scope.guest = function () {
$modalInstance.dismiss('cancel');
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
])
// Version Controller // Version Controller
.controller('VersionCtrl', [ .controller('VersionCtrl', [
'$scope', '$scope',
@ -433,37 +380,33 @@ angular.module('OpenSlidesApp.core.site', [
}; };
}) })
// Customslide Controller
.controller('CustomslideListCtrl', [
'$scope',
'$http',
'Customslide',
function($scope, $http, Customslide) {
Customslide.bindAll({}, $scope, 'customslides');
// setup table sorting // Provide generic motion form fields for create and update view
$scope.sortColumn = 'title'; .factory('CustomslideFormFieldFactory', [
$scope.reverse = false; 'gettext',
// function to sort by clicked column 'CKEditorOptions',
$scope.toggleSort = function ( column ) { function (gettext, CKEditorOptions) {
if ( $scope.sortColumn === column ) { return {
$scope.reverse = !$scope.reverse; getFormFields: function () {
return [
{
key: 'title',
type: 'input',
templateOptions: {
label: gettext('Title'),
required: true
}
},
{
key: 'text',
type: 'textarea',
templateOptions: {
label: gettext('Text')
},
ngModelElAttrs: {'ckeditor': 'CKEditorOptions'}
}];
} }
$scope.sortColumn = column; }
};
// save changed customslide
$scope.save = function (customslide) {
Customslide.save(customslide);
};
$scope.delete = function (customslide) {
//TODO: add confirm message
Customslide.destroy(customslide.id).then(
function(success) {
//TODO: success message
}
);
};
} }
]) ])
@ -660,6 +603,7 @@ angular.module('OpenSlidesApp.core.site', [
} }
]) ])
// Customslide Controllers
.controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) { .controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) {
Customslide.bindOne(customslide.id, $scope, 'customslide'); Customslide.bindOne(customslide.id, $scope, 'customslide');
Customslide.loadRelations(customslide, 'agenda_item'); Customslide.loadRelations(customslide, 'agenda_item');
@ -668,15 +612,18 @@ angular.module('OpenSlidesApp.core.site', [
.controller('CustomslideCreateCtrl', [ .controller('CustomslideCreateCtrl', [
'$scope', '$scope',
'$state', '$state',
'CKEditorOptions',
'Customslide', 'Customslide',
function($scope, $state, CKEditorOptions, Customslide) { 'CustomslideFormFieldFactory',
function($scope, $state, Customslide, CustomslideFormFieldFactory) {
$scope.customslide = {}; $scope.customslide = {};
$scope.CKEditorOptions = CKEditorOptions; // get all form fields
$scope.formFields = CustomslideFormFieldFactory.getFormFields();
// save form
$scope.save = function (customslide) { $scope.save = function (customslide) {
Customslide.create(customslide).then( Customslide.create(customslide).then(
function(success) { function(success) {
$state.go('core.customslide.list'); $scope.closeThisDialog();
} }
); );
}; };
@ -686,16 +633,20 @@ angular.module('OpenSlidesApp.core.site', [
.controller('CustomslideUpdateCtrl', [ .controller('CustomslideUpdateCtrl', [
'$scope', '$scope',
'$state', '$state',
'CKEditorOptions',
'Customslide', 'Customslide',
'CustomslideFormFieldFactory',
'customslide', 'customslide',
function($scope, $state, CKEditorOptions, Customslide, customslide) { function($scope, $state, Customslide, CustomslideFormFieldFactory, customslide) {
$scope.customslide = customslide; // set initial values for form model
$scope.CKEditorOptions = CKEditorOptions; $scope.model = customslide;
// get all form fields
$scope.formFields = CustomslideFormFieldFactory.getFormFields();
// save form
$scope.save = function (customslide) { $scope.save = function (customslide) {
Customslide.save(customslide).then( Customslide.save(customslide).then(
function(success) { function(success) {
$state.go('core.customslide.list'); $scope.closeThisDialog();
} }
); );
}; };
@ -791,10 +742,12 @@ angular.module('OpenSlidesApp.core.site', [
// increment unread messages counter for each new message // increment unread messages counter for each new message
$scope.$watch('chatmessages', function (newVal, oldVal) { $scope.$watch('chatmessages', function (newVal, oldVal) {
// add new message id if there is really a new message which is not yet tracked // add new message id if there is really a new message which is not yet tracked
if ((oldVal[oldVal.length-1].id != newVal[newVal.length-1].id) && if (oldVal.length > 0) {
($.inArray(newVal[newVal.length-1].id, NewChatMessages) == -1)) { if ((oldVal[oldVal.length-1].id != newVal[newVal.length-1].id) &&
NewChatMessages.push(newVal[newVal.length-1].id); ($.inArray(newVal[newVal.length-1].id, NewChatMessages) == -1)) {
$scope.unreadMessages = NewChatMessages.length; NewChatMessages.push(newVal[newVal.length-1].id);
$scope.unreadMessages = NewChatMessages.length;
}
} }
}) })
} }

View File

@ -1,24 +1,23 @@
<h1>{{ customslide.title }}</h1> <h1>{{ customslide.title }}</h1>
<div id="submenu"> <div id="submenu">
<a ui-sref="core.customslide.list" class="btn btn-sm btn-default"> <a ui-sref="agenda.item.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i> <i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate> <translate>Back to overview</translate>
</a> </a>
<!-- projector, TODO: add link to activate slide--> <!-- List of speakers -->
<a href="#TODO" os-perms="core.can_manage_projector" class="btn btn-default btn-sm" <a ui-sref="agenda.item.detail({id: customslide.agenda_item_id})" class="btn btn-sm btn-default">
title="{{ 'Show' | translate }}"> <i class="fa fa-microphone fa-lg"></i>
<i class="fa fa-video-camera"></i> <translate>List of speakers</translate>
</a> </a>
<!-- edit --> <!-- project -->
<a ui-sref="core.customslide.detail.update({id: customslide.id })" os-perms="core.can_mange_projector" <a os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
class="btn btn-default btn-sm" ng-class="{ 'btn-primary': customslide.isProjected() }"
title="{{ 'Edit' | translate}}"> ng-click="customslide.project()"
<i class="fa fa-pencil"></i> title="{{ 'Project agenda item' | translate }}">
<i class="fa fa-video-camera"></i>
</a> </a>
</div> </div>
{{ customslide.agenda_item }} <div ng-bind-html="customslide.text"></div>
<div class="white-space-pre-line">{{ customslide.text }}</div>

View File

@ -1,27 +1,13 @@
<h1 ng-if="customslide.id" translate>Edit custom slide</h1> <h1 ng-if="customslide.id" translate>Edit agenda item</h1>
<h1 ng-if="!customslide.id" translate>New custom slide</h1> <h2 ng-if="!customslide.id" translate>New agenda item</h2>
<div id="submenu"> <form name="customslideForm" ng-submit="save(model)">
<a ui-sref="core.customslide.list" class="btn btn-sm btn-default"> <formly-form model="model" fields="formFields">
<i class="fa fa-angle-double-left fa-lg"></i> <button type="submit" ng-disabled="customslideForm.$invalid" class="btn btn-primary" translate>
<translate>Back to overview</translate> Submit
</a> </button>
</div> <button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel
<form name="customslideForm"> </button>
<div class="form-group" > </formly-form>
<label for="inputTitle" translate>Title</label>
<input type="text" ng-model="customslide.title" class="form-control" name="inputTitle" required>
</div>
<div class="form-group">
<label for="customSlideTextCKEditor" translate>Text</label>
<div id="customSlideTextCKEditor" ckeditor="CKEditorOptions" ng-model="customslide.text"></div>
</div>
<button type="submit" ng-click="save(customslide)" class="btn btn-primary" translate>
Save
</button>
<button ui-sref="core.customslide.list" class="btn btn-default" translate>
Cancel
</button>
</form> </form>

View File

@ -0,0 +1,28 @@
<form ng-submit="login(username, password)">
<div class="modal-header">
<img src="/static/img/logo-login.png" alt="OpenSlides" class="center-block">
</div>
<div class="modal-body">
<uib-alert ng-repeat="alert in alerts" type="{{ alert.type }}" close="closeAlert($index)">
<span ng-bind-html="alert.msg"><span>
</uib-alert>
<div class="input-group form-group">
<div class="input-group-addon"><i class="fa fa-user"></i></div>
<input os-focus-me type="text" ng-model="username" class="form-control input-lg"
placeholder="{{ 'Username' | translate }}">
</div>
<div class="input-group form-group">
<div class="input-group-addon"><i class="fa fa-key"></i></div>
<input type="password" ng-model="password" class="form-control input-lg"
placeholder="{{ 'Password' | translate }}">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary pull-right" translate>
Login
</button>
<button ng-if="guestAllowed" ng-click="guestLogin()" class="btn btn-default" translate>
Continue as guest
</button>
</div>
</div>
</form>

View File

@ -1,4 +1,4 @@
<div ng-controller="SlideCustomSlideCtrl" class="content scrollcontent"> <div ng-controller="SlideCustomSlideCtrl" class="content scrollcontent">
<h1>{{ customslide.title }}</h1> <h1>{{ customslide.title }}</h1>
<div class="white-space-pre-line">{{ customslide.text }}</div> <div ng-bind-html="customslide.text"></div>
</div> </div>

View File

@ -22,6 +22,7 @@
</div> </div>
<div class="navbar-right" ng-controller="userMenu"> <div class="navbar-right" ng-controller="userMenu">
<div class="btn-group"> <div class="btn-group">
<!-- Logout / user setttings button -->
<div ng-if="operator.isAuthenticated()"> <div ng-if="operator.isAuthenticated()">
<!-- chatbox --> <!-- chatbox -->
@ -84,47 +85,9 @@
</ul> </ul>
</div> </div>
</div> </div>
<!-- Login button -->
<!-- Login dialog (modal) --> <div ng-if="!operator.isAuthenticated()">
<div ng-controller="LoginFormCtrl" ng-if="!operator.isAuthenticated()"> <button class="btn btn-default" ng-click="openLoginForm()">
<script type="text/ng-template" id="LoginForm.html">
<form ng-submit="login(username, password)">
<div class="modal-header">
<h3 class="modal-title" translate>Please sign in!</h3>
</div>
<div class="modal-body">
<p ng-if='loginFailed' class="text-danger">
<strong translate>Username or password is not correct.</strong>
<div class="input-group form-group">
<div class="input-group-addon"><i class="fa fa-user"></i></div>
<input os-focus-me type="text" ng-model="username" class="form-control input-lg"
placeholder="{{ 'Username' | translate }}">
</div>
<div class="input-group form-group">
<div class="input-group-addon"><i class="fa fa-key"></i></div>
<input type="password" ng-model="password" class="form-control input-lg"
placeholder="{{ 'Password' | translate }}">
</div>
</div>
<div class="modal-footer">
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg btn-block" translate>
Login
</button>
</div>
<div class="form-group">
<!-- TODO: show only if anonymous user is activate -->
<button ng-click="guest()" class="btn btn-default" translate>
Continue as guest
</button>
<button ng-click="cancel()" class="btn btn-default" translate>
Cancel
</button>
</div>
</div>
</form>
</script>
<button class="btn btn-default" ng-click="open()">
<i class="fa fa-sign-in"></i> <i class="fa fa-sign-in"></i>
<translate>Login</translate> <translate>Login</translate>
</button> </button>
@ -173,13 +136,18 @@
</ul> </ul>
</div><!--/#main-menu--> </div><!--/#main-menu-->
<!-- Content --> <!-- Content -->
<div id="content" class="col-md-10"> <div id="content" class="col-md-7">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div ui-view></div> <div ui-view></div>
</div> </div>
</div> </div>
</div><!--/#content--> </div><!--/#content-->
<div class="col-sm-3" os-perms="core.can_see_projector">
<div class="well">
<div ng-include src="'static/templates/core/projector-controls.html'"></div>
</div>
</div>
</div><!--/.row--> </div><!--/.row-->
<hr> <hr>

View File

@ -180,14 +180,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
}); });
}); });
// hover edit actions
$scope.hoverIn = function () {
$scope.showEditActions = true;
};
$scope.hoverOut = function () {
$scope.showEditActions = false;
};
// save changed motion // save changed motion
$scope.update = function (motion) { $scope.update = function (motion) {
// get (unchanged) values from latest version for update method // get (unchanged) values from latest version for update method

View File

@ -17,6 +17,11 @@
<i class="fa fa-file-pdf-o fa-lg"></i> <i class="fa fa-file-pdf-o fa-lg"></i>
<translate>PDF</translate> <translate>PDF</translate>
</a> </a>
<!-- List of speakers -->
<a ui-sref="agenda.item.detail({id: motion.agenda_item_id})" class="btn btn-sm btn-default">
<i class="fa fa-microphone fa-lg"></i>
<translate>List of speakers</translate>
</a>
<!-- project --> <!-- project -->
<a os-perms="core.can_manage_projector" class="btn btn-default btn-sm" <a os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
ng-class="{ 'btn-primary': motion.isProjected() }" ng-class="{ 'btn-primary': motion.isProjected() }"
@ -171,7 +176,7 @@
</div> </div>
</form> </form>
</div> </div>
<div ng-show="!poll.isEditMode && poll.yes >= -2"> <div ng-show="!poll.isEditMode && poll.yes >= -2" class="pollresults">
<!-- yes --> <!-- yes -->
<div class="result_label"> <div class="result_label">
<i class="fa fa-thumbs-up"></i> <i class="fa fa-thumbs-up"></i>

View File

@ -563,7 +563,8 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
'DS', 'DS',
'User', 'User',
'operator', 'operator',
function($scope, $http, DS, User, operator) { 'ngDialog',
function($scope, $http, DS, User, operator, ngDialog) {
$scope.logout = function() { $scope.logout = function() {
$http.post('/users/logout/').success(function(data) { $http.post('/users/logout/').success(function(data) {
operator.setUser(null); operator.setUser(null);
@ -571,6 +572,59 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
// DS.flush(); // DS.flush();
}); });
}; };
$scope.openLoginForm = function () {
ngDialog.open({
template: 'static/templates/core/login-form.html',
controller: 'LoginFormCtrl',
});
};
}
])
.controller('LoginFormCtrl', [
'$scope',
'$http',
'operator',
'gettext',
'Config',
function ($scope, $http, operator, gettext, Config) {
$scope.alerts = [];
// TODO: add welcome message only on first time (or if admin password not changed)
$scope.alerts.push({
type: 'success',
msg: gettext("Installation was successfully.") + "<br>" +
gettext("Use <strong>admin</strong> and <strong>admin</strong> for first login.") + "<p>" +
gettext("Important: Please change your password!")
});
// close alert function
$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
};
// check if guest login is allowed
$scope.guestAllowed = true; //TODO Config.get('general_system_enable_anonymous').value;
// login
$scope.login = function () {
$http.post(
'/users/login/',
{'username': $scope.username, 'password': $scope.password}
).success(function(data) {
if (data.success) {
operator.setUser(data.user_id);
$scope.closeThisDialog();
} else {
$scope.alerts.push({
type: 'danger',
msg: gettext('Username or password was not correct.')
});
//Username or password is not correct.
}
});
};
// guest login
$scope.guestLogin = function () {
$scope.closeThisDialog();
};
} }
]); ]);