Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into transportv2

Conflicts:
	molly/apps/transport/templates/transport/index.html
  • Loading branch information...
commit 62a780299e24d0fcf26ff402a2d3cf61ad7a43d2 2 parents 8dc224b + e3ba473
@cnorthwood cnorthwood authored
Showing with 419 additions and 301 deletions.
  1. +8 −2 docs/source/getting-started/configuring.txt
  2. +24 −14 docs/source/getting-started/installing.txt
  3. +1 −1  docs/source/ref/apps/library.txt
  4. +1 −1  molly/__init__.py
  5. +8 −7 molly/apps/feature_vote/views.py
  6. +25 −23 molly/apps/feedback/templates/feedback/index.html
  7. +1 −1  molly/apps/feedback/views.py
  8. +2 −2 molly/apps/library/models.py
  9. +4 −8 molly/apps/library/providers/z3950.py
  10. +91 −99 molly/apps/library/templates/library/item_detail.html
  11. +6 −1 molly/apps/library/templates/library/item_holdings_detail.html
  12. +1 −1  molly/apps/library/templates/library/item_list.html
  13. +1 −1  molly/apps/library/views.py
  14. +1 −1  molly/apps/places/models.py
  15. +14 −2 molly/apps/places/providers/acislive.py
  16. +3 −3 molly/apps/places/providers/naptan.py
  17. +3 −1 molly/apps/places/providers/osm.py
  18. +22 −9 molly/apps/places/providers/timetables.py
  19. +2 −0  molly/apps/places/static/places/js/smart.js
  20. +0 −1  molly/apps/places/views.py
  21. +2 −0  molly/apps/transport/static/transport/js/smart.js
  22. +2 −1  molly/apps/transport/templates/transport/public_transport.html
  23. +18 −0 molly/auth/static/auth/css/smart.css
  24. +11 −9 molly/auth/templates/auth/secure.html
  25. +6 −3 molly/auth/views.py
  26. +2 −1  molly/batch_processing/models.py
  27. +3 −3 molly/favourites/views.py
  28. +18 −3 molly/geolocation/providers/cloudmade.py
  29. +2 −0  molly/geolocation/providers/places.py
  30. +1 −1  { → molly}/installer/__init__.py
  31. +12 −12 { → molly}/installer/commands.py
  32. +3 −1 { → molly}/installer/dbcreate.py
  33. +2 −2 { → molly}/installer/dbprep.py
  34. +3 −3 { → molly}/installer/deploy.py
  35. 0  { → molly}/installer/site/__init__.py
  36. 0  { → molly}/installer/site/apache/httpd.conf
  37. 0  { → molly}/installer/site/apache/molly.wsgi
  38. 0  { → molly}/installer/site/manage.py
  39. +8 −1 { → molly}/installer/site/settings.py
  40. 0  { → molly}/installer/site/templates/base.html
  41. +4 −4 { → molly}/installer/sysprep/__init__.py
  42. +1 −1  { → molly}/installer/sysprep/fedora.py
  43. +1 −1  { → molly}/installer/sysprep/packagekit.py
  44. +3 −2 { → molly}/installer/sysprep/redhat.py
  45. +1 −1  { → molly}/installer/sysprep/ubuntu.py
  46. +1 −1  { → molly}/installer/sysprep/ubuntu_lucid.py
  47. +1 −1  { → molly}/installer/sysprep/ubuntu_maverick.py
  48. 0  { → molly}/installer/utils.py
  49. +1 −1  { → molly}/installer/virtualenv.py
  50. +11 −8 molly/maps/osm/__init__.py
  51. +2 −2 molly/media/site/css/smart.css
  52. +0 −1  molly/media/site/js/smart.js
  53. +24 −21 molly/templates/base.html
  54. +6 −6 molly/url_shortener/views.py
  55. +19 −5 molly/utils/i18n.py
  56. +0 −21 molly/utils/media.py
  57. +16 −0 molly/utils/middleware.py
  58. +3 −2 molly/utils/views.py
  59. +14 −5 setup.py
View
10 docs/source/getting-started/configuring.txt
@@ -151,7 +151,7 @@ A typical setup may look like this::
'molly.wurfl.middleware.WurflMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.locale.LocaleMiddleware',
+ 'molly.utils.middleware.CookieLocaleMiddleware',
'molly.utils.middleware.ErrorHandlingMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'molly.auth.middleware.SecureSessionMiddleware',
@@ -416,7 +416,7 @@ directory, and is optional.
Sample::
- CACHE_DIR = '/var/cache/molly-images' # This must be set, and there is no default
+ EXTERNAL_IMAGES_DIR = '/var/cache/molly-images'
IDENTIFIER_SCHEME_PREFERENCE
""""""""""""""""""""""""""""
@@ -438,6 +438,12 @@ STATICFILES_DIRS_ setting::
MARKER_DIR = os.path.join(CACHE_DIR, 'markers')
+NO_CACHE
+""""""""
+
+When set to true, then cache headers are suppressed from responses. This can be
+used as a workaround to a `MOLLY-177 <http://issues.mollyproject.org/browse/MOLLY-177>`_.
+
SITE_NAME
"""""""""
View
38 docs/source/getting-started/installing.txt
@@ -59,6 +59,10 @@ following packages, or their equivalent on your platform, are available:
Configuring PostgreSQL
----------------------
+.. warning:: The following installation instructions are unlikely to be suitable
+ for production databases. Experienced DBAs will want to skip
+ this section.
+
Molly requires Postgis to be installed into the database in order to work
correctly. A convenience script is included in Molly to create such a database
on supported platforms (Ubuntu, Fedora and CentOS)::
@@ -77,29 +81,38 @@ authenticate to Postgres, e.g.,::
contains information on how to create a spatial database template
by hand, if you would like to do it that way.
-Molly also includes a convenience function to create a ``pg_hba.conf`` which
-includes a default security configuration for Molly to access the database. If
-you already have a working Postgres database, or want to configure security by
-hand, then skip this step.
-
-.. warning:: This will override your existing ``pg_hba.conf`` and may break an
- existing PostgreSQL installation. It is recommended you do this
- only on a new, blank installation, and when Molly is the only user
- of the database.
+.. warning:: The next step will override your existing ``pg_hba.conf`` and may
+ break an existing PostgreSQL installation. It is recommended you do
+ this only on a new, blank installation, and when Molly is the only
+ user of the database.
-.. code-block::
+Many Linux distributions' default configuration for Postgres are not immediately
+usable by Molly - typically, they are not configured to allow users to connect
+to the database by password authentication. Molly includes a convenience
+function to create a ``pg_hba.conf`` which includes a default security
+configuration for Molly to access the database. If you already have a working
+Postgres database, or want to configure security by hand, then skip this step::
sudo ./setup.py dbprep --configure-security
Creating your database
----------------------
+.. note:: The function described below is simplistic and experienced DBAs may
+ prefer creating the database and permissions by hand. If you choose to
+ do so, then ensure you create the database for Molly from a Postgis
+ template.
+
+.. warning:: This will delete any database called ``molly`` that already exists
+ and change the password of any user called ``molly`` which exists.
+
As a convenience function, Molly provides a command to automatically create a
database user and database for Molly to use::
./setup.py dbcreate
-.. warning:: This will delete any database called ``molly`` that already exists
+.. info:: This assumes that the Postgis template is called ``template_postgis``,
+ which is the default and recommended value.
By default, if running as a superuser, this will create a database called
``molly`` and a user called ``molly`` which has access to this database. A
@@ -115,9 +128,6 @@ the following options can be used:
As with the ``dbprep`` command, this can also take superuser credentials which
are used to run these commands, if the current user doesn't have credentials.
-.. note:: If you want to configure your database by hand, then please ensure
- that the database Molly uses is created from a Postgis template.
-
Creating a site template
------------------------
View
2  docs/source/ref/apps/library.txt
@@ -41,7 +41,7 @@ the Z39.50 protocol. It supports the following options:
* control_number_key (optional, defaults to 12): The 'use attribute' to query
on when doing a control number lookup,
* results_encoding (optional, defaults to marc8): The encoding results come
- back in. Valid values are 'marc8' and 'unicode'.
+ back in. Valid values are 'marc8' and 'unicode'. Aleph needs 'unicode'.
Writing Your Own Providers
--------------------------
View
2  molly/__init__.py
@@ -4,4 +4,4 @@
A framework for creating Mobile Web applications for HE/FE institutions.
"""
-__version__ = '1.2'
+__version__ = '1.2.1'
View
15 molly/apps/feature_vote/views.py
@@ -56,17 +56,18 @@ def handle_GET(self, request, context):
def handle_POST(self, request, context):
form = context['form']
+ post = request.POST.copy()
- if 'vote_up.x' in request.POST:
- request.POST['vote_up'] = request.POST['vote_up.x']
+ if 'vote_up.x' in post:
+ post['vote_up'] = post['vote_up.x']
- if 'vote_down.x' in request.POST:
- request.POST['vote_down'] = request.POST['vote_down.x']
+ if 'vote_down.x' in post:
+ post['vote_down'] = post['vote_down.x']
- if 'vote_up' in request.POST or 'vote_down' in request.POST:
- feature = get_object_or_404(Feature, id = request.POST.get('id', 0))
+ if 'vote_up' in post or 'vote_down' in post:
+ feature = get_object_or_404(Feature, id=post.get('id', 0))
previous_vote = request.session['feature_vote:votes'].get(feature.id, 0)
- vote = previous_vote + (1 if 'vote_up' in request.POST else -1)
+ vote = previous_vote + (1 if 'vote_up' in post else -1)
vote = min(max(-1, vote), 1)
request.session['feature_vote:votes'][feature.id] = vote
request.session.modified = True
View
48 molly/apps/feedback/templates/feedback/index.html
@@ -3,16 +3,16 @@
{% block content %}
{% if sent %}
- <div class="section">
- <h2>{% trans "Thank you for your feedback!" %}</h2>
- <div class="note"><div class="bubble pad-10">
- {% if referer %}
- <p><a href="{{ referer }}">{% trans "You may wish to go back." %}</a></p>
- {% else %}
- <p><a href="{% url home:index %}">{% trans "You may wish to go back to the home page." %}</a></p>
- {% endif %}
+ <div class="section">
+ <h2>{% trans "Thank you for your feedback!" %}</h2>
+ <div class="note"><div class="bubble pad-10">
+ {% if feedback_referrer %}
+ <p><a href="{{ feedback_referrer }}">{% trans "You may wish to go back." %}</a></p>
+ {% else %}
+ <p><a href="{% url home:index %}">{% trans "You may wish to go back to the home page." %}</a></p>
+ {% endif %}
</div></div>
- </div>
+ </div>
{% endif %}
{% if not sent %}
@@ -23,20 +23,22 @@
</style>
<form method="post" action=".">
- {% csrf_token %}
-<input type="hidden" name="referer" value="{{ referer }}"/>
-
-<div class="section">
- <div class="form">
- <h3>{{ feedback_form.email.label}}</h3>
- {% if feedback_form.email.errors %}
- <p>{{ feedback_form.email.errors.0 }}</p>
- {% endif %} <p>{{ feedback_form.email }}</p>
- <h3>{{ feedback_form.body.label}}</h3>
- <p>{{ feedback_form.body }}</p>
- <p><input type="submit" value='{% trans "Submit" %}' /></p>
- </div>
-</div>
+ {% csrf_token %}
+ <input type="hidden" name="referer" value="{{ feedback_referrer }}"/>
+
+ <div class="section">
+ <div class="form">
+ <h3>{{ feedback_form.email.label}}</h3>
+ {% if feedback_form.email.errors %}
+ <p>{{ feedback_form.email.errors.0 }}</p>
+ {% endif %}
+ <p>{{ feedback_form.email }}</p>
+
+ <h3>{{ feedback_form.body.label}}</h3>
+ <p>{{ feedback_form.body }}</p>
+ <p><input type="submit" value='{% trans "Submit" %}' /></p>
+ </div>
+ </div>
</form>
{% endif %}
View
2  molly/apps/feedback/views.py
@@ -26,7 +26,7 @@ def initial_context(self, request):
def handle_GET(self, request, context):
context.update({
'sent': request.GET.get('sent') == 'true',
- 'referer': request.GET.get('referer', ''),
+ 'feedback_referrer': request.GET.get('referer', ''),
})
return self.render(request, context, 'feedback/index',
expires=timedelta(days=365))
View
4 molly/apps/library/models.py
@@ -200,7 +200,7 @@ def __init__(self, location):
self.location = tuple(location)
def __unicode__(self):
- return " - ".join(self.location)
+ return "/".join(self.location)
__repr__ = __unicode__
def __hash__(self):
@@ -223,7 +223,7 @@ def get_entity(self):
if hasattr(app_by_local_name('library'), 'library_identifier'):
library_identifier = app_by_local_name('library').library_identifier
try:
- return get_entity(library_identifier, self.location[-1])
+ return get_entity(library_identifier, '/'.join(self.location))
except (Http404, Entity.MultipleObjectsReturned):
return None
else:
View
12 molly/apps/library/providers/z3950.py
@@ -63,12 +63,7 @@ def __init__(self, result, results_encoding):
heading, data = item.split(' ', 1)
heading = int(heading)
if heading == self.USM_CONTROL_NUMBER:
- # We strip the 'UkOxUb' from the front.
-
- if data.startswith('UkOxUb'):
- self.control_number = data[6:]
- else:
- self.control_number = data
+ self.control_number = data
# We'll use a slice as data may not contain that many characters.
# LCN 12110145 is an example where this would otherwise fail.
@@ -94,7 +89,7 @@ def __init__(self, result, results_encoding):
self.libraries = {}
for datum in self.metadata[self.USM_LOCATION]:
- library = Library(datum['b'])
+ library = Library(datum['b'] + datum.get('c', []))
if not 'p' in datum:
availability = LibrarySearchResult.AVAIL_UNKNOWN
datum['y'] = ['Check web OPAC']
@@ -450,4 +445,5 @@ def f(y):
return translator.translate(y).replace(u'\x1b', u'\xa0')
else:
return y.decode('ascii').replace(u'\x1b', u'\xa0')
- return f(x)
+ return f(x)
+
View
190 molly/apps/library/templates/library/item_detail.html
@@ -3,111 +3,103 @@
{% block content %}
<div class="section">
-<div class="header">
- <h2>{{ item.title }}</h2>
-</div>
- <div class="article"><div class="bubble pad-5">
- <table>
-{% if item.author %}
- <tr>
- <th>{% trans "Author" %}:</th>
- <td>{{ item.author }}</td>
- </tr>
-{% endif %}
-{% if item.publisher %}
- <tr>
- <th>{% trans "Publisher" %}:</th>
- <td>{{ item.publisher }}</td>
- </tr>
-{% endif %}
-{% if item.edition %}
- <tr>
- <th>{% trans "Edition" %}:</th>
- <td>{{ item.edition }}</td>
- </tr>
-{% endif %}
-{% if item.description %}
- <tr>
- <th>{% trans "Description" %}:</th>
- <td>{{ item.description }}</td>
- </tr>
-{% endif %}
-
-
-
-{% if item.isbns %}
-{% for isbn in item.isbns %}
- <tr>
-{% if forloop.first %}
- <th{% ifnotequal item.isbns|length 1 %} rowspan="{{ item.isbns|length }}"{% endifnotequal %}>
- {% blocktrans count item.isbns|length as count %}ISBN{% plural %}ISBNs{% endblocktrans %}:</th>
-{% endif %}
- <td>{{ isbn }}</td>
- </tr>
-{% endfor %}
-{% endif %}
-
-
-{% if item.issns %}
-{% for issn in item.issns %}
- <tr>
-{% if forloop.first %}
- <th{% ifnotequal item.issns|length 1 %} rowspan="{{ item.issns|length }}"{% endifnotequal %}>
- {% blocktrans count item.issns|length as count %}ISSN{% plural %}ISSNs{% endblocktrans %}:</th>
-{% endif %}
- <td>{{ issn }}</td>
- </tr>
-{% endfor %}
-{% endif %}
-
+ <div class="header">
+ <h2>{{ item.title }}</h2>
+ </div>
+ <div class="article"><div class="bubble pad-5"><table>
+ {% if item.author %}
+ <tr>
+ <th>{% trans "Author" %}:</th>
+ <td>{{ item.author }}</td>
+ </tr>
+ {% endif %}
+ {% if item.publisher %}
+ <tr>
+ <th>{% trans "Publisher" %}:</th>
+ <td>{{ item.publisher }}</td>
+ </tr>
+ {% endif %}
+ {% if item.edition %}
+ <tr>
+ <th>{% trans "Edition" %}:</th>
+ <td>{{ item.edition }}</td>
+ </tr>
+ {% endif %}
+ {% if item.description %}
+ <tr>
+ <th>{% trans "Description" %}:</th>
+ <td>{{ item.description }}</td>
+ </tr>
+ {% endif %}
+ {% if item.isbns %}
+ {% for isbn in item.isbns %}
+ <tr>
+ {% if forloop.first %}
+ <th{% ifnotequal item.isbns|length 1 %} rowspan="{{ item.isbns|length }}"{% endifnotequal %}>
+ {% blocktrans count item.isbns|length as count %}ISBN{% plural %}ISBNs{% endblocktrans %}:</th>
+ {% endif %}
+ <td>{{ isbn }}</td>
+ </tr>
+ {% endfor %}
+ {% endif %}
+
+ {% if item.issns %}
+ {% for issn in item.issns %}
+ <tr>
+ {% if forloop.first %}
+ <th{% ifnotequal item.issns|length 1 %} rowspan="{{ item.issns|length }}"{% endifnotequal %}>
+ {% blocktrans count item.issns|length as count %}ISSN{% plural %}ISSNs{% endblocktrans %}:</th>
+ {% endif %}
+ <td>{{ issn }}</td>
+ </tr>
+ {% endfor %}
+ {% endif %}
- </table>
- </div></div>
+ </table></div></div>
</div>
{% if item.libraries %}
<div class="section">
-<div class="header">
- <h2>{% trans "Library holdings" %}</h2>
-</div>
-<div class="section-content no-round-bottom">
- {% if map %}{% render_map map %}{% endif %}
- <div class="note"><div class="bubble pad-10">
- {% trans "Please note that all libraries have admission and borrowing policies; inclusion in this list does not imply access." %}{% if map_hash %} {% trans "Closest results are not necessarily best." %}{% endif %}
- </div></div>
-</div>
-<table class="content">
- <tbody>
-{% for library, books in item.libraries.items %}
- <tr class="sub-section-divider">
- <th colspan="2">
- {% if library.marker_number %}{{ library.marker_number }}. {% endif %}
- {% with library.location|slice:"-1:" as location_code %}
- <a href="{% url library:item-holdings-detail control_number,location_code.0 %}">
- {% if library.get_entity %}
- {{ library.get_entity.title }}
- {% else %}
- {{ library.location|join:" - " }}
- {% endif %}
- </a>
- {% endwith %}
- </td>
- </tr>
-
- {% for book in books %}
- <tr>
- <td>
- {% trans "Shelfmark" %}: <em>{{ book.shelfmark }}</em>
- {% if book.materials_specified %}, {% trans "issue" %}: <em>{{ books.0.materials_specified }}</em>{% endif %}
- </td>
- <td><em>{{ book.availability_display }}</em></td>
-
- </tr>
- {% endfor %}
-{% endfor %}
- </tbody>
-</table>
+ <div class="header">
+ <h2>{% trans "Library holdings" %}</h2>
+ </div>
+ <div class="section-content no-round-bottom">
+ {% if map %}{% render_map map %}{% endif %}
+ <div class="note"><div class="bubble pad-10">
+ {% trans "Please note that all libraries have admission and borrowing policies; inclusion in this list does not imply access." %}{% if map_hash %} {% trans "Closest results are not necessarily best." %}{% endif %}
+ </div></div>
+ </div>
+ <table class="content">
+ <tbody>
+ {% for library, books in item.libraries.items %}
+ <tr class="sub-section-divider">
+ <th colspan="2">
+ {% if library.marker_number %}{{ library.marker_number }}. {% endif %}
+ {% with library.location as location_code %}
+ <a href="{% url library:item-holdings-detail control_number,location_code|join:"/" %}">
+ {% if library.get_entity %}
+ {{ library.get_entity.title }}
+ {% else %}
+ {{ library.location|join:"/" }}
+ {% endif %}
+ </a>
+ {% endwith %}
+ </td>
+ </tr>
+
+ {% for book in books %}
+ <tr>
+ <td>
+ {% trans "Shelfmark" %}: <em>{{ book.shelfmark }}</em>
+ {% if book.materials_specified %}, {% trans "issue" %}: <em>{{ books.0.materials_specified }}</em>{% endif %}
+ </td>
+ <td><em>{{ book.availability_display }}</em></td>
+ </tr>
+ {% endfor %}
+ {% endfor %}
+ </tbody>
+ </table>
</div>
{% endif %}
View
7 molly/apps/library/templates/library/item_holdings_detail.html
@@ -63,7 +63,12 @@
<div class="section">
<div class="header">
- <h2>{{ library.location|join:" - " }}</h2>
+ <h2>{% if library.get_entity %}
+ {{ library.get_entity.title }}
+ {% else %}
+ {{ library.location|join:"/" }}
+ {% endif %}
+ </h2>
</div>
{% if library.get_entity.location %}
{% render_location_map library.get_entity.location %}
View
2  molly/apps/library/templates/library/item_list.html
@@ -36,7 +36,7 @@
{% endwith %}
{% endif %}
<a href="{% url library:item-detail item.control_number %}">
- {{ item }}{% if item.author %}
+ {{ item.title }}{% if item.author %}
<br/><small><strong>{% trans "Author" %}:</strong> {{ item.author }}</small>{% endif %}
<br/><small><strong>{% trans "Publisher" %}:</strong> {{ item.publisher }}</small>{% if item.edition %}
<br/><small><strong>{% trans "Edition" %}:</strong> {{ item.edition }}</small>{% endif %}
View
2  molly/apps/library/views.py
@@ -204,7 +204,7 @@ def initial_context(self, request, control_number, sublocation):
# Find which particular library we're interested in
library = None
for item_library in item.libraries:
- if item_library.location[-1] == sublocation:
+ if item_library.location == tuple(sublocation.split('/')):
library = item_library
if library is None:
View
2  molly/apps/places/models.py
@@ -124,7 +124,7 @@ class EntityGroup(models.Model):
@property
def title(self):
- return name_in_language(self, 'title')
+ return name_in_language(self, 'title', '')
source = models.ForeignKey(Source)
ref_code = models.CharField(max_length=256)
View
16 molly/apps/places/providers/acislive.py
@@ -38,11 +38,19 @@ class ACISLiveMapsProvider(BaseMapsProvider):
'040': ('http://bucks.acislive.com/',
lambda entity: entity.identifiers.get('atco'),
lambda entity: entity.identifiers.get('atco'),
- ), # Cambridgeshire
+ ), # Buckinghamshire
'050': ('http://www.cambridgeshirebus.info/',
lambda entity: entity.identifiers.get('atco'),
lambda entity: entity.identifiers.get('atco'),
), # Cambridgeshire
+ '060': ('http://cheshire.acislive.com/',
+ lambda entity: entity.identifiers.get('atco'),
+ lambda entity: entity.identifiers.get('atco'),
+ ), # Cheshire East
+ '080': ('http://cornwall.acislive.com/',
+ lambda entity: entity.identifiers.get('atco'),
+ lambda entity: entity.identifiers.get('atco'),
+ ), # Cornwall
'160': ('http://gloucestershire.acislive.com/',
lambda entity: entity.identifiers.get('atco'),
lambda entity: entity.identifiers.get('atco'),
@@ -74,7 +82,11 @@ class ACISLiveMapsProvider(BaseMapsProvider):
'450': ('http://wymetro.acislive.com/',
lambda entity: entity.identifiers.get('plate'),
lambda entity: entity.identifiers.get('plate'),
- ), #West Yorkshire
+ ), # West Yorkshire
+ '450': ('http://swindon.acislive.com/',
+ lambda entity: entity.identifiers.get('atco'),
+ lambda entity: entity.identifiers.get('atco'),
+ ), # Swindon
'571': ('http://cardiff.acislive.com/',
lambda entity: entity.identifiers.get('atco'),
lambda entity: entity.identifiers.get('atco'),
View
6 molly/apps/places/providers/naptan.py
@@ -238,8 +238,7 @@ def add_stop(self, meta, entity_type, source):
else:
title = common_name
- if street != None and street != '-' \
- and street not in common_name:
+ if street != None and street != '-':
# Deal with all-caps street names
if street.upper() == street:
fixedstreet = ''
@@ -257,7 +256,8 @@ def add_stop(self, meta, entity_type, source):
fixedstreet += letter.lower()
street = fixedstreet
- title += ', ' + street
+ if street not in title:
+ title += ', ' + street
locality_lang = self.nptg_localities.get(locality)
if locality_lang != None:
View
4 molly/apps/places/providers/osm.py
@@ -53,6 +53,7 @@ def startDocument(self):
self.node_locations = {}
def startElement(self, name, attrs):
+
if name == 'node':
lon, lat = float(attrs['lon']), float(attrs['lat'])
@@ -187,7 +188,7 @@ def endElement(self, name):
def endDocument(self):
for entity in Entity.objects.filter(source=self.source):
- if not entity.identifiers['osm'] in self.ids:
+ if not entity.identifiers.get('osm') in self.ids:
entity.delete()
self.delete_count += 1
@@ -299,6 +300,7 @@ def import_data(self, metadata, output):
while buffer:
parser.feed(bunzip.decompress(buffer))
buffer = osm.read(8192)
+ parser.close()
for lang_code, lang_name in settings.LANGUAGES:
with override(lang_code):
View
31 molly/apps/places/providers/timetables.py
@@ -1,7 +1,8 @@
from collections import namedtuple, defaultdict
-from datetime import datetime, time
+from datetime import datetime, time, timedelta
from logging import getLogger
from operator import itemgetter
+
from django.db.models import Q
from molly.apps.places.providers import BaseMapsProvider
@@ -22,13 +23,13 @@ def augment_metadata(self, entities, **kwargs):
today = datetime.now()
if today.time() < time(4):
today -= timedelta(days=1)
-
+
def midnight_4am(left, right):
"""
Search comparison function where before 4 am is later than midnight
"""
- return cmp(left.hour if left.hour >= 4 else left.hour + 24,
- right.hour if right.hour >= 4 else right.hour + 24)
+ return cmp((left.hour if left.hour >= 4 else left.hour + 24, left.minute, left.second),
+ (right.hour if right.hour >= 4 else right.hour + 24, right.minute, right.second))
for entity in entities:
@@ -37,24 +38,36 @@ def midnight_4am(left, right):
continue
services = defaultdict(list)
- for stop in entity.scheduledstop_set.filter(
- Q(sta__gte=today.time()) | Q(sta__lt=time(4))):
+ if today.time() < time(22):
+ until = [Q(sta__gte=today.time()) | Q(std__gte=today.time()), Q(sta__lt=(today + timedelta(hours=2)).time()) | Q(std__lt=(today + timedelta(hours=2)).time())]
+ else:
+ until = [Q(sta__gte=today.time()) | Q(std__gte=today.time()) | Q(sta__lt=(today + timedelta(hours=2)).time()) | Q(std__lt=(today + timedelta(hours=2)).time())]
+
+ for stop in entity.scheduledstop_set.filter(*until):
if not stop.journey.runs_on(today.date()):
continue
- services[(stop.journey.route.service_id, stop.journey.route.service_name)].append((stop.journey, stop.std if stop.std else stop.sta))
+ service_id = stop.journey.route.service_id
+ destination = stop.journey.scheduledstop_set.all().reverse()[0].entity.title
+
+ # Now try and tidy up destination
+ destination = destination.split(', ')[-1]
+ if '(' in destination:
+ destination = destination[:destination.find('(')].strip()
+
+ services[(service_id, destination)].append((stop.journey, stop.std or stop.sta))
services = ((route, sorted(ss, key=itemgetter(1), cmp=midnight_4am))
for route, ss in services.items())
services = [{
'service': service_id,
- 'destination': service_name,
+ 'destination': destination,
'next': ss[0][1].strftime('%H:%M'),
'following': map(lambda t: t[1].strftime('%H:%M'), ss[1:4]),
'journey': ss[0][0]
- } for (service_id, service_name), ss in sorted(services, key=lambda x: x[1][0][1])]
+ } for (service_id, destination), ss in sorted(services, key=lambda x: x[1][0][1])]
entity.metadata['real_time_information'] = {
'services': services,
View
2  molly/apps/places/static/places/js/smart.js
@@ -220,6 +220,8 @@ function rebuildRTI(elem, metadata){
var service = metadata.services[i]
if (service.route) {
var route_link = '<a href="service?route=' + encodeURIComponent(service.service) + '">' + service.service + '</a>'
+ } else if (service.journey) {
+ var route_link = '<a href="service?journey=' + service.journey.id + '">' + service.service + '</a>'
} else {
var route_link = service.service
}
View
1  molly/apps/places/views.py
@@ -688,7 +688,6 @@ def initial_context(self, request, scheme, value):
else:
- print stop.sta, stop.std
calling_point = {
'entity': stop.entity,
# Show arrival time (if it exists, else departure time)
View
2  molly/apps/transport/static/transport/js/smart.js
@@ -73,6 +73,8 @@ function refreshTransport(data){
tr = tbody.find('tr:last')
if (service.route) {
var route_link = '<a href="' + entity._url + 'service?route=' + encodeURIComponent(service.service) + '" class="subtle-link">' + service.service + '</a>'
+ } else if (service.journey) {
+ var route_link = '<a href="' + entity._url + 'service?journey=' + service.journey.id + '" class="subtle-link">' + service.service + '</a>'
} else {
var route_link = service.service
}
View
3  molly/apps/transport/templates/transport/public_transport.html
@@ -90,8 +90,9 @@
<tr>
<td class="center"><big>
{% if service.route %}<a href="{{ entity.get_absolute_url }}service?route={{ service.service|urlencode }}" class="subtle-link">{% endif %}
+ {% if service.journey %}<a href="{{ entity.get_absolute_url }}service?journey={{ service.journey.id }}" class="subtle-link">{% endif %}
{{ service.service }}
- {% if service.route %}</a>{% endif %}
+ {% if service.route or service.journey %}</a>{% endif %}
</big></td>
<td>{{ service.destination }}</td>
<td>{{ service.next }}{% if service.following %}<small>, {{ service.following.0 }}{% if service.following.1 %}, &hellip;{% endif %}</small>{% endif %}</td>
View
18 molly/auth/static/auth/css/smart.css
@@ -0,0 +1,18 @@
+.secure-link-block {
+ line-height: 1.8em;
+ margin: 2px 10px 2px 10px;
+ background-color: #ffffbf;
+ padding: 10px 10px 10px 38px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ display: block;
+ border-radius: 6px;
+ background-image: url('../../auth/images/padlock.png');
+ background-repeat: no-repeat;
+ background-position: 10px center;
+ text-align: center;
+}
+
+.secure-link-block a {
+ padding: 5px 1px;
+}
View
20 molly/auth/templates/auth/secure.html
@@ -2,15 +2,17 @@
{% block bottom_links %}
- <div style="width:100%; float:left; margin-top: 2px; margin-bottom:2px;">
- <div class="center" style="line-height:1.8em; margin:0 10px 0 10px; background-color:#ffffbf; padding:10px; -webkit-border-radius:6px; -moz-border-radius:6px; display:block;"
- href="{% url feature_vote:index %}">
- <div style="background-image:url('{{ STATIC_URL }}auth/images/padlock.png'); background-repeat:no-repeat; background-position:left center; padding-left:28px">
- <a style="padding:5px 1px;" href="{% url auth:index %}">{% trans "Security preferences" %}</a>
- <a style="padding:5px 1px;" href="{% url auth:clear-session %}?return_url={{ full_path }}" rel="nofollow">{% trans "Clear secure session" %}</a><br/>
- {% blocktrans with user|externalservicetoken_count as number_services count user.usersession_set.all.count as number_devices %}{{ number_services }} accessible from {{ number_devices }} device.{% plural %}{{ number_services }} accessible from {{ number_devices }} devices.{% endblocktrans %}
- <a style="padding:5px 1px;" href="{% url auth:index %}">{% trans "Change" %}</a>
- </div></div>
+ <div class="secure-link-block">
+ <a href="{% url auth:index %}">{% trans "Security preferences" %}</a>
+
+ {% if not suppress_clear_session_link %}
+ <a href="{% url auth:clear-session %}?return_url={{ full_path }}" rel="nofollow">
+ {% endif %}
+ {% trans "Clear secure session" %}
+ {% if not suppress_clear_session_link %}</a>{% endif %}<br/>
+
+ {% blocktrans with user|externalservicetoken_count as number_services count user.usersession_set.all.count as number_devices %}{{ number_services }} accessible from {{ number_devices }} device.{% plural %}{{ number_services }} accessible from {{ number_devices }} devices.{% endblocktrans %}
+ <a href="{% url auth:index %}">{% trans "Change" %}</a>
</div>
{{ block.super }}
{% endblock %}
View
9 molly/auth/views.py
@@ -135,14 +135,16 @@ def handle_POST(self, request, context):
form.save()
return self.redirect('.', request)
-
+
class ClearSessionView(SecureView):
+
def initial_context(self, request):
return {
'return_url': request.REQUEST.get('return_url', '/'),
+ 'suppress_clear_session_link': True,
}
-
+
@BreadcrumbFactory
def breadcrumb(self, request, context):
return Breadcrumb(
@@ -152,9 +154,10 @@ def breadcrumb(self, request, context):
lazy_reverse('auth:clear-session'),
)
-
+
def handle_GET(self, request, context):
return self.render(request, context, 'auth/clear_session')
+
def handle_POST(self, request, context):
UserSession.objects.filter(secure_session_key = request.secure_session.session_key).delete()
logout(request)
View
3  molly/batch_processing/models.py
@@ -91,4 +91,5 @@ def run(self, tee_to_stdout=False):
self.save()
def __unicode__(self):
- return self.title
+ return self.title
+
View
6 molly/favourites/views.py
@@ -1,7 +1,7 @@
import logging
from django.http import Http404
-from django.core.urlresolvers import resolve
+from django.core.urlresolvers import resolve, reverse
from django.utils.translation import ugettext as _
from molly.utils.views import BaseView
@@ -100,7 +100,7 @@ def handle_POST(self, request, context):
# If the source was the favourites page, redirect back there
if 'return_to_favourites' in request.POST:
- return self.redirect(lazy_reverse('favourites:index'), request)
+ return self.redirect(reverse('favourites:index'), request)
# else the source
else:
@@ -108,4 +108,4 @@ def handle_POST(self, request, context):
else:
# Missing POST data, probably a bad request
- return self.redirect(lazy_reverse('favourites:index'), request)
+ return self.redirect(reverse('favourites:index'), request)
View
21 molly/geolocation/providers/cloudmade.py
@@ -26,12 +26,23 @@ def reverse_geocode(self, lon, lat):
'type': 'road',
}
- data = urllib2.urlopen(self.REVERSE_GEOCODE_URL % params)
-
- json = simplejson.load(data)
+ try:
+ request_url = self.REVERSE_GEOCODE_URL % params
+ response = urllib2.urlopen(request_url)
+ if response.code != 200:
+ logger.error("Request to %s returned response code %d" % (request_url, response.code))
+ return []
+ json = simplejson.loads(response.read().replace('&apos;', "'"), 'utf8')
+ except urllib2.HTTPError, e:
+ logger.error("Cloudmade returned a non-OK response code %d", e.code)
+ return []
+ except urllib2.URLError, e:
+ logger.error("Encountered an error reaching Cloudmade: %s", str(e))
+ return []
if not json:
return []
+
else:
name = json['features'][0]['properties'].get('name')
try:
@@ -47,6 +58,10 @@ def reverse_geocode(self, lon, lat):
}]
def geocode(self, query):
+
+ if not query:
+ return []
+
if self.search_locality and not (', ' in query or ' near ' in query):
query += ', %s' % self.search_locality
View
2  molly/geolocation/providers/places.py
@@ -18,10 +18,12 @@ def geocode(self, query):
entities = entities.filter(
_identifiers__scheme__in = self.search_identifiers,
_identifiers__value__iexact = query,
+ location__isnull = False
)
else:
entities = entities.filter(
_identifiers__value__iexact = query,
+ location__isnull = False
)
entities = chain(
View
2  installer/__init__.py → molly/installer/__init__.py
@@ -1,6 +1,6 @@
# Packages which Molly needs, but Pip can't handle
PIP_PACKAGES = [
- ('PyZ3950', 'git+http://github.com/oucs/PyZ3950.git'), # Custom PyZ3950, contains some bug fixes
+ ('PyZ3950', 'git+git://github.com/oucs/PyZ3950.git'), # Custom PyZ3950, contains some bug fixes
('django-compress', 'git+git://github.com/mollyproject/django-compress.git#egg=django-compress'), # Fork of django-compress contains some extra features we need
('PIL', 'PIL'), # Because it doesn't install properly when called using setuptools...
]
View
24 installer/commands.py → molly/installer/commands.py
@@ -1,3 +1,4 @@
+import sys
import os
import string
from random import choice
@@ -6,15 +7,14 @@
from distutils.core import Command
from distutils.errors import DistutilsArgError, DistutilsExecError
-from installer.deploy import deploy
-from installer.virtualenv import Virtualenv, NotAVirtualenvError
-from installer.dbcreate import create
-from installer import PIP_PACKAGES
+from molly.installer.deploy import deploy
+from molly.installer.virtualenv import Virtualenv, NotAVirtualenvError
+from molly.installer.dbcreate import create
+from molly.installer import PIP_PACKAGES
try:
- from installer.sysprep import PYTHON26
+ from molly.installer.sysprep import PYTHON26
except (NotImplementedError, ImportError):
- import sys
PYTHON26 = sys.executable
class DeployCommand(Command):
@@ -55,7 +55,7 @@ def run(self):
self.listen_externally, self.port)
try:
- from installer.sysprep import SysPreparer
+ from molly.installer.sysprep import SysPreparer
except NotImplementedError:
class SysprepCommand(Command):
@@ -172,13 +172,13 @@ def run(self):
configure_pg_hba()
try:
- from installer.sysprep import postgres_setup
+ from molly.installer.sysprep import postgres_setup
except (ImportError, NotImplementedError):
def postgres_setup(*args, **kwargs):
pass
try:
- from installer.dbprep import configure_pg_hba, create_postgis_template
+ from molly.installer.dbprep import configure_pg_hba, create_postgis_template
except NotImplementedError:
DBPrepCommand = NullDBPrepCommand
@@ -194,9 +194,9 @@ class DBCreateCommand(Command):
user_options = [
('admin-username=', 'u', 'The username of the database superuser to connect as [default=None]'),
('admin-password=', 'p', 'The password of the database superuser to connect as [default=None]'),
- ('molly-username', 'c', 'The username of the database user to create [default=molly]'),
- ('molly-password', 'w', 'The password of the database user to create [default=random]'),
- ('molly-database', 'd', 'Force installing, even if virtualenv already exists [default=molly]'),
+ ('molly-username=', 'c', 'The username of the database user to create [default=molly]'),
+ ('molly-password=', 'w', 'The password of the database user to create [default=random]'),
+ ('molly-database=', 'd', 'The name of the database to create [default=molly]'),
]
def initialize_options(self):
View
4 installer/dbcreate.py → molly/installer/dbcreate.py
@@ -3,7 +3,9 @@
information as super user, or by running as root.
"""
-from installer.utils import quiet_exec, CommandFailed
+import os
+
+from molly.installer.utils import quiet_exec, CommandFailed
def create(dba_user, dba_pass, username, password, database):
View
4 installer/dbprep.py → molly/installer/dbprep.py
@@ -6,8 +6,8 @@
import os
from distutils.errors import DistutilsSetupError
-from installer.utils import quiet_exec
-from installer.sysprep import POSTGIS_PATH, SPATIAL_REF_SYS_PATH, PG_HBA_PATH
+from molly.installer.utils import quiet_exec
+from molly.installer.sysprep import POSTGIS_PATH, SPATIAL_REF_SYS_PATH, PG_HBA_PATH
def create_postgis_template(username, password):
# Setup PostGIS
View
6 installer/deploy.py → molly/installer/deploy.py
@@ -8,7 +8,7 @@
import shutil
from distutils.errors import DistutilsSetupError
-from installer.utils import CommandFailed
+from molly.installer.utils import CommandFailed
logger = logging.getLogger(__name__)
@@ -21,7 +21,7 @@ def deploy(venv, site_path, development=False, listen_externally=False,
"""
logger.info('Doing a %s install into %s using site %s',
- 'development' if development else 'production',
+ {True: 'development', False: 'production'}.get(development),
venv.path, site_path)
if not development:
@@ -54,7 +54,7 @@ def deploy(venv, site_path, development=False, listen_externally=False,
# Okay, now install Molly
print "Installing Molly...",
sys.stdout.flush()
- molly_setup = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'setup.py')
+ molly_setup = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..', 'setup.py')
if development:
venv('python %s develop' % molly_setup)
else:
View
0  installer/site/__init__.py → molly/installer/site/__init__.py
File renamed without changes
View
0  installer/site/apache/httpd.conf → molly/installer/site/apache/httpd.conf
File renamed without changes
View
0  installer/site/apache/molly.wsgi → molly/installer/site/apache/molly.wsgi
File renamed without changes
View
0  installer/site/manage.py → molly/installer/site/manage.py
File renamed without changes
View
9 installer/site/settings.py → molly/installer/site/settings.py
@@ -17,6 +17,13 @@
from molly.conf.settings import Application, extract_installed_apps, Authentication, ExtraBase, Provider
from molly.utils.media import get_compress_groups
+# The following import and mimetypes.add_types correct the - possibly wrong - mime type of svg files
+# in certain versions of Django.
+import mimetypes
+
+mimetypes.add_type("image/svg+xml", ".svg", True)
+mimetypes.add_type("image/svg+xml", ".svgz", True)
+
# The following creates two useful variables - a path to where Molly is
# installed, and also to the root of where your site is installed. These can be
# used in place of absolute URLs so you can move your installation around
@@ -229,7 +236,7 @@
'molly.wurfl.middleware.WurflMiddleware', # This enables Molly's device detection capability (required)
'django.middleware.common.CommonMiddleware', # This incorporates some convenience functions from Django (required)
'django.contrib.sessions.middleware.SessionMiddleware', # This enables Django's session storage framework (required)
- 'django.middleware.locale.LocaleMiddleware', # This enables i18n support in Molly (required)
+ 'molly.utils.middleware.CookieLocaleMiddleware', # This enables i18n support in Molly (required)
'molly.utils.middleware.ErrorHandlingMiddleware', # This enables Molly's error handling and reporting framework
'django.contrib.auth.middleware.AuthenticationMiddleware', # This allows for users to be logged in in Django (required)
'molly.auth.middleware.SecureSessionMiddleware', # This adds the capability to have secure sessions (sessions which are HTTPS only)
View
0  installer/site/templates/base.html → molly/installer/site/templates/base.html
File renamed without changes
View
8 installer/sysprep/__init__.py → molly/installer/sysprep/__init__.py
@@ -12,14 +12,14 @@
distribution, distribution_version, distribution_id = platform.linux_distribution()
if distribution == 'Fedora':
- from installer.sysprep.fedora import *
+ from molly.installer.sysprep.fedora import *
elif distribution == 'redhat':
- from installer.sysprep.rhel import *
+ from molly.installer.sysprep.redhat import *
elif distribution == 'Ubuntu':
if distribution_version == '10.04':
- from installer.sysprep.ubuntu_lucid import *
+ from molly.installer.sysprep.ubuntu_lucid import *
elif distribution_version == '10.10':
- from installer.sysprep.ubuntu_maverick import *
+ from molly.installer.sysprep.ubuntu_maverick import *
else:
raise NotImplementedError()
else:
View
2  installer/sysprep/fedora.py → molly/installer/sysprep/fedora.py
@@ -1,7 +1,7 @@
import sys
import os
-from installer.sysprep.packagekit import PackagekitSysPreparer, postgres_setup
+from molly.installer.sysprep.packagekit import PackagekitSysPreparer, postgres_setup
POSTGIS_PATH = '/usr/share/pgsql/contrib/postgis-1.5/postgis.sql'
SPATIAL_REF_SYS_PATH = '/usr/share/pgsql/contrib/postgis-1.5/spatial_ref_sys.sql'
View
2  installer/sysprep/packagekit.py → molly/installer/sysprep/packagekit.py
@@ -2,7 +2,7 @@
Wrapper for installations on packagekit/yum distributions
"""
-from installer.utils import quiet_exec
+from molly.installer.utils import quiet_exec
try:
# Use the Packagekit library if this exists
View
5 installer/sysprep/redhat.py → molly/installer/sysprep/redhat.py
@@ -8,8 +8,8 @@
else:
distribution, distribution_version, distribution_id = platform.linux_distribution()
-from installer.utils import quiet_exec
-from installer.sysprep.packagekit import PackagekitSysPreparer, postgres_setup
+from molly.installer.utils import quiet_exec
+from molly.installer.sysprep.packagekit import PackagekitSysPreparer, postgres_setup
PYTHON26 = '/usr/bin/python26'
@@ -46,6 +46,7 @@ class SysPreparer(PackagekitSysPreparer):
'make',
'gcc',
'openssl-devel',
+ 'libyaml-devel',
]
def sysprep(self):
View
2  installer/sysprep/ubuntu.py → molly/installer/sysprep/ubuntu.py
@@ -1,4 +1,4 @@
-from installer.utils import quiet_exec
+from molly.installer.utils import quiet_exec
class AptSysPreparer(object):
View
2  installer/sysprep/ubuntu_lucid.py → molly/installer/sysprep/ubuntu_lucid.py
@@ -1,4 +1,4 @@
-from installer.sysprep.ubuntu import AptSysPreparer
+from molly.installer.sysprep.ubuntu import AptSysPreparer
POSTGIS_PATH = '/usr/share/postgresql/8.4/contrib/postgis.sql'
SPATIAL_REF_SYS_PATH = '/usr/share/postgresql/8.4/contrib/spatial_ref_sys.sql'
View
2  installer/sysprep/ubuntu_maverick.py → molly/installer/sysprep/ubuntu_maverick.py
@@ -1,4 +1,4 @@
-from installer.sysprep.ubuntu import AptSysPreparer
+from molly.installer.sysprep.ubuntu import AptSysPreparer
POSTGIS_PATH = '/usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql'
SPATIAL_REF_SYS_PATH = '/usr/share/postgresql/8.4/contrib/postgis-1.5/spatial_ref_sys.sql'
View
0  installer/utils.py → molly/installer/utils.py
File renamed without changes
View
2  installer/virtualenv.py → molly/installer/virtualenv.py
@@ -8,7 +8,7 @@
import logging
import shutil
-from installer.utils import quiet_exec, CommandFailed
+from molly.installer.utils import quiet_exec, CommandFailed
logger = logging.getLogger(__name__)
View
19 molly/maps/osm/__init__.py
@@ -24,7 +24,7 @@
('yellow', '#f0ff00', '#4b5000', '#000000'),
)
-MARKER_RANGE = xrange(1, 100)
+MARKER_RANGE = xrange(1, 200)
logger = logging.getLogger(__name__)
@@ -133,13 +133,16 @@ def get_or_create_map(generator, args):
generated__lt=datetime.now()-timedelta(weeks=1)).order_by('generated')
if to_delete.count() > 0:
# But only clear up 50 at a time
- youngest = to_delete[0].last_accessed
- to_delete = to_delete[:50]
- for generated_map in to_delete:
- generated_map.delete()
- age = (datetime.now()-youngest)
- age = age.days*24 + age.seconds/3600.0
- logger.info("Cleared out old maps, youngest is %f hours", age)
+ try:
+ youngest = to_delete[0].last_accessed
+ to_delete = to_delete[:50]
+ for generated_map in to_delete:
+ generated_map.delete()
+ age = (datetime.now()-youngest)
+ age = age.days*24 + age.seconds/3600.0
+ logger.info("Cleared out old maps, youngest is %f hours", age)
+ except IndexError:
+ logger.info("Maps disappeared whilst trying to delete - race condition?", exc_info=True)
return hash, metadata
View
4 molly/media/site/css/smart.css
@@ -911,12 +911,12 @@ div#bottom-links div.full-width {
width: 100%;
}
-div#bottom-links div#rim-left {
+div#bottom-links div.rim-left {
width: 49%;
float: left;
}
-div#bottom-links div#rim-right {
+div#bottom-links div.rim-right {
width: 49%;
float: right;
}
View
1  molly/media/site/js/smart.js
@@ -89,7 +89,6 @@ function async_load(url, query, meth) {
display_loading_screen()
query['format'] = 'fragment';
- query['language_code'] = language_code;
async_load_xhr = $.ajax({
'url': to_absolute(url),
'data': query,
View
45 molly/templates/base.html
@@ -157,46 +157,49 @@ <h1 class="{% if not breadcrumbs.2 %}{{ breadcrumbs.0 }}-header with-icon{% endi
{% block bottom_links %}
{# Give all non-feature phones a stylised bunch of links #}
{% ifnotequal style_group "dumb" %}
+
{# As older Blackberry (RIM) devices have poor browsers we reduce button width and float each side #}
- {% if device.brand_name == "RIM" %}
- <div id="rim-left">
- <a href="{% url feedback:index %}?referer={{ full_path|urlencode }}">{% trans "Feedback" %}</a>
+ <div class="{% if device.brand_name == "RIM" %}rim-left{% else %}half-width{% endif %}">
+
+ {# This stops links to feedback on the feedback page - causes an infinite loop! #}
+ <a href="{% url feedback:index %}?referer={% if feedback_referrer %}{{ feedback_referrer|urlencode }}{% else %}{{ full_path|urlencode }}{% endif %}">{% trans "Feedback" %}</a>
</div>
- <div id="rim-right">
- <a href="{% url url_shortener:index %}?path={{ full_path|urlencode }}">{% trans "Share" %}</a>
+ <div class="{% if device.brand_name == "RIM" %}rim-right{% else %}half-width{% endif %}">
+ <a href="{% url url_shortener:index %}?path={% if share_path %}{{ share_path|urlencode }}{% else %}{{ full_path|urlencode }}{% endif %}">{% trans "Share" %}</a>
</div>
- {# All other devices #}
- {% else %}
- <div class="half-width"><a href="{% url feedback:index %}?referer={{ full_path|urlencode }}">{% trans "Feedback" %}</a></div>
- <div class="half-width"><a href="{% url url_shortener:index %}?path={{ full_path|urlencode }}">{% trans "Share" %}</a></div>
- {% endif %}
{% if "feature_vote"|app_is_loaded or "feature-suggestions"|app_is_loaded %}
<div class="{% if LANGUAGES|length != 2 %}full-width{% else %}half-width{% endif %}">
<a href="{% url feature_vote:index %}">{% trans "Suggest a feature" %}</a></div>
{% endif %}
+
{# If there are only two languages to switch between, show the switch here #}
{% if LANGUAGES|length == 2 %}
<div class="half-width">
{% for language_code, language_name in LANGUAGES %}
- {% if language_code != LANGUAGE_CODE %}
- <form action="{% url set-language %}" method="post" class="has-ajax-handler">
- <input type="hidden" name="next" value="{{ full_path|urlencode }}" />
- {% csrf_token %}<a>
- <button type="submit" name="language" value="{{ language_code }}">
- {{ language_name }}
- </button></a>
- </form>
- {% endif %}
+ {% if language_code != LANGUAGE_CODE %}
+ <form action="{% url set-language %}" method="post" class="has-ajax-handler">
+ <input type="hidden" name="next" value="{{ full_path|urlencode }}" />
+ {% csrf_token %}<a>
+ <button type="submit" name="language" value="{{ language_code }}">
+ {{ language_name }}
+ </button></a>
+ </form>
+ {% endif %}
{% endfor %}
</div>
{% endif %}
<div class="clear"> </div>
+
{# Give a simple list for feature phones #}
{% else %}
<ul>
- <li><a href="{% url feedback:index %}?referer={{ full_path|urlencode }}">{% trans "Feedback" %}</a></li>
- <li><a href="{% url url_shortener:index %}?path={{ full_path|urlencode }}">{% trans "Share" %}</a></li>
+ <li>
+ <a href="{% url feedback:index %}?referer={% if feedback_referrer %}{{ feedback_referrer|urlencode }}{% else %}{{ full_path|urlencode }}{% endif %}">{% trans "Feedback" %}</a>
+ </li>
+ <li>
+ <a href="{% url url_shortener:index %}?path={% if share_path %}{{ share_path|urlencode }}{% else %}{{ full_path|urlencode }}{% endif %}">{% trans "Share" %}</a>
+ </li>
{% if "feature_vote"|app_is_loaded or "feature-suggestions"|app_is_loaded %}
<li><a href="{% url feature_vote:index %}">{% trans "Suggest a feature" %}</a></li>
{% endif %}
View
12 molly/url_shortener/views.py
@@ -34,7 +34,7 @@ def initial_context(self, request):
raise Http404
return {
- 'path': path,
+ 'share_path': path,
'view': view,
'view_args': view_args,
'view_kwargs': view_kwargs,
@@ -56,8 +56,8 @@ def breadcrumb(request, context):
return (
breadcrumb[0],
breadcrumb[1],
- (breadcrumb[4], context['path']),
- breadcrumb[1] == (breadcrumb[4], context['path']),
+ (breadcrumb[4], context['share_path']),
+ breadcrumb[1] == (breadcrumb[4], context['share_path']),
_('Shorten link'),
)
else:
@@ -65,8 +65,8 @@ def breadcrumb(request, context):
return (
view.conf.local_name,
index,
- (u'Back\u2026', context['path']),
- context['path'] == index[1],
+ (u'Back\u2026', context['share_path']),
+ context['share_path'] == index[1],
_('Shorten link'),
)
@@ -74,7 +74,7 @@ def handle_GET(self, request, context):
try:
path = request.GET['path']
except (KeyError):
- return self.invalid_path(request, context)
+ raise Http404()
if IndexView in getattr(context['view'], '__mro__', ()):
return self.redirect(path, request, 'perm')
View
24 molly/utils/i18n.py
@@ -1,3 +1,5 @@
+from copy import copy
+
from django.utils.translation import get_language
from django.db.models import Model
from django.conf import settings
@@ -26,16 +28,17 @@ def name_in_language(obj, field, default=None):
return getattr(obj.names.get(language_code=language_code),
field)
except ObjectDoesNotExist:
- if default:
+ if default is not None:
return default
else:
raise
else:
- if default:
+ if default is not None:
return default
else:
raise
+
def set_name_in_language(obj, lang, **fields):
"""
Assuming the object follows the Molly pattern for i18n data (related manager
@@ -55,14 +58,26 @@ def set_name_in_language(obj, lang, **fields):
setattr(name, k, v)
name.save()
+
class SetLanguageView(BaseView):
def handle_GET(self, request, context):
return self.render(request, context, 'i18n/index')
def handle_POST(self, request, context):
- # Do Django's built in language setter
- return set_language(request)
+ if hasattr(request, 'session'):
+ # MOLLY-177: Force using cookies to set language
+ session = request.session
+ del request.session
+ ret = set_language(request)
+ request.session = session
+ return ret
+
+ else:
+
+ # Do Django's built in language setter
+ return set_language(request)
+
# TODO: When Molly moves to Django 1.4, this can be removed
@@ -239,7 +254,6 @@ def javascript_catalog(request, domain='djangojs', packages=None):
for k, v in pdict.items():
src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
src.extend(csrc)
- src.append("""var language_code = '%s'""" % locale)
src.append(LibFoot)
src.append(InterPolate)
src.append(LibFormatHead)
View
21 molly/utils/media.py
@@ -4,27 +4,6 @@
import os, os.path
-# os.path.relpath doesn't exist in Py2.5, so we define our own.
-# Many thanks to Elliot of saltycrane.com[0] and James Gardner[1].
-# [0] http://www.saltycrane.com/blog/2010/03/ospathrelpath-source-code-python-25/
-# [1] http://jimmyg.org/work/code/barenecessities/0.2.5/manual.html#the-relpath-function
-if not hasattr(os.path, 'relpath'):
- # This code taken from [0], above; modified slightly.
- import posixpath
- def relpath(path, start=posixpath.curdir):
- """Return a relative version of a path"""
- if not path:
- raise ValueError("no path specified")
- start_list = posixpath.abspath(start).split(posixpath.sep)
- path_list = posixpath.abspath(path).split(posixpath.sep)
- # Work out how much of the filepath is shared by start and path.
- i = len(posixpath.commonprefix([start_list, path_list]))
- rel_list = [posixpath.pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return posixpath.curdir
- return posixpath.join(*rel_list)
- os.path.relpath = relpath
-
def get_compress_groups(STATIC_ROOT):
COMPRESS_CSS, COMPRESS_JS = {}, {}
View
16 molly/utils/middleware.py
@@ -4,6 +4,7 @@
from django.http import Http404
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
+from django.middleware.locale import LocaleMiddleware
from molly.utils.views import handler500
@@ -21,3 +22,18 @@ def process_exception(self, request, exception):
logger.exception("[500] %s at %s" % (type(exception).__name__, request.path))
return handler500(request, exc_info=sys.exc_info())
+class CookieLocaleMiddleware(LocaleMiddleware):
+
+ def process_request(self, request):
+
+ if hasattr(request, 'session'):
+ # MOLLY-177: Force using cookies to set language
+ session = request.session
+ del request.session
+ super(CookieLocaleMiddleware, self).process_request(request)
+ request.session = session
+
+ else:
+
+ super(CookieLocaleMiddleware, self).process_request(request)
+
View
5 molly/utils/views.py
@@ -60,7 +60,7 @@ def tidy_query_string(url):
scheme, netloc, path, params, query, fragment = urlparse(url)
args = []
for k, vs in parse_qs(query).items():
- if k in ('format', 'language_code'):
+ if k in ['format',]:
continue
else:
for v in vs:
@@ -278,7 +278,8 @@ def render(self, request, context, template_name, expires=None):
except NotImplementedError:
continue
else:
- if expires is not None and not settings.DEBUG:
+ if expires is not None and not settings.DEBUG and \
+ not getattr(settings, 'NO_CACHE', False):
response['Expires'] = formatdate(
mktime((datetime.now() + expires).timetuple()))
View
19 setup.py
@@ -1,7 +1,15 @@
#!/usr/bin/env python
# Bootstrap setup tools
-import ez_setup
-ez_setup.use_setuptools()
+try:
+ from setuptools import setup
+except ImportError:
+ try:
+ import ez_setup
+ except ImportError:
+ print "Install failed: You don't have setuptools installed, and we couldn't install it for you"
+ else:
+ ez_setup.use_setuptools()
+ from setuptools import setup
from setuptools import setup
from distutils.command.install import INSTALL_SCHEMES
@@ -9,8 +17,8 @@
from molly import __version__ as molly_version
-from installer.utils import get_packages_and_data
-from installer.commands import (DeployCommand, SysprepCommand,
+from molly.installer.utils import get_packages_and_data
+from molly.installer.commands import (DeployCommand, SysprepCommand,
CreateVirtualenvCommand, DBPrepCommand,
DBCreateCommand, SiteCreateCommand)
@@ -71,13 +79,14 @@
classifiers=[
'Framework :: Django',
'Development Status :: 5 - Production/Stable',
- 'License :: OSI Approved :: Academic Free License',
+ 'License :: OSI Approved :: Academic Free License (AFL)',
'Intended Audience :: Education',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Education',
'Topic :: Internet',
],
+ setup_requires = ["setuptools"],
install_requires = [
"python-Levenshtein",
"pywurfl",
Please sign in to comment.
Something went wrong with that request. Please try again.