diff options
-rw-r--r-- | chimere/forms.py | 11 | ||||
-rw-r--r-- | chimere/models.py | 6 | ||||
-rw-r--r-- | chimere/search_indexes.py | 59 | ||||
-rw-r--r-- | chimere/settings.sample.py | 9 | ||||
-rw-r--r-- | chimere/static/chimere/css/styles.css | 39 | ||||
-rw-r--r-- | chimere/static/chimere/js/base.js | 2 | ||||
-rw-r--r-- | chimere/static/chimere/js/jquery.chimere.js | 41 | ||||
-rw-r--r-- | chimere/static/chimere/js/search-autocomplete.js | 78 | ||||
-rw-r--r-- | chimere/static/chimere/js/search.js | 32 | ||||
-rw-r--r-- | chimere/templates/chimere/base.html | 1 | ||||
-rw-r--r-- | chimere/templates/chimere/blocks/footer.html | 2 | ||||
-rw-r--r-- | chimere/templates/chimere/main_map.html | 18 | ||||
-rw-r--r-- | chimere/templates/search/indexes/chimere/marker_text.txt | 3 | ||||
-rw-r--r-- | chimere/templates/search/indexes/chimere/route_text.txt | 1 | ||||
-rw-r--r-- | chimere/templates/search/search.html | 54 | ||||
-rw-r--r-- | chimere/templatetags/unescape.py | 15 | ||||
-rw-r--r-- | chimere/urls.py | 18 | ||||
-rw-r--r-- | chimere/views.py | 27 | ||||
-rw-r--r-- | haystack-requirements.txt | 4 | ||||
-rw-r--r-- | requirements.txt | 2 | ||||
-rw-r--r-- | requirements_searchengine.txt | 12 |
21 files changed, 417 insertions, 17 deletions
diff --git a/chimere/forms.py b/chimere/forms.py index a57fe87..aae86c6 100644 --- a/chimere/forms.py +++ b/chimere/forms.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2008-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2008-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as @@ -20,6 +20,7 @@ """ Forms """ + from django import forms from django.conf import settings from django.contrib.gis.db import models @@ -31,6 +32,9 @@ from django.contrib.auth.models import User, Permission, ContentType from django.contrib.admin.widgets import AdminDateWidget from django.core.mail import EmailMessage, BadHeaderError +if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') and settings.CHIMERE_SEARCH_ENGINE: + from haystack.forms import SearchForm as HaystackSearchForm + from chimere.models import Marker, Route, PropertyModel, Property, Area,\ News, Category, SubCategory, RouteFile, MultimediaFile, MultimediaType, \ PictureFile, Importer, PropertyModelChoice, IFRAME_LINKS, \ @@ -631,3 +635,8 @@ class RoutingForm(forms.Form): for speed, lbl in settings.CHIMERE_ROUTING_SPEEDS[transport]: self.fields['speed'].widget.choices.append( ("%s_%d" % (transport, speed), _(lbl))) + +SearchForm = None +if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') and settings.CHIMERE_SEARCH_ENGINE: + class SearchForm(HaystackSearchForm): + pass diff --git a/chimere/models.py b/chimere/models.py index 5727098..20efb2d 100644 --- a/chimere/models.py +++ b/chimere/models.py @@ -682,7 +682,7 @@ class Marker(GeographicItem): def default_category(self): # Should we select only available ones ? # Should we catch if not exists ? - cats = self.categories + cats = self.categories.filter(available=True, category__available=True) if cats.count(): return cats.all()[0] @@ -1539,12 +1539,14 @@ class PropertyModel(models.Model): ('P', _('Password')), ('D', _("Date")), ('C', _("Choices")), + ('B', _("Boolean")), ) TYPE_WIDGET = {'T':forms.TextInput, 'L':TextareaWidget, 'P':forms.PasswordInput, 'D':DatePickerWidget, - 'C':forms.Select + 'C':forms.Select, + 'B':forms.CheckboxInput, } type = models.CharField(_(u"Type"), max_length=1, choices=TYPE) def __unicode__(self): diff --git a/chimere/search_indexes.py b/chimere/search_indexes.py new file mode 100644 index 0000000..5e4a69a --- /dev/null +++ b/chimere/search_indexes.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +import datetime +from haystack import indexes +from django.db.models import Q + +from chimere import models +from chimere.views import checkDate + +class GeographicItemIndex(indexes.SearchIndex): + text = indexes.CharField(document=True, use_template=True) + categories = indexes.MultiValueField() + # for autocomplete + content_auto = indexes.EdgeNgramField(model_attr='name') + + def index_queryset(self, using=None): + q = checkDate(Q(status='A', categories__available=True, + categories__category__available=True)) + return self.get_model().objects.filter(q).distinct('pk').order_by('pk') + + def prepare_categories(self, obj): + cats = [] + for cat in obj.categories.all(): + cats.append(cat.name) + return cats + +class MarkerIndex(GeographicItemIndex, indexes.Indexable): + location = indexes.LocationField(model_attr='point') + def get_model(self): + return models.Marker + +""" +class RouteIndex(GeographicItemIndex, indexes.Indexable): + location = indexes.LocationField() + def get_model(self): + return models.Route + + def prepare_location(self, obj): + centroid = obj.route.centroid + return "%s,%s" % (centroid.y, centroid.x) +""" diff --git a/chimere/settings.sample.py b/chimere/settings.sample.py index d0ffc42..1ddef5f 100644 --- a/chimere/settings.sample.py +++ b/chimere/settings.sample.py @@ -118,6 +118,15 @@ NOMINATIM_URL = 'http://nominatim.openstreetmap.org/search' CHIMERE_THUMBS_SCALE_HEIGHT=250 CHIMERE_THUMBS_SCALE_WIDTH=None +# search engine +CHIMERE_SEARCH_ENGINE = False +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.solr_backend.SolrEngine', + 'URL': 'http://127.0.0.1:8080/solr' + }, +} + CHIMERE_CSV_ENCODING = 'ISO-8859-1' # generic contact email diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css index ab68d68..c0b3b0e 100644 --- a/chimere/static/chimere/css/styles.css +++ b/chimere/static/chimere/css/styles.css @@ -21,6 +21,7 @@ h2, h3, th, .action li, .action li a, color:#fff; } +#search-listing ul li a, .action li.ui-state-active a, #content .olControlLayerSwitcher, .action li li.ui-state-active a{ @@ -82,7 +83,7 @@ div.warning, .action li.selected, #content .olControlLayerSwitcher .layersDiv, #panel, #map-footer, #chimere_itinerary_panel, -#utils-div{ +#search-box, #utils-div{ border:1px solid #327e04; } @@ -772,6 +773,42 @@ table.inline-table td input[type=file]{ margin-right: auto; } +#search-box{ + position:absolute; + z-index:200; + left:70px; + top:50px; + padding:0.3em; + padding-right:1.4em; + width:auto; + background-color:white; +} + +#search-listing{ + overflow:auto; +} + +#search-listing ul{ + list-style-type:none; + margin:0; + padding:4px; +} + +#search-listing ul li{ + padding:4px; +} + +#search-listing a{ + padding: 0.2em 0.5em; + border-radius:5px; +} + +#search-listing a:hover{ + text-decoration:none; + background-color:rgb(175, 231, 175); +} + + .alert-box .ui-dialog-titlebar { display:none; } diff --git a/chimere/static/chimere/js/base.js b/chimere/static/chimere/js/base.js index fe8d954..0db11c1 100644 --- a/chimere/static/chimere/js/base.js +++ b/chimere/static/chimere/js/base.js @@ -1,5 +1,5 @@ /* base function shared by some pages */ -/* Copyright (C) 2009-2013 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +/* Copyright (C) 2009-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/chimere/static/chimere/js/jquery.chimere.js b/chimere/static/chimere/js/jquery.chimere.js index 2e147c8..844ec1c 100644 --- a/chimere/static/chimere/js/jquery.chimere.js +++ b/chimere/static/chimere/js/jquery.chimere.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2008-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +/* Copyright (C) 2008-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -629,6 +629,10 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { } }); }, + razMap: function() { + settings.layerMarkers.clearMarkers(); + settings.layerVectors.removeAllFeatures(); + }, /* * Update the categories div in ajax */ @@ -734,8 +738,8 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { var id = this.id.substr(this.id.lastIndexOf("_")+1); helpers.zoom_to_subcategories([id]); }); - $(".toggle_category").parent().bind("click", function (e) { - var item = $(this).children('.toggle_category'); + $(".toggle_category").bind("click", function (e) { + var item = $(this); var id = item.attr('id').substr(item.attr('id').lastIndexOf("_")+1); methods.toggle_category(id); }); @@ -882,7 +886,7 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { if (settings.current_popup == feature.popup) { feature.popup.hide(); if (!settings.simple){ - $('#detail').hide(); + $('#detail').fadeOut(); } } else { settings.current_popup.hide(); @@ -1331,7 +1335,7 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { } else { if (!settings.popupContentFull) { - $('#detail').html(data).show(); + $('#detail').html(data).fadeIn(); } else { settings.current_popup.setContentHTML("<div class='cloud'>" + data + "</div>"); @@ -1450,6 +1454,10 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { settings.map.zoomToExtent(extent, true); return true; }, + zoomToMarkerExtent: function(){ + settings.map.zoomToExtent( + settings.layerMarkers.getDataExtent()); + }, // methods for edition setMarker: function (event){ event = event || window.event; @@ -1553,6 +1561,23 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { var bounds = settings.layerVectors.getDataExtent(); if (bounds) settings.map.zoomToExtent(bounds); }, + showPopup: function (feature_pk) { + for(j=0; j<settings.layerMarkers.markers.length;j++){ + var c_marker = settings.layerMarkers.markers[j]; + if(c_marker.pk == feature_pk){ + c_marker.events.triggerEvent('click'); + return + } + } + //feature.markerClick(); + //OpenLayers.Popup.popupSelect.clickFeature(feature); + /* + settings.current_popup = feature.marker._popup(); + if (!settings.current_popup.visible()){ + settings.current_popup.show(); + methods.display_feature_detail(feature.pk); + }*/ + }, hidePopup: function (evt) { $('#'+settings.marker_hover_id).hide(); if (settings.hide_popup_fx) { @@ -1562,7 +1587,7 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { if (settings.current_popup) { if (!settings.simple){ - $('#detail').hide(); + $('#detail').fadeOut(); } if (settings.current_popup.visible()){ settings.current_popup.hide(); @@ -1667,6 +1692,10 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { methods.loadCategories(); } }, + zoom_to_latlon: function (){ + var lonlat = new OpenLayers.LonLat(lon, lat); + settings.map.setCenter(f.lonlat); + }, getSavedExtent: function() { /* get the current extent from a cookie */ var cookies = document.cookie.split(';'); diff --git a/chimere/static/chimere/js/search-autocomplete.js b/chimere/static/chimere/js/search-autocomplete.js new file mode 100644 index 0000000..cd1b0e1 --- /dev/null +++ b/chimere/static/chimere/js/search-autocomplete.js @@ -0,0 +1,78 @@ + +var no_result_message = "No exact match."; + +var Autocomplete = function(options) { + this.form_selector = options.form_selector; + this.url = options.url || '/search/autocomplete/'; + this.delay = parseInt(options.delay || 300); + this.minimum_length = parseInt(options.minimum_length || 3); + this.form_elem = null; + this.query_box = null; +} + +Autocomplete.prototype.setup = function() { + var self = this; + + this.form_elem = $(this.form_selector); + this.query_box = this.form_elem.find('input[name=q]'); + + // watch the input box. + this.query_box.on('keyup', function() { + var query = self.query_box.val(); + if (query){ + $('#haystack-search').removeAttr("disabled"); + } else { + $('#haystack-search').attr('disabled', 'disabled'); + } + + if(query.length < self.minimum_length) { + return false; + } + + self.fetch(query); + }) + + // on selecting a result, populate the search field. + this.form_elem.on('click', '.ac-result', function(ev) { + self.query_box.val($(this).text()); + $('.ac-results').remove(); + return false; + }) +} + +Autocomplete.prototype.fetch = function(query) { + var self = this ; + + $.ajax({ + url: this.url, + data: { 'q': query }, + success: function(data) { + self.show_results(data); + } + }) +} + +Autocomplete.prototype.show_results = function(data) { + // Remove any existing results. + $('.ac-results').remove(); + + var results = data.results || [] + var results_wrapper = $('<div class="ac-results"></div>'); + var base_elem = $('<div class="result-wrapper"><a href="#" class="ac-result"></a></div>'); + + if(results.length > 0) { + for(var res_offset in results) { + var elem = base_elem.clone(); + // don't use .html(...) here, as it opens to XSS. + elem.find('.ac-result').text(results[res_offset]); + results_wrapper.append(elem); + } + } + else { + var elem = base_elem.clone(); + elem.text(no_result_message); + results_wrapper.append(elem); + } + + this.query_box.after(results_wrapper) +} diff --git a/chimere/static/chimere/js/search.js b/chimere/static/chimere/js/search.js new file mode 100644 index 0000000..6f46f3f --- /dev/null +++ b/chimere/static/chimere/js/search.js @@ -0,0 +1,32 @@ +function load_search_box(){ + if (!search_url) return; + $.ajax({url: search_url}).done(function( data ) { + $("#search-box").html(data); + }); +} + +function haystack_search(evt, page){ + search_result = new Array(); + $('#categories').find('#ul_categories > li > input').attr("checked", false); + if (!$('#id_q').val()) return false; + + var c_url = search_url + "?q=" + $('#id_q').val(); + if (page){ + c_url += '&page=' + page; + } + $.get(c_url).done(function( data ) { + $('.ac-results').remove(); + $('#search-result').html(data).show('slow'); + }); + return false; +} + +// disable enter +$(window).keydown(function(event){ + if ($("#haystack-search").length && event.keyCode == 13) { + event.preventDefault(); + $("#haystack-search").click(); + return false; + } +}); + diff --git a/chimere/templates/chimere/base.html b/chimere/templates/chimere/base.html index 894cb01..82f28c4 100644 --- a/chimere/templates/chimere/base.html +++ b/chimere/templates/chimere/base.html @@ -24,6 +24,7 @@ {% endblock %} {% block content %} {% block top %}{% endblock %} + {% if has_search %} {% block search_box %}{% endblock %} {% endif %} {% block message_map %}{% endblock %} {% block message_edit %}{% endblock %} {% block bottom %}{% endblock %} diff --git a/chimere/templates/chimere/blocks/footer.html b/chimere/templates/chimere/blocks/footer.html index a783939..8958939 100644 --- a/chimere/templates/chimere/blocks/footer.html +++ b/chimere/templates/chimere/blocks/footer.html @@ -1,3 +1,3 @@ {% load i18n %} -{% trans "This site uses Chimère"%} - <img src="{{STATIC_URL}}chimere/img/copyleft.png" alt="copyleft"/> 2008-2013 <a href='http://www.chymeres.net/'>Chimère project</a> - {% trans "Map"%} <img src="{{STATIC_URL}}chimere/img/copyleft.png" alt="copyleft"/> <a href='http://openstreetmap.org/'>OpenStreetMap</a> +{% trans "This site uses Chimère"%} <img src="{{STATIC_URL}}chimere/img/copyleft.png" alt="copyleft"/> 2008-2014 <a href='http://www.chymeres.net/'>Chimère project</a> - {% trans "Map"%} <img src="{{STATIC_URL}}chimere/img/copyleft.png" alt="copyleft"/> <a href='http://openstreetmap.org/'>OpenStreetMap</a> diff --git a/chimere/templates/chimere/main_map.html b/chimere/templates/chimere/main_map.html index b7aa868..a3dec9a 100644 --- a/chimere/templates/chimere/main_map.html +++ b/chimere/templates/chimere/main_map.html @@ -5,6 +5,9 @@ {% head_chimere %} {% head_jme %} <script src="{{ STATIC_URL }}chimere/js/jquery.chimere.js" type="text/javascript"></script> +{% if has_search %} +<script src="{{ STATIC_URL }}chimere/js/search.js" type="text/javascript"></script> +<script src="{{ STATIC_URL }}chimere/js/search-autocomplete.js" type="text/javascript"></script>{% endif %} {{ block.super }} {% endblock %} {% block message_edit %}{% endblock %} @@ -36,6 +39,10 @@ </div> <div id='detail' class='ui-widget ui-corner-all'></div> {% endblock %} +{% block search_box %} +<div id='search-box' class='ui-widget ui-corner-all'> +</div> +{% endblock %} {% block content %} {{block.super}} <div id='main-map'></div> @@ -46,7 +53,16 @@ {% map 'main-map' %} <div id='chimere_message'></div> <script type='text/javascript'> - $(function(){$('#chimere_message').dialog({'autoOpen':false});}); + var has_search = {% if has_search %}true{% else %}false{% endif %}; + // array to keep trace of already displayed items + var search_result = new Array(); + var search_url = "/search/"; + $(function(){ + $('#chimere_message').dialog({'autoOpen':false}); + if (has_search){ + load_search_box(); + } + }); </script> {% endblock %} {% block footer %} diff --git a/chimere/templates/search/indexes/chimere/marker_text.txt b/chimere/templates/search/indexes/chimere/marker_text.txt new file mode 100644 index 0000000..ad5bae1 --- /dev/null +++ b/chimere/templates/search/indexes/chimere/marker_text.txt @@ -0,0 +1,3 @@ +{% load unescape %} +{{object.name}} +{{object.description|safe|striptags|unescape}} diff --git a/chimere/templates/search/indexes/chimere/route_text.txt b/chimere/templates/search/indexes/chimere/route_text.txt new file mode 100644 index 0000000..2fad18d --- /dev/null +++ b/chimere/templates/search/indexes/chimere/route_text.txt @@ -0,0 +1 @@ +{{object.name}} diff --git a/chimere/templates/search/search.html b/chimere/templates/search/search.html new file mode 100644 index 0000000..b40359a --- /dev/null +++ b/chimere/templates/search/search.html @@ -0,0 +1,54 @@ +{% load url from future %}{% load i18n %} +{% if query %} +<script type='text/javascript'> +var geo_objects = [{% for result in page.object_list %}{{result.object.getGeoJSON|safe}}{% if not forloop.last %}, {% endif %}{% endfor %}]; +var geo_features = {}; +for (idx=0 ; idx < geo_objects.length ; idx++){ + var c_idx = geo_objects[idx].properties.pk; + if (search_result.indexOf(c_idx) == -1){ + search_result.push(c_idx); + geo_features[c_idx] = $('#main-map').chimere('addMarker', + geo_objects[idx]); + } +} +{% if page.object_list.count %}$("#main-map").chimere("zoomToMarkerExtent");{% endif %} +</script> +<div id='search-listing'> + <ul> +{% for result in page.object_list %} + <li> + <img src='{{MEDIA_URL}}{{result.object.default_category.icon.image}}'/><a href="#" onclick="$('#main-map').chimere('showPopup', {{result.object.pk}});return false;">{{ result.object }}</a> + </li> +{% empty %} + <li>{% trans "No results found." %}</li> +{% endfor %} + </ul> +</div> +{% if page.has_previous or page.has_next %} + <div id='search-nav'> + {% if page.has_previous %}<a href="#" onclick="haystack_search(this, {{ page.previous_page_number }});">{% trans "Previous" %}</a>{% endif %} + {% if page.has_next %}<a href="#" onclick="haystack_search(this, {{ page.next_page_number }});">{% trans "More results..." %}</a>{% endif %} + </div> +{% endif %} + +{% else %} +<form id='search-form' class='autocomplete-me'> + <input type="text" id="id_q" name="q" autocomplete="off"/> + <button name='haystack-search' id='haystack-search' type='button' disabled='disabled' class="btn btn-default">{% trans "Search" %}</button> +</form> +<div id='search-result'></div> +<script type='text/javascript'> +no_result_message = "{% trans 'No exact match.' %}"; +$(function(){ + $('#haystack-search').click( + function(evt){ + $("#main-map").chimere("razMap"); + haystack_search(evt); + }); + window.autocomplete = new Autocomplete({ + form_selector: '.autocomplete-me' + }); + window.autocomplete.setup(); +}); +</script> +{% endif %} diff --git a/chimere/templatetags/unescape.py b/chimere/templatetags/unescape.py new file mode 100644 index 0000000..59809a3 --- /dev/null +++ b/chimere/templatetags/unescape.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from django import template +import HTMLParser + +register = template.Library() + +def unescape(value): + parser = HTMLParser.HTMLParser() + return parser.unescape(value) + +register.filter(unescape) + + diff --git a/chimere/urls.py b/chimere/urls.py index 164ef68..f67662b 100644 --- a/chimere/urls.py +++ b/chimere/urls.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2008-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2008-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as @@ -68,6 +68,22 @@ if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') \ 'route', name="route"), ) +if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \ + and settings.CHIMERE_SEARCH_ENGINE: + from chimere.forms import SearchForm + from chimere.views import SearchView + from haystack.views import search_view_factory + urlpatterns += patterns('chimere.views', + url(r'^search/?$', search_view_factory( + view_class=SearchView, + template='search/search.html', + form_class=SearchForm + ), name='haystack_search'), + url(r'^search/autocomplete/$', 'autocomplete', + name='autocomplete-search') + ) + #urlpatterns += [url(r'^search/', include('haystack.urls')),] + urlpatterns += patterns('chimere.views', url(r'^charte/?$', 'charte', name="charte"), url(r'^(?P<area_name>[a-zA-Z0-9_-]+/)?contact/?$', 'contactus', diff --git a/chimere/views.py b/chimere/views.py index a417fad..a696d5a 100644 --- a/chimere/views.py +++ b/chimere/views.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2008-2013 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2008-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> # # RSS : Copyright (C) 2010 Pierre Clarenc <pierre.crc_AT_gmailDOTcom>, # Samuel Renard <renard.samuel_AT_gmailDOTcom>, @@ -175,13 +175,15 @@ def index(request, area_name=None, default_area=None, simple=False, 'actions':actions(response_dct['area_name']), 'action_selected':('view',), 'error_message':'', + 'is_map':True, 'news_visible': news_visible, 'areas_visible': settings.CHIMERE_DISPLAY_AREAS, 'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER, 'dynamic_categories':response_dct['dynamic_categories'], 'zoomout':zoomout, 'has_default_area':Area.objects.filter(default=True).count(), - 'zoomout':zoomout + 'zoomout':zoomout, + 'has_search':settings.CHIMERE_SEARCH_ENGINE }) if hasattr(settings, 'CONTACT_EMAIL') and settings.CONTACT_EMAIL: response_dct['contact_email'] = settings.CONTACT_EMAIL @@ -945,3 +947,24 @@ def rss(request, area_name=''): else: return render_to_response('chimere/feeds/rss.html', response_dct, context_instance=RequestContext(request)) + +from django.core.paginator import Paginator, InvalidPage + +SearchView = None +autocomplete = None +if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \ + and settings.CHIMERE_SEARCH_ENGINE: + from haystack.views import SearchView as HaystackSearchView + from haystack.query import SearchQuerySet + class SearchView(HaystackSearchView): + pass + def autocomplete(request): + sqs = SearchQuerySet().autocomplete( + content_auto=request.GET.get('q', ''))[:5] + suggestions = [result.object.name for result in sqs if result.object] + # make sure it returns a JSON object, not a bare list. + # otherwise, it could be vulnerable to an XSS attack. + the_data = json.dumps({ + 'results': suggestions + }) + return HttpResponse(the_data, content_type='application/json') diff --git a/haystack-requirements.txt b/haystack-requirements.txt new file mode 100644 index 0000000..8b85888 --- /dev/null +++ b/haystack-requirements.txt @@ -0,0 +1,4 @@ +python-geopy python-django-haystack python-pysolr +# jetty solr-jetty +# sudo rm /var/lib/jetty/webapps/solr +# sudo ln -s /usr/share/solr/web/ /var/lib/jetty/webapps/solr diff --git a/requirements.txt b/requirements.txt index ca8db85..bd9e95d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ django>=1.4,<1.4.99 beautifulsoup psycopg2 -pil +Pillow lxml south>=0.7.3,<0.7.99 simplejson diff --git a/requirements_searchengine.txt b/requirements_searchengine.txt new file mode 100644 index 0000000..a87b1bb --- /dev/null +++ b/requirements_searchengine.txt @@ -0,0 +1,12 @@ +django>=1.4,<1.4.99 +beautifulsoup +psycopg2 +pil +lxml +south>=0.7.3,<0.7.99 +simplejson +feedparser +django-tinymce +django-haystack==2.1 +geopy +pysolr |