summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chimere/forms.py11
-rw-r--r--chimere/models.py6
-rw-r--r--chimere/search_indexes.py59
-rw-r--r--chimere/settings.sample.py9
-rw-r--r--chimere/static/chimere/css/styles.css39
-rw-r--r--chimere/static/chimere/js/base.js2
-rw-r--r--chimere/static/chimere/js/jquery.chimere.js41
-rw-r--r--chimere/static/chimere/js/search-autocomplete.js78
-rw-r--r--chimere/static/chimere/js/search.js32
-rw-r--r--chimere/templates/chimere/base.html1
-rw-r--r--chimere/templates/chimere/blocks/footer.html2
-rw-r--r--chimere/templates/chimere/main_map.html18
-rw-r--r--chimere/templates/search/indexes/chimere/marker_text.txt3
-rw-r--r--chimere/templates/search/indexes/chimere/route_text.txt1
-rw-r--r--chimere/templates/search/search.html54
-rw-r--r--chimere/templatetags/unescape.py15
-rw-r--r--chimere/urls.py18
-rw-r--r--chimere/views.py27
-rw-r--r--haystack-requirements.txt4
-rw-r--r--requirements.txt2
-rw-r--r--requirements_searchengine.txt12
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