diff --git a/.travis.yml b/.travis.yml index fa70d3d03..21ada7602 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ language: python python: - "2.6" - "2.7" -install: "pip install -r requirements.txt --use-mirrors" +install: "pip install -r requirements.txt" script: "fab travis_ci" diff --git a/README.rst b/README.rst index 688e2365f..994dc02ef 100644 --- a/README.rst +++ b/README.rst @@ -256,6 +256,8 @@ OpenSlides uses the following projects or parts of them: * `Django haystack `_, License: BSD +* `natsort `_, License: MIT + * `pdf.js `_, License: Apache License v2.0 * `Pillow `_, License: Standard @@ -296,6 +298,9 @@ OpenSlides uses the following projects or parts of them: - `jQuery DataTables Plugin `_, License: BSD/GPLv2 + - `DataTables Natural Sort Plugin `_, + License: MIT + - `jQuery Cookie Plugin `_, License: MIT/GPL diff --git a/extras/win32-portable/create_portable.txt b/extras/win32-portable/create_portable.txt index e7eda72e2..19251c131 100644 --- a/extras/win32-portable/create_portable.txt +++ b/extras/win32-portable/create_portable.txt @@ -7,7 +7,7 @@ How to create a new portable Windows distribution of OpenSlides: 2. Install all required python packages (see requirements_production.txt): - easy_install -Z django django-mptt beautifulsoup4 bleach jsonfield pillow reportlab setuptools sockjs_tornado tornado django-haystack whoosh + easy_install -Z django django-mptt beautifulsoup4 bleach jsonfield natsort pillow reportlab setuptools sockjs_tornado tornado django-haystack whoosh 3. Install pywin32 from binary installer: diff --git a/extras/win32-portable/licenses/natsort b/extras/win32-portable/licenses/natsort new file mode 100644 index 000000000..993858af2 --- /dev/null +++ b/extras/win32-portable/licenses/natsort @@ -0,0 +1,19 @@ +Copyright (c) 2012 Seth M. Morton + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extras/win32-portable/prepare_portable.py b/extras/win32-portable/prepare_portable.py index 95a7bf026..7013cd851 100755 --- a/extras/win32-portable/prepare_portable.py +++ b/extras/win32-portable/prepare_portable.py @@ -99,6 +99,9 @@ SITE_PACKAGES = { "tornado": { "copy": ["tornado"], }, + "natsort": { + "copy": ["natsort"], + }, "whoosh": { "copy": ["whoosh"], }, diff --git a/openslides/core/signals.py b/openslides/core/signals.py index 4e84d46cf..145988979 100644 --- a/openslides/core/signals.py +++ b/openslides/core/signals.py @@ -65,7 +65,7 @@ def setup_general_config(sender, **kwargs): default_value=True, form_field=forms.BooleanField( label=ugettext_lazy('Show logo on projector'), - help_text=ugettext_lazy('You can find and replace the logo under "openslides/static/img/projector/static/img/logo-projector.png".'), + help_text=ugettext_lazy('You can find and replace the logo under "openslides/projector/static/img/logo-projector.png".'), required=False)) projector_enable_title = ConfigVariable( diff --git a/openslides/core/static/js/jquery/dataTables.bootstrap.js b/openslides/core/static/js/jquery/dataTables.bootstrap.js index 6e160ad3a..9fd6ad615 100644 --- a/openslides/core/static/js/jquery/dataTables.bootstrap.js +++ b/openslides/core/static/js/jquery/dataTables.bootstrap.js @@ -11,6 +11,7 @@ /* Table initialisation */ $(document).ready(function() { $('#dataTable').dataTable( { + "bRetrieve": true, "aLengthMenu": [[10, 25, 50, -1], [10, 25, 50, gettext("All")]], "aoColumnDefs": [ { "bSortable": false, "aTargets": [ -1 ] } diff --git a/openslides/core/static/js/naturalSort.js b/openslides/core/static/js/naturalSort.js new file mode 100644 index 000000000..c831beba7 --- /dev/null +++ b/openslides/core/static/js/naturalSort.js @@ -0,0 +1,50 @@ +/* + * Natural Sort algorithm for Javascript - Version 0.6 - Released under MIT license + * Author: Jim Palmer (based on chunking idea from Dave Koelle) + * Contributors: Mike Grier (mgrier.com), Clint Priest, Kyle Adams, guillermo + */ + +function naturalSort (a, b) { + var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, + sre = /(^[ ]*|[ ]*$)/g, + dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, + hre = /^0x[0-9a-f]+$/i, + ore = /^0/, + // convert all to strings and trim() + x = a.toString().replace(sre, '') || '', + y = b.toString().replace(sre, '') || '', + // chunk/tokenize + xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), + yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), + // numeric, hex or date detection + xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)), + yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null; + // first try and sort Hex codes or Dates + if (yD) + if ( xD < yD ) return -1; + else if ( xD > yD ) return 1; + // natural sorting through split numeric strings and default strings + for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { + // find floats not starting with '0', string or 0 if not defined (Clint Priest) + oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; + oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if (isNaN(oFxNcL) !== isNaN(oFyNcL)) return (isNaN(oFxNcL)) ? 1 : -1; + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + else if (typeof oFxNcL !== typeof oFyNcL) { + oFxNcL += ''; + oFyNcL += ''; + } + if (oFxNcL < oFyNcL) return -1; + if (oFxNcL > oFyNcL) return 1; + } + return 0; +} + +// use naturalSort as dataTable extension +jQuery.fn.dataTableExt.oSort['natural-asc'] = function(a,b) { + return naturalSort(a,b); +}; +jQuery.fn.dataTableExt.oSort['natural-desc'] = function(a,b) { + return naturalSort(a,b) * -1; +}; diff --git a/openslides/motion/pdf.py b/openslides/motion/pdf.py index 17963400d..6636bf47b 100644 --- a/openslides/motion/pdf.py +++ b/openslides/motion/pdf.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- +from operator import attrgetter import os import random from bs4 import BeautifulSoup from django.conf import settings from django.utils.translation import ugettext as _ +from natsort import natsorted from reportlab.lib import colors from reportlab.lib.units import cm from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle @@ -25,6 +27,7 @@ def motions_to_pdf(pdf): Create a PDF with all motions. """ motions = Motion.objects.all() + motions = natsorted(motions, key=attrgetter('identifier')) all_motion_cover(pdf, motions) for motion in motions: pdf.append(PageBreak()) @@ -254,7 +257,7 @@ def all_motion_cover(pdf, motions): identifier = "" if motion.identifier: identifier = "%s " % motion.identifier - pdf.append(Paragraph("%s%s" % (identifier, motion.title), stylesheet['Heading3'])) + pdf.append(Paragraph("%s   %s" % (identifier, motion.title), stylesheet['Heading3'])) def motion_poll_to_pdf(pdf, poll): diff --git a/openslides/motion/templates/motion/motion_list.html b/openslides/motion/templates/motion/motion_list.html index 768c18b37..44cd4a549 100644 --- a/openslides/motion/templates/motion/motion_list.html +++ b/openslides/motion/templates/motion/motion_list.html @@ -12,7 +12,15 @@ {% block javascript %} + + {% endblock %} {% block content %} @@ -78,7 +86,7 @@ {% endif %} - {% if perms.core.can_manage_projector %} + {% if perms.projector.can_manage_projector %} diff --git a/openslides/projector/static/img/logo-projector.png b/openslides/projector/static/img/logo-projector.png index 461e9de7c..9b81eb762 100644 Binary files a/openslides/projector/static/img/logo-projector.png and b/openslides/projector/static/img/logo-projector.png differ diff --git a/requirements.txt b/requirements.txt index 70c53d1cb..bee236a1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,3 @@ mock==1.0.1 # Requirements for OpenSlides handbook/documentation in alphabetical order Sphinx==1.2b3 sphinx-bootstrap-theme==0.2.4 - -# For Python 2.6 support -argparse==1.2.1 diff --git a/requirements_production.txt b/requirements_production.txt index ff4343e08..07a945ae6 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -5,9 +5,14 @@ bleach>=1.2,<1.3 django-haystack>=2.1,<2.2 django-mptt>=0.6,<0.7 jsonfield>=0.9,<0.10 +natsort>=3.0,<3.1 pillow>=2.2,<2.3 reportlab>=2.7,<2.8 setuptools>=2.1,<2.2 sockjs-tornado>=1.0,<1.1 tornado>=3.1,<3.2 whoosh>=2.5,<2.6 + +# For Python 2.6 support +--allow-external argparse +argparse==1.2.1