Implement full text search (client side) Fixes #1699
This commit is contained in:
parent
23ad11d232
commit
2a9e0b4b81
@ -94,6 +94,14 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
},
|
},
|
||||||
getAgendaTitle: function () {
|
getAgendaTitle: function () {
|
||||||
return this.title;
|
return this.title;
|
||||||
|
},
|
||||||
|
// link name which is shown in search result
|
||||||
|
getSearchResultName: function () {
|
||||||
|
return this.getAgendaTitle();
|
||||||
|
},
|
||||||
|
// subtitle of search result
|
||||||
|
getSearchResultSubtitle: function () {
|
||||||
|
return "Election";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
relations: {
|
relations: {
|
||||||
|
@ -199,25 +199,15 @@ img {
|
|||||||
#nav .searchbar {
|
#nav .searchbar {
|
||||||
float: right;
|
float: right;
|
||||||
margin-top: 35px;
|
margin-top: 35px;
|
||||||
|
display: inline-table;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchbar .input-medium {
|
#nav .searchbar input {
|
||||||
float: left;
|
width: 150px;
|
||||||
height: 28px;
|
|
||||||
width: 200px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
padding: 0 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchbar .btn {
|
#nav .searchbar .btn {
|
||||||
float: left;
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
background: #e8eaed;
|
background: #e8eaed;
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-left: none;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,6 +607,13 @@ img {
|
|||||||
content: ":";
|
content: ":";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* search results */
|
||||||
|
.searchresults li {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.searchresults li .subtitle {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
/* ngDialog: override ngdialog-theme-default */
|
/* ngDialog: override ngdialog-theme-default */
|
||||||
|
|
||||||
@ -796,12 +793,19 @@ tr.selected td {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* display for resolutions smaller that 880px */
|
/* display for resolutions smaller that 900px */
|
||||||
@media only screen and (max-width: 880px) {
|
@media only screen and (max-width: 900px) {
|
||||||
|
|
||||||
#nav .navbar li a { padding: 24px 5px; }
|
#nav .navbar li a { padding: 24px 5px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* display for resolutions smaller that 760px */
|
||||||
|
@media only screen and (max-width: 760px) {
|
||||||
|
|
||||||
|
#nav .navbar li a { padding: 24px 2px; }
|
||||||
|
#nav .searchbar input { width: 100px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* display for resolutions smaller that 480px */
|
/* display for resolutions smaller that 480px */
|
||||||
@media only screen and (max-width: 480px) {
|
@media only screen and (max-width: 480px) {
|
||||||
@ -821,7 +825,7 @@ tr.selected td {
|
|||||||
#nav .navbar .button { display: block;}
|
#nav .navbar .button { display: block;}
|
||||||
#nav .navbar .button i { padding-top: 15px;}
|
#nav .navbar .button i { padding-top: 15px;}
|
||||||
#nav .searchbar { margin-top: 15px; }
|
#nav .searchbar { margin-top: 15px; }
|
||||||
.searchbar .input-medium { width: 150px; }
|
#nav .searchbar input { width: 150px; }
|
||||||
|
|
||||||
#chatbox { width: 100%; top: 130px; }
|
#chatbox { width: 100%; top: 130px; }
|
||||||
.optional { /* hide optional column */
|
.optional { /* hide optional column */
|
||||||
|
@ -251,7 +251,15 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
},
|
},
|
||||||
getAgendaTitle: function () {
|
getAgendaTitle: function () {
|
||||||
return this.title;
|
return this.title;
|
||||||
}
|
},
|
||||||
|
// link name which is shown in search result
|
||||||
|
getSearchResultName: function () {
|
||||||
|
return this.getAgendaTitle();
|
||||||
|
},
|
||||||
|
// subtitle of search result
|
||||||
|
getSearchResultSubtitle: function () {
|
||||||
|
return "Agenda item";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
relations: {
|
relations: {
|
||||||
belongsTo: {
|
belongsTo: {
|
||||||
|
@ -209,11 +209,13 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
abstract: true,
|
abstract: true,
|
||||||
template: "<ui-view/>",
|
template: "<ui-view/>",
|
||||||
})
|
})
|
||||||
|
|
||||||
// legal notice and version
|
// legal notice and version
|
||||||
.state('legalnotice', {
|
.state('legalnotice', {
|
||||||
url: '/legalnotice',
|
url: '/legalnotice',
|
||||||
controller: 'LegalNoticeCtrl',
|
controller: 'LegalNoticeCtrl',
|
||||||
})
|
})
|
||||||
|
|
||||||
//config
|
//config
|
||||||
.state('config', {
|
.state('config', {
|
||||||
url: '/config',
|
url: '/config',
|
||||||
@ -224,6 +226,14 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// search
|
||||||
|
.state('search', {
|
||||||
|
url: '/search?q',
|
||||||
|
controller: 'SearchCtrl',
|
||||||
|
templateUrl: 'static/templates/search.html',
|
||||||
|
})
|
||||||
|
|
||||||
// customslide
|
// customslide
|
||||||
.state('core.customslide', {
|
.state('core.customslide', {
|
||||||
url: '/customslide',
|
url: '/customslide',
|
||||||
@ -449,6 +459,47 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Search Bar Controller
|
||||||
|
.controller('SearchBarCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'$state',
|
||||||
|
function ($scope, $state) {
|
||||||
|
$scope.search = function(query) {
|
||||||
|
$scope.query = '';
|
||||||
|
$state.go('search', {q: query});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
// Search Controller
|
||||||
|
.controller('SearchCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'$http',
|
||||||
|
'$stateParams',
|
||||||
|
'DS',
|
||||||
|
function ($scope, $http, $stateParams, DS) {
|
||||||
|
// search function
|
||||||
|
$scope.search = function(query) {
|
||||||
|
$http.get('/core/search_api/?q=' + query).then(function(success) {
|
||||||
|
var elements = success.data.elements;
|
||||||
|
$scope.results = [];
|
||||||
|
angular.forEach(elements, function(element) {
|
||||||
|
DS.find(element.collection, element.id).then(function(data) {
|
||||||
|
data.urlState = element.collection.replace('/','.')+'.detail';
|
||||||
|
data.urlParam = {id: element.id};
|
||||||
|
$scope.results.push(data);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// run search with get parameter from url
|
||||||
|
if ($stateParams.q) {
|
||||||
|
$scope.search($stateParams.q);
|
||||||
|
$scope.query = $stateParams.q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
// Provide generic customslide form fields for create and update view
|
// Provide generic customslide form fields for create and update view
|
||||||
.factory('CustomslideForm', [
|
.factory('CustomslideForm', [
|
||||||
|
@ -135,6 +135,19 @@
|
|||||||
</a>
|
</a>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="searchbar pull-right" ng-controller="SearchBarCtrl">
|
||||||
|
<form ng-submit="search(query)">
|
||||||
|
<div class="input-group">
|
||||||
|
<input ng-model="query" class="form-control" type="text" placeholder="{{ 'Search' | translate}}">
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="submit" class="btn btn-default">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div><!--end nav-->
|
</div><!--end nav-->
|
||||||
|
|
||||||
|
@ -15,6 +15,10 @@ urlpatterns = [
|
|||||||
views.VersionView.as_view(),
|
views.VersionView.as_view(),
|
||||||
name='core_version'),
|
name='core_version'),
|
||||||
|
|
||||||
|
url(r'^core/search_api/$',
|
||||||
|
views.SearchView.as_view(),
|
||||||
|
name='core_search'),
|
||||||
|
|
||||||
url(r'^angular_js/(?P<openslides_app>site|projector)/$',
|
url(r'^angular_js/(?P<openslides_app>site|projector)/$',
|
||||||
views.AppsJsView.as_view(),
|
views.AppsJsView.as_view(),
|
||||||
name='core_apps_js'),
|
name='core_apps_js'),
|
||||||
@ -22,9 +26,6 @@ urlpatterns = [
|
|||||||
# View for the projectors are handelt by angular.
|
# View for the projectors are handelt by angular.
|
||||||
url(r'^projector.*$', views.ProjectorView.as_view()),
|
url(r'^projector.*$', views.ProjectorView.as_view()),
|
||||||
|
|
||||||
url(r'^search/$',
|
|
||||||
views.SearchView.as_view(),
|
|
||||||
name='core_search'),
|
|
||||||
|
|
||||||
# Main entry point for all angular pages.
|
# Main entry point for all angular pages.
|
||||||
# Has to be the last entry in the urls.py
|
# Has to be the last entry in the urls.py
|
||||||
|
@ -202,6 +202,14 @@ angular.module('OpenSlidesApp.motions', ['OpenSlidesApp.users'])
|
|||||||
}
|
}
|
||||||
return "Motion " + value + ': ' + this.getTitle();
|
return "Motion " + value + ': ' + this.getTitle();
|
||||||
},
|
},
|
||||||
|
// link name which is shown in search result
|
||||||
|
getSearchResultName: function () {
|
||||||
|
return this.getAgendaTitle();
|
||||||
|
},
|
||||||
|
// subtitle of search result
|
||||||
|
getSearchResultSubtitle: function () {
|
||||||
|
return "Motion";
|
||||||
|
},
|
||||||
isAllowed: function (action) {
|
isAllowed: function (action) {
|
||||||
/*
|
/*
|
||||||
* Return true if the requested user is allowed to do the specific action.
|
* Return true if the requested user is allowed to do the specific action.
|
||||||
|
@ -134,6 +134,14 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
});
|
});
|
||||||
return _.uniq(allPerms);
|
return _.uniq(allPerms);
|
||||||
},
|
},
|
||||||
|
// link name which is shown in search result
|
||||||
|
getSearchResultName: function () {
|
||||||
|
return this.get_full_name();
|
||||||
|
},
|
||||||
|
// subtitle of search result
|
||||||
|
getSearchResultSubtitle: function () {
|
||||||
|
return "Participant";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user