From 2a9e0b4b818dc6f87f6fb325763f474749add58e Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Wed, 27 Jan 2016 00:16:30 +0100 Subject: [PATCH] Implement full text search (client side) Fixes #1699 --- .../assignments/static/js/assignments/base.js | 8 +++ openslides/core/static/css/app.css | 38 +++++++------- openslides/core/static/js/core/base.js | 10 +++- openslides/core/static/js/core/site.js | 51 +++++++++++++++++++ openslides/core/static/templates/index.html | 13 +++++ openslides/core/urls.py | 7 +-- openslides/motions/static/js/motions/base.js | 8 +++ openslides/users/static/js/users/base.js | 8 +++ 8 files changed, 122 insertions(+), 21 deletions(-) diff --git a/openslides/assignments/static/js/assignments/base.js b/openslides/assignments/static/js/assignments/base.js index 487195755..b55a03da0 100644 --- a/openslides/assignments/static/js/assignments/base.js +++ b/openslides/assignments/static/js/assignments/base.js @@ -94,6 +94,14 @@ angular.module('OpenSlidesApp.assignments', []) }, getAgendaTitle: function () { 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: { diff --git a/openslides/core/static/css/app.css b/openslides/core/static/css/app.css index d278a6923..618941d4e 100644 --- a/openslides/core/static/css/app.css +++ b/openslides/core/static/css/app.css @@ -199,25 +199,15 @@ img { #nav .searchbar { float: right; margin-top: 35px; + display: inline-table; } -.searchbar .input-medium { - float: left; - height: 28px; - width: 200px; - border: 1px solid #ccc; - padding: 0 5px; - font-size: 14px; +#nav .searchbar input { + width: 150px; } -.searchbar .btn { - float: left; - height: 30px; - width: 30px; +#nav .searchbar .btn { background: #e8eaed; - border: 1px solid #ccc; - border-left: none; - font-size: 14px; color: #555; } @@ -617,6 +607,13 @@ img { content: ":"; } +/* search results */ +.searchresults li { + margin-bottom: 12px; +} +.searchresults li .subtitle { + color: #999999; +} /* ngDialog: override ngdialog-theme-default */ @@ -796,12 +793,19 @@ tr.selected td { -/* display for resolutions smaller that 880px */ -@media only screen and (max-width: 880px) { +/* display for resolutions smaller that 900px */ +@media only screen and (max-width: 900px) { #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 */ @media only screen and (max-width: 480px) { @@ -821,7 +825,7 @@ tr.selected td { #nav .navbar .button { display: block;} #nav .navbar .button i { padding-top: 15px;} #nav .searchbar { margin-top: 15px; } - .searchbar .input-medium { width: 150px; } + #nav .searchbar input { width: 150px; } #chatbox { width: 100%; top: 130px; } .optional { /* hide optional column */ diff --git a/openslides/core/static/js/core/base.js b/openslides/core/static/js/core/base.js index cfb47e8b5..83c970df6 100644 --- a/openslides/core/static/js/core/base.js +++ b/openslides/core/static/js/core/base.js @@ -251,7 +251,15 @@ angular.module('OpenSlidesApp.core', [ }, getAgendaTitle: function () { 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: { belongsTo: { diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js index 30f5f0a5d..e0f4fc54e 100644 --- a/openslides/core/static/js/core/site.js +++ b/openslides/core/static/js/core/site.js @@ -209,11 +209,13 @@ angular.module('OpenSlidesApp.core.site', [ abstract: true, template: "", }) + // legal notice and version .state('legalnotice', { url: '/legalnotice', controller: 'LegalNoticeCtrl', }) + //config .state('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 .state('core.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 .factory('CustomslideForm', [ diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html index 436524e2f..f7f534fa7 100644 --- a/openslides/core/static/templates/index.html +++ b/openslides/core/static/templates/index.html @@ -135,6 +135,19 @@ + + diff --git a/openslides/core/urls.py b/openslides/core/urls.py index a3fc131a6..76ea25daa 100644 --- a/openslides/core/urls.py +++ b/openslides/core/urls.py @@ -15,6 +15,10 @@ urlpatterns = [ views.VersionView.as_view(), name='core_version'), + url(r'^core/search_api/$', + views.SearchView.as_view(), + name='core_search'), + url(r'^angular_js/(?Psite|projector)/$', views.AppsJsView.as_view(), name='core_apps_js'), @@ -22,9 +26,6 @@ urlpatterns = [ # View for the projectors are handelt by angular. url(r'^projector.*$', views.ProjectorView.as_view()), - url(r'^search/$', - views.SearchView.as_view(), - name='core_search'), # Main entry point for all angular pages. # Has to be the last entry in the urls.py diff --git a/openslides/motions/static/js/motions/base.js b/openslides/motions/static/js/motions/base.js index f43455e68..fb0ce89a9 100644 --- a/openslides/motions/static/js/motions/base.js +++ b/openslides/motions/static/js/motions/base.js @@ -202,6 +202,14 @@ angular.module('OpenSlidesApp.motions', ['OpenSlidesApp.users']) } 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) { /* * Return true if the requested user is allowed to do the specific action. diff --git a/openslides/users/static/js/users/base.js b/openslides/users/static/js/users/base.js index 81ef0fac6..6e82a054b 100644 --- a/openslides/users/static/js/users/base.js +++ b/openslides/users/static/js/users/base.js @@ -134,6 +134,14 @@ angular.module('OpenSlidesApp.users', []) }); 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"; + }, }, }); }