Merge branch 'stable/1.6.x'
Conflicts: CHANGELOG README.rst openslides/assignment/models.py openslides/users/forms.py requirements_production.txt tests/settings.py
This commit is contained in:
commit
0a24b7267b
@ -24,7 +24,9 @@ Version 1.7 (unreleased)
|
||||
|
||||
Core:
|
||||
- New feature to tag motions, agenda and assignments.
|
||||
Motion:
|
||||
- Fixed search index problem to index contents of many-to-many table
|
||||
(e.g. tags of a motion).
|
||||
Motions:
|
||||
- New Feature to create amendments, which are related to a parent motion.
|
||||
- Added possibility to hide motions from non staff users in some states.
|
||||
Other:
|
||||
|
@ -105,7 +105,7 @@ portable version you should observe the following install steps.*
|
||||
To install Virtual Python Environment builder, open command line (cmd)
|
||||
and run::
|
||||
|
||||
> easy_install https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.11.6.tar.gz
|
||||
> easy_install https://pypi.python.org/packages/source/v/virtualenv/virtualenv-12.0.5.tar.gz
|
||||
|
||||
Create your OpenSlides directory, change to it, setup and activate the
|
||||
virtual environment::
|
||||
|
@ -5,34 +5,21 @@ How to create a new portable Windows distribution of OpenSlides:
|
||||
Follow the instructions in the README, section III (Windows installation), step 1.
|
||||
|
||||
|
||||
2. Install all required python packages (see requirements_production.txt):
|
||||
2. Install all required python packages from requirements_production.txt:
|
||||
(Note: You have to use 'easy_install -Z $PACKAGENAME')
|
||||
|
||||
easy_install -Z "django<1.7" ^
|
||||
backports.ssl_match_hostname ^
|
||||
"beautifulsoup4<4.4" ^
|
||||
"bleach<1.5" ^
|
||||
"django-ckeditor-updated<4.3" ^
|
||||
"django-haystack<2.2" ^
|
||||
"django-mptt<0.7" ^
|
||||
"jsonfield<0.10" ^
|
||||
"natsort<3.3" ^
|
||||
"reportlab<2.8" ^
|
||||
"roman<2.1" ^
|
||||
"sockjs_tornado<1.1" ^
|
||||
"tornado<3.3" ^
|
||||
"whoosh<2.6" ^
|
||||
"setuptools<3.7"
|
||||
python install-requirements.py
|
||||
|
||||
|
||||
3. Install pywin32 from binary installer:
|
||||
http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win32-py2.7.exe/download
|
||||
http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/pywin32-219.win32-py2.7.exe/download
|
||||
|
||||
Pywin32 is used to update the version resource of the prebuild openslides.exe.
|
||||
It is not strictly required but at least for published releases it is highly advisable.
|
||||
|
||||
|
||||
4. Install wxPython from binary installer:
|
||||
http://downloads.sourceforge.net/wxpython/wxPython2.8-win32-unicode-2.8.12.1-py27.exe
|
||||
http://sourceforge.net/projects/wxpython/files/wxPython/2.8.12.1/wxPython2.8-win32-unicode-2.8.12.1-py27.exe/download
|
||||
|
||||
WxPython is required to build the OpenSlides GUI frontend.
|
||||
|
||||
|
12
extras/win32-portable/install-requirements.py
Normal file
12
extras/win32-portable/install-requirements.py
Normal file
@ -0,0 +1,12 @@
|
||||
import os
|
||||
|
||||
f = open("../../requirements_production.txt")
|
||||
reqs = f.read().split("\n")
|
||||
|
||||
for req in reqs:
|
||||
# ignore comments or pip options
|
||||
if req.startswith('--') or req.startswith('#'):
|
||||
continue
|
||||
# ignore blank lines
|
||||
if len(req) > 0:
|
||||
os.system('easy_install -Z -U "%s"' % req)
|
@ -83,5 +83,8 @@
|
||||
{% endwith %}
|
||||
<a href="{{ node|absolute_url }}">{% if node.type == node.ORGANIZATIONAL_ITEM %}<i>[{% endif %}{{ node }}{% if node.type == node.ORGANIZATIONAL_ITEM %}]</i>{% endif %}</a>
|
||||
{{ node.get_title_supplement|safe }}
|
||||
{% for tag in node.tags.all %}
|
||||
<span class="label">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,7 +41,7 @@
|
||||
{% endif %}
|
||||
{% if perms.core.can_manage_tags %}
|
||||
<a href="{% url 'core_tag_list' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Manage tags' %}">
|
||||
<i class="icon-th"></i>
|
||||
<i class="icon-tags"></i>
|
||||
<span class="optional-small"> {% trans 'Tags' %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
@ -41,6 +41,13 @@
|
||||
</div>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<!-- Tags -->
|
||||
{% for tag in item.tags.all %}
|
||||
<span class="label">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Title -->
|
||||
<p>
|
||||
{% if not item.content_object %}
|
||||
{{ item.text|safe }}
|
||||
@ -49,6 +56,7 @@
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<!-- Comment -->
|
||||
{% if perms.agenda.can_manage_agenda %}
|
||||
{% if item.comment %}
|
||||
<h3>{% trans "Comment" %}</h3>
|
||||
@ -56,7 +64,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{# List of Speakers #}
|
||||
<!-- List of Speakers -->
|
||||
<h3>{% trans "List of speakers" %} {% if item.speaker_list_closed %}<span class="label label-important">{% trans 'closed' %}</span>{% endif %}</h3>
|
||||
<p>
|
||||
{% if perms.agenda.can_manage_agenda %}
|
||||
|
@ -1,2 +1,3 @@
|
||||
{{ object.title }}
|
||||
{{ object.text }}
|
||||
{{ object.tags.all }}
|
||||
|
@ -50,6 +50,11 @@
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<!-- Tags -->
|
||||
{% for tag in assignment.tags.all %}
|
||||
<span class="optional label">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span9">
|
||||
<!-- Description -->
|
||||
|
@ -23,7 +23,7 @@
|
||||
{% endif %}
|
||||
{% if perms.core.can_manage_tags %}
|
||||
<a href="{% url 'core_tag_list' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Manage tags' %}">
|
||||
<i class="icon-th"></i>
|
||||
<i class="icon-tags"></i>
|
||||
<span class="optional-small"> {% trans 'Tags' %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -46,7 +46,11 @@
|
||||
</thead>
|
||||
{% for object in object_list %}
|
||||
<tr class="{% if object.is_active_slide %}activeline{% endif %}">
|
||||
<td><a href="{{ object|absolute_url:'detail' }}">{{ object }}</a></td>
|
||||
<td><a href="{{ object|absolute_url:'detail' }}">{{ object }}</a>
|
||||
{% for tag in object.tags.all %}
|
||||
<span class="optional label">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="optional">
|
||||
<!-- posts -->
|
||||
{% trans "Posts" context "Number of searched candidates for an election" %}:
|
||||
|
@ -1,3 +1,4 @@
|
||||
{{ object.name }}
|
||||
{{ object.description }}
|
||||
{{ object.candidates }}
|
||||
{{ object.tags.all }}
|
||||
|
@ -1,8 +1,9 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.utils.html import urlize
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from sockjs.tornado import SockJSConnection
|
||||
|
||||
|
||||
@ -18,22 +19,25 @@ class ChatboxSocketHandler(SockJSConnection):
|
||||
"""
|
||||
from openslides.users.models import User
|
||||
|
||||
# TODO: Use the django way to get the session to be compatible with
|
||||
# other auth-backends; see comment in pull request #1220:
|
||||
# https://github.com/OpenSlides/OpenSlides/pull/1220#discussion_r11565705
|
||||
session_key = info.get_cookie(settings.SESSION_COOKIE_NAME).value
|
||||
session = Session.objects.get(session_key=session_key)
|
||||
# get the session (compatible with other auth-backends)
|
||||
engine = import_module(settings.SESSION_ENGINE)
|
||||
try:
|
||||
self.user = User.objects.get(pk=session.get_decoded().get('_auth_user_id'))
|
||||
session_key = info.get_cookie(settings.SESSION_COOKIE_NAME).value
|
||||
session = engine.SessionStore(session_key)
|
||||
pk = session.get_decoded().get('_auth_user_id')
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
try:
|
||||
self.user = User.objects.get(pk)
|
||||
except User.DoesNotExist:
|
||||
return_value = False
|
||||
else:
|
||||
return False
|
||||
|
||||
if self.user.has_perm('core.can_use_chat'):
|
||||
self.clients.add(self)
|
||||
return_value = True
|
||||
return True
|
||||
else:
|
||||
return_value = False
|
||||
return return_value
|
||||
return False
|
||||
|
||||
def on_message(self, message):
|
||||
"""
|
||||
|
@ -113,7 +113,7 @@
|
||||
<hr />
|
||||
<footer>
|
||||
<small>
|
||||
© Copyright 2011–2014 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a> | <a href="{% url 'core_version' %}">Version</a>
|
||||
© Copyright 2011–2015 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a> | <a href="{% url 'core_version' %}">Version</a>
|
||||
</small>
|
||||
</footer>
|
||||
</div><!--/#content-->
|
||||
@ -134,7 +134,7 @@
|
||||
<script type="text/javascript" src="{% static 'js/jquery/jquery.bsmselect.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
// use jquery-bsmselect for all <select multiple> form elements
|
||||
$("select[multiple]").bsmSelect({
|
||||
$("select[multiple]:not(.dont_use_bsmselect)").bsmSelect({
|
||||
removeLabel: '<sup><b>X</b></sup>',
|
||||
containerClass: 'bsmContainer',
|
||||
listClass: 'bsmList-custom',
|
||||
|
@ -5,17 +5,23 @@
|
||||
{% block title %}{% trans "Tags" %} – {{ block.super }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Tags" %}</h1>
|
||||
<h1>{% trans "Tags" %}
|
||||
<small class="pull-right">
|
||||
<a href="javascript:window.history.back()" class="btn btn-mini">
|
||||
<i class="icon-chevron-left"></i><span class="optional-small"> {% trans "Back to overview" %}</span>
|
||||
</a>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="tag-edit">Name:</label>
|
||||
<label for="tag-edit">{% trans 'Enter new tag name' %}:</label>
|
||||
<input id="tag-edit" name="new">
|
||||
<a href="#" id="tag-save" class="btn btn-primary">{% trans 'Save' %}</a>
|
||||
</div>
|
||||
|
||||
<table id="tag-table" class="table table-striped table-bordered">
|
||||
<tr>
|
||||
<th>{% trans "Tag name" %}</th>
|
||||
<th>{% trans "Tag" %}</th>
|
||||
<th class="mini_width">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
<tr id="dummy-tag" class="tag-row" style="display:none">
|
||||
@ -47,6 +53,7 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p>{% trans "You can use these tags for agenda items, motions and elections." %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
|
@ -131,7 +131,7 @@ HAYSTACK_CONNECTIONS = {
|
||||
}
|
||||
|
||||
# Haystack updates search index after each save/delete action by apps
|
||||
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
|
||||
HAYSTACK_SIGNAL_PROCESSOR = 'openslides.utils.haystack_processor.OpenSlidesProcessor'
|
||||
|
||||
# Adds all automaticly collected plugins
|
||||
INSTALLED_PLUGINS = collect_plugins()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-10-16 23:27+0200\n"
|
||||
"POT-Creation-Date: 2015-01-11 19:03+0100\n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
File diff suppressed because it is too large
Load Diff
30352
openslides/mediafile/static/js/pdf.worker.js
vendored
30352
openslides/mediafile/static/js/pdf.worker.js
vendored
File diff suppressed because it is too large
Load Diff
@ -72,7 +72,7 @@ def setup_motion_config(sender, **kwargs):
|
||||
|
||||
motion_amendments_prefix = ConfigVariable(
|
||||
name='motion_amendments_prefix',
|
||||
default_value=pgettext('Prefix for amendment', 'A'),
|
||||
default_value=pgettext('Prefix for the identifier for amendments', 'A'),
|
||||
form_field=forms.CharField(
|
||||
required=False,
|
||||
label=ugettext_lazy('Prefix for the identifier for amendments')))
|
||||
|
@ -30,7 +30,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if motion.is_amendment %}
|
||||
(Amendment of <a href="{{ motion.parent|absolute_url }}">{{ motion.parent.identifier|default:motion.parent }}</a>)
|
||||
({% trans "Amendment of" %} <a href="{{ motion.parent|absolute_url }}">{{ motion.parent.identifier|default:motion.parent }}</a>)
|
||||
{% endif %}
|
||||
</small>
|
||||
<small class="pull-right">
|
||||
@ -71,8 +71,6 @@
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
{{ motion.tags.all|join:', ' }}
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span8">
|
||||
{# TODO: show only for workflow with versioning #}
|
||||
@ -256,7 +254,9 @@
|
||||
{% block meta_box_poll_extras %}{% endblock %}
|
||||
</p>
|
||||
{% empty %}
|
||||
{% if not allowed_actions.create_poll %}
|
||||
–
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if allowed_actions.create_poll %}
|
||||
<p>
|
||||
@ -268,13 +268,19 @@
|
||||
{% endwith %}
|
||||
|
||||
<!-- Category -->
|
||||
<h5>{% trans "Category" %}:</h5>
|
||||
{% if motion.category %}
|
||||
<h5>{% trans "Category" %}:</h5>
|
||||
{{ motion.category }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
|
||||
<!-- Tags -->
|
||||
{% for tag in motion.tags.all %}
|
||||
{% if forloop.first %}
|
||||
<h5>{% trans "Tags" %}:</h5>
|
||||
{% endif %}
|
||||
<span class="optional label">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Creation Time -->
|
||||
<h5>
|
||||
{% if motion.versions.count > 1 %}
|
||||
|
@ -38,7 +38,7 @@
|
||||
{% endif %}
|
||||
{% if perms.core.can_manage_tags %}
|
||||
<a href="{% url 'core_tag_list' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Manage tags' %}">
|
||||
<i class="icon-th"></i>
|
||||
<i class="icon-tags"></i>
|
||||
<span class="optional-small"> {% trans 'Tags' %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -72,15 +72,19 @@
|
||||
</thead>
|
||||
{% for motion in motion_list %}
|
||||
<tr class="{% if motion.is_active_slide %}activeline{% endif %}">
|
||||
<td class="nobr">{{ motion.identifier|default:'' }}</td>
|
||||
<td>
|
||||
<a href="{{ motion|absolute_url }}">{{ motion.title }}</a>
|
||||
<td class="nobr">{{ motion.identifier|default:'' }}
|
||||
{% if motion.is_amendment %}
|
||||
<a class="label label-success" data-original-title="Amendment" rel="tooltip">
|
||||
<a class="badge badge-success" data-original-title="{% trans 'Amendment' %}" rel="tooltip">
|
||||
{{ 'motion_amendments_prefix'|get_config }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ motion|absolute_url }}">{{ motion.title }}</a>
|
||||
{% for tag in motion.tags.all %}
|
||||
<span class="optional label">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="optional">{% if motion.category %}{{ motion.category }}{% else %}–{% endif %}</td>
|
||||
<td class="optional-small"><span class="label label-info">{% trans motion.state.name %}</span></td>
|
||||
<td class="optional">
|
||||
|
@ -78,7 +78,7 @@
|
||||
<small>
|
||||
{% trans "Motion" %} {{ motion.identifier|default:'' }}
|
||||
{% if motion.is_amendment %}
|
||||
(Amendment of {{ motion.parent.identifier|default:motion.parent }})
|
||||
({% trans "Amendment of" %} {{ motion.parent.identifier|default:motion.parent }})
|
||||
{% endif %}
|
||||
{% if motion.get_active_version.version_number > 1 %} | {% trans 'Version' %} {{ motion.active_version.version_number }}{% endif %}
|
||||
</small>
|
||||
|
@ -5,3 +5,4 @@
|
||||
{{ object.submitters }}
|
||||
{{ object.supporters }}
|
||||
{{ object.category }}
|
||||
{{ object.tags.all }}
|
||||
|
@ -59,11 +59,16 @@ class SlideMixin(object):
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""
|
||||
Updates the projector, if the object is on the projector and is deleted.
|
||||
Updates the projector if the object is on the projector and is deleted.
|
||||
"""
|
||||
from openslides.projector.api import update_projector
|
||||
# Checking active slide has to be done before calling super().delete()
|
||||
# because super().delete() deletes the object and than we have no
|
||||
# access to the former existing primary key any more. But updating
|
||||
# projector has to be done after deleting the object of course.
|
||||
update_required = self.is_active_slide()
|
||||
value = super(SlideMixin, self).delete(*args, **kwargs)
|
||||
if self.is_active_slide():
|
||||
if update_required:
|
||||
update_projector()
|
||||
return value
|
||||
|
||||
|
@ -75,10 +75,11 @@ class UserUpdateForm(UserCreateForm):
|
||||
|
||||
class GroupForm(CssClassMixin, forms.ModelForm):
|
||||
permissions = LocalizedModelMultipleChoiceField(
|
||||
queryset=Permission.objects.all(), label=ugettext_lazy('Permissions'),
|
||||
required=False)
|
||||
queryset=Permission.objects.all(), label=ugettext_lazy('Permissions'), required=False,
|
||||
widget=forms.SelectMultiple(attrs={'class': 'dont_use_bsmselect'}))
|
||||
users = forms.ModelMultipleChoiceField(
|
||||
queryset=User.objects.all(), label=ugettext_lazy('Users'), required=False)
|
||||
queryset=User.objects.all(), label=ugettext_lazy('Participants'), required=False,
|
||||
widget=forms.SelectMultiple(attrs={'class': 'dont_use_bsmselect'}))
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
|
23
openslides/utils/haystack_processor.py
Normal file
23
openslides/utils/haystack_processor.py
Normal file
@ -0,0 +1,23 @@
|
||||
from django.db import models
|
||||
from haystack.signals import RealtimeSignalProcessor
|
||||
|
||||
|
||||
class OpenSlidesProcessor(RealtimeSignalProcessor):
|
||||
def setup(self):
|
||||
# Naive (listen to all model saves).
|
||||
super(OpenSlidesProcessor, self).setup()
|
||||
models.signals.m2m_changed.connect(self.handle_many_to_many)
|
||||
|
||||
def teardown(self):
|
||||
# Naive (listen to all model saves).
|
||||
super(OpenSlidesProcessor, self).teardown()
|
||||
models.signals.m2m_changed.disconnect(self.handle_many_to_many)
|
||||
|
||||
def handle_many_to_many(self, sender, instance, **kwargs):
|
||||
"""
|
||||
Given an individual model instance, determine which backends the
|
||||
update should be sent to & update the object on those backends.
|
||||
"""
|
||||
model_class = type(instance)
|
||||
if kwargs['action'] == 'post_add' or kwargs['action'] == 'post_clear' or kwargs['action'] == 'post_remove':
|
||||
self.handle_save(model_class, instance, **kwargs)
|
Loading…
Reference in New Issue
Block a user