From d5f718fbba47e9f804a7704b0a01b0dc89082daf Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sun, 1 Mar 2020 17:16:38 +0100 Subject: [PATCH 01/66] start activity view and urls --- apps/activity/urls.py | 15 ++++++++++++ apps/activity/views.py | 33 +++++++++++++++++++++++++++ note_kfet/urls.py | 3 ++- templates/activity/activity_list.html | 26 +++++++++++++++++++++ templates/base.html | 2 +- 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 apps/activity/urls.py create mode 100644 apps/activity/views.py create mode 100644 templates/activity/activity_list.html diff --git a/apps/activity/urls.py b/apps/activity/urls.py new file mode 100644 index 00000000..125ac7af --- /dev/null +++ b/apps/activity/urls.py @@ -0,0 +1,15 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.urls import path + +from . import views + +app_name = 'activity' +urlpatterns = [ + path('', views.ActivityListView.as_view(),name='activity_list'), + path('/', views.ActivityDetailView.as_view(),name='activity_detail'), + path('/entry/', views.ActivityEntryView.as_view(),name='activity_entry'), + path('/update/', views.ActivityUpdateView.as_view(),name='activity_update'), + path('new/', views.ActivityCreateView.as_view(),name='activity_create'), +] diff --git a/apps/activity/views.py b/apps/activity/views.py new file mode 100644 index 00000000..7ea61d2e --- /dev/null +++ b/apps/activity/views.py @@ -0,0 +1,33 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from dal import autocomplete +from django.contrib.auth.mixins import LoginRequiredMixin +from django.db.models import Q +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django.views.generic import CreateView, DetailView,\ + ListView, UpdateView, TemplateView +from django_tables2.views import SingleTableView + +from .models import Activity, ActivityType + +class ActivityCreateView(CreateView): + model = Activity + template_name = 'activity_create.html' + +class ActivityListView(SingleTableView): + model = Activity + template_name = 'activity_list.html' + +class ActivityDetailView(DetailView): + model = Activity + template_name = 'activty_detail.html' + +class ActivityUpdateView(UpdateView): + model = Activity + template_name = 'activity_update.html' + +class ActivityEntryView(TemplateView): + pass + diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 303e229a..79b6dba6 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -11,10 +11,11 @@ urlpatterns = [ # Include project routers path('note/', include('note.urls')), + path('accounts/', include('member.urls')), + path('activity/', include('activity.urls')), # Include Django Contrib and Core routers path('i18n/', include('django.conf.urls.i18n')), - path('accounts/', include('member.urls')), path('accounts/', include('django.contrib.auth.urls')), path('admin/doc/', include('django.contrib.admindocs.urls')), path('admin/', admin.site.urls), diff --git a/templates/activity/activity_list.html b/templates/activity/activity_list.html new file mode 100644 index 00000000..7f682d4c --- /dev/null +++ b/templates/activity/activity_list.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% load render_table from django_tables2 %} +{% load i18n crispy_forms_tags%} +{% block content %} + +{% trans 'New activity' %} + +
+
+ {% render_table table %} +
+
+ +{% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/templates/base.html b/templates/base.html index 6814bedf..c0d4c43b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -73,7 +73,7 @@ SPDX-License-Identifier: GPL-3.0-or-later Clubs {% endif %} + {% if "auth.user"|model_list|length >= 2 %} + + {% endif %} {% if "member.club"|not_empty_model_list %}
  • ' - ); - - var message = this.options.get('translations').get(params.message); - - $message.append( - escapeMarkup( - message(params.args) - ) - ); - - $message[0].className += ' select2-results__message'; - - this.$results.append($message); - }; - - Results.prototype.hideMessages = function () { - this.$results.find('.select2-results__message').remove(); - }; - - Results.prototype.append = function (data) { - this.hideLoading(); - - var $options = []; - - if (data.results == null || data.results.length === 0) { - if (this.$results.children().length === 0) { - this.trigger('results:message', { - message: 'noResults' - }); - } - - return; - } - - data.results = this.sort(data.results); - - for (var d = 0; d < data.results.length; d++) { - var item = data.results[d]; - - var $option = this.option(item); - - $options.push($option); - } - - this.$results.append($options); - }; - - Results.prototype.position = function ($results, $dropdown) { - var $resultsContainer = $dropdown.find('.select2-results'); - $resultsContainer.append($results); - }; - - Results.prototype.sort = function (data) { - var sorter = this.options.get('sorter'); - - return sorter(data); - }; - - Results.prototype.highlightFirstItem = function () { - var $options = this.$results - .find('.select2-results__option[aria-selected]'); - - var $selected = $options.filter('[aria-selected=true]'); - - // Check if there are any selected options - if ($selected.length > 0) { - // If there are selected options, highlight the first - $selected.first().trigger('mouseenter'); - } else { - // If there are no selected options, highlight the first option - // in the dropdown - $options.first().trigger('mouseenter'); - } - - this.ensureHighlightVisible(); - }; - - Results.prototype.setClasses = function () { - var self = this; - - this.data.current(function (selected) { - var selectedIds = $.map(selected, function (s) { - return s.id.toString(); - }); - - var $options = self.$results - .find('.select2-results__option[aria-selected]'); - - $options.each(function () { - var $option = $(this); - - var item = Utils.GetData(this, 'data'); - - // id needs to be converted to a string when comparing - var id = '' + item.id; - - if ((item.element != null && item.element.selected) || - (item.element == null && $.inArray(id, selectedIds) > -1)) { - $option.attr('aria-selected', 'true'); - } else { - $option.attr('aria-selected', 'false'); - } - }); - - }); - }; - - Results.prototype.showLoading = function (params) { - this.hideLoading(); - - var loadingMore = this.options.get('translations').get('searching'); - - var loading = { - disabled: true, - loading: true, - text: loadingMore(params) - }; - var $loading = this.option(loading); - $loading.className += ' loading-results'; - - this.$results.prepend($loading); - }; - - Results.prototype.hideLoading = function () { - this.$results.find('.loading-results').remove(); - }; - - Results.prototype.option = function (data) { - var option = document.createElement('li'); - option.className = 'select2-results__option'; - - var attrs = { - 'role': 'treeitem', - 'aria-selected': 'false' - }; - - if (data.disabled) { - delete attrs['aria-selected']; - attrs['aria-disabled'] = 'true'; - } - - if (data.id == null) { - delete attrs['aria-selected']; - } - - if (data._resultId != null) { - option.id = data._resultId; - } - - if (data.title) { - option.title = data.title; - } - - if (data.children) { - attrs.role = 'group'; - attrs['aria-label'] = data.text; - delete attrs['aria-selected']; - } - - for (var attr in attrs) { - var val = attrs[attr]; - - option.setAttribute(attr, val); - } - - if (data.children) { - var $option = $(option); - - var label = document.createElement('strong'); - label.className = 'select2-results__group'; - - var $label = $(label); - this.template(data, label); - - var $children = []; - - for (var c = 0; c < data.children.length; c++) { - var child = data.children[c]; - - var $child = this.option(child); - - $children.push($child); - } - - var $childrenContainer = $('
      ', { - 'class': 'select2-results__options select2-results__options--nested' - }); - - $childrenContainer.append($children); - - $option.append(label); - $option.append($childrenContainer); - } else { - this.template(data, option); - } - - Utils.StoreData(option, 'data', data); - - return option; - }; - - Results.prototype.bind = function (container, $container) { - var self = this; - - var id = container.id + '-results'; - - this.$results.attr('id', id); - - container.on('results:all', function (params) { - self.clear(); - self.append(params.data); - - if (container.isOpen()) { - self.setClasses(); - self.highlightFirstItem(); - } - }); - - container.on('results:append', function (params) { - self.append(params.data); - - if (container.isOpen()) { - self.setClasses(); - } - }); - - container.on('query', function (params) { - self.hideMessages(); - self.showLoading(params); - }); - - container.on('select', function () { - if (!container.isOpen()) { - return; - } - - self.setClasses(); - - if (self.options.get('scrollAfterSelect')) { - self.highlightFirstItem(); - } - }); - - container.on('unselect', function () { - if (!container.isOpen()) { - return; - } - - self.setClasses(); - - if (self.options.get('scrollAfterSelect')) { - self.highlightFirstItem(); - } - }); - - container.on('open', function () { - // When the dropdown is open, aria-expended="true" - self.$results.attr('aria-expanded', 'true'); - self.$results.attr('aria-hidden', 'false'); - - self.setClasses(); - self.ensureHighlightVisible(); - }); - - container.on('close', function () { - // When the dropdown is closed, aria-expended="false" - self.$results.attr('aria-expanded', 'false'); - self.$results.attr('aria-hidden', 'true'); - self.$results.removeAttr('aria-activedescendant'); - }); - - container.on('results:toggle', function () { - var $highlighted = self.getHighlightedResults(); - - if ($highlighted.length === 0) { - return; - } - - $highlighted.trigger('mouseup'); - }); - - container.on('results:select', function () { - var $highlighted = self.getHighlightedResults(); - - if ($highlighted.length === 0) { - return; - } - - var data = Utils.GetData($highlighted[0], 'data'); - - if ($highlighted.attr('aria-selected') == 'true') { - self.trigger('close', {}); - } else { - self.trigger('select', { - data: data - }); - } - }); - - container.on('results:previous', function () { - var $highlighted = self.getHighlightedResults(); - - var $options = self.$results.find('[aria-selected]'); - - var currentIndex = $options.index($highlighted); - - // If we are already at the top, don't move further - // If no options, currentIndex will be -1 - if (currentIndex <= 0) { - return; - } - - var nextIndex = currentIndex - 1; - - // If none are highlighted, highlight the first - if ($highlighted.length === 0) { - nextIndex = 0; - } - - var $next = $options.eq(nextIndex); - - $next.trigger('mouseenter'); - - var currentOffset = self.$results.offset().top; - var nextTop = $next.offset().top; - var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset); - - if (nextIndex === 0) { - self.$results.scrollTop(0); - } else if (nextTop - currentOffset < 0) { - self.$results.scrollTop(nextOffset); - } - }); - - container.on('results:next', function () { - var $highlighted = self.getHighlightedResults(); - - var $options = self.$results.find('[aria-selected]'); - - var currentIndex = $options.index($highlighted); - - var nextIndex = currentIndex + 1; - - // If we are at the last option, stay there - if (nextIndex >= $options.length) { - return; - } - - var $next = $options.eq(nextIndex); - - $next.trigger('mouseenter'); - - var currentOffset = self.$results.offset().top + - self.$results.outerHeight(false); - var nextBottom = $next.offset().top + $next.outerHeight(false); - var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset; - - if (nextIndex === 0) { - self.$results.scrollTop(0); - } else if (nextBottom > currentOffset) { - self.$results.scrollTop(nextOffset); - } - }); - - container.on('results:focus', function (params) { - params.element.addClass('select2-results__option--highlighted'); - }); - - container.on('results:message', function (params) { - self.displayMessage(params); - }); - - if ($.fn.mousewheel) { - this.$results.on('mousewheel', function (e) { - var top = self.$results.scrollTop(); - - var bottom = self.$results.get(0).scrollHeight - top + e.deltaY; - - var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0; - var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height(); - - if (isAtTop) { - self.$results.scrollTop(0); - - e.preventDefault(); - e.stopPropagation(); - } else if (isAtBottom) { - self.$results.scrollTop( - self.$results.get(0).scrollHeight - self.$results.height() - ); - - e.preventDefault(); - e.stopPropagation(); - } - }); - } - - this.$results.on('mouseup', '.select2-results__option[aria-selected]', - function (evt) { - var $this = $(this); - - var data = Utils.GetData(this, 'data'); - - if ($this.attr('aria-selected') === 'true') { - if (self.options.get('multiple')) { - self.trigger('unselect', { - originalEvent: evt, - data: data - }); - } else { - self.trigger('close', {}); - } - - return; - } - - self.trigger('select', { - originalEvent: evt, - data: data - }); - }); - - this.$results.on('mouseenter', '.select2-results__option[aria-selected]', - function (evt) { - var data = Utils.GetData(this, 'data'); - - self.getHighlightedResults() - .removeClass('select2-results__option--highlighted'); - - self.trigger('results:focus', { - data: data, - element: $(this) - }); - }); - }; - - Results.prototype.getHighlightedResults = function () { - var $highlighted = this.$results - .find('.select2-results__option--highlighted'); - - return $highlighted; - }; - - Results.prototype.destroy = function () { - this.$results.remove(); - }; - - Results.prototype.ensureHighlightVisible = function () { - var $highlighted = this.getHighlightedResults(); - - if ($highlighted.length === 0) { - return; - } - - var $options = this.$results.find('[aria-selected]'); - - var currentIndex = $options.index($highlighted); - - var currentOffset = this.$results.offset().top; - var nextTop = $highlighted.offset().top; - var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset); - - var offsetDelta = nextTop - currentOffset; - nextOffset -= $highlighted.outerHeight(false) * 2; - - if (currentIndex <= 2) { - this.$results.scrollTop(0); - } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) { - this.$results.scrollTop(nextOffset); - } - }; - - Results.prototype.template = function (result, container) { - var template = this.options.get('templateResult'); - var escapeMarkup = this.options.get('escapeMarkup'); - - var content = template(result, container); - - if (content == null) { - container.style.display = 'none'; - } else if (typeof content === 'string') { - container.innerHTML = escapeMarkup(content); - } else { - $(container).append(content); - } - }; - - return Results; -}); diff --git a/static/vendor/select2/src/js/select2/selection/allowClear.js b/static/vendor/select2/src/js/select2/selection/allowClear.js deleted file mode 100644 index 0de5b9bb..00000000 --- a/static/vendor/select2/src/js/select2/selection/allowClear.js +++ /dev/null @@ -1,113 +0,0 @@ -define([ - 'jquery', - '../keys', - '../utils' -], function ($, KEYS, Utils) { - function AllowClear () { } - - AllowClear.prototype.bind = function (decorated, container, $container) { - var self = this; - - decorated.call(this, container, $container); - - if (this.placeholder == null) { - if (this.options.get('debug') && window.console && console.error) { - console.error( - 'Select2: The `allowClear` option should be used in combination ' + - 'with the `placeholder` option.' - ); - } - } - - this.$selection.on('mousedown', '.select2-selection__clear', - function (evt) { - self._handleClear(evt); - }); - - container.on('keypress', function (evt) { - self._handleKeyboardClear(evt, container); - }); - }; - - AllowClear.prototype._handleClear = function (_, evt) { - // Ignore the event if it is disabled - if (this.options.get('disabled')) { - return; - } - - var $clear = this.$selection.find('.select2-selection__clear'); - - // Ignore the event if nothing has been selected - if ($clear.length === 0) { - return; - } - - evt.stopPropagation(); - - var data = Utils.GetData($clear[0], 'data'); - - var previousVal = this.$element.val(); - this.$element.val(this.placeholder.id); - - var unselectData = { - data: data - }; - this.trigger('clear', unselectData); - if (unselectData.prevented) { - this.$element.val(previousVal); - return; - } - - for (var d = 0; d < data.length; d++) { - unselectData = { - data: data[d] - }; - - // Trigger the `unselect` event, so people can prevent it from being - // cleared. - this.trigger('unselect', unselectData); - - // If the event was prevented, don't clear it out. - if (unselectData.prevented) { - this.$element.val(previousVal); - return; - } - } - - this.$element.trigger('change'); - - this.trigger('toggle', {}); - }; - - AllowClear.prototype._handleKeyboardClear = function (_, evt, container) { - if (container.isOpen()) { - return; - } - - if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) { - this._handleClear(evt); - } - }; - - AllowClear.prototype.update = function (decorated, data) { - decorated.call(this, data); - - if (this.$selection.find('.select2-selection__placeholder').length > 0 || - data.length === 0) { - return; - } - - var removeAll = this.options.get('translations').get('removeAllItems'); - - var $remove = $( - '' + - '×' + - '' - ); - Utils.StoreData($remove[0], 'data', data); - - this.$selection.find('.select2-selection__rendered').prepend($remove); - }; - - return AllowClear; -}); diff --git a/static/vendor/select2/src/js/select2/selection/base.js b/static/vendor/select2/src/js/select2/selection/base.js deleted file mode 100644 index 6d373bd7..00000000 --- a/static/vendor/select2/src/js/select2/selection/base.js +++ /dev/null @@ -1,160 +0,0 @@ -define([ - 'jquery', - '../utils', - '../keys' -], function ($, Utils, KEYS) { - function BaseSelection ($element, options) { - this.$element = $element; - this.options = options; - - BaseSelection.__super__.constructor.call(this); - } - - Utils.Extend(BaseSelection, Utils.Observable); - - BaseSelection.prototype.render = function () { - var $selection = $( - '' - ); - - this._tabindex = 0; - - if (Utils.GetData(this.$element[0], 'old-tabindex') != null) { - this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex'); - } else if (this.$element.attr('tabindex') != null) { - this._tabindex = this.$element.attr('tabindex'); - } - - $selection.attr('title', this.$element.attr('title')); - $selection.attr('tabindex', this._tabindex); - - this.$selection = $selection; - - return $selection; - }; - - BaseSelection.prototype.bind = function (container, $container) { - var self = this; - - var id = container.id + '-container'; - var resultsId = container.id + '-results'; - - this.container = container; - - this.$selection.on('focus', function (evt) { - self.trigger('focus', evt); - }); - - this.$selection.on('blur', function (evt) { - self._handleBlur(evt); - }); - - this.$selection.on('keydown', function (evt) { - self.trigger('keypress', evt); - - if (evt.which === KEYS.SPACE) { - evt.preventDefault(); - } - }); - - container.on('results:focus', function (params) { - self.$selection.attr('aria-activedescendant', params.data._resultId); - }); - - container.on('selection:update', function (params) { - self.update(params.data); - }); - - container.on('open', function () { - // When the dropdown is open, aria-expanded="true" - self.$selection.attr('aria-expanded', 'true'); - self.$selection.attr('aria-owns', resultsId); - - self._attachCloseHandler(container); - }); - - container.on('close', function () { - // When the dropdown is closed, aria-expanded="false" - self.$selection.attr('aria-expanded', 'false'); - self.$selection.removeAttr('aria-activedescendant'); - self.$selection.removeAttr('aria-owns'); - - window.setTimeout(function () { - self.$selection.focus(); - }, 0); - - self._detachCloseHandler(container); - }); - - container.on('enable', function () { - self.$selection.attr('tabindex', self._tabindex); - }); - - container.on('disable', function () { - self.$selection.attr('tabindex', '-1'); - }); - }; - - BaseSelection.prototype._handleBlur = function (evt) { - var self = this; - - // This needs to be delayed as the active element is the body when the tab - // key is pressed, possibly along with others. - window.setTimeout(function () { - // Don't trigger `blur` if the focus is still in the selection - if ( - (document.activeElement == self.$selection[0]) || - ($.contains(self.$selection[0], document.activeElement)) - ) { - return; - } - - self.trigger('blur', evt); - }, 1); - }; - - BaseSelection.prototype._attachCloseHandler = function (container) { - var self = this; - - $(document.body).on('mousedown.select2.' + container.id, function (e) { - var $target = $(e.target); - - var $select = $target.closest('.select2'); - - var $all = $('.select2.select2-container--open'); - - $all.each(function () { - var $this = $(this); - - if (this == $select[0]) { - return; - } - - var $element = Utils.GetData(this, 'element'); - - $element.select2('close'); - }); - }); - }; - - BaseSelection.prototype._detachCloseHandler = function (container) { - $(document.body).off('mousedown.select2.' + container.id); - }; - - BaseSelection.prototype.position = function ($selection, $container) { - var $selectionContainer = $container.find('.selection'); - $selectionContainer.append($selection); - }; - - BaseSelection.prototype.destroy = function () { - this._detachCloseHandler(this.container); - }; - - BaseSelection.prototype.update = function (data) { - throw new Error('The `update` method must be defined in child classes.'); - }; - - return BaseSelection; -}); diff --git a/static/vendor/select2/src/js/select2/selection/clickMask.js b/static/vendor/select2/src/js/select2/selection/clickMask.js deleted file mode 100644 index 2b4ac307..00000000 --- a/static/vendor/select2/src/js/select2/selection/clickMask.js +++ /dev/null @@ -1,29 +0,0 @@ -define([ - 'jquery' -], function ($) { - function ClickMask () { } - - ClickMask.prototype.bind = function (decorate, $container, container) { - var self = this; - - decorate.call(this, $container, container); - - this.$mask = $( - '
      ' - ); - - this.$mask.on('mousedown touchstart click', function () { - self.trigger('close', {}); - }); - }; - - ClickMask.prototype._attachCloseHandler = function (decorate, container) { - $(document.body).append(this.$mask); - }; - - ClickMask.prototype._detachCloseHandler = function (deocrate, container) { - this.$mask.detach(); - }; - - return ClickMask; -}); diff --git a/static/vendor/select2/src/js/select2/selection/eventRelay.js b/static/vendor/select2/src/js/select2/selection/eventRelay.js deleted file mode 100644 index 0f59822b..00000000 --- a/static/vendor/select2/src/js/select2/selection/eventRelay.js +++ /dev/null @@ -1,48 +0,0 @@ -define([ - 'jquery' -], function ($) { - function EventRelay () { } - - EventRelay.prototype.bind = function (decorated, container, $container) { - var self = this; - var relayEvents = [ - 'open', 'opening', - 'close', 'closing', - 'select', 'selecting', - 'unselect', 'unselecting', - 'clear', 'clearing' - ]; - - var preventableEvents = [ - 'opening', 'closing', 'selecting', 'unselecting', 'clearing' - ]; - - decorated.call(this, container, $container); - - container.on('*', function (name, params) { - // Ignore events that should not be relayed - if ($.inArray(name, relayEvents) === -1) { - return; - } - - // The parameters should always be an object - params = params || {}; - - // Generate the jQuery event for the Select2 event - var evt = $.Event('select2:' + name, { - params: params - }); - - self.$element.trigger(evt); - - // Only handle preventable events if it was one - if ($.inArray(name, preventableEvents) === -1) { - return; - } - - params.prevented = evt.isDefaultPrevented(); - }); - }; - - return EventRelay; -}); diff --git a/static/vendor/select2/src/js/select2/selection/multiple.js b/static/vendor/select2/src/js/select2/selection/multiple.js deleted file mode 100644 index 6f106788..00000000 --- a/static/vendor/select2/src/js/select2/selection/multiple.js +++ /dev/null @@ -1,111 +0,0 @@ -define([ - 'jquery', - './base', - '../utils' -], function ($, BaseSelection, Utils) { - function MultipleSelection ($element, options) { - MultipleSelection.__super__.constructor.apply(this, arguments); - } - - Utils.Extend(MultipleSelection, BaseSelection); - - MultipleSelection.prototype.render = function () { - var $selection = MultipleSelection.__super__.render.call(this); - - $selection.addClass('select2-selection--multiple'); - - $selection.html( - '
        ' - ); - - return $selection; - }; - - MultipleSelection.prototype.bind = function (container, $container) { - var self = this; - - MultipleSelection.__super__.bind.apply(this, arguments); - - this.$selection.on('click', function (evt) { - self.trigger('toggle', { - originalEvent: evt - }); - }); - - this.$selection.on( - 'click', - '.select2-selection__choice__remove', - function (evt) { - // Ignore the event if it is disabled - if (self.options.get('disabled')) { - return; - } - - var $remove = $(this); - var $selection = $remove.parent(); - - var data = Utils.GetData($selection[0], 'data'); - - self.trigger('unselect', { - originalEvent: evt, - data: data - }); - } - ); - }; - - MultipleSelection.prototype.clear = function () { - var $rendered = this.$selection.find('.select2-selection__rendered'); - $rendered.empty(); - $rendered.removeAttr('title'); - }; - - MultipleSelection.prototype.display = function (data, container) { - var template = this.options.get('templateSelection'); - var escapeMarkup = this.options.get('escapeMarkup'); - - return escapeMarkup(template(data, container)); - }; - - MultipleSelection.prototype.selectionContainer = function () { - var $container = $( - '
      • ' + - '' + - '×' + - '' + - '
      • ' - ); - - return $container; - }; - - MultipleSelection.prototype.update = function (data) { - this.clear(); - - if (data.length === 0) { - return; - } - - var $selections = []; - - for (var d = 0; d < data.length; d++) { - var selection = data[d]; - - var $selection = this.selectionContainer(); - var formatted = this.display(selection, $selection); - - $selection.append(formatted); - $selection.attr('title', selection.title || selection.text); - - Utils.StoreData($selection[0], 'data', selection); - - $selections.push($selection); - } - - var $rendered = this.$selection.find('.select2-selection__rendered'); - - Utils.appendMany($rendered, $selections); - }; - - return MultipleSelection; -}); diff --git a/static/vendor/select2/src/js/select2/selection/placeholder.js b/static/vendor/select2/src/js/select2/selection/placeholder.js deleted file mode 100644 index 87fbedac..00000000 --- a/static/vendor/select2/src/js/select2/selection/placeholder.js +++ /dev/null @@ -1,49 +0,0 @@ -define([ - '../utils' -], function (Utils) { - function Placeholder (decorated, $element, options) { - this.placeholder = this.normalizePlaceholder(options.get('placeholder')); - - decorated.call(this, $element, options); - } - - Placeholder.prototype.normalizePlaceholder = function (_, placeholder) { - if (typeof placeholder === 'string') { - placeholder = { - id: '', - text: placeholder - }; - } - - return placeholder; - }; - - Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { - var $placeholder = this.selectionContainer(); - - $placeholder.html(this.display(placeholder)); - $placeholder.addClass('select2-selection__placeholder') - .removeClass('select2-selection__choice'); - - return $placeholder; - }; - - Placeholder.prototype.update = function (decorated, data) { - var singlePlaceholder = ( - data.length == 1 && data[0].id != this.placeholder.id - ); - var multipleSelections = data.length > 1; - - if (multipleSelections || singlePlaceholder) { - return decorated.call(this, data); - } - - this.clear(); - - var $placeholder = this.createPlaceholder(this.placeholder); - - this.$selection.find('.select2-selection__rendered').append($placeholder); - }; - - return Placeholder; -}); diff --git a/static/vendor/select2/src/js/select2/selection/search.js b/static/vendor/select2/src/js/select2/selection/search.js deleted file mode 100644 index 3ff96f94..00000000 --- a/static/vendor/select2/src/js/select2/selection/search.js +++ /dev/null @@ -1,228 +0,0 @@ -define([ - 'jquery', - '../utils', - '../keys' -], function ($, Utils, KEYS) { - function Search (decorated, $element, options) { - decorated.call(this, $element, options); - } - - Search.prototype.render = function (decorated) { - var $search = $( - '' - ); - - this.$searchContainer = $search; - this.$search = $search.find('input'); - - var $rendered = decorated.call(this); - - this._transferTabIndex(); - - return $rendered; - }; - - Search.prototype.bind = function (decorated, container, $container) { - var self = this; - - decorated.call(this, container, $container); - - container.on('open', function () { - self.$search.trigger('focus'); - }); - - container.on('close', function () { - self.$search.val(''); - self.$search.removeAttr('aria-activedescendant'); - self.$search.trigger('focus'); - }); - - container.on('enable', function () { - self.$search.prop('disabled', false); - - self._transferTabIndex(); - }); - - container.on('disable', function () { - self.$search.prop('disabled', true); - }); - - container.on('focus', function (evt) { - self.$search.trigger('focus'); - }); - - container.on('results:focus', function (params) { - self.$search.attr('aria-activedescendant', params.id); - }); - - this.$selection.on('focusin', '.select2-search--inline', function (evt) { - self.trigger('focus', evt); - }); - - this.$selection.on('focusout', '.select2-search--inline', function (evt) { - self._handleBlur(evt); - }); - - this.$selection.on('keydown', '.select2-search--inline', function (evt) { - evt.stopPropagation(); - - self.trigger('keypress', evt); - - self._keyUpPrevented = evt.isDefaultPrevented(); - - var key = evt.which; - - if (key === KEYS.BACKSPACE && self.$search.val() === '') { - var $previousChoice = self.$searchContainer - .prev('.select2-selection__choice'); - - if ($previousChoice.length > 0) { - var item = Utils.GetData($previousChoice[0], 'data'); - - self.searchRemoveChoice(item); - - evt.preventDefault(); - } - } - }); - - // Try to detect the IE version should the `documentMode` property that - // is stored on the document. This is only implemented in IE and is - // slightly cleaner than doing a user agent check. - // This property is not available in Edge, but Edge also doesn't have - // this bug. - var msie = document.documentMode; - var disableInputEvents = msie && msie <= 11; - - // Workaround for browsers which do not support the `input` event - // This will prevent double-triggering of events for browsers which support - // both the `keyup` and `input` events. - this.$selection.on( - 'input.searchcheck', - '.select2-search--inline', - function (evt) { - // IE will trigger the `input` event when a placeholder is used on a - // search box. To get around this issue, we are forced to ignore all - // `input` events in IE and keep using `keyup`. - if (disableInputEvents) { - self.$selection.off('input.search input.searchcheck'); - return; - } - - // Unbind the duplicated `keyup` event - self.$selection.off('keyup.search'); - } - ); - - this.$selection.on( - 'keyup.search input.search', - '.select2-search--inline', - function (evt) { - // IE will trigger the `input` event when a placeholder is used on a - // search box. To get around this issue, we are forced to ignore all - // `input` events in IE and keep using `keyup`. - if (disableInputEvents && evt.type === 'input') { - self.$selection.off('input.search input.searchcheck'); - return; - } - - var key = evt.which; - - // We can freely ignore events from modifier keys - if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { - return; - } - - // Tabbing will be handled during the `keydown` phase - if (key == KEYS.TAB) { - return; - } - - self.handleSearch(evt); - } - ); - }; - - /** - * This method will transfer the tabindex attribute from the rendered - * selection to the search box. This allows for the search box to be used as - * the primary focus instead of the selection container. - * - * @private - */ - Search.prototype._transferTabIndex = function (decorated) { - this.$search.attr('tabindex', this.$selection.attr('tabindex')); - this.$selection.attr('tabindex', '-1'); - }; - - Search.prototype.createPlaceholder = function (decorated, placeholder) { - this.$search.attr('placeholder', placeholder.text); - }; - - Search.prototype.update = function (decorated, data) { - var searchHadFocus = this.$search[0] == document.activeElement; - - this.$search.attr('placeholder', ''); - - decorated.call(this, data); - - this.$selection.find('.select2-selection__rendered') - .append(this.$searchContainer); - - this.resizeSearch(); - if (searchHadFocus) { - var isTagInput = this.$element.find('[data-select2-tag]').length; - if (isTagInput) { - // fix IE11 bug where tag input lost focus - this.$element.focus(); - } else { - this.$search.focus(); - } - } - }; - - Search.prototype.handleSearch = function () { - this.resizeSearch(); - - if (!this._keyUpPrevented) { - var input = this.$search.val(); - - this.trigger('query', { - term: input - }); - } - - this._keyUpPrevented = false; - }; - - Search.prototype.searchRemoveChoice = function (decorated, item) { - this.trigger('unselect', { - data: item - }); - - this.$search.val(item.text); - this.handleSearch(); - }; - - Search.prototype.resizeSearch = function () { - this.$search.css('width', '25px'); - - var width = ''; - - if (this.$search.attr('placeholder') !== '') { - width = this.$selection.find('.select2-selection__rendered').innerWidth(); - } else { - var minimumWidth = this.$search.val().length + 1; - - width = (minimumWidth * 0.75) + 'em'; - } - - this.$search.css('width', width); - }; - - return Search; -}); diff --git a/static/vendor/select2/src/js/select2/selection/single.js b/static/vendor/select2/src/js/select2/selection/single.js deleted file mode 100644 index e9e99aa4..00000000 --- a/static/vendor/select2/src/js/select2/selection/single.js +++ /dev/null @@ -1,100 +0,0 @@ -define([ - 'jquery', - './base', - '../utils', - '../keys' -], function ($, BaseSelection, Utils, KEYS) { - function SingleSelection () { - SingleSelection.__super__.constructor.apply(this, arguments); - } - - Utils.Extend(SingleSelection, BaseSelection); - - SingleSelection.prototype.render = function () { - var $selection = SingleSelection.__super__.render.call(this); - - $selection.addClass('select2-selection--single'); - - $selection.html( - '' + - '' + - '' + - '' - ); - - return $selection; - }; - - SingleSelection.prototype.bind = function (container, $container) { - var self = this; - - SingleSelection.__super__.bind.apply(this, arguments); - - var id = container.id + '-container'; - - this.$selection.find('.select2-selection__rendered') - .attr('id', id) - .attr('role', 'textbox') - .attr('aria-readonly', 'true'); - this.$selection.attr('aria-labelledby', id); - - this.$selection.on('mousedown', function (evt) { - // Only respond to left clicks - if (evt.which !== 1) { - return; - } - - self.trigger('toggle', { - originalEvent: evt - }); - }); - - this.$selection.on('focus', function (evt) { - // User focuses on the container - }); - - this.$selection.on('blur', function (evt) { - // User exits the container - }); - - container.on('focus', function (evt) { - if (!container.isOpen()) { - self.$selection.focus(); - } - }); - }; - - SingleSelection.prototype.clear = function () { - var $rendered = this.$selection.find('.select2-selection__rendered'); - $rendered.empty(); - $rendered.removeAttr('title'); // clear tooltip on empty - }; - - SingleSelection.prototype.display = function (data, container) { - var template = this.options.get('templateSelection'); - var escapeMarkup = this.options.get('escapeMarkup'); - - return escapeMarkup(template(data, container)); - }; - - SingleSelection.prototype.selectionContainer = function () { - return $(''); - }; - - SingleSelection.prototype.update = function (data) { - if (data.length === 0) { - this.clear(); - return; - } - - var selection = data[0]; - - var $rendered = this.$selection.find('.select2-selection__rendered'); - var formatted = this.display(selection, $rendered); - - $rendered.empty().append(formatted); - $rendered.attr('title', selection.title || selection.text); - }; - - return SingleSelection; -}); diff --git a/static/vendor/select2/src/js/select2/selection/stopPropagation.js b/static/vendor/select2/src/js/select2/selection/stopPropagation.js deleted file mode 100644 index 382f1e72..00000000 --- a/static/vendor/select2/src/js/select2/selection/stopPropagation.js +++ /dev/null @@ -1,38 +0,0 @@ -define([ - -], function () { - function StopPropagation () { } - - StopPropagation.prototype.bind = function (decorated, container, $container) { - decorated.call(this, container, $container); - - var stoppedEvents = [ - 'blur', - 'change', - 'click', - 'dblclick', - 'focus', - 'focusin', - 'focusout', - 'input', - 'keydown', - 'keyup', - 'keypress', - 'mousedown', - 'mouseenter', - 'mouseleave', - 'mousemove', - 'mouseover', - 'mouseup', - 'search', - 'touchend', - 'touchstart' - ]; - - this.$selection.on(stoppedEvents.join(' '), function (evt) { - evt.stopPropagation(); - }); - }; - - return StopPropagation; -}); diff --git a/static/vendor/select2/src/js/select2/translation.js b/static/vendor/select2/src/js/select2/translation.js deleted file mode 100644 index 5394c18b..00000000 --- a/static/vendor/select2/src/js/select2/translation.js +++ /dev/null @@ -1,36 +0,0 @@ -define([ - 'jquery', - 'require' -], function ($, require) { - function Translation (dict) { - this.dict = dict || {}; - } - - Translation.prototype.all = function () { - return this.dict; - }; - - Translation.prototype.get = function (key) { - return this.dict[key]; - }; - - Translation.prototype.extend = function (translation) { - this.dict = $.extend({}, translation.all(), this.dict); - }; - - // Static functions - - Translation._cache = {}; - - Translation.loadPath = function (path) { - if (!(path in Translation._cache)) { - var translations = require(path); - - Translation._cache[path] = translations; - } - - return new Translation(Translation._cache[path]); - }; - - return Translation; -}); diff --git a/static/vendor/select2/src/js/select2/utils.js b/static/vendor/select2/src/js/select2/utils.js deleted file mode 100644 index 53d82f3d..00000000 --- a/static/vendor/select2/src/js/select2/utils.js +++ /dev/null @@ -1,338 +0,0 @@ -define([ - 'jquery' -], function ($) { - var Utils = {}; - - Utils.Extend = function (ChildClass, SuperClass) { - var __hasProp = {}.hasOwnProperty; - - function BaseConstructor () { - this.constructor = ChildClass; - } - - for (var key in SuperClass) { - if (__hasProp.call(SuperClass, key)) { - ChildClass[key] = SuperClass[key]; - } - } - - BaseConstructor.prototype = SuperClass.prototype; - ChildClass.prototype = new BaseConstructor(); - ChildClass.__super__ = SuperClass.prototype; - - return ChildClass; - }; - - function getMethods (theClass) { - var proto = theClass.prototype; - - var methods = []; - - for (var methodName in proto) { - var m = proto[methodName]; - - if (typeof m !== 'function') { - continue; - } - - if (methodName === 'constructor') { - continue; - } - - methods.push(methodName); - } - - return methods; - } - - Utils.Decorate = function (SuperClass, DecoratorClass) { - var decoratedMethods = getMethods(DecoratorClass); - var superMethods = getMethods(SuperClass); - - function DecoratedClass () { - var unshift = Array.prototype.unshift; - - var argCount = DecoratorClass.prototype.constructor.length; - - var calledConstructor = SuperClass.prototype.constructor; - - if (argCount > 0) { - unshift.call(arguments, SuperClass.prototype.constructor); - - calledConstructor = DecoratorClass.prototype.constructor; - } - - calledConstructor.apply(this, arguments); - } - - DecoratorClass.displayName = SuperClass.displayName; - - function ctr () { - this.constructor = DecoratedClass; - } - - DecoratedClass.prototype = new ctr(); - - for (var m = 0; m < superMethods.length; m++) { - var superMethod = superMethods[m]; - - DecoratedClass.prototype[superMethod] = - SuperClass.prototype[superMethod]; - } - - var calledMethod = function (methodName) { - // Stub out the original method if it's not decorating an actual method - var originalMethod = function () {}; - - if (methodName in DecoratedClass.prototype) { - originalMethod = DecoratedClass.prototype[methodName]; - } - - var decoratedMethod = DecoratorClass.prototype[methodName]; - - return function () { - var unshift = Array.prototype.unshift; - - unshift.call(arguments, originalMethod); - - return decoratedMethod.apply(this, arguments); - }; - }; - - for (var d = 0; d < decoratedMethods.length; d++) { - var decoratedMethod = decoratedMethods[d]; - - DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod); - } - - return DecoratedClass; - }; - - var Observable = function () { - this.listeners = {}; - }; - - Observable.prototype.on = function (event, callback) { - this.listeners = this.listeners || {}; - - if (event in this.listeners) { - this.listeners[event].push(callback); - } else { - this.listeners[event] = [callback]; - } - }; - - Observable.prototype.trigger = function (event) { - var slice = Array.prototype.slice; - var params = slice.call(arguments, 1); - - this.listeners = this.listeners || {}; - - // Params should always come in as an array - if (params == null) { - params = []; - } - - // If there are no arguments to the event, use a temporary object - if (params.length === 0) { - params.push({}); - } - - // Set the `_type` of the first object to the event - params[0]._type = event; - - if (event in this.listeners) { - this.invoke(this.listeners[event], slice.call(arguments, 1)); - } - - if ('*' in this.listeners) { - this.invoke(this.listeners['*'], arguments); - } - }; - - Observable.prototype.invoke = function (listeners, params) { - for (var i = 0, len = listeners.length; i < len; i++) { - listeners[i].apply(this, params); - } - }; - - Utils.Observable = Observable; - - Utils.generateChars = function (length) { - var chars = ''; - - for (var i = 0; i < length; i++) { - var randomChar = Math.floor(Math.random() * 36); - chars += randomChar.toString(36); - } - - return chars; - }; - - Utils.bind = function (func, context) { - return function () { - func.apply(context, arguments); - }; - }; - - Utils._convertData = function (data) { - for (var originalKey in data) { - var keys = originalKey.split('-'); - - var dataLevel = data; - - if (keys.length === 1) { - continue; - } - - for (var k = 0; k < keys.length; k++) { - var key = keys[k]; - - // Lowercase the first letter - // By default, dash-separated becomes camelCase - key = key.substring(0, 1).toLowerCase() + key.substring(1); - - if (!(key in dataLevel)) { - dataLevel[key] = {}; - } - - if (k == keys.length - 1) { - dataLevel[key] = data[originalKey]; - } - - dataLevel = dataLevel[key]; - } - - delete data[originalKey]; - } - - return data; - }; - - Utils.hasScroll = function (index, el) { - // Adapted from the function created by @ShadowScripter - // and adapted by @BillBarry on the Stack Exchange Code Review website. - // The original code can be found at - // http://codereview.stackexchange.com/q/13338 - // and was designed to be used with the Sizzle selector engine. - - var $el = $(el); - var overflowX = el.style.overflowX; - var overflowY = el.style.overflowY; - - //Check both x and y declarations - if (overflowX === overflowY && - (overflowY === 'hidden' || overflowY === 'visible')) { - return false; - } - - if (overflowX === 'scroll' || overflowY === 'scroll') { - return true; - } - - return ($el.innerHeight() < el.scrollHeight || - $el.innerWidth() < el.scrollWidth); - }; - - Utils.escapeMarkup = function (markup) { - var replaceMap = { - '\\': '\', - '&': '&', - '<': '<', - '>': '>', - '"': '"', - '\'': ''', - '/': '/' - }; - - // Do not try to escape the markup if it's not a string - if (typeof markup !== 'string') { - return markup; - } - - return String(markup).replace(/[&<>"'\/\\]/g, function (match) { - return replaceMap[match]; - }); - }; - - // Append an array of jQuery nodes to a given element. - Utils.appendMany = function ($element, $nodes) { - // jQuery 1.7.x does not support $.fn.append() with an array - // Fall back to a jQuery object collection using $.fn.add() - if ($.fn.jquery.substr(0, 3) === '1.7') { - var $jqNodes = $(); - - $.map($nodes, function (node) { - $jqNodes = $jqNodes.add(node); - }); - - $nodes = $jqNodes; - } - - $element.append($nodes); - }; - - // Cache objects in Utils.__cache instead of $.data (see #4346) - Utils.__cache = {}; - - var id = 0; - Utils.GetUniqueElementId = function (element) { - // Get a unique element Id. If element has no id, - // creates a new unique number, stores it in the id - // attribute and returns the new id. - // If an id already exists, it simply returns it. - - var select2Id = element.getAttribute('data-select2-id'); - if (select2Id == null) { - // If element has id, use it. - if (element.id) { - select2Id = element.id; - element.setAttribute('data-select2-id', select2Id); - } else { - element.setAttribute('data-select2-id', ++id); - select2Id = id.toString(); - } - } - return select2Id; - }; - - Utils.StoreData = function (element, name, value) { - // Stores an item in the cache for a specified element. - // name is the cache key. - var id = Utils.GetUniqueElementId(element); - if (!Utils.__cache[id]) { - Utils.__cache[id] = {}; - } - - Utils.__cache[id][name] = value; - }; - - Utils.GetData = function (element, name) { - // Retrieves a value from the cache by its key (name) - // name is optional. If no name specified, return - // all cache items for the specified element. - // and for a specified element. - var id = Utils.GetUniqueElementId(element); - if (name) { - if (Utils.__cache[id]) { - if (Utils.__cache[id][name] != null) { - return Utils.__cache[id][name]; - } - return $(element).data(name); // Fallback to HTML5 data attribs. - } - return $(element).data(name); // Fallback to HTML5 data attribs. - } else { - return Utils.__cache[id]; - } - }; - - Utils.RemoveData = function (element) { - // Removes all cached items for a specified element. - var id = Utils.GetUniqueElementId(element); - if (Utils.__cache[id] != null) { - delete Utils.__cache[id]; - } - }; - - return Utils; -}); diff --git a/static/vendor/select2/src/js/wrapper.end.js b/static/vendor/select2/src/js/wrapper.end.js deleted file mode 100644 index 43678296..00000000 --- a/static/vendor/select2/src/js/wrapper.end.js +++ /dev/null @@ -1,12 +0,0 @@ - // Autoload the jQuery bindings - // We know that all of the modules exist above this, so we're safe - var select2 = S2.require('jquery.select2'); - - // Hold the AMD module references on the jQuery function that was just loaded - // This allows Select2 to use the internal loader outside of this file, such - // as in the language files. - jQuery.fn.select2.amd = S2; - - // Return the Select2 instance for anyone who is importing it. - return select2; -})); diff --git a/static/vendor/select2/src/js/wrapper.start.js b/static/vendor/select2/src/js/wrapper.start.js deleted file mode 100644 index 1dfa51ea..00000000 --- a/static/vendor/select2/src/js/wrapper.start.js +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * Select2 <%= package.version %> - * https://select2.github.io - * - * Released under the MIT license - * https://github.com/select2/select2/blob/master/LICENSE.md - */ -;(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else if (typeof module === 'object' && module.exports) { - // Node/CommonJS - module.exports = function (root, jQuery) { - if (jQuery === undefined) { - // require('jQuery') returns a factory that requires window to - // build a jQuery instance, we normalize how we use modules - // that require this pattern but the window provided is a noop - // if it's defined (how jquery works) - if (typeof window !== 'undefined') { - jQuery = require('jquery'); - } - else { - jQuery = require('jquery')(root); - } - } - factory(jQuery); - return jQuery; - }; - } else { - // Browser globals - factory(jQuery); - } -} (function (jQuery) { - // This is needed so we can catch the AMD loader configuration and use it - // The inner file should be wrapped (by `banner.start.js`) in a function that - // returns the AMD loader references. - var S2 = \ No newline at end of file diff --git a/static/vendor/select2/tests/a11y/search-tests.js b/static/vendor/select2/tests/a11y/search-tests.js deleted file mode 100644 index 58e56492..00000000 --- a/static/vendor/select2/tests/a11y/search-tests.js +++ /dev/null @@ -1,51 +0,0 @@ -module('Accessibility - Search'); - -var MultipleSelection = require('select2/selection/multiple'); -var InlineSearch = require('select2/selection/search'); - -var $ = require('jquery'); - -var Utils = require('select2/utils'); -var Options = require('select2/options'); -var options = new Options({}); - -test('aria-autocomplete attribute is present', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch); - var selection = new CustomSelection($select, options); - var $selection = selection.render(); - - // Update the selection so the search is rendered - selection.update([]); - - assert.equal( - $selection.find('input').attr('aria-autocomplete'), - 'list', - 'The search box is marked as autocomplete' - ); -}); - -test('aria-activedescendant should be removed when closed', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch); - var selection = new CustomSelection($select, options); - var $selection = selection.render(); - - var container = new MockContainer(); - selection.bind(container, $('')); - - // Update the selection so the search is rendered - selection.update([]); - - var $search = $selection.find('input'); - $search.attr('aria-activedescendant', 'something'); - - container.trigger('close'); - - assert.ok( - !$search.attr('aria-activedescendant'), - 'There is no active descendant when the dropdown is closed' - ); -}); diff --git a/static/vendor/select2/tests/a11y/selection-tests.js b/static/vendor/select2/tests/a11y/selection-tests.js deleted file mode 100644 index e2c14602..00000000 --- a/static/vendor/select2/tests/a11y/selection-tests.js +++ /dev/null @@ -1,154 +0,0 @@ -module('Accessibility - All'); - -var BaseSelection = require('select2/selection/base'); -var SingleSelection = require('select2/selection/single'); -var MultipleSelection = require('select2/selection/multiple'); - -var $ = require('jquery'); - -var Options = require('select2/options'); -var options = new Options({}); - -test('title is carried over from original element', function (assert) { - var $select = $('#qunit-fixture .single'); - - var selection = new BaseSelection($select, options); - var $selection = selection.render(); - - assert.equal( - $selection.attr('title'), - $select.attr('title'), - 'The title should have been copied over from the original element' - ); -}); - -test('aria-expanded reflects the state of the container', function (assert) { - var $select = $('#qunit-fixture .single'); - - var selection = new BaseSelection($select, options); - var $selection = selection.render(); - - var container = new MockContainer(); - - selection.bind(container, $('')); - - assert.equal( - $selection.attr('aria-expanded'), - 'false', - 'The container should not be expanded when it is closed' - ); - - container.trigger('open'); - - assert.equal( - $selection.attr('aria-expanded'), - 'true', - 'The container should be expanded when it is opened' - ); -}); - -test('static aria attributes are present', function (assert) { - var $select = $('#qunit-fixture .single'); - - var selection = new BaseSelection($select, options); - var $selection = selection.render(); - - assert.equal( - $selection.attr('role'), - 'combobox', - 'The container should identify as a combobox' - ); - - assert.equal( - $selection.attr('aria-haspopup'), - 'true', - 'The dropdown is considered a popup of the container' - ); -}); - -test('the container should be in the tab order', function (assert) { - var $select = $('#qunit-fixture .single'); - - var selection = new BaseSelection($select, options); - var $selection = selection.render(); - - var container = new MockContainer(); - selection.bind(container, $('')); - - assert.equal( - $selection.attr('tabindex'), - '0', - 'The tab index should allow it to fit in the natural tab order' - ); - - container.trigger('disable'); - - assert.equal( - $selection.attr('tabindex'), - '-1', - 'The selection should be dropped out of the tab order when disabled' - ); - - container.trigger('enable'); - - assert.equal( - $selection.attr('tabindex'), - '0', - 'The tab index should be restored when re-enabled' - ); -}); - -test('a custom tabindex is copied', function (assert) { - var $select = $('#qunit-fixture .single'); - $select.attr('tabindex', '999'); - - var selection = new BaseSelection($select, options); - var $selection = selection.render(); - - var container = new MockContainer(); - selection.bind(container, $('')); - - assert.equal( - $selection.attr('tabindex'), - '999', - 'The tab index should match the original tab index' - ); - - container.trigger('disable'); - - assert.equal( - $selection.attr('tabindex'), - '-1', - 'The selection should be dropped out of the tab order when disabled' - ); - - container.trigger('enable'); - - assert.equal( - $selection.attr('tabindex'), - '999', - 'The tab index should be restored when re-enabled' - ); -}); - -module('Accessibility - Single'); - -test('aria-labelledby should match the rendered container', function (assert) { - var $select = $('#qunit-fixture .single'); - - var selection = new SingleSelection($select, options); - var $selection = selection.render(); - - var container = new MockContainer(); - selection.bind(container, $('')); - - var $rendered = $selection.find('.select2-selection__rendered'); - - assert.equal( - $selection.attr('aria-labelledby'), - $rendered.attr('id'), - 'The rendered selection should label the container' - ); -}); - -module('Accessibility - Multiple'); diff --git a/static/vendor/select2/tests/data/array-tests.js b/static/vendor/select2/tests/data/array-tests.js deleted file mode 100644 index d0dead68..00000000 --- a/static/vendor/select2/tests/data/array-tests.js +++ /dev/null @@ -1,332 +0,0 @@ -module('Data adapters - Array'); - -var ArrayData = require('select2/data/array'); -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var UserDefinedType = function (id, text) { - var self = this; - - self.id = id; - self.text = text; - - return self; -}; - -var arrayOptions = new Options({ - data: [ - { - id: 'default', - text: 'Default' - }, - { - id: '1', - text: 'One' - }, - { - id: '2', - text: '2' - }, - new UserDefinedType(1, 'aaaaaa') - ] -}); - -var extraOptions = new Options ({ - data: [ - { - id: 'default', - text: 'Default', - extra: true - }, - { - id: 'One', - text: 'One', - extra: true - } - ] -}); - -var nestedOptions = new Options({ - data: [ - { - text: 'Default', - children: [ - { - text: 'Next', - children: [ - { - id: 'a', - text: 'Option' - } - ] - } - ] - } - ] -}); - -test('current gets default for single', function (assert) { - var $select = $('#qunit-fixture .single-empty'); - - var data = new ArrayData($select, arrayOptions); - - data.current(function (val) { - assert.equal( - val.length, - 1, - 'There should always be a selected item for array data.' - ); - - var item = val[0]; - - assert.equal( - item.id, - 'default', - 'The first item should be selected' - ); - }); -}); - -test('current gets default for multiple', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new ArrayData($select, arrayOptions); - - data.current(function (val) { - assert.equal( - val.length, - 0, - 'There should be no default selection.' - ); - }); -}); - -test('current works with existing selections', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new ArrayData($select, arrayOptions); - - $select.val(['One']); - - data.current(function (val) { - assert.equal( - val.length, - 1, - 'There should only be one existing selection.' - ); - - var option = val[0]; - - assert.equal( - option.id, - 'One', - 'The id should be equal to the value of the option tag.' - ); - - assert.equal( - option.text, - 'One', - 'The text should be equal to the text of the option tag.' - ); - }); -}); - -test('current works with selected data', function (assert) { - var $select = $('#qunit-fixture .single-empty'); - - var data = new ArrayData($select, arrayOptions); - - data.select({ - id: '2', - text: '2' - }); - - data.current(function (val) { - assert.equal( - val.length, - 1, - 'There should only be one option selected.' - ); - - var option = val[0]; - - assert.equal( - option.id, - '2', - 'The id should match the original id from the array.' - ); - - assert.equal( - option.text, - '2', - 'The text should match the original text from the array.' - ); - }); -}); - -test('select works for single', function (assert) { - var $select = $('#qunit-fixture .single-empty'); - - var data = new ArrayData($select, arrayOptions); - - assert.equal( - $select.val(), - 'default', - 'There should already be a selection' - ); - - data.select({ - id: '1', - text: 'One' - }); - - assert.equal( - $select.val(), - '1', - 'The selected value should be the same as the selected id' - ); -}); - -test('multiple sets the value', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new ArrayData($select, arrayOptions); - - assert.ok( - $select.val() == null || $select.val().length == 0, - 'nothing should be selected' - ); - - data.select({ - id: 'default', - text: 'Default' - }); - - assert.deepEqual($select.val(), ['default']); -}); - -test('multiple adds to the old value', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new ArrayData($select, arrayOptions); - - $select.val(['One']); - - assert.deepEqual($select.val(), ['One']); - - data.select({ - id: 'default', - text: 'Default' - }); - - assert.deepEqual($select.val(), ['One', 'default']); -}); - -test('option tags are automatically generated', function (assert) { - var $select = $('#qunit-fixture .single-empty'); - - var data = new ArrayData($select, arrayOptions); - - assert.equal( - $select.find('option').length, - 4, - 'An element should be created for the two with children' - ); -}); - -test('optgroup tags have the right properties', function (assert) { - var $select = $('#qunit-fixture .single-empty'); - - var data = new ArrayData($select, nestedOptions); - - var $group = $select.children('optgroup'); - - assert.equal( - $group.prop('label'), - 'Default', - 'An `` label should match the text property' - ); - - assert.equal( - $group.children().length, - 1, - 'The should have one child under it' - ); -}); - -test('existing selections are respected on initialization', function (assert) { - var $select = $( - '' - ); - - var options = new Options({ - data: [ - { - id: 'Second', - text: 'Second' - }, - { - id: 'Third', - text: 'Third' - } - ] - }); - - assert.equal($select.val(), 'Second'); - - var data = new ArrayData($select, options); - - assert.equal($select.val(), 'Second'); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/data/base-tests.js b/static/vendor/select2/tests/data/base-tests.js deleted file mode 100644 index b90158f3..00000000 --- a/static/vendor/select2/tests/data/base-tests.js +++ /dev/null @@ -1,29 +0,0 @@ -module('Data adapters - Base'); - -var BaseData = require('select2/data/base'); -var $ = require('jquery'); -var Options = require('select2/options'); - -var options = new Options({}); - -test('current is required', function (assert) { - var data = new BaseData($('#qunit-fixture select'), options); - - assert.throws( - function () { - data.current(function () {}); - }, - 'current has no default implementation' - ); -}); - -test('query is required', function (assert) { - var data = new BaseData($('#qunit-fixture select'), options); - - assert.throws( - function () { - data.query({}, function () {}); - }, - 'query has no default implementation' - ); -}); diff --git a/static/vendor/select2/tests/data/inputData-tests.js b/static/vendor/select2/tests/data/inputData-tests.js deleted file mode 100644 index f2124efb..00000000 --- a/static/vendor/select2/tests/data/inputData-tests.js +++ /dev/null @@ -1,158 +0,0 @@ -module('Data adapters - compatibility'); - -var $ = require('jquery'); - -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var ArrayData = require('select2/data/array'); -var InputData = require('select2/compat/inputData'); - -var InputAdapter = Utils.Decorate(ArrayData, InputData); - -test('test that options can be selected', function (assert) { - var options = new Options({ - data: [ - { - id: 'test', - text: 'Test' - } - ] - }); - var $element = $(''); - - var adapter = new InputAdapter($element, options); - - adapter.select({ - id: 'test' - }); - - assert.equal( - $element.val(), - 'test', - 'The id of the item should be the value' - ); -}); - -test('unselect the single selected option clears the value', function (assert) { - var options = new Options({ - data: [ - { - id: 'test', - text: 'Test', - selected: true - } - ] - }); - var $element = $(''); - - var adapter = new InputAdapter($element, options); - - adapter.unselect({ - id: 'test' - }); - - assert.equal( - $element.val(), - '', - 'The id should no longer be in the value' - ); -}); - -test('options can be unselected individually', function (assert) { - var options = new Options({ - data: [ - { - id: 'test', - text: 'Test' - }, - { - id: 'test2', - text: 'Test2' - }, - { - id: 'test3', - text: 'Test3' - } - ] - }); - var $element = $(''); - $element.val('test,test2,test3'); - - var adapter = new InputAdapter($element, options); - - adapter.unselect({ - id: 'test2' - }); - - assert.equal( - $element.val(), - 'test,test3', - 'The value should contain all the still selected options' - ); -}); - -test('default values can be set', function (assert) { - assert.expect(4); - - var options = new Options({ - data: [ - { - id: 'test', - text: 'Test' - } - ] - }); - var $element = $(''); - - var adapter = new InputAdapter($element, options); - - adapter.current(function (data) { - assert.equal( - data.length, - 1, - 'There should only be a single selected option' - ); - - var item = data[0]; - - assert.equal(item.id, 'test'); - assert.equal(item.text, 'Test'); - }); - - assert.equal( - $element.val(), - 'test', - 'The value should not have been altered' - ); -}); - -test('no default value', function (assert) { - assert.expect(2); - - var options = new Options({ - data: [ - { - id: 'test', - text: 'Test' - } - ] - }); - var $element = $(''); - - var adapter = new InputAdapter($element, options); - - adapter.current(function (data) { - assert.equal( - data.length, - 0, - 'There should be no selected options' - ); - }); - - assert.equal( - $element.val(), - '', - 'The value should not have been altered' - ); -}); diff --git a/static/vendor/select2/tests/data/maximumInputLength-tests.js b/static/vendor/select2/tests/data/maximumInputLength-tests.js deleted file mode 100644 index e8557139..00000000 --- a/static/vendor/select2/tests/data/maximumInputLength-tests.js +++ /dev/null @@ -1,138 +0,0 @@ -module('Data adapters - Maximum input length'); - -var MaximumInputLength = require('select2/data/maximumInputLength'); -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -function MaximumInputStub () { - this.called = false; -} - -MaximumInputStub.prototype.query = function (params, callback) { - this.called = true; -}; - -var MaximumInputData = Utils.Decorate(MaximumInputStub, MaximumInputLength); - -test('0 never displays the notice', function (assert) { - var zeroOptions = new Options({ - maximumInputLength: 0 - }); - - var data = new MaximumInputData(null, zeroOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumInputData(null, zeroOptions); - - data.query({ - term: 'test' - }); - - assert.ok(data.called); -}); - -test('< 0 never displays the notice', function (assert) { - var negativeOptions = new Options({ - maximumInputLength: -1 - }); - - var data = new MaximumInputData(null, negativeOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumInputData(null, negativeOptions); - - data.query({ - term: 'test' - }); - - assert.ok(data.called); -}); - -test('triggers when input is too long', function (assert) { - var options = new Options({ - maximumInputLength: 1 - }); - - var data = new MaximumInputData(null, options); - - data.trigger = function () { - assert.ok(true, 'The event should be triggered.'); - }; - - data.query({ - term: 'no' - }); - - assert.ok(!data.called, 'The adapter should not be called'); -}); - -test('does not trigger when equal', function (assert) { - var options = new Options({ - maximumInputLength: 10 - }); - - var data = new MaximumInputData(null, options); - - data.trigger = function () { - assert.ok(false, 'The event should not be triggered.'); - }; - - data.query({ - term: '1234567890' - }); - - assert.ok(data.called); -}); - -test('does not trigger when less', function (assert) { - var options = new Options({ - maximumInputLength: 10 - }); - - var data = new MaximumInputData(null, options); - - data.trigger = function () { - assert.ok(false, 'The event should not be triggered.'); - }; - - data.query({ - term: '123' - }); - - assert.ok(data.called); -}); - -test('works with null term', function (assert) { - var options = new Options({ - maximumInputLength: 1 - }); - - var data = new MaximumInputData(null, options); - - data.trigger = function () { - assert.ok(false, 'The event should not be triggered'); - }; - - data.query({}); - - assert.ok(data.called); -}); diff --git a/static/vendor/select2/tests/data/maximumSelectionLength-tests.js b/static/vendor/select2/tests/data/maximumSelectionLength-tests.js deleted file mode 100644 index 89943b38..00000000 --- a/static/vendor/select2/tests/data/maximumSelectionLength-tests.js +++ /dev/null @@ -1,202 +0,0 @@ -module('Data adapters - Maximum selection length'); - -var MaximumSelectionLength = require('select2/data/maximumSelectionLength'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -function MaximumSelectionStub () { - this.called = false; - this.currentData = []; -} - -MaximumSelectionStub.prototype.current = function (callback) { - callback(this.currentData); -}; - -MaximumSelectionStub.prototype.val = function (val) { - this.currentData.push(val); -}; - -MaximumSelectionStub.prototype.query = function (params, callback) { - this.called = true; -}; - -var MaximumSelectionData = Utils.Decorate( - MaximumSelectionStub, - MaximumSelectionLength -); - -test('0 never displays the notice', function (assert) { - var zeroOptions = new Options({ - maximumSelectionLength: 0 - }); - - var data = new MaximumSelectionData(null, zeroOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, zeroOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.val('1'); - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, zeroOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.val('1'); - data.val('2'); - - data.query({ - term: '' - }); - - assert.ok(data.called); -}); - -test('< 0 never displays the notice', function (assert) { - var negativeOptions = new Options({ - maximumSelectionLength: -1 - }); - - var data = new MaximumSelectionData(null, negativeOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, negativeOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.val('1'); - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, negativeOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.val('1'); - data.val('2'); - - data.query({ - term: '' - }); - - assert.ok(data.called); -}); - -test('triggers when >= 1 selection' , function (assert) { - var maxOfOneOptions = new Options({ - maximumSelectionLength: 1 - }); - var data = new MaximumSelectionData(null, maxOfOneOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, maxOfOneOptions); - - data.trigger = function () { - assert.ok(true, 'The event should be triggered.'); - }; - - data.val('1'); - - data.query({ - term: '' - }); - - assert.ok(!data.called); - -}); - -test('triggers when >= 2 selections' , function (assert) { - var maxOfTwoOptions = new Options({ - maximumSelectionLength: 2 - }); - var data = new MaximumSelectionData(null, maxOfTwoOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, maxOfTwoOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.val('1'); - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MaximumSelectionData(null, maxOfTwoOptions); - - data.trigger = function () { - assert.ok(true, 'The event should be triggered.'); - }; - - data.val('1'); - data.val('2'); - - data.query({ - term: '' - }); - - assert.ok(!data.called); - -}); diff --git a/static/vendor/select2/tests/data/minimumInputLength-tests.js b/static/vendor/select2/tests/data/minimumInputLength-tests.js deleted file mode 100644 index a67db08d..00000000 --- a/static/vendor/select2/tests/data/minimumInputLength-tests.js +++ /dev/null @@ -1,138 +0,0 @@ -module('Data adapters - Minimum input length'); - -var MinimumInputLength = require('select2/data/minimumInputLength'); -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -function StubData () { - this.called = false; -} - -StubData.prototype.query = function (params, callback) { - this.called = true; -}; - -var MinimumData = Utils.Decorate(StubData, MinimumInputLength); - -test('0 never displays the notice', function (assert) { - var zeroOptions = new Options({ - minimumInputLength: 0 - }); - - var data = new MinimumData(null, zeroOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MinimumData(null, zeroOptions); - - data.query({ - term: 'test' - }); - - assert.ok(data.called); -}); - -test('< 0 never displays the notice', function (assert) { - var negativeOptions = new Options({ - minimumInputLength: -1 - }); - - var data = new MinimumData(null, negativeOptions); - - data.trigger = function () { - assert.ok(false, 'No events should be triggered'); - }; - - data.query({ - term: '' - }); - - assert.ok(data.called); - - data = new MinimumData(null, negativeOptions); - - data.query({ - term: 'test' - }); - - assert.ok(data.called); -}); - -test('triggers when input is not long enough', function (assert) { - var options = new Options({ - minimumInputLength: 10 - }); - - var data = new MinimumData(null, options); - - data.trigger = function () { - assert.ok(true, 'The event should be triggered.'); - }; - - data.query({ - term: 'no' - }); - - assert.ok(!data.called); -}); - -test('does not trigger when equal', function (assert) { - var options = new Options({ - minimumInputLength: 10 - }); - - var data = new MinimumData(null, options); - - data.trigger = function () { - assert.ok(false, 'The event should not be triggered.'); - }; - - data.query({ - term: '1234567890' - }); - - assert.ok(data.called); -}); - -test('does not trigger when greater', function (assert) { - var options = new Options({ - minimumInputLength: 10 - }); - - var data = new MinimumData(null, options); - - data.trigger = function () { - assert.ok(false, 'The event should not be triggered.'); - }; - - data.query({ - term: '12345678901' - }); - - assert.ok(data.called); -}); - -test('works with null term', function (assert) { - var options = new Options({ - minimumInputLength: 1 - }); - - var data = new MinimumData(null, options); - - data.trigger = function () { - assert.ok(true, 'The event should be triggered'); - }; - - data.query({}); - - assert.ok(!data.called); -}); diff --git a/static/vendor/select2/tests/data/select-tests.js b/static/vendor/select2/tests/data/select-tests.js deleted file mode 100644 index b59c6d4b..00000000 --- a/static/vendor/select2/tests/data/select-tests.js +++ /dev/null @@ -1,557 +0,0 @@ -module('Data adapters - Select - current'); - -var SelectData = require('select2/data/select'); -var $ = require('jquery'); -var Options = require('select2/options'); -var selectOptions = new Options({}); - -test('current gets default for single', function (assert) { - var $select = $('#qunit-fixture .single'); - - var data = new SelectData($select, selectOptions); - - data.current(function (data) { - assert.equal( - data.length, - 1, - 'There should only be one selected option' - ); - - var option = data[0]; - - assert.equal( - option.id, - 'One', - 'The value of the option tag should be the id' - ); - - assert.equal( - option.text, - 'One', - 'The text within the option tag should be the text' - ); - }); -}); - -test('current gets default for multiple', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new SelectData($select, selectOptions); - - data.current(function (data) { - assert.equal( - data.length, - 0, - 'Multiple selects have no default selection.' - ); - }); -}); - -test('current gets options with explicit value', function (assert) { - var $select = $('#qunit-fixture .single'); - - var $option = $(''); - $select.append($option); - - var data = new SelectData($select, selectOptions); - - $select.val('1'); - - data.current(function (data) { - assert.equal( - data.length, - 1, - 'There should be one selected option' - ); - - var option = data[0]; - - assert.equal( - option.id, - '1', - 'The option value should be the selected id' - ); - - assert.equal( - option.text, - 'One', - 'The text should match the text for the option tag' - ); - }); -}); - -test('current gets options with implicit value', function (assert) { - var $select = $('#qunit-fixture .single'); - - var data = new SelectData($select, selectOptions); - - $select.val('One'); - - data.current(function (val) { - assert.equal( - val.length, - 1, - 'There should only be one selected value' - ); - - var option = val[0]; - - assert.equal( - option.id, - 'One', - 'The id should be the same as the option text' - ); - - assert.equal( - option.text, - 'One', - 'The text should be the same as the option text' - ); - }); -}); - -test('select works for single', function (assert) { - var $select = $('#qunit-fixture .single-with-placeholder'); - - var data = new SelectData($select, selectOptions); - - assert.equal($select.val(), 'placeholder'); - - data.select({ - id: 'One', - text: 'One' - }); - - assert.equal($select.val(), 'One'); -}); - -test('multiple sets the value', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new SelectData($select, selectOptions); - - assert.ok( - $select.val() == null || $select.val().length == 0, - 'nothing should be selected' - ); - - data.select({ - id: 'Two', - text: 'Two' - }); - - assert.deepEqual($select.val(), ['Two']); -}); - -test('multiple adds to the old value', function (assert) { - var $select = $('#qunit-fixture .multiple'); - - var data = new SelectData($select, selectOptions); - - $select.val(['Two']); - - assert.deepEqual($select.val(), ['Two']); - - data.select({ - id: 'One', - text: 'One' - }); - - assert.deepEqual($select.val(), ['One', 'Two']); -}); - -test('duplicates - single - same id on select triggers change', - function (assert) { - var $select = $('#qunit-fixture .duplicates'); - - var data = new SelectData($select, data); - var second = $('#qunit-fixture .duplicates option')[2]; - - var changeTriggered = false; - - assert.equal($select.val(), 'one'); - - $select.on('change', function () { - changeTriggered = true; - }); - - data.select({ - id: 'one', - text: 'Uno', - element: second - }); - - assert.equal( - $select.val(), - 'one', - 'The value never changed' - ); - - assert.ok( - changeTriggered, - 'The change event should be triggered' - ); - - assert.ok( - second.selected, - 'The second duplicate is selected, not the first' - ); -}); - -test('duplicates - single - different id on select triggers change', - function (assert) { - var $select = $('#qunit-fixture .duplicates'); - - var data = new SelectData($select, data); - var second = $('#qunit-fixture .duplicates option')[2]; - - var changeTriggered = false; - - $select.val('two'); - - $select.on('change', function () { - changeTriggered = true; - }); - - data.select({ - id: 'one', - text: 'Uno', - element: second - }); - - assert.equal( - $select.val(), - 'one', - 'The value changed to the duplicate id' - ); - - assert.ok( - changeTriggered, - 'The change event should be triggered' - ); - - assert.ok( - second.selected, - 'The second duplicate is selected, not the first' - ); -}); - -test('duplicates - multiple - same id on select triggers change', -function (assert) { - var $select = $('#qunit-fixture .duplicates-multi'); - - var data = new SelectData($select, data); - var second = $('#qunit-fixture .duplicates-multi option')[2]; - - var changeTriggered = false; - - $select.val(['one']); - - $select.on('change', function () { - changeTriggered = true; - }); - - data.select({ - id: 'one', - text: 'Uno', - element: second - }); - - assert.deepEqual( - $select.val(), - ['one', 'one'], - 'The value now has duplicates' - ); - - assert.ok( - changeTriggered, - 'The change event should be triggered' - ); - - assert.ok( - second.selected, - 'The second duplicate is selected, not the first' - ); -}); - -test('duplicates - multiple - different id on select triggers change', -function (assert) { - var $select = $('#qunit-fixture .duplicates-multi'); - - var data = new SelectData($select, data); - var second = $('#qunit-fixture .duplicates-multi option')[2]; - - var changeTriggered = false; - - $select.val(['two']); - - $select.on('change', function () { - changeTriggered = true; - }); - - data.select({ - id: 'one', - text: 'Uno', - element: second - }); - - assert.deepEqual( - $select.val(), - ['two', 'one'], - 'The value has the new id' - ); - - assert.ok( - changeTriggered, - 'The change event should be triggered' - ); - - assert.ok( - second.selected, - 'The second duplicate is selected, not the first' - ); -}); - -module('Data adapter - Select - query'); - -test('all options are returned with no term', function (assert) { - var $select = $('#qunit-fixture .single'); - - var data = new SelectData($select, selectOptions); - - data.query({}, function (data) { - assert.equal( - data.results.length, - 1, - 'The number of items returned should be equal to the number of options' - ); - }); -}); - -test('the matcher checks the text', function (assert) { - var $select = $('#qunit-fixture .single'); - - var data = new SelectData($select, selectOptions); - - data.query({ - term: 'One' - }, function (data) { - assert.equal( - data.results.length, - 1, - 'Only the "One" option should be found' - ); - }); -}); - -test('the matcher ignores case', function (assert) { - var $select = $('#qunit-fixture .single'); - - var data = new SelectData($select, selectOptions); - - data.query({ - term: 'one' - }, function (data) { - assert.equal( - data.results.length, - 1, - 'The "One" option should still be found' - ); - }); -}); - -test('no options may be returned with no matches', function (assert) { - var $select = $('#qunit-fixture .single'); - - var data = new SelectData($select, selectOptions); - - data.query({ - term: 'qwerty' - }, function (data) { - assert.equal( - data.results.length, - 0, - 'Only matching items should be returned' - ); - }); -}); - -test('optgroup tags are marked with children', function (assert) { - var $select = $('#qunit-fixture .groups'); - - var data = new SelectData($select, selectOptions); - - data.query({}, function (data) { - assert.ok( - 'children' in data.results[0], - 'The optgroup element should have children when queried' - ); - }); -}); - -test('empty optgroups are still shown when queried', function (assert) { - var $select = $('#qunit-fixture .groups'); - - var data = new SelectData($select, selectOptions); - - data.query({}, function (data) { - assert.equal( - data.results.length, - 2, - 'The empty optgroup element should still be returned when queried' - ); - - var item = data.results[1]; - - assert.equal( - item.text, - 'Empty', - 'The text of the empty optgroup should match the label' - ); - - assert.equal( - item.children.length, - 0, - 'There should be no children in the empty opgroup' - ); - }); -}); - -test('multiple options with the same value are returned', function (assert) { - var $select = $('#qunit-fixture .duplicates'); - - var data = new SelectData($select, selectOptions); - - data.query({}, function (data) { - assert.equal( - data.results.length, - 3, - 'The duplicate option should still be returned when queried' - ); - - var first = data.results[0]; - var duplicate = data.results[2]; - - assert.equal( - first.id, - duplicate.id, - 'The duplicates should have the same id' - ); - - assert.notEqual( - first.text, - duplicate.text, - 'The duplicates do not have the same text' - ); - }); -}); - -test('data objects use the text of the option', function (assert) { - var $select = $('#qunit-fixture .duplicates'); - - var data = new SelectData($select, selectOptions); - - var $option = $(''); - - var item = data.item($option); - - assert.equal(item.id, '&'); - assert.equal(item.text, '&'); -}); - -test('select option construction accepts id=0 (zero) value', function (assert) { - var $select = $('#qunit-fixture .single'); - - var selectOptions = [{ id: 0, text: 'Zero Value'}]; - var data = new SelectData($select, selectOptions); - - var optionElem = data.option(selectOptions[0]); - - // If was "Zero Value"", then it ignored id property - assert.equal( - optionElem[0].value, - '0', - 'Built option value should be "0" (zero as a string).' - ); -}); - -test('select option construction accepts id="" (empty string) value', - function (assert) { - var $select = $('#qunit-fixture .single'); - - var selectOptions = [{ id: '', text: 'Empty String'}]; - var data = new SelectData($select, selectOptions); - - var optionElem = data.option(selectOptions[0]); - - assert.equal( - optionElem[0].value, - '', - 'Built option value should be an empty string.' - ); -}); - -test('user-defined types are normalized properly', function (assert) { - var $select = $('#qunit-fixture .user-defined'), - - UserDefinedType = function (id, text) { - var self = this; - - self.id = id; - self.text = text; - - return self; - }; - - var testData = [ - 'Test', - { - id: 4, - text: 'item' - }, - new UserDefinedType(1, 'aaaaaa') - ]; - - var data = new SelectData($select, selectOptions); - - var normalizedItem = data._normalizeItem(testData[0]); - var normalizedItem2 = data._normalizeItem(testData[1]); - var normalizedItem3 = data._normalizeItem(testData[2]); - - assert.equal( - testData[0], - normalizedItem.id, - 'id property should be equal to text after normalize' - ); - - assert.equal( - testData[0], - normalizedItem.text, - 'text property should be equal after normalize' - ); - - assert.equal( - testData[1].id, - normalizedItem2.id, - 'id property should be equal after normalize' - ); - - assert.equal( - testData[1].text, - normalizedItem2.text, - 'text property should be equal after normalize' - ); - - assert.equal( - testData[2].id, - normalizedItem3.id, - 'id property should be equal after normalize' - ); - - assert.equal( - testData[2].text, - normalizedItem3.text, - 'text property should be equal after normalize' - ); - -}); diff --git a/static/vendor/select2/tests/data/tags-tests.js b/static/vendor/select2/tests/data/tags-tests.js deleted file mode 100644 index 6af8d8a7..00000000 --- a/static/vendor/select2/tests/data/tags-tests.js +++ /dev/null @@ -1,276 +0,0 @@ -module('Data adapters - Tags'); - -var SelectData = require('select2/data/select'); -var Tags = require('select2/data/tags'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var SelectTags = Utils.Decorate(SelectData, Tags); -var options = new Options({ - tags: true -}); - -test('does not trigger on blank or null terms', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: '' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'One'); - assert.equal(item.text, 'One'); - }); - - data.query({ - term: null - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'One'); - assert.equal(item.text, 'One'); - }); -}); - -test('white space is trimmed by default', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: ' ' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'One'); - assert.equal(item.text, 'One'); - }); - - data.query({ - term: ' One ' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'One'); - assert.equal(item.text, 'One'); - }); -}); - -test('does not create option if text is same but lowercase', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: 'one' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'One'); - assert.equal(item.text, 'One'); - }); -}); - -test('does not trigger for additional pages', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - page: 2 - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'One'); - assert.equal(item.text, 'One'); - }); -}); - -test('creates tag at beginning', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: 'o' - }, function (data) { - assert.equal(data.results.length, 2); - - var first = data.results[0]; - - assert.equal(first.id, 'o'); - assert.equal(first.text, 'o'); - }); -}); - -test('tags can be the only result', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: 'test' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'test'); - assert.equal(item.text, 'test'); - }); -}); - -test('tags are injected as options', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: 'test' - }, function (data) { - assert.equal(data.results.length, 1); - - var $children = $('#qunit-fixture .single option'); - - assert.equal($children.length, 2); - - var $tag = $children.last(); - - assert.equal($tag.val(), 'test'); - assert.equal($tag.text(), 'test'); - }); -}); - -test('old tags are removed automatically', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: 'first' - }, function (data) { - assert.equal(data.results.length, 1); - - var $children = $('#qunit-fixture .single option'); - - assert.equal($children.length, 2); - }); - - data.query({ - term: 'second' - }, function (data) { - assert.equal(data.results.length, 1); - - var $children = $('#qunit-fixture .single option'); - - assert.equal($children.length, 2); - - var $tag = $children.last(); - - assert.equal($tag.val(), 'second'); - assert.equal($tag.text(), 'second'); - }); -}); - -test('insertTag controls the tag location', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.insertTag = function (data, tag) { - data.push(tag); - }; - - data.query({ - term: 'o' - }, function (data) { - assert.equal(data.results.length, 2); - - var item = data.results[1]; - - assert.equal(item.id, 'o'); - assert.equal(item.text, 'o'); - }); -}); - -test('insertTag can be controlled through the options', function (assert) { - var options = new Options({ - insertTag: function (data, tag) { - data.push(tag); - } - }); - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.query({ - term: 'o' - }, function (data) { - assert.equal(data.results.length, 2); - - var item = data.results[1]; - - assert.equal(item.id, 'o'); - assert.equal(item.text, 'o'); - }); -}); - -test('createTag controls the tag object', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.createTag = function (params) { - return { - id: 0, - text: params.term - }; - }; - - data.query({ - term: 'test' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 0); - assert.equal(item.text, 'test'); - }); -}); - -test('createTag returns null for no tag', function (assert) { - var data = new SelectTags($('#qunit-fixture .single'), options); - - data.createTag = function (params) { - return null; - }; - - data.query({ - term: 'o' - }, function (data) { - assert.equal(data.results.length, 1); - }); -}); - -test('the createTag options customizes the function', function (assert) { - var data = new SelectTags( - $('#qunit-fixture .single'), - new Options({ - tags: true, - createTag: function (params) { - return { - id: params.term, - text: params.term, - tag: true - }; - } - }) - ); - - data.query({ - term: 'test' - }, function (data) { - assert.equal(data.results.length, 1); - - var item = data.results[0]; - - assert.equal(item.id, 'test'); - assert.equal(item.text, 'test'); - assert.equal(item.tag, true); - }); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/data/tokenizer-tests.js b/static/vendor/select2/tests/data/tokenizer-tests.js deleted file mode 100644 index 2fa01211..00000000 --- a/static/vendor/select2/tests/data/tokenizer-tests.js +++ /dev/null @@ -1,219 +0,0 @@ -module('Data adaptor - Tokenizer'); - -test('triggers the select event', function (assert) { - assert.expect(2); - - var SelectData = require('select2/data/select'); - var Tokenizer = require('select2/data/tokenizer'); - var Tags = require('select2/data/tags'); - - var Options = require('select2/options'); - var Utils = require('select2/utils'); - - var $ = require('jquery'); - - var TokenizedSelect = Utils.Decorate( - Utils.Decorate(SelectData, Tags), - Tokenizer - ); - var $select = $('#qunit-fixture .single'); - - var options = new Options({ - tags: true, - tokenSeparators: [','] - }); - - var container = new MockContainer(); - container.dropdown = container.selection = {}; - - var $container = $('
        '); - - var data = new TokenizedSelect($select, options); - data.bind(container, $container); - - data.on('select', function () { - assert.ok(true, 'The select event should be triggered'); - }); - - data.query({ - term: 'first,second' - }, function () { - assert.ok(true, 'The callback should have succeeded'); - }); -}); - -test('createTag can return null', function (assert) { - assert.expect(3); - - var SelectData = require('select2/data/select'); - var Tokenizer = require('select2/data/tokenizer'); - var Tags = require('select2/data/tags'); - - var Options = require('select2/options'); - var Utils = require('select2/utils'); - - var $ = require('jquery'); - - var TokenizedSelect = Utils.Decorate( - Utils.Decorate(SelectData, Tags), - Tokenizer - ); - var $select = $('#qunit-fixture .single'); - - var options = new Options({ - tags: true, - tokenSeparators: [','], - createTag: function () { - assert.ok(true, 'createTag should have been called'); - - return null; - } - }); - - var container = new MockContainer(); - container.dropdown = container.selection = {}; - - var $container = $('
        '); - - var data = new TokenizedSelect($select, options); - data.bind(container, $container); - - data.on('select', function (params) { - if (params.data == null) { - assert.ok(false, 'Null data should never be selected'); - } - }); - - data.query({ - term: 'first,second' - }, function () { - assert.ok(true, 'The callback should have succeeded'); - }); -}); - -test('createTag returning null does not cut the term', function (assert) { - assert.expect(4); - - var SelectData = require('select2/data/select'); - var Tokenizer = require('select2/data/tokenizer'); - var Tags = require('select2/data/tags'); - - var Options = require('select2/options'); - var Utils = require('select2/utils'); - - var $ = require('jquery'); - - var TokenizedSelect = Utils.Decorate( - Utils.Decorate(SelectData, Tags), - Tokenizer - ); - var $select = $('#qunit-fixture .single'); - - var options = new Options({ - tags: true, - tokenSeparators: [',', '"'], - createTag: function (params) { - var term = params.term; - - // Ignore blanks - if (term.length === 0) { - return null; - } - - // Ignore the leading quote - if (term === '"') { - return null; - } - - // If there is a leading quote, check for a second one - if (term[0] === '"' && term[term.length - 1] !== '"') { - return null; - } - - var text = term.substr(1, term.length - 2); - - return { - id: term, - text: text - }; - } - }); - - var container = new MockContainer(); - container.dropdown = container.selection = {}; - - var $container = $('
        '); - - var data = new TokenizedSelect($select, options); - data.bind(container, $container); - - data.on('select', function (params) { - assert.ok(params.data, 'Data should not be null'); - - assert.equal( - params.data.id, - '"first, second"', - 'The id should have the quotes' - ); - - assert.equal( - params.data.text, - 'first, second', - 'The text should not have the quotes' - ); - }); - - data.query({ - term: '"first, second",abc' - }, function () { - assert.ok(true, 'The callback should have succeeded'); - }); -}); - -test('works with multiple tokens given', function (assert) { - assert.expect(4); - - var SelectData = require('select2/data/select'); - var Tokenizer = require('select2/data/tokenizer'); - var Tags = require('select2/data/tags'); - - var Options = require('select2/options'); - var Utils = require('select2/utils'); - - var $ = require('jquery'); - - var TokenizedSelect = Utils.Decorate( - Utils.Decorate(SelectData, Tags), - Tokenizer - ); - var $select = $('#qunit-fixture .multiple'); - - var options = new Options({ - tags: true, - tokenSeparators: [','] - }); - - var container = new MockContainer(); - container.dropdown = container.selection = {}; - - var $container = $('
        '); - - var data = new TokenizedSelect($select, options); - data.bind(container, $container); - - data.on('select', function () { - assert.ok(true, 'The select event should be triggered'); - }); - - data.query({ - term: 'first,second,third' - }, function () { - assert.ok(true, 'The callback should have succeeded'); - }); - - assert.equal( - $select.children('option').length, - 3, - 'The two new tags should have been created' - ); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/dropdown/dropdownCss-tests.js b/static/vendor/select2/tests/dropdown/dropdownCss-tests.js deleted file mode 100644 index 9969335e..00000000 --- a/static/vendor/select2/tests/dropdown/dropdownCss-tests.js +++ /dev/null @@ -1,104 +0,0 @@ -module('Dropdown - dropdownCssClass compatibility'); - -var $ = require('jquery'); -var Utils = require('select2/utils'); -var Options = require('select2/options'); - -var Dropdown = require('select2/dropdown'); -var DropdownCSS = Utils.Decorate( - Dropdown, - require('select2/compat/dropdownCss') -); - -test('all classes will be copied if :all: is used', function (assert) { - var $element = $(''); - var options = new Options({ - dropdownCssClass: ':all:' - }); - - var select = new DropdownCSS($element, options); - var $dropdown = select.render(); - - assert.ok($dropdown.hasClass('test')); - assert.ok($dropdown.hasClass('copy')); - assert.ok($dropdown.hasClass('works')); - assert.ok(!$dropdown.hasClass(':all:')); -}); - -test(':all: can be used with other classes', function (assert) { - var $element = $(''); - var options = new Options({ - dropdownCssClass: ':all: other' - }); - - var select = new DropdownCSS($element, options); - var $dropdown = select.render(); - - assert.ok($dropdown.hasClass('test')); - assert.ok($dropdown.hasClass('copy')); - assert.ok($dropdown.hasClass('works')); - assert.ok($dropdown.hasClass('other')); - assert.ok(!$dropdown.hasClass(':all:')); -}); - -test('classes can be passed in as a string', function (assert) { - var $element = $(''); - var options = new Options({ - dropdownCssClass: 'other' - }); - - var select = new DropdownCSS($element, options); - var $dropdown = select.render(); - - assert.ok($dropdown.hasClass('other')); -}); - -test('a function can be used based on the element', function (assert){ - var $element = $(''); - var options = new Options({ - dropdownCssClass: function ($element) { - return 'function'; - } - }); - - var select = new DropdownCSS($element, options); - var $dropdown = select.render(); - - assert.ok($dropdown.hasClass('function')); - assert.ok(!$dropdown.hasClass('test')); -}); - -test(':all: works around custom adapters', function (assert) { - var $element = $(''); - var options = new Options({ - dropdownCssClass: ':all: something', - adaptDropdownCssClass: function (clazz) { - return clazz + '-modified'; - } - }); - - var select = new DropdownCSS($element, options); - var $dropdown = select.render(); - - assert.ok($dropdown.hasClass('something')); - - assert.ok($dropdown.hasClass('test')); - assert.ok($dropdown.hasClass('test-modified')); -}); - -module('Dropdown - adaptDropdownCss compatibility'); - -test('only return when adapted', function (assert) { - var $element = $(''); - var options = new Options({ - adaptDropdownCssClass: function (clazz) { - return 'modified'; - } - }); - - var select = new DropdownCSS($element, options); - var $dropdown = select.render(); - - assert.ok(!$dropdown.hasClass('original')); - assert.ok($dropdown.hasClass('modified')); -}); diff --git a/static/vendor/select2/tests/dropdown/positioning-tests.js b/static/vendor/select2/tests/dropdown/positioning-tests.js deleted file mode 100644 index 2da68982..00000000 --- a/static/vendor/select2/tests/dropdown/positioning-tests.js +++ /dev/null @@ -1,177 +0,0 @@ -module('Dropdown - attachBody - positioning'); - -test('appends to the dropdown parent', function (assert) { - assert.expect(4); - - var $ = require('jquery'); - - var $select = $(''); - var $parent = $('
        '); - - var $container = $(''); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $select.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Dropdown = require('select2/dropdown'); - var AttachBody = require('select2/dropdown/attachBody'); - - var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody); - - var dropdown = new DropdownAdapter($select, new Options({ - dropdownParent: $parent - })); - - assert.equal( - $parent.children().length, - 1, - 'Only the select should be in the container' - ); - - var $dropdown = dropdown.render(); - - dropdown.bind(container, $container); - - dropdown.position($dropdown, $container); - - assert.equal( - $parent.children().length, - 1, - 'The dropdown should not be placed until after it is opened' - ); - - dropdown._showDropdown(); - - assert.equal( - $parent.children().length, - 2, - 'The dropdown should now be in the container as well' - ); - - assert.ok( - $.contains($parent[0], $dropdown[0]), - 'The dropdown should be contained within the parent container' - ); -}); - -test('dropdown is positioned down with static margins', function (assert) { - var $ = require('jquery'); - var $select = $(''); - var $parent = $('
        '); - $parent.css({ - position: 'static', - marginTop: '5px', - marginLeft: '10px' - }); - - var $container = $('test'); - var container = new MockContainer(); - - $('#qunit-fixture').empty(); - - $parent.appendTo($('#qunit-fixture')); - $container.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Dropdown = require('select2/dropdown'); - var AttachBody = require('select2/dropdown/attachBody'); - - var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody); - - var dropdown = new DropdownAdapter($select, new Options({ - dropdownParent: $parent - })); - - var $dropdown = dropdown.render(); - - assert.equal( - $dropdown[0].style.top, - 0, - 'The drodpown should not have any offset before it is displayed' - ); - - dropdown.bind(container, $container); - dropdown.position($dropdown, $container); - dropdown._showDropdown(); - - assert.ok( - dropdown.$dropdown.hasClass('select2-dropdown--below'), - 'The dropdown should be forced down' - ); - - assert.equal( - $dropdown.css('top').replace(/\D+/, ''), - $container.outerHeight() + 5, - 'The offset should be 5px at the top' - ); - - assert.equal( - $dropdown.css('left'), - '10px', - 'The offset should be 10px on the left' - ); -}); - -test('dropdown is positioned down with absolute offsets', function (assert) { - var $ = require('jquery'); - var $select = $(''); - var $parent = $('
        '); - $parent.css({ - position: 'absolute', - top: '10px', - left: '5px' - }); - - var $container = $('test'); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $container.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Dropdown = require('select2/dropdown'); - var AttachBody = require('select2/dropdown/attachBody'); - - var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody); - - var dropdown = new DropdownAdapter($select, new Options({ - dropdownParent: $parent - })); - - var $dropdown = dropdown.render(); - - assert.equal( - $dropdown[0].style.top, - 0, - 'The drodpown should not have any offset before it is displayed' - ); - - dropdown.bind(container, $container); - dropdown.position($dropdown, $container); - dropdown._showDropdown(); - - assert.ok( - dropdown.$dropdown.hasClass('select2-dropdown--below'), - 'The dropdown should be forced down' - ); - - assert.equal( - $dropdown.css('top').replace(/\D+/, ''), - $container.outerHeight(), - 'There should not be an extra top offset' - ); - - assert.equal( - $dropdown.css('left'), - '0px', - 'There should not be an extra left offset' - ); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/dropdown/selectOnClose-tests.js b/static/vendor/select2/tests/dropdown/selectOnClose-tests.js deleted file mode 100644 index 3f1d1d41..00000000 --- a/static/vendor/select2/tests/dropdown/selectOnClose-tests.js +++ /dev/null @@ -1,137 +0,0 @@ -module('Dropdown - selectOnClose'); - -var $ = require('jquery'); - -var Utils = require('select2/utils'); -var Options = require('select2/options'); - -var SelectData = require('select2/data/select'); - -var Results = require('select2/results'); -var SelectOnClose = require('select2/dropdown/selectOnClose'); - -var ModifiedResults = Utils.Decorate(Results, SelectOnClose); - -var options = new Options({ - selectOnClose: true -}); - -test('will not trigger if no results were given', function (assert) { - assert.expect(0); - - var $element = $(''); - var select = new ModifiedResults($element, options, new SelectData($element)); - - var $dropdown = select.render(); - - var container = new MockContainer(); - select.bind(container, $('
        ')); - - select.on('select', function () { - assert.ok(false, 'The select event should not have been triggered'); - }); - - container.trigger('close'); -}); - -test('will not trigger if the results list is empty', function (assert) { - assert.expect(1); - - var $element = $(''); - var select = new ModifiedResults($element, options, new SelectData($element)); - - var $dropdown = select.render(); - - var container = new MockContainer(); - select.bind(container, $('
        ')); - - select.on('select', function () { - assert.ok(false, 'The select event should not have been triggered'); - }); - - select.append({ - results: [] - }); - - assert.equal( - $dropdown.find('li').length, - 0, - 'There should not be any results in the dropdown' - ); - - container.trigger('close'); -}); - -test('will not trigger if no results here highlighted', function (assert) { - assert.expect(2); - - var $element = $(''); - var select = new ModifiedResults($element, options, new SelectData($element)); - - var $dropdown = select.render(); - - var container = new MockContainer(); - select.bind(container, $('
        ')); - - select.on('select', function () { - assert.ok(false, 'The select event should not have been triggered'); - }); - - select.append({ - results: [ - { - id: '1', - text: 'Test' - } - ] - }); - - assert.equal( - $dropdown.find('li').length, - 1, - 'There should be one result in the dropdown' - ); - - assert.equal( - $.trim($dropdown.find('li').text()), - 'Test', - 'The result should be the same as the one we appended' - ); - - container.trigger('close'); -}); - -test('will trigger if there is a highlighted result', function (assert) { - assert.expect(2); - - var $element = $(''); - var select = new ModifiedResults($element, options, new SelectData($element)); - - var $dropdown = select.render(); - - var container = new MockContainer(); - select.bind(container, $('
        ')); - - select.on('select', function () { - assert.ok(true, 'The select event should have been triggered'); - }); - - select.append({ - results: [ - { - id: '1', - text: 'Test' - } - ] - }); - - assert.equal( - $dropdown.find('li').length, - 1, - 'There should be one result in the dropdown' - ); - - $dropdown.find('li').addClass('select2-results__option--highlighted'); - - container.trigger('close'); -}); diff --git a/static/vendor/select2/tests/dropdown/stopPropagation-tests.js b/static/vendor/select2/tests/dropdown/stopPropagation-tests.js deleted file mode 100644 index 240a3e66..00000000 --- a/static/vendor/select2/tests/dropdown/stopPropagation-tests.js +++ /dev/null @@ -1,33 +0,0 @@ -module('Dropdown - Stoping event propagation'); - -var Dropdown = require('select2/dropdown'); -var StopPropagation = require('select2/dropdown/stopPropagation'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var CustomDropdown = Utils.Decorate(Dropdown, StopPropagation); - -var options = new Options(); - -test('click event does not propagate', function (assert) { - assert.expect(1); - - var $container = $('#qunit-fixture .event-container'); - var container = new MockContainer(); - - var dropdown = new CustomDropdown($('#qunit-fixture select'), options); - - var $dropdown = dropdown.render(); - dropdown.bind(container, $container); - - $container.append($dropdown); - $container.on('click', function () { - assert.ok(false, 'The click event should have been stopped'); - }); - - $dropdown.trigger('click'); - - assert.ok(true, 'Something went wrong if this failed'); -}); diff --git a/static/vendor/select2/tests/helpers.js b/static/vendor/select2/tests/helpers.js deleted file mode 100644 index 9409c8b6..00000000 --- a/static/vendor/select2/tests/helpers.js +++ /dev/null @@ -1,50 +0,0 @@ -// Restore the require/define -var require = $.fn.select2.amd.require; -var define = $.fn.select2.amd.define; - -// Disable jQuery's binding to $ -jQuery.noConflict(); - -var Utils = require('select2/utils'); - -function MockContainer () { - MockContainer.__super__.constructor.call(this); -} - -Utils.Extend(MockContainer, Utils.Observable); - -MockContainer.prototype.isOpen = function () { - return this.isOpen; -}; - -var log = []; -var testName; - -QUnit.done(function (test_results) { - var tests = []; - for(var i = 0, len = log.length; i < len; i++) { - var details = log[i]; - tests.push({ - name: details.name, - result: details.result, - expected: details.expected, - actual: details.actual, - source: details.source - }); - } - test_results.tests = tests; - - window.global_test_results = test_results; -}); -QUnit.testStart(function(testDetails){ - QUnit.log(function(details){ - if (!details.result) { - details.name = testDetails.name; - log.push(details); - } - }); -}); - -define('qunit', function () { - return QUnit; -}) \ No newline at end of file diff --git a/static/vendor/select2/tests/integration-jq1.html b/static/vendor/select2/tests/integration-jq1.html deleted file mode 100644 index d8d0ef55..00000000 --- a/static/vendor/select2/tests/integration-jq1.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - -
        -
        - - - - - - - - - - - - diff --git a/static/vendor/select2/tests/integration-jq2.html b/static/vendor/select2/tests/integration-jq2.html deleted file mode 100644 index 97f73828..00000000 --- a/static/vendor/select2/tests/integration-jq2.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - -
        -
        - - - - - - - - - - - - diff --git a/static/vendor/select2/tests/integration-jq3.html b/static/vendor/select2/tests/integration-jq3.html deleted file mode 100644 index 2f99fb5b..00000000 --- a/static/vendor/select2/tests/integration-jq3.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - -
        -
        - - - - - - - - - - - - diff --git a/static/vendor/select2/tests/integration/dom-changes.js b/static/vendor/select2/tests/integration/dom-changes.js deleted file mode 100644 index 2d89ae2a..00000000 --- a/static/vendor/select2/tests/integration/dom-changes.js +++ /dev/null @@ -1,257 +0,0 @@ -module('DOM integration'); - -test('adding a new unselected option changes nothing', function (assert) { - // Any browsers which support mutation observers will not trigger the event - var expected = 4; - if (window.MutationObserver) { - expected = 2; - } else if (!window.addEventListener) { - expected = 2; - } - - assert.expect(expected); - - var asyncDone = null; - var syncDone = assert.async(); - - if (expected != 2) { - asyncDone = assert.async(); - } - - var $ = require('jquery'); - var Options = require('select2/options'); - var Select2 = require('select2/core'); - - var $select = $( - '' - ); - - $('#qunit-fixture').append($select); - - var select = new Select2($select); - - select.on('selection:update', function (args) { - assert.equal( - args.data.length, - 1, - 'There was more than one selection' - ); - - assert.equal( - args.data[0].id, - 'One', - 'The selection changed to something other than One' - ); - - if (expected != 2) { - asyncDone(); - } - }); - - assert.equal( - $select.val(), - 'One' - ); - - var $option = $(''); - - $select.append($option); - - assert.equal( - $select.val(), - 'One' - ); - - syncDone(); -}); - -test('adding a new selected option changes the value', function (assert) { - // handle IE 8 not being supported - var expected = 4; - if (!window.MutationObserver && !window.addEventListener) { - expected = 2; - } - - assert.expect(expected); - - var asyncDone = null; - var syncDone = assert.async(); - - if (expected != 2) { - asyncDone = assert.async(); - } - - var $ = require('jquery'); - var Options = require('select2/options'); - var Select2 = require('select2/core'); - - var $select = $( - '' - ); - - $('#qunit-fixture').append($select); - - var select = new Select2($select); - - select.on('selection:update', function (args) { - assert.equal( - args.data.length, - 1, - 'There was more than one selection' - ); - - assert.equal( - args.data[0].id, - 'Three', - 'The selection did not change to Three' - ); - - if (expected != 2) { - asyncDone(); - } - }); - - assert.equal( - $select.val(), - 'One' - ); - - var $option = $(''); - - $select.append($option); - - assert.equal( - $select.val(), - 'Three' - ); - - syncDone(); -}); - -test('removing an unselected option changes nothing', function (assert) { - // Any browsers which support mutation observers will not trigger the event - var expected = 4; - if (!window.MutationObserver && !window.addEventListener) { - expected = 2; - } - - assert.expect(expected); - - var asyncDone = null; - var syncDone = assert.async(); - - if (expected != 2) { - asyncDone = assert.async(); - } - - var $ = require('jquery'); - var Options = require('select2/options'); - var Select2 = require('select2/core'); - - var $select = $( - '' - ); - - $('#qunit-fixture').append($select); - - var select = new Select2($select); - - select.on('selection:update', function (args) { - assert.equal( - args.data.length, - 1, - 'There was more than one selection' - ); - - assert.equal( - args.data[0].id, - 'One', - 'The selection changed to something other than One' - ); - - if (expected != 2) { - asyncDone(); - } - }); - - assert.equal( - $select.val(), - 'One' - ); - - $select.children().eq(1).remove(); - - assert.equal( - $select.val(), - 'One' - ); - - syncDone(); -}); - -test('removing a selected option changes the value', function (assert) { - // handle IE 8 not being supported - var expected = 3; - if (!window.MutationObserver && !window.addEventListener) { - expected = 2; - } - - assert.expect(expected); - - var asyncDone = null; - var syncDone = assert.async(); - - if (expected != 2) { - asyncDone = assert.async(); - } - - var $ = require('jquery'); - var Options = require('select2/options'); - var Select2 = require('select2/core'); - - var $select = $( - '' - ); - - $('#qunit-fixture').append($select); - - var select = new Select2($select); - - select.on('selection:update', function (args) { - assert.equal( - args.data.length, - 1, - 'There was more than one selection' - ); - - if (expected != 2) { - asyncDone(); - } - }); - - assert.equal( - $select.val(), - 'One' - ); - - $select.children().eq(0).remove(); - - assert.equal( - $select.val(), - 'Two' - ); - - syncDone(); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/integration/jquery-calls.js b/static/vendor/select2/tests/integration/jquery-calls.js deleted file mode 100644 index d7c3926f..00000000 --- a/static/vendor/select2/tests/integration/jquery-calls.js +++ /dev/null @@ -1,98 +0,0 @@ -module('select2(val)'); - -var Utils = require('select2/utils'); - -test('multiple elements with arguments works', function (assert) { - var $ = require('jquery'); - require('jquery.select2'); - - var $first = $( - '' - ); - var $second = $first.clone(); - - var $both = $first.add($second); - $both.select2(); - - $both.select2('val', '2'); - - assert.equal( - $first.val(), - '2', - 'The call should change the value on the first element' - ); - assert.equal( - $second.val(), - '2', - 'The call should also change the value on the second element' - ); -}); - -test('initializes when jQuery $.data contains' + - ' cyclic reference', function (assert) { - var $ = require('jquery'); - require('jquery.select2'); - - var $select = $( - '' - ); - - // Add a circular reference object using jQuery. - var recursiveObject = {}; - - recursiveObject.same = recursiveObject; - - $select.data('same', recursiveObject); - - $select.select2(); - - assert.equal( - $select.val(), - '3', - 'The option value should be pulled correctly' - ); -}); - -test('$element.data returns instance and options correctly', - function (assert) { - var $ = require('jquery'); - require('jquery.select2'); - - var $select = $( - '' - ); - - // Initialize. - $select.select2({maximumSelectionLength: 2, multiple: true}); - - assert.equal( - $select.val(), - '3', - 'Only 1 option should be pulled.' - ); - - // Try to resolve instance via .data('select2'). - var $instance = $select.data('select2'); - assert.ok($instance); - assert.ok($instance.options); - - // Ensure $select.data('select2') is the same instance - // created by .select2() - assert.equal($instance, Utils.GetData($instance.$element[0], - 'select2')); - - // Ensure initialized property matches. - assert.equal($instance.options.options.maximumSelectionLength, - 2); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/integration/select2-methods.js b/static/vendor/select2/tests/integration/select2-methods.js deleted file mode 100644 index b1e344a5..00000000 --- a/static/vendor/select2/tests/integration/select2-methods.js +++ /dev/null @@ -1,139 +0,0 @@ -module('select2(data)'); - -var $ = require('jquery'); -var Select2 = require('select2/core'); -var Options = require('select2/options'); - -test('single default selection returned', function (assert) { - var $select = $( - '' - ); - var options = new Options({}); - - var select = new Select2($select, options); - - var items = select.data(); - - assert.equal( - items.length, - 1, - 'The one selected item should be returned' - ); - - var first = items[0]; - - assert.equal( - first.id, - '3', - 'The first option was correct' - ); - - assert.equal( - first.text, - 'Three', - 'The first option was correct' - ); -}); - -test('multiple default selections returned', function (assert) { - var $select = $( - '' - ); - var options = new Options({}); - - var select = new Select2($select, options); - - var items = select.data(); - - assert.equal( - items.length, - 2, - 'The two selected items should be returned' - ); - - var first = items[0]; - - assert.equal( - first.id, - 'One', - 'The first option was correct' - ); - - var second = items[1]; - - assert.equal( - second.id, - '3', - 'The option value should be pulled correctly' - ); -}); - -module('select2(val)'); - -test('single value matches jquery value', function (assert) { - var $select = $( - '' - ); - var options = new Options({}); - - var select = new Select2($select, options); - - var value = select.val(); - - assert.equal( - value, - '3', - 'The value should match the option tag attribute' - ); - - assert.equal( - value, - $select.val(), - 'The value should match the jquery value' - ); -}); - -test('multiple value matches the jquery value', function (assert) { - var $select = $( - '' - ); - var options = new Options({}); - - var select = new Select2($select, options); - - var value = select.val(); - - assert.equal( - value.length, - 2, - 'Two options should be selected' - ); - - assert.deepEqual( - value, - ['One', '3'], - 'The values should match the option tag attribute' - ); - - assert.deepEqual( - value, - $select.val(), - 'The values should match the jquery values' - ); -}); diff --git a/static/vendor/select2/tests/options/ajax-tests.js b/static/vendor/select2/tests/options/ajax-tests.js deleted file mode 100644 index eb65e9ef..00000000 --- a/static/vendor/select2/tests/options/ajax-tests.js +++ /dev/null @@ -1,50 +0,0 @@ -module('Defaults - Ajax'); - -test('options are merged recursively with default options', function (assert) { - var defaults = require('select2/defaults'); - - var ajaxDelay = 250; - var ajaxUrl = 'http://www.test.com'; - - var mergedOptions; - - defaults.set('ajax--delay', ajaxDelay); - - mergedOptions = defaults.apply({ - ajax: { - url: ajaxUrl - } - }); - - assert.equal( - mergedOptions.ajax.delay, - ajaxDelay, - 'Ajax default options are present on the merged options' - ); - - assert.equal( - mergedOptions.ajax.url, - ajaxUrl, - 'Ajax provided options are present on the merged options' - ); - - defaults.reset(); -}); - -test('more than one default option can be changed via set()', function(assert) { - var defaults = require('select2/defaults'); - var ajaxDelay = 123; - var dataDataType = 'xml'; - defaults.set('ajax--delay', ajaxDelay); - defaults.set('ajax--data-type', dataDataType); - - assert.equal( - defaults.defaults.ajax.delay, - ajaxDelay, - 'Both ajax.delay and ajax.dataType present in defaults'); - assert.equal( - defaults.defaults.ajax.dataType, - dataDataType, - 'Both ajax.delay and ajax.dataType present in defaults'); - defaults.reset(); -}); diff --git a/static/vendor/select2/tests/options/data-tests.js b/static/vendor/select2/tests/options/data-tests.js deleted file mode 100644 index 107a2f0c..00000000 --- a/static/vendor/select2/tests/options/data-tests.js +++ /dev/null @@ -1,44 +0,0 @@ -module('Options - Attributes'); - -var $ = require('jquery'); - -var Options = require('select2/options'); - -test('no nesting', function (assert) { - var $test = $(''); - - var options = new Options({}, $test); - - assert.equal(options.get('test'), 'test'); -}); - -test('with nesting', function (assert) { - var $test = $(''); - - if ($test[0].dataset == null) { - assert.ok( - true, - 'We can not run this test with jQuery 1.x if dataset is not implemented' - ); - - return; - } - - var options = new Options({}, $test); - - assert.ok(!(options.get('first-Second'))); - assert.equal(options.get('first').second, 'test'); -}); - -test('overrides initialized data', function (assert) { - var $test = $(''); - - var options = new Options({ - options: 'yes', - override: 'no' - }, $test); - - assert.equal(options.get('options'), 'yes'); - assert.equal(options.get('override'), 'yes'); - assert.equal(options.get('data'), 'yes'); -}); diff --git a/static/vendor/select2/tests/options/deprecated-tests.js b/static/vendor/select2/tests/options/deprecated-tests.js deleted file mode 100644 index a51bba31..00000000 --- a/static/vendor/select2/tests/options/deprecated-tests.js +++ /dev/null @@ -1,250 +0,0 @@ -module('Options - Deprecated - initSelection'); - -var $ = require('jquery'); -var Options = require('select2/options'); - -test('converted into dataAdapter.current', function (assert) { - assert.expect(5); - - var $test = $(''); - var called = false; - - var options = new Options({ - initSelection: function ($element, callback) { - called = true; - - callback([{ - id: '1', - text: '2' - }]); - } - }, $test); - - assert.ok(!called, 'initSelection should not have been called'); - - var DataAdapter = options.get('dataAdapter'); - var data = new DataAdapter($test, options); - - data.current(function (data) { - assert.equal( - data.length, - 1, - 'There should have only been one object selected' - ); - - var item = data[0]; - - assert.equal( - item.id, - '1', - 'The id should have been set by initSelection' - ); - - assert.equal( - item.text, - '2', - 'The text should have been set by initSelection' - ); - }); - - assert.ok(called, 'initSelection should have been called'); -}); - -test('single option converted to array automatically', function (assert) { - assert.expect(2); - - var $test = $(''); - var called = false; - - var options = new Options({ - initSelection: function ($element, callback) { - called = true; - - callback({ - id: '1', - text: '2' - }); - } - }, $test); - - var DataAdapter = options.get('dataAdapter'); - var data = new DataAdapter($test, options); - - data.current(function (data) { - assert.ok( - $.isArray(data), - 'The data should have been converted to an array' - ); - }); - - assert.ok(called, 'initSelection should have been called'); -}); - -test('only called once', function (assert) { - assert.expect(8); - - var $test = $(''); - var called = 0; - - var options = new Options({ - initSelection: function ($element, callback) { - called++; - - callback([{ - id: '1', - text: '2' - }]); - } - }, $test); - - var DataAdapter = options.get('dataAdapter'); - var data = new DataAdapter($test, options); - - data.current(function (data) { - assert.equal( - data.length, - 1, - 'There should have only been a single option' - ); - - var item = data[0]; - - assert.equal( - item.id, - '1', - 'The id should match the one given by initSelection' - ); - - assert.equal( - item.text, - '2', - 'The text should match the one given by initSelection' - ); - }); - - assert.equal( - called, - 1, - 'initSelection should have been called' - ); - - data.current(function (data) { - assert.equal( - data.length, - 1, - 'There should have only been a single option' - ); - - var item = data[0]; - - assert.equal( - item.id, - '3', - 'The id should match the value given in the DOM' - ); - - assert.equal( - item.text, - '4', - 'The text should match the text given in the DOM' - ); - }); - - assert.equal( - called, - 1, - 'initSelection should have only been called once' - ); -}); - -module('Options - Deprecated - query'); - -test('converted into dataAdapter.query automatically', function (assert) { - assert.expect(6); - - var $test = $(''); - var called = false; - - var options = new Options({ - query: function (params) { - called = true; - - params.callback({ - results: [ - { - id: 'test', - text: params.term - } - ] - }); - } - }, $test); - - assert.ok(!called, 'The query option should not have been called'); - - var DataAdapter = options.get('dataAdapter'); - var data = new DataAdapter($test, options); - - data.query({ - term: 'term' - }, function (data) { - assert.ok( - 'results' in data, - 'It should have included the results key' - ); - - assert.equal( - data.results.length, - 1, - 'There should have only been a single result returned' - ); - - var item = data.results[0]; - - assert.equal( - item.id, - 'test', - 'The id should have been returned from the query function' - ); - - assert.equal( - item.text, - 'term', - 'The text should have matched the term that was passed in' - ); - }); - - assert.ok(called, 'The query function should have been called'); -}); - -module('Options - deprecated - data-ajax-url'); - -test('converted ajax-url to ajax--url automatically', function (assert) { - var $test = $(''); - var options = new Options({}, $test); - - assert.ok( - options.get('ajax'), - 'The `ajax` key was automatically created' - ); - assert.equal( - options.get('ajax').url, - 'test://url', - 'The `url` property for the `ajax` option was filled in correctly' - ); -}); - -test('converted select2-tags to data/tags automatically', function (assert) { - var $test = $(''); - var options = new Options({}, $test); - - assert.ok( - options.get('tags'), - 'The `tags` key is automatically set to true' - ); - assert.equal( - options.get('data'), - 'original data', - 'The `data` key is created with the original data' - ); -}); diff --git a/static/vendor/select2/tests/options/translation-tests.js b/static/vendor/select2/tests/options/translation-tests.js deleted file mode 100644 index ab433b61..00000000 --- a/static/vendor/select2/tests/options/translation-tests.js +++ /dev/null @@ -1,28 +0,0 @@ -module('Options - Translations'); - -var $ = require('jquery'); -var Options = require('select2/options'); - -test('partial dictionaries can be passed', function (assert) { - var options = new Options({ - language: { - searching: function () { - return 'Something'; - } - } - }); - - var translations = options.get('translations'); - - assert.equal( - translations.get('searching')(), - 'Something', - 'The partial dictionary still overrides translations' - ); - - assert.equal( - translations.get('noResults')(), - 'No results found', - 'You can still get English translations for keys not passed in' - ); -}); diff --git a/static/vendor/select2/tests/options/width-tests.js b/static/vendor/select2/tests/options/width-tests.js deleted file mode 100644 index e7240349..00000000 --- a/static/vendor/select2/tests/options/width-tests.js +++ /dev/null @@ -1,66 +0,0 @@ -module('Options - Width'); - -var $ = require('jquery'); - -var Select2 = require('select2/core'); -var select = new Select2($('')); - -test('string passed as width', function (assert) { - var $test = $(''); - - var width = select._resolveWidth($test, '80%'); - - assert.equal(width, '80%'); -}); - -test('width from style attribute', function (assert) { - var $test = $(''); - - var width = select._resolveWidth($test, 'style'); - - assert.equal(width, null); -}); - -test('width from computed element width', function (assert) { - var $style = $( - '' - ); - var $test = $(''); - - $('#qunit-fixture').append($style); - $('#qunit-fixture').append($test); - - var width = select._resolveWidth($test, 'element'); - - assert.equal(width, '500px'); -}); - -test('resolve gets the style if it is there', function (assert) { - var $test = $(''); - - $('#qunit-fixture').append($style); - $('#qunit-fixture').append($test); - - var width = select._resolveWidth($test, 'resolve'); - - assert.equal(width, '500px'); -}); diff --git a/static/vendor/select2/tests/results/focusing-tests.js b/static/vendor/select2/tests/results/focusing-tests.js deleted file mode 100644 index 08a2ef3c..00000000 --- a/static/vendor/select2/tests/results/focusing-tests.js +++ /dev/null @@ -1,241 +0,0 @@ -module('Results - highlighting results'); - -test('results:all with no data skips results:focus', function (assert) { - assert.expect(0); - - var $ = require('jquery'); - - var $select = $(''); - var $parent = $('
        '); - - var $container = $(''); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $select.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Results = require('select2/results'); - - var results = new Results($select, new Options({})); - - // Fake the data adapter for the `setClasses` method - results.data = {}; - results.data.current = function (callback) { - callback([{ id: 'test' }]); - }; - - results.render(); - - results.bind(container, $container); - - results.on('results:focus', function (params) { - assert.ok(false, 'The results:focus event was triggered'); - }); - - container.trigger('results:all', { - data: { - results: [] - } - }); -}); - -test('results:all triggers results:focus on the first item', function (assert) { - assert.expect(2); - - var $ = require('jquery'); - - var $select = $(''); - var $parent = $('
        '); - - var $container = $(''); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $select.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Results = require('select2/results'); - - var results = new Results($select, new Options({})); - - // Fake the data adapter for the `setClasses` method - results.data = {}; - results.data.current = function (callback) { - callback([{ id: 'test' }]); - }; - - results.render(); - - results.bind(container, $container); - - results.on('results:focus', function (params) { - assert.equal(params.data.id, 'test'); - assert.equal(params.data.text, 'Test'); - }); - - container.trigger('results:all', { - data: { - results: [ - { - id: 'test', - text: 'Test' - } - ] - } - }); -}); - -test('results:append does not trigger results:focus', function (assert) { - assert.expect(0); - - var $ = require('jquery'); - - var $select = $(''); - var $parent = $('
        '); - - var $container = $(''); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $select.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Results = require('select2/results'); - - var results = new Results($select, new Options({})); - - // Fake the data adapter for the `setClasses` method - results.data = {}; - results.data.current = function (callback) { - callback([{ id: 'test' }]); - }; - - results.render(); - - results.bind(container, $container); - - results.on('results:focus', function () { - assert.ok(false, 'The results:focus event was triggered'); - }); - - container.trigger('results:append', { - data: { - results: [ - { - id: 'test', - text: 'Test' - } - ] - } - }); -}); - -test('scrollAfterSelect triggers results:focus', function (assert) { - assert.expect(3); - - var $ = require('jquery'); - - var $select = $(''); - var $parent = $('
        '); - - var $container = $(''); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $select.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Results = require('select2/results'); - - var options = new Options({ scrollAfterSelect: true }); - var results = new Results($select, options); - - // Fake the data adapter for the `setClasses` method - results.data = {}; - results.data.current = function (callback) { - callback([{ id: 'test' }]); - }; - - results.render(); - - results.bind(container, $container); - - // check that default for scrollAfterSelect is true - assert.equal(options.get('scrollAfterSelect'), true); - - results.append({ - results: [ - { - id: 'test', - text: 'Test' - } - ] - }); - - results.on('results:focus', function (params) { - assert.equal(params.data.id, 'test'); - assert.equal(params.data.text, 'Test'); - }); - - container.trigger('select', {}); -}); - -test('!scrollAfterSelect does not trigger results:focus', function (assert) { - assert.expect(1); - - var $ = require('jquery'); - - var $select = $(''); - var $parent = $('
        '); - - var $container = $(''); - var container = new MockContainer(); - - $parent.appendTo($('#qunit-fixture')); - $select.appendTo($parent); - - var Utils = require('select2/utils'); - var Options = require('select2/options'); - - var Results = require('select2/results'); - - var options = new Options({ scrollAfterSelect: false }); - var results = new Results($select, options); - - // Fake the data adapter for the `setClasses` method - results.data = {}; - results.data.current = function (callback) { - callback([{ id: 'test' }]); - }; - - results.render(); - - results.bind(container, $container); - - // check that default for scrollAfterSelect is false - assert.equal(options.get('scrollAfterSelect'), false); - - results.append({ - results: [ - { - id: 'test', - text: 'Test' - } - ] - }); - - results.on('results:focus', function () { - assert.ok(false, 'The results:focus event was triggered'); - }); - - container.trigger('select', {}); -}); diff --git a/static/vendor/select2/tests/selection/allowClear-tests.js b/static/vendor/select2/tests/selection/allowClear-tests.js deleted file mode 100644 index a7ca726d..00000000 --- a/static/vendor/select2/tests/selection/allowClear-tests.js +++ /dev/null @@ -1,330 +0,0 @@ -module('Selection containers - Placeholders - Allow clear'); - -var Placeholder = require('select2/selection/placeholder'); -var AllowClear = require('select2/selection/allowClear'); - -var SingleSelection = require('select2/selection/single'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var AllowClearPlaceholder = Utils.Decorate( - Utils.Decorate(SingleSelection, Placeholder), - AllowClear -); - -var allowClearOptions = new Options({ - placeholder: { - id: 'placeholder', - text: 'This is the placeholder' - }, - allowClear: true -}); - -test('clear is not displayed for single placeholder', function (assert) { - var selection = new AllowClearPlaceholder( - $('#qunit-fixture .single-with-placeholder'), - allowClearOptions - ); - - var $selection = selection.render(); - - selection.update([{ - id: 'placeholder' - }]); - - assert.equal( - $selection.find('.select2-selection__clear').length, - 0, - 'The clear icon should not be displayed' - ); -}); - -test('clear is not displayed for multiple placeholder', function (assert) { - var selection = new AllowClearPlaceholder( - $('#qunit-fixture .multiple'), - allowClearOptions - ); - - var $selection = selection.render(); - - selection.update([]); - - assert.equal( - $selection.find('.select2-selection__clear').length, - 0, - 'The clear icon should not be displayed' - ); -}); - - -test('clear is displayed for placeholder', function (assert) { - var selection = new AllowClearPlaceholder( - $('#qunit-fixture .single-with-placeholder'), - allowClearOptions - ); - - var $selection = selection.render(); - - selection.update([{ - id: 'one', - test: 'one' - }]); - - assert.equal( - $selection.find('.select2-selection__clear').length, - 1, - 'The clear icon should be displayed' - ); -}); - -test('clear icon should have title displayed', function (assert) { - var selection = new AllowClearPlaceholder( - $('#qunit-fixture .single-with-placeholder'), - allowClearOptions - ); - - var $selection = selection.render(); - - selection.update([{ - id: 'one', - test: 'one' - }]); - - assert.equal( - $selection.find('.select2-selection__clear').attr('title'), - 'Remove all items', - 'The clear icon should have title displayed' - ); -}); - -test('clicking clear will set the placeholder value', function (assert) { - var $element = $('#qunit-fixture .single-with-placeholder'); - - var selection = new AllowClearPlaceholder( - $element, - allowClearOptions - ); - var container = new MockContainer(); - - var $selection = selection.render(); - - selection.bind(container, $('
        ')); - - $element.val('One'); - selection.update([{ - id: 'One', - text: 'One' - }]); - - var $remove = $selection.find('.select2-selection__clear'); - $remove.trigger('mousedown'); - - assert.equal( - $element.val(), - 'placeholder', - 'The value should have been reset to the placeholder' - ); -}); - -test('clicking clear will trigger the unselect event', function (assert) { - assert.expect(4); - - var $element = $('#qunit-fixture .single-with-placeholder'); - - var selection = new AllowClearPlaceholder( - $element, - allowClearOptions - ); - var container = new MockContainer(); - - var $selection = selection.render(); - - selection.bind(container, $('
        ')); - - $element.val('One'); - selection.update([{ - id: 'One', - text: 'One' - }]); - - selection.on('unselect', function (ev) { - assert.ok( - 'data' in ev && ev.data, - 'The event should have been triggered with the data property' - ); - - assert.ok( - $.isPlainObject(ev.data), - 'The data should be an object' - ); - - assert.equal( - ev.data.id, - 'One', - 'The data should be the unselected object' - ); - - assert.equal( - $element.val(), - 'placeholder', - 'The previous value should be unselected' - ); - }); - - var $remove = $selection.find('.select2-selection__clear'); - $remove.trigger('mousedown'); -}); - -test('preventing the unselect event cancels the clearing', function (assert) { - var $element = $('#qunit-fixture .single-with-placeholder'); - - var selection = new AllowClearPlaceholder( - $element, - allowClearOptions - ); - var container = new MockContainer(); - - var $selection = selection.render(); - - selection.bind(container, $('
        ')); - - $element.val('One'); - selection.update([{ - id: 'One', - text: 'One' - }]); - - selection.on('unselect', function (ev) { - ev.prevented = true; - }); - - var $remove = $selection.find('.select2-selection__clear'); - $remove.trigger('mousedown'); - - assert.equal( - $element.val(), - 'One', - 'The placeholder should not have been set' - ); -}); - -test('clicking clear will trigger the clear event', function (assert) { - assert.expect(5); - - var $element = $('#qunit-fixture .single-with-placeholder'); - - var selection = new AllowClearPlaceholder( - $element, - allowClearOptions - ); - var container = new MockContainer(); - - var $selection = selection.render(); - - selection.bind(container, $('
        ')); - - $element.val('One'); - selection.update([{ - id: 'One', - text: 'One' - }]); - - selection.on('clear', function (ev) { - assert.ok( - 'data' in ev && ev.data, - 'The event should have been triggered with the data property' - ); - - assert.ok( - $.isArray(ev.data), - 'The data should be an array' - ); - - assert.equal( - ev.data.length, - 1, - 'The data should contain one item for each value' - ); - - assert.equal( - ev.data[0].id, - 'One', - 'The data should contain unselected objects' - ); - - assert.equal( - $element.val(), - 'placeholder', - 'The previous value should be unselected' - ); - }); - - var $remove = $selection.find('.select2-selection__clear'); - $remove.trigger('mousedown'); -}); - -test('preventing the clear event cancels the clearing', function (assert) { - var $element = $('#qunit-fixture .single-with-placeholder'); - - var selection = new AllowClearPlaceholder( - $element, - allowClearOptions - ); - var container = new MockContainer(); - - var $selection = selection.render(); - - selection.bind(container, $('
        ')); - - $element.val('One'); - selection.update([{ - id: 'One', - text: 'One' - }]); - - selection.on('clear', function (ev) { - ev.prevented = true; - }); - - var $remove = $selection.find('.select2-selection__clear'); - $remove.trigger('mousedown'); - - assert.equal( - $element.val(), - 'One', - 'The placeholder should not have been set' - ); -}); - -test('clear does not work when disabled', function (assert) { - var $element = $('#qunit-fixture .single-with-placeholder'); - - var selection = new AllowClearPlaceholder( - $element, - allowClearOptions - ); - var container = new MockContainer(); - - var $selection = selection.render(); - - selection.bind(container, $('
        ')); - - selection.update([{ - id: 'One', - text: 'One' - }]); - - $element.val('One'); - selection.options.set('disabled', true); - - var $remove = $selection.find('.select2-selection__clear'); - $remove.trigger('mousedown'); - - assert.equal( - $element.val(), - 'One', - 'The placeholder should not have been set' - ); -}); diff --git a/static/vendor/select2/tests/selection/containerCss-tests.js b/static/vendor/select2/tests/selection/containerCss-tests.js deleted file mode 100644 index 522703a2..00000000 --- a/static/vendor/select2/tests/selection/containerCss-tests.js +++ /dev/null @@ -1,104 +0,0 @@ -module('Dropdown - containerCssClass compatibility'); - -var $ = require('jquery'); -var Utils = require('select2/utils'); -var Options = require('select2/options'); - -var SingleSelection = require('select2/selection/single'); -var ContainerCSS = Utils.Decorate( - SingleSelection, - require('select2/compat/containerCss') -); - -test('all classes will be copied if :all: is used', function (assert) { - var $element = $(''); - var options = new Options({ - containerCssClass: ':all:' - }); - - var select = new ContainerCSS($element, options); - var $container = select.render(); - - assert.ok($container.hasClass('test')); - assert.ok($container.hasClass('copy')); - assert.ok($container.hasClass('works')); - assert.ok(!$container.hasClass(':all:')); -}); - -test(':all: can be used with other classes', function (assert) { - var $element = $(''); - var options = new Options({ - containerCssClass: ':all: other' - }); - - var select = new ContainerCSS($element, options); - var $container = select.render(); - - assert.ok($container.hasClass('test')); - assert.ok($container.hasClass('copy')); - assert.ok($container.hasClass('works')); - assert.ok($container.hasClass('other')); - assert.ok(!$container.hasClass(':all:')); -}); - -test('classes can be passed in as a string', function (assert) { - var $element = $(''); - var options = new Options({ - containerCssClass: 'other' - }); - - var select = new ContainerCSS($element, options); - var $container = select.render(); - - assert.ok($container.hasClass('other')); -}); - -test('a function can be used based on the element', function (assert){ - var $element = $(''); - var options = new Options({ - containerCssClass: function ($element) { - return 'function'; - } - }); - - var select = new ContainerCSS($element, options); - var $container = select.render(); - - assert.ok($container.hasClass('function')); - assert.ok(!$container.hasClass('test')); -}); - -test(':all: works around custom adapters', function (assert) { - var $element = $(''); - var options = new Options({ - containerCssClass: ':all: something', - adaptContainerCssClass: function (clazz) { - return clazz + '-modified'; - } - }); - - var select = new ContainerCSS($element, options); - var $container = select.render(); - - assert.ok($container.hasClass('something')); - - assert.ok($container.hasClass('test')); - assert.ok($container.hasClass('test-modified')); -}); - -module('Selection - adaptContainerCss compatibility'); - -test('only return when adapted', function (assert) { - var $element = $(''); - var options = new Options({ - adaptContainerCssClass: function (clazz) { - return 'modified'; - } - }); - - var select = new ContainerCSS($element, options); - var $container = select.render(); - - assert.ok(!$container.hasClass('original')); - assert.ok($container.hasClass('modified')); -}); diff --git a/static/vendor/select2/tests/selection/multiple-tests.js b/static/vendor/select2/tests/selection/multiple-tests.js deleted file mode 100644 index 5e996ea6..00000000 --- a/static/vendor/select2/tests/selection/multiple-tests.js +++ /dev/null @@ -1,151 +0,0 @@ -module('Selection containers - Multiple'); - -var MultipleSelection = require('select2/selection/multiple'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var options = new Options({}); - -test('display uses templateSelection', function (assert) { - var called = false; - - var templateOptions = new Options({ - templateSelection: function (data) { - called = true; - - return data.text; - } - }); - - var selection = new MultipleSelection( - $('#qunit-fixture .multiple'), - templateOptions - ); - - var out = selection.display({ - text: 'test' - }); - - assert.ok(called); - - assert.equal(out, 'test'); -}); - -test('templateSelection can addClass', function (assert) { - var called = false; - - var templateOptions = new Options({ - templateSelection: function (data, container) { - called = true; - container.addClass('testclass'); - return data.text; - } - }); - - var selection = new MultipleSelection( - $('#qunit-fixture .multiple'), - templateOptions - ); - - var $container = selection.selectionContainer(); - - var out = selection.display({ - text: 'test' - }, $container); - - assert.ok(called); - - assert.equal(out, 'test'); - - assert.ok($container.hasClass('testclass')); -}); - -test('empty update clears the selection', function (assert) { - var selection = new MultipleSelection( - $('#qunit-fixture .multiple'), - options - ); - - var $selection = selection.render(); - var $rendered = $selection.find('.select2-selection__rendered'); - - $rendered.text('testing'); - $rendered.attr('title', 'testing'); - - selection.update([]); - - assert.equal($rendered.text(), ''); - assert.equal($rendered.attr('title'), undefined); -}); - -test('escapeMarkup is being used', function (assert) { - var selection = new MultipleSelection( - $('#qunit-fixture .multiple'), - options - ); - - var $selection = selection.render(); - var $rendered = $selection.find('.select2-selection__rendered'); - - var unescapedText = ''; - - selection.update([{ - text: unescapedText - }]); - - assert.equal( - $rendered.text().substr(1), - unescapedText, - 'The text should be escaped by default to prevent injection' - ); -}); - -test('clear button respects the disabled state', function (assert) { - var options = new Options({ - disabled: true - }); - - var $select = $('#qunit-fixture .multiple'); - - var container = new MockContainer(); - var $container = $('
        '); - - var selection = new MultipleSelection( - $select, - options - ); - - var $selection = selection.render(); - $container.append($selection); - - selection.bind(container, $container); - - // Select an option - selection.update([{ - text: 'Test' - }]); - - var $rendered = $selection.find('.select2-selection__rendered'); - - var $pill = $rendered.find('.select2-selection__choice'); - - assert.equal($pill.length, 1, 'There should only be one selection'); - - var $remove = $pill.find('.select2-selection__choice__remove'); - - assert.equal( - $remove.length, - 1, - 'The remove icon is displayed for the selection' - ); - - // Set up the unselect handler - selection.on('unselect', function (params) { - assert.ok(false, 'The unselect handler should not be triggered'); - }); - - // Trigger the handler for the remove icon - $remove.trigger('click'); -}); diff --git a/static/vendor/select2/tests/selection/placeholder-tests.js b/static/vendor/select2/tests/selection/placeholder-tests.js deleted file mode 100644 index 8a436ff3..00000000 --- a/static/vendor/select2/tests/selection/placeholder-tests.js +++ /dev/null @@ -1,74 +0,0 @@ -module('Selection containers - Placeholders'); - -var Placeholder = require('select2/selection/placeholder'); -var SingleSelection = require('select2/selection/single'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var SinglePlaceholder = Utils.Decorate(SingleSelection, Placeholder); - -var placeholderOptions = new Options({ - placeholder: { - id: 'placeholder', - text: 'This is the placeholder' - } -}); - -test('normalizing placeholder ignores objects', function (assert) { - var selection = new SinglePlaceholder( - $('#qunit-fixture .single'), - placeholderOptions - ); - - var original = { - id: 'test', - text: 'testing' - }; - - var normalized = selection.normalizePlaceholder(original); - - assert.equal(original, normalized); -}); - -test('normalizing placeholder gives object for string', function (assert) { - var selection = new SinglePlaceholder( - $('#qunit-fixture .single'), - placeholderOptions - ); - - var normalized = selection.normalizePlaceholder('placeholder'); - - assert.equal(normalized.id, ''); - assert.equal(normalized.text, 'placeholder'); -}); - - -test('text is shown for placeholder option on single', function (assert) { - var selection = new SinglePlaceholder( - $('#qunit-fixture .single'), - placeholderOptions - ); - - var $selection = selection.render(); - - selection.update([{ - id: 'placeholder' - }]); - - assert.equal($selection.text(), 'This is the placeholder'); -}); - -test('placeholder is shown when no options are selected', function (assert) { - var selection = new SinglePlaceholder( - $('#qunit-fixture .multiple'), - placeholderOptions - ); - - var $selection = selection.render(); - - selection.update([]); - - assert.equal($selection.text(), 'This is the placeholder'); -}); diff --git a/static/vendor/select2/tests/selection/search-tests.js b/static/vendor/select2/tests/selection/search-tests.js deleted file mode 100644 index 43345d72..00000000 --- a/static/vendor/select2/tests/selection/search-tests.js +++ /dev/null @@ -1,191 +0,0 @@ -module('Selection containers - Inline search'); - -var MultipleSelection = require('select2/selection/multiple'); -var InlineSearch = require('select2/selection/search'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var options = new Options({}); - -test('backspace will remove a choice', function (assert) { - assert.expect(3); - - var KEYS = require('select2/keys'); - - var $container = $('#qunit-fixture .event-container'); - var container = new MockContainer(); - - var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch); - - var $element = $('#qunit-fixture .multiple'); - var selection = new CustomSelection($element, options); - - var $selection = selection.render(); - selection.bind(container, $container); - - // The unselect event should be triggered at some point - selection.on('unselect', function () { - assert.ok(true, 'A choice was unselected'); - }); - - // Add some selections and render the search - selection.update([ - { - id: '1', - text: 'One' - } - ]); - - var $search = $selection.find('input'); - var $choices = $selection.find('.select2-selection__choice'); - - assert.equal($search.length, 1, 'The search was visible'); - assert.equal($choices.length, 1, 'The choice was rendered'); - - // Trigger the backspace on the search - var backspace = $.Event('keydown', { - which: KEYS.BACKSPACE - }); - $search.trigger(backspace); -}); - -test('backspace will set the search text', function (assert) { - assert.expect(3); - - var KEYS = require('select2/keys'); - - var $container = $('#qunit-fixture .event-container'); - var container = new MockContainer(); - - var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch); - - var $element = $('#qunit-fixture .multiple'); - var selection = new CustomSelection($element, options); - - var $selection = selection.render(); - selection.bind(container, $container); - - // Add some selections and render the search - selection.update([ - { - id: '1', - text: 'One' - } - ]); - - var $search = $selection.find('input'); - var $choices = $selection.find('.select2-selection__choice'); - - assert.equal($search.length, 1, 'The search was visible'); - assert.equal($choices.length, 1, 'The choice was rendered'); - - // Trigger the backspace on the search - var backspace = $.Event('keydown', { - which: KEYS.BACKSPACE - }); - $search.trigger(backspace); - - assert.equal($search.val(), 'One', 'The search text was set'); -}); - -test('updating selection does not shift the focus', function (assert) { - // Check for IE 8, which triggers a false negative during testing - if (window.attachEvent && !window.addEventListener) { - // We must expect 0 assertions or the test will fail - assert.expect(0); - return; - } - - var $container = $('#qunit-fixture .event-container'); - var container = new MockContainer(); - - var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch); - - var $element = $('#qunit-fixture .multiple'); - var selection = new CustomSelection($element, options); - - var $selection = selection.render(); - selection.bind(container, $container); - - // Update the selection so the search is rendered - selection.update([]); - - // Make it visible so the browser can place focus on the search - $container.append($selection); - - var $search = $selection.find('input'); - $search.trigger('focus'); - - assert.equal($search.length, 1, 'The search was not visible'); - - assert.equal( - document.activeElement, - $search[0], - 'The search did not have focus originally' - ); - - // Trigger an update, this should redraw the search box - selection.update([]); - - assert.equal($search.length, 1, 'The search box disappeared'); - - assert.equal( - document.activeElement, - $search[0], - 'The search did not have focus after the selection was updated' - ); -}); - -test('the focus event shifts the focus', function (assert) { - // Check for IE 8, which triggers a false negative during testing - if (window.attachEvent && !window.addEventListener) { - // We must expect 0 assertions or the test will fail - assert.expect(0); - return; - } - - var $container = $('#qunit-fixture .event-container'); - var container = new MockContainer(); - - var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch); - - var $element = $('#qunit-fixture .multiple'); - var selection = new CustomSelection($element, options); - - var $selection = selection.render(); - selection.bind(container, $container); - - // Update the selection so the search is rendered - selection.update([]); - - // Make it visible so the browser can place focus on the search - $container.append($selection); - - // The search should not be automatically focused - - var $search = $selection.find('input'); - - assert.notEqual( - document.activeElement, - $search[0], - 'The search had focus originally' - ); - - assert.equal($search.length, 1, 'The search was not visible'); - - // Focus the container - - container.trigger('focus'); - - // Make sure it focuses the search - - assert.equal($search.length, 1, 'The search box disappeared'); - - assert.equal( - document.activeElement, - $search[0], - 'The search did not have focus originally' - ); -}); \ No newline at end of file diff --git a/static/vendor/select2/tests/selection/single-tests.js b/static/vendor/select2/tests/selection/single-tests.js deleted file mode 100644 index 9ab16367..00000000 --- a/static/vendor/select2/tests/selection/single-tests.js +++ /dev/null @@ -1,119 +0,0 @@ -module('Selection containers - Single'); - -var SingleSelection = require('select2/selection/single'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var options = new Options({}); - -test('display uses templateSelection', function (assert) { - var called = false; - - var templateOptions = new Options({ - templateSelection: function (data) { - called = true; - - return data.text; - } - }); - - var selection = new SingleSelection( - $('#qunit-fixture .single'), - templateOptions - ); - - var out = selection.display({ - text: 'test' - }); - - assert.ok(called); - - assert.equal(out, 'test'); -}); - -test('templateSelection can addClass', function (assert) { - var called = false; - - var templateOptions = new Options({ - templateSelection: function (data, container) { - called = true; - container.addClass('testclass'); - return data.text; - } - }); - - var selection = new SingleSelection( - $('#qunit-fixture .single'), - templateOptions - ); - - var $container = selection.selectionContainer(); - - var out = selection.display({ - text: 'test' - }, $container); - - assert.ok(called); - - assert.equal(out, 'test'); - - assert.ok($container.hasClass('testclass')); -}); - -test('empty update clears the selection', function (assert) { - var selection = new SingleSelection( - $('#qunit-fixture .single'), - options - ); - - var $selection = selection.render(); - var $rendered = $selection.find('.select2-selection__rendered'); - - $rendered.text('testing'); - $rendered.attr('title', 'testing'); - - selection.update([]); - - assert.equal($rendered.text(), ''); - assert.equal($rendered.attr('title'), undefined); -}); - -test('update renders the data text', function (assert) { - var selection = new SingleSelection( - $('#qunit-fixture .single'), - options - ); - - var $selection = selection.render(); - var $rendered = $selection.find('.select2-selection__rendered'); - - selection.update([{ - text: 'test' - }]); - - assert.equal($rendered.text(), 'test'); -}); - -test('escapeMarkup is being used', function (assert) { - var selection = new SingleSelection( - $('#qunit-fixture .single'), - options - ); - - var $selection = selection.render(); - var $rendered = $selection.find('.select2-selection__rendered'); - - var unescapedText = ''; - - selection.update([{ - text: unescapedText - }]); - - assert.equal( - $rendered.text(), - unescapedText, - 'The text should be escaped by default to prevent injection' - ); -}); diff --git a/static/vendor/select2/tests/selection/stopPropagation-tests.js b/static/vendor/select2/tests/selection/stopPropagation-tests.js deleted file mode 100644 index d8d8897b..00000000 --- a/static/vendor/select2/tests/selection/stopPropagation-tests.js +++ /dev/null @@ -1,33 +0,0 @@ -module('Selection containers - Stoping event propagation'); - -var SingleSelection = require('select2/selection/single'); -var StopPropagation = require('select2/selection/stopPropagation'); - -var $ = require('jquery'); -var Options = require('select2/options'); -var Utils = require('select2/utils'); - -var CutomSelection = Utils.Decorate(SingleSelection, StopPropagation); - -var options = new Options(); - -test('click event does not propagate', function (assert) { - assert.expect(1); - - var $container = $('#qunit-fixture .event-container'); - var container = new MockContainer(); - - var selection = new CutomSelection($('#qunit-fixture select'), options); - - var $selection = selection.render(); - selection.bind(container, $container); - - $container.append($selection); - $container.on('click', function () { - assert.ok(false, 'The click event should have been stopped'); - }); - - $selection.trigger('click'); - - assert.ok(true, 'Something went wrong if this failed'); -}); diff --git a/static/vendor/select2/tests/unit-jq1.html b/static/vendor/select2/tests/unit-jq1.html deleted file mode 100644 index 7de1bfa2..00000000 --- a/static/vendor/select2/tests/unit-jq1.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - -
        -
        -
        - -
        - - - - - - - - - - - - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/vendor/select2/tests/unit-jq2.html b/static/vendor/select2/tests/unit-jq2.html deleted file mode 100644 index eb7eb12d..00000000 --- a/static/vendor/select2/tests/unit-jq2.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - -
        -
        -
        - -
        - - - - - - - - - - - - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/vendor/select2/tests/unit-jq3.html b/static/vendor/select2/tests/unit-jq3.html deleted file mode 100644 index a14fa9e6..00000000 --- a/static/vendor/select2/tests/unit-jq3.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - -
        -
        -
        - -
        - - - - - - - - - - - - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/vendor/select2/tests/utils/decorator-tests.js b/static/vendor/select2/tests/utils/decorator-tests.js deleted file mode 100644 index 4888991f..00000000 --- a/static/vendor/select2/tests/utils/decorator-tests.js +++ /dev/null @@ -1,189 +0,0 @@ -module('Decorators'); - -var Utils = require('select2/utils'); - -test('overridden - method', function (assert) { - function BaseClass () {} - - BaseClass.prototype.hello = function () { - return 'A'; - }; - - function DecoratorClass () {} - - DecoratorClass.prototype.hello = function () { - return 'B'; - }; - - var DecoratedClass = Utils.Decorate(BaseClass, DecoratorClass); - - var inst = new DecoratedClass(); - - assert.strictEqual(inst.hello(), 'B'); -}); - -test('overridden - constructor', function (assert) { - function BaseClass () { - this.inherited = true; - } - - BaseClass.prototype.hello = function () { - return 'A'; - }; - - function DecoratorClass (decorated) { - this.called = true; - } - - DecoratorClass.prototype.other = function () { - return 'B'; - }; - - var DecoratedClass = Utils.Decorate(BaseClass, DecoratorClass); - - var inst = new DecoratedClass(); - - assert.ok(inst.called); - assert.ok(!inst.inherited); -}); - -test('not overridden - method', function (assert) { - function BaseClass () {} - - BaseClass.prototype.hello = function () { - return 'A'; - }; - - function DecoratorClass () {} - - DecoratorClass.prototype.other = function () { - return 'B'; - }; - - var DecoratedClass = Utils.Decorate(BaseClass, DecoratorClass); - - var inst = new DecoratedClass(); - - assert.strictEqual(inst.hello(), 'A'); -}); - -test('not overridden - constructor', function (assert) { - function BaseClass () { - this.called = true; - } - - BaseClass.prototype.hello = function () { - return 'A'; - }; - - function DecoratorClass () {} - - DecoratorClass.prototype.other = function () { - return 'B'; - }; - - var DecoratedClass = Utils.Decorate(BaseClass, DecoratorClass); - - var inst = new DecoratedClass(); - - assert.ok(inst.called); -}); - -test('inherited - method', function (assert) { - function BaseClass () {} - - BaseClass.prototype.hello = function () { - return 'A'; - }; - - function DecoratorClass (decorated) {} - - DecoratorClass.prototype.hello = function (decorated) { - return 'B' + decorated.call(this) + 'C'; - }; - - var DecoratedClass = Utils.Decorate(BaseClass, DecoratorClass); - - var inst = new DecoratedClass(); - - assert.strictEqual(inst.hello(), 'BAC'); -}); - -test('inherited - constructor', function (assert) { - function BaseClass () { - this.inherited = true; - } - - BaseClass.prototype.hello = function () { - return 'A'; - }; - - function DecoratorClass (decorated) { - this.called = true; - - decorated.call(this); - } - - DecoratorClass.prototype.other = function () { - return 'B'; - }; - - var DecoratedClass = Utils.Decorate(BaseClass, DecoratorClass); - - var inst = new DecoratedClass(); - - assert.ok(inst.called); - assert.ok(inst.inherited); -}); - -test('inherited - three levels', function (assert) { - function BaseClass (testArgument) { - this.baseCalled = true; - this.baseTestArgument = testArgument; - } - - BaseClass.prototype.test = function (a) { - return a + 'c'; - }; - - function MiddleClass (decorated, testArgument) { - this.middleCalled = true; - this.middleTestArgument = testArgument; - - decorated.call(this, testArgument); - } - - MiddleClass.prototype.test = function (decorated, a) { - return decorated.call(this, a + 'b'); - }; - - function DecoratorClass (decorated, testArgument) { - this.decoratorCalled = true; - this.decoratorTestArgument = testArgument; - - decorated.call(this, testArgument); - } - - DecoratorClass.prototype.test = function (decorated, a) { - return decorated.call(this, a + 'a'); - }; - - var DecoratedClass = Utils.Decorate( - Utils.Decorate(BaseClass, MiddleClass), - DecoratorClass - ); - - var inst = new DecoratedClass('test'); - - assert.ok(inst.baseCalled, 'The base class contructor was called'); - assert.ok(inst.middleCalled, 'The middle class constructor was called'); - assert.ok(inst.decoratorCalled, 'The decorator constructor was called'); - - assert.strictEqual(inst.baseTestArgument, 'test'); - assert.strictEqual(inst.middleTestArgument, 'test'); - assert.strictEqual(inst.decoratorTestArgument, 'test'); - - var out = inst.test('test'); - - assert.strictEqual(out, 'testabc'); -}); diff --git a/static/vendor/select2/tests/utils/escapeMarkup-tests.js b/static/vendor/select2/tests/utils/escapeMarkup-tests.js deleted file mode 100644 index 726f09de..00000000 --- a/static/vendor/select2/tests/utils/escapeMarkup-tests.js +++ /dev/null @@ -1,36 +0,0 @@ -module('Utils - escapeMarkup'); - -var Utils = require('select2/utils'); - -test('text passes through', function (assert) { - var text = 'testing this'; - var escaped = Utils.escapeMarkup(text); - - assert.equal(text, escaped); -}); - -test('html tags are escaped', function (assert) { - var text = ''; - var escaped = Utils.escapeMarkup(text); - - assert.notEqual(text, escaped); - assert.equal(escaped.indexOf(' +{% endblock %} diff --git a/templates/member/signup.html b/templates/registration/signup.html similarity index 100% rename from templates/member/signup.html rename to templates/registration/signup.html From b1cd46bf7de7533078ba34b59072fac05950d7cc Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 08:01:51 +0200 Subject: [PATCH 46/66] Invalidate registrations, fix profile creation --- apps/member/signals.py | 4 +- apps/member/views.py | 13 +++-- apps/registration/urls.py | 6 ++- apps/registration/views.py | 47 +++++++++++++--- apps/treasury/forms.py | 2 +- .../account_activation_email.html | 2 +- .../registration/future_profile_detail.html | 54 +++++++++++++++++++ templates/registration/future_user_list.html | 14 +++-- 8 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 templates/registration/future_profile_detail.html diff --git a/apps/member/signals.py b/apps/member/signals.py index 2b03e3ce..fbb66c1f 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -10,7 +10,7 @@ def save_user_profile(instance, created, raw, **_kwargs): # When provisionning data, do not try to autocreate return - if created: + if created and instance.is_active: from .models import Profile Profile.objects.get_or_create(user=instance) - instance.profile.save() + instance.profile.save() diff --git a/apps/member/views.py b/apps/member/views.py index 29b16222..ed5826ef 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -102,11 +102,8 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): return super().form_valid(form) def get_success_url(self, **kwargs): - if kwargs: - return reverse_lazy('member:user_detail', - kwargs={'pk': kwargs['id']}) - else: - return reverse_lazy('member:user_detail', args=(self.object.id,)) + url = 'member:user_detail' if self.object.profile.registration_valid else 'registration:future_user_detail' + return reverse_lazy(url, args=(self.object.id,)) class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): @@ -117,6 +114,12 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): context_object_name = "user_object" template_name = "member/profile_detail.html" + def get_queryset(self, **kwargs): + """ + We can't display information of a not registered user. + """ + return super().get_queryset().filter(profile__registration_valid=True) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = context['user_object'] diff --git a/apps/registration/urls.py b/apps/registration/urls.py index ce2977dc..ae9b2fca 100644 --- a/apps/registration/urls.py +++ b/apps/registration/urls.py @@ -8,7 +8,9 @@ from . import views app_name = 'registration' urlpatterns = [ path('signup/', views.UserCreateView.as_view(), name="signup"), - path('validate_email/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'), - path('validate_email//', views.UserActivateView.as_view(), name='account_activation'), + path('validate_email/sent/', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'), + path('validate_email///', views.UserActivateView.as_view(), name='account_activation'), path('validate_user/', views.FutureUserListView.as_view(), name="future_user_list"), + path('validate_user//', views.FutureUserDetailView.as_view(), name="future_user_detail"), + path('validate_user//invalidate/', views.FutureUserInvalidateView.as_view(), name="future_user_invalidate"), ] diff --git a/apps/registration/views.py b/apps/registration/views.py index 543f2fb9..7f06f92f 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -5,15 +5,18 @@ from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.core.exceptions import ValidationError -from django.shortcuts import resolve_url +from django.shortcuts import resolve_url, redirect from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.http import urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ +from django.views import View from django.views.decorators.csrf import csrf_protect -from django.views.generic import CreateView, TemplateView +from django.views.generic import CreateView, TemplateView, DetailView from django_tables2 import SingleTableView from member.forms import ProfileForm +from member.models import Profile +from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin from .forms import SignUpForm @@ -42,15 +45,19 @@ class UserCreateView(CreateView): If the form is valid, then the user is created with is_active set to False so that the user cannot log in until the email has been validated. """ - profile_form = ProfileForm(self.request.POST) + profile_form = ProfileForm(data=self.request.POST) if not profile_form.is_valid(): return self.form_invalid(form) user = form.save(commit=False) user.is_active = False - user.profile = profile_form.save(commit=False) + profile_form.instance.user = user + profile = profile_form.save(commit=False) + user.profile = profile user.save() - user.profile.save() + user.refresh_from_db() + profile.user = user + profile.save() user.profile.send_email_validation_link() @@ -86,7 +93,6 @@ class UserActivateView(TemplateView): return self.render_to_response(self.get_context_data()) def get_user(self, uidb64): - print(uidb64) try: # urlsafe_base64_decode() decodes to bytestring uid = urlsafe_base64_decode(uidb64).decode() @@ -131,3 +137,32 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi return context + +class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + Affiche les informations sur un utilisateur, sa note, ses clubs... + """ + model = User + context_object_name = "user_object" + template_name = "registration/future_profile_detail.html" + + def get_queryset(self, **kwargs): + """ + We only display information of a not registered user. + """ + return super().get_queryset().filter(profile__registration_valid=False) + + +class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View): + """ + Affiche les informations sur un utilisateur, sa note, ses clubs... + """ + + def dispatch(self, request, *args, **kwargs): + user = User.objects.filter(profile__registration_valid=False)\ + .filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\ + .get(pk=self.kwargs["pk"]) + + user.delete() + + return redirect('registration:future_user_list') diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py index ad479e14..b09a46c7 100644 --- a/apps/treasury/forms.py +++ b/apps/treasury/forms.py @@ -53,7 +53,7 @@ ProductFormSet = forms.inlineformset_factory( class ProductFormSetHelper(FormHelper): """ - Specify some template informations for the product form. + Specify some template information for the product form. """ def __init__(self, form=None): diff --git a/templates/registration/account_activation_email.html b/templates/registration/account_activation_email.html index e8f2032d..252b83c7 100644 --- a/templates/registration/account_activation_email.html +++ b/templates/registration/account_activation_email.html @@ -2,7 +2,7 @@ Hi {{ user.username }}, Welcome to {{ site_name }}. Please click on the link below to confirm your registration. -{{ protocol }}://{{ domain }}{% url 'member:account_activation' uidb64=uid token=token %} +{{ protocol }}://{{ domain }}{% url 'registration:account_activation' uidb64=uid token=token %} This link is only valid for a couple of days, after that you will need to contact us to validate your email. diff --git a/templates/registration/future_profile_detail.html b/templates/registration/future_profile_detail.html new file mode 100644 index 00000000..9b7449e2 --- /dev/null +++ b/templates/registration/future_profile_detail.html @@ -0,0 +1,54 @@ +{% extends "base.html" %} +{% load static %} +{% load i18n %} +{% load render_table from django_tables2 %} +{% load pretty_money %} + +{% block content %} + +
        +
        +

        {% trans "Account #" %} {{ object.pk }}

        +
        +
        +
        +
        {% trans 'name'|capfirst %}, {% trans 'first name' %}
        +
        {{ object.last_name }} {{ object.first_name }}
        + +
        {% trans 'username'|capfirst %}
        +
        {{ object.username }}
        + +
        {% trans 'email'|capfirst %}
        +
        {{ object.email }}
        + +
        {% trans 'password'|capfirst %}
        +
        + + {% trans 'Change password' %} + +
        + +
        {% trans 'section'|capfirst %}
        +
        {{ object.profile.section }}
        + +
        {% trans 'address'|capfirst %}
        +
        {{ object.profile.address }}
        + +
        {% trans 'phone number'|capfirst %}
        +
        {{ object.profile.phone_number }}
        + +
        {% trans 'paid'|capfirst %}
        +
        {{ object.profile.paid|yesno }}
        +
        + + {% if object.pk == user.pk %} + {% trans 'Manage auth token' %} + {% endif %} +
        + +
        + +{% endblock %} \ No newline at end of file diff --git a/templates/registration/future_user_list.html b/templates/registration/future_user_list.html index e500ee19..490fe2ab 100644 --- a/templates/registration/future_user_list.html +++ b/templates/registration/future_user_list.html @@ -4,13 +4,19 @@ {% load i18n %} {% block content %} -
        - {% render_table table %} -
        +
        - + {% if table.data %} +
        + {% render_table table %} +
        + {% else %} +
        + {% trans "There is no pending user." %} +
        + {% endif %} {% endblock %} {% block extrajavascript %} From fbc25240e68b5282da82c0786627bec3973bab2d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 09:09:21 +0200 Subject: [PATCH 47/66] Validate registrations, closes #14, #18, #19 --- apps/member/models.py | 1 + apps/note/admin.py | 13 ++- apps/registration/forms.py | 46 +++++++++ apps/registration/views.py | 97 ++++++++++++++++++- note_kfet/inputs.py | 2 +- .../registration/future_profile_detail.html | 95 ++++++++++-------- 6 files changed, 204 insertions(+), 50 deletions(-) diff --git a/apps/member/models.py b/apps/member/models.py index 46051fe1..9f1b341b 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -230,6 +230,7 @@ class Membership(models.Model): ) date_start = models.DateField( + default=datetime.date.today, verbose_name=_('membership starts on'), ) diff --git a/apps/note/admin.py b/apps/note/admin.py index 7b4ba870..dc6470d2 100644 --- a/apps/note/admin.py +++ b/apps/note/admin.py @@ -8,7 +8,7 @@ from polymorphic.admin import PolymorphicChildModelAdmin, \ from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser from .models.transactions import Transaction, TemplateCategory, TransactionTemplate, \ - RecurrentTransaction, MembershipTransaction + RecurrentTransaction, MembershipTransaction, SpecialTransaction class AliasInlines(admin.TabularInline): @@ -102,7 +102,7 @@ class TransactionAdmin(PolymorphicParentModelAdmin): """ Admin customisation for Transaction """ - child_models = (RecurrentTransaction, MembershipTransaction) + child_models = (RecurrentTransaction, MembershipTransaction, SpecialTransaction) list_display = ('created_at', 'poly_source', 'poly_destination', 'quantity', 'amount', 'valid') list_filter = ('valid',) @@ -141,7 +141,14 @@ class TransactionAdmin(PolymorphicParentModelAdmin): @admin.register(MembershipTransaction) class MembershipTransactionAdmin(PolymorphicChildModelAdmin): """ - Admin customisation for Transaction + Admin customisation for MembershipTransaction + """ + + +@admin.register(SpecialTransaction) +class SpecialTransactionAdmin(PolymorphicChildModelAdmin): + """ + Admin customisation for SpecialTransaction """ diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 3f4063d5..656f2243 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -1,9 +1,12 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.utils.translation import gettext_lazy as _ +from note.models import NoteSpecial +from note_kfet.inputs import AmountInput class SignUpForm(UserCreationForm): @@ -19,3 +22,46 @@ class SignUpForm(UserCreationForm): class Meta: model = User fields = ('first_name', 'last_name', 'username', 'email', ) + + +class ValidationForm(forms.Form): + credit_type = forms.ModelChoiceField( + queryset=NoteSpecial.objects, + label=_("Credit type"), + empty_label=_("No credit"), + required=False, + ) + + credit_amount = forms.IntegerField( + label=_("Credit amount"), + required=False, + initial=0, + widget=AmountInput(), + ) + + last_name = forms.CharField( + label=_("Last name"), + required=False, + ) + + first_name = forms.CharField( + label=_("First name"), + required=False, + ) + + bank = forms.CharField( + label=_("Bank"), + required=False, + ) + + join_BDE = forms.BooleanField( + label=_("Join BDE"), + required=False, + initial=True, + ) + + join_Kfet = forms.BooleanField( + label=_("Join Kfet"), + required=False, + initial=True, + ) diff --git a/apps/registration/views.py b/apps/registration/views.py index 7f06f92f..0f09cda4 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -12,14 +12,16 @@ from django.utils.http import urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ from django.views import View from django.views.decorators.csrf import csrf_protect -from django.views.generic import CreateView, TemplateView, DetailView +from django.views.generic import CreateView, TemplateView, DetailView, FormView from django_tables2 import SingleTableView from member.forms import ProfileForm -from member.models import Profile +from member.models import Membership, Club +from note.models import SpecialTransaction +from note.templatetags.pretty_money import pretty_money from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin -from .forms import SignUpForm +from .forms import SignUpForm, ValidationForm from .tables import FutureUserTable from .tokens import account_activation_token @@ -138,11 +140,12 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi return context -class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): +class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, FormView): """ Affiche les informations sur un utilisateur, sa note, ses clubs... """ model = User + form_class = ValidationForm context_object_name = "user_object" template_name = "registration/future_profile_detail.html" @@ -152,6 +155,92 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView) """ return super().get_queryset().filter(profile__registration_valid=False) + def get_form(self, form_class=None): + form = super().get_form(form_class) + user = self.get_object() + form.fields["last_name"].initial = user.last_name + form.fields["first_name"].initial = user.first_name + return form + + def form_valid(self, form): + user = self.object = self.get_object() + + print(form.cleaned_data) + credit_type = form.cleaned_data["credit_type"] + credit_amount = form.cleaned_data["credit_amount"] + last_name = form.cleaned_data["last_name"] + first_name = form.cleaned_data["first_name"] + bank = form.cleaned_data["bank"] + join_BDE = form.cleaned_data["join_BDE"] + join_Kfet = form.cleaned_data["join_Kfet"] + + fee = 0 + bde = Club.objects.get(name="BDE") + bde_fee = bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid + if join_BDE: + fee += bde_fee + kfet = Club.objects.get(name="Kfet") + kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid + if join_Kfet: + fee += kfet_fee + + if join_Kfet and not join_BDE: + form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club.")) + + if fee > credit_amount: + form.add_error('credit_type', + _("The entered amount is not enough for the memberships, should be at least {}") + .format(pretty_money(fee))) + return self.form_invalid(form) + + if credit_type is not None and credit_amount > 0: + if not last_name or not first_name or not bank: + if not last_name: + form.add_error('last_name', _("This field is required.")) + if not first_name: + form.add_error('first_name', _("This field is required.")) + if not bank: + form.add_error('bank', _("This field is required.")) + return self.form_invalid(form) + + ret = super().form_valid(form) + user.is_active = True + user.profile.registration_valid = True + user.save() + user.profile.save() + + if credit_type is not None and credit_amount > 0: + SpecialTransaction.objects.create( + source=credit_type, + destination=user.note, + quantity=1, + amount=credit_amount, + reason="Crédit " + credit_type.special_type + " (Inscription)", + last_name=last_name, + first_name=first_name, + bank=bank, + valid=True, + ) + + if join_BDE: + Membership.objects.create( + club=bde, + user=user, + fee=bde_fee, + ) + + if join_Kfet: + Membership.objects.create( + club=kfet, + user=user, + fee=kfet_fee, + ) + + return ret + + def get_success_url(self): + return reverse_lazy('member:user_detail', args=(self.get_object().pk, )) + class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View): """ diff --git a/note_kfet/inputs.py b/note_kfet/inputs.py index ecd758e0..b3cccbce 100644 --- a/note_kfet/inputs.py +++ b/note_kfet/inputs.py @@ -13,7 +13,7 @@ class AmountInput(NumberInput): template_name = "note/amount_input.html" def format_value(self, value): - return None if value is None or value == "" else "{:.02f}".format(value / 100, ) + return None if value is None or value == "" else "{:.02f}".format(int(value) / 100, ) def value_from_datadict(self, data, files, name): val = super().value_from_datadict(data, files, name) diff --git a/templates/registration/future_profile_detail.html b/templates/registration/future_profile_detail.html index 9b7449e2..2172749a 100644 --- a/templates/registration/future_profile_detail.html +++ b/templates/registration/future_profile_detail.html @@ -1,54 +1,65 @@ {% extends "base.html" %} {% load static %} {% load i18n %} -{% load render_table from django_tables2 %} +{% load crispy_forms_tags %} {% load pretty_money %} {% block content %} +
        +
        +

        {% trans "Account #" %} {{ object.pk }}

        +
        +
        +
        +
        {% trans 'name'|capfirst %}, {% trans 'first name' %}
        +
        {{ object.last_name }} {{ object.first_name }}
        -
        -
        -

        {% trans "Account #" %} {{ object.pk }}

        +
        {% trans 'username'|capfirst %}
        +
        {{ object.username }}
        + +
        {% trans 'email'|capfirst %}
        +
        {{ object.email }}
        + +
        {% trans 'password'|capfirst %}
        +
        + + {% trans 'Change password' %} + +
        + +
        {% trans 'section'|capfirst %}
        +
        {{ object.profile.section }}
        + +
        {% trans 'address'|capfirst %}
        +
        {{ object.profile.address }}
        + +
        {% trans 'phone number'|capfirst %}
        +
        {{ object.profile.phone_number }}
        + +
        {% trans 'paid'|capfirst %}
        +
        {{ object.profile.paid|yesno }}
        +
        +
        +
        -
        -
        -
        {% trans 'name'|capfirst %}, {% trans 'first name' %}
        -
        {{ object.last_name }} {{ object.first_name }}
        -
        {% trans 'username'|capfirst %}
        -
        {{ object.username }}
        +
        -
        {% trans 'email'|capfirst %}
        -
        {{ object.email }}
        - -
        {% trans 'password'|capfirst %}
        -
        - - {% trans 'Change password' %} - -
        - -
        {% trans 'section'|capfirst %}
        -
        {{ object.profile.section }}
        - -
        {% trans 'address'|capfirst %}
        -
        {{ object.profile.address }}
        - -
        {% trans 'phone number'|capfirst %}
        -
        {{ object.profile.phone_number }}
        - -
        {% trans 'paid'|capfirst %}
        -
        {{ object.profile.paid|yesno }}
        -
        - - {% if object.pk == user.pk %} - {% trans 'Manage auth token' %} - {% endif %} +
        +
        +
        +

        {% trans "Validate account" %}

        +
        +
        + {% csrf_token %} + {{ form|crispy }} +
        + +
        - -
        - {% endblock %} \ No newline at end of file From 3516b1fa044f619536393645312729e62e4b42d7 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 09:48:23 +0200 Subject: [PATCH 48/66] Add links to resend mail confirmations --- apps/member/models.py | 6 ++-- apps/registration/tokens.py | 2 +- apps/registration/urls.py | 6 ++-- apps/registration/views.py | 31 ++++++++++++++----- templates/member/profile_tables.html | 9 ++++++ ...te.html => email_validation_complete.html} | 0 ...email.html => email_validation_email.html} | 2 +- ....html => email_validation_email_sent.html} | 0 .../registration/future_profile_detail.html | 11 ++++++- 9 files changed, 51 insertions(+), 16 deletions(-) rename templates/registration/{account_activation_complete.html => email_validation_complete.html} (100%) rename templates/registration/{account_activation_email.html => email_validation_email.html} (72%) rename templates/registration/{account_activation_email_sent.html => email_validation_email_sent.html} (100%) diff --git a/apps/member/models.py b/apps/member/models.py index 9f1b341b..2cbb71eb 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -12,7 +12,7 @@ from django.urls import reverse, reverse_lazy from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.translation import gettext_lazy as _ -from registration.tokens import account_activation_token +from registration.tokens import email_validation_token from note.models import MembershipTransaction @@ -73,13 +73,13 @@ class Profile(models.Model): def send_email_validation_link(self): subject = "Activate your Note Kfet account" - message = loader.render_to_string('registration/account_activation_email.html', + message = loader.render_to_string('registration/email_validation_email.html', { 'user': self.user, 'domain': "nk20.ynerant.fr", 'site_name': "La Note Kfet", 'protocol': 'https', - 'token': account_activation_token.make_token(self.user), + 'token': email_validation_token.make_token(self.user), 'uid': urlsafe_base64_encode(force_bytes(self.user.pk)).decode('UTF-8'), }) self.user.email_user(subject, message) diff --git a/apps/registration/tokens.py b/apps/registration/tokens.py index f45c00de..c5ddc82b 100644 --- a/apps/registration/tokens.py +++ b/apps/registration/tokens.py @@ -27,4 +27,4 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator): return str(user.pk) + str(user.profile.email_confirmed) + str(login_timestamp) + str(timestamp) -account_activation_token = AccountActivationTokenGenerator() +email_validation_token = AccountActivationTokenGenerator() diff --git a/apps/registration/urls.py b/apps/registration/urls.py index ae9b2fca..14678cbb 100644 --- a/apps/registration/urls.py +++ b/apps/registration/urls.py @@ -8,8 +8,10 @@ from . import views app_name = 'registration' urlpatterns = [ path('signup/', views.UserCreateView.as_view(), name="signup"), - path('validate_email/sent/', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'), - path('validate_email///', views.UserActivateView.as_view(), name='account_activation'), + path('validate_email/sent/', views.UserValidationEmailSentView.as_view(), name='email_validation_sent'), + path('validate_email/resend//', views.UserResendValidationEmailView.as_view(), + name='email_validation_resend'), + path('validate_email///', views.UserValidateView.as_view(), name='email_validation'), path('validate_user/', views.FutureUserListView.as_view(), name="future_user_list"), path('validate_user//', views.FutureUserDetailView.as_view(), name="future_user_detail"), path('validate_user//invalidate/', views.FutureUserInvalidateView.as_view(), name="future_user_invalidate"), diff --git a/apps/registration/views.py b/apps/registration/views.py index 0f09cda4..2ce349c4 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -16,14 +16,14 @@ from django.views.generic import CreateView, TemplateView, DetailView, FormView from django_tables2 import SingleTableView from member.forms import ProfileForm from member.models import Membership, Club -from note.models import SpecialTransaction +from note.models import SpecialTransaction, Transaction from note.templatetags.pretty_money import pretty_money from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin from .forms import SignUpForm, ValidationForm from .tables import FutureUserTable -from .tokens import account_activation_token +from .tokens import email_validation_token class UserCreateView(CreateView): @@ -32,7 +32,7 @@ class UserCreateView(CreateView): """ form_class = SignUpForm - success_url = reverse_lazy('registration:account_activation_sent') + success_url = reverse_lazy('registration:email_validation_sent') template_name = 'registration/signup.html' second_form = ProfileForm @@ -66,9 +66,9 @@ class UserCreateView(CreateView): return super().form_valid(form) -class UserActivateView(TemplateView): +class UserValidateView(LoginRequiredMixin, ProtectQuerysetMixin, TemplateView): title = _("Account Activation") - template_name = 'registration/account_activation_complete.html' + template_name = 'registration/email_validation_complete.html' @method_decorator(csrf_protect) def dispatch(self, *args, **kwargs): @@ -84,7 +84,7 @@ class UserActivateView(TemplateView): user = self.get_user(kwargs['uidb64']) token = kwargs['token'] - if user is not None and account_activation_token.check_token(user, token): + if user is not None and email_validation_token.check_token(user, token): self.validlink = True user.is_active = True user.profile.email_confirmed = True @@ -116,11 +116,26 @@ class UserActivateView(TemplateView): return context -class UserActivationEmailSentView(TemplateView): - template_name = 'registration/account_activation_email_sent.html' +class UserValidationEmailSentView(LoginRequiredMixin, ProtectQuerysetMixin, TemplateView): + template_name = 'registration/email_validation_email_sent.html' title = _('Account activation email sent') +class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, DetailView): + model = User + + def get_queryset(self, **kwargs): + return super().get_queryset(**kwargs).filter(profile__email_confirmed=False) + + def get(self, request, *args, **kwargs): + user = self.get_object() + + user.profile.send_email_validation_link() + + url = 'member:user_detail' if user.profile.registration_valid else 'registration:future_user_detail' + return redirect(url, user.id) + + class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ Affiche la liste des utilisateurs, avec une fonction de recherche statique diff --git a/templates/member/profile_tables.html b/templates/member/profile_tables.html index 9d2c687f..9016289a 100644 --- a/templates/member/profile_tables.html +++ b/templates/member/profile_tables.html @@ -1,5 +1,14 @@ {% load render_table from django_tables2 %} {% load i18n %} +{% load perms %} + +{% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm:object.profile %} +
        + {% trans "This user doesn't have confirmed his/her e-mail address." %} + {% trans "Click here to resend a validation link." %} +
        +{% endif %} +
        diff --git a/templates/registration/account_activation_complete.html b/templates/registration/email_validation_complete.html similarity index 100% rename from templates/registration/account_activation_complete.html rename to templates/registration/email_validation_complete.html diff --git a/templates/registration/account_activation_email.html b/templates/registration/email_validation_email.html similarity index 72% rename from templates/registration/account_activation_email.html rename to templates/registration/email_validation_email.html index 252b83c7..feb46876 100644 --- a/templates/registration/account_activation_email.html +++ b/templates/registration/email_validation_email.html @@ -2,7 +2,7 @@ Hi {{ user.username }}, Welcome to {{ site_name }}. Please click on the link below to confirm your registration. -{{ protocol }}://{{ domain }}{% url 'registration:account_activation' uidb64=uid token=token %} +{{ protocol }}://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %} This link is only valid for a couple of days, after that you will need to contact us to validate your email. diff --git a/templates/registration/account_activation_email_sent.html b/templates/registration/email_validation_email_sent.html similarity index 100% rename from templates/registration/account_activation_email_sent.html rename to templates/registration/email_validation_email_sent.html diff --git a/templates/registration/future_profile_detail.html b/templates/registration/future_profile_detail.html index 2172749a..d422aec4 100644 --- a/templates/registration/future_profile_detail.html +++ b/templates/registration/future_profile_detail.html @@ -2,7 +2,7 @@ {% load static %} {% load i18n %} {% load crispy_forms_tags %} -{% load pretty_money %} +{% load perms %} {% block content %}
        @@ -20,6 +20,15 @@
        {% trans 'email'|capfirst %}
        {{ object.email }}
        + {% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm:object.profile %} +
        +
        + {% trans "This user doesn't have confirmed his/her e-mail address." %} + {% trans "Click here to resend a validation link." %} +
        +
        + {% endif %} +
        {% trans 'password'|capfirst %}
        From f286f99ced381cb982a34feff1c7f9ba1ec70d83 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 15:31:39 +0200 Subject: [PATCH 49/66] =?UTF-8?q?Handle=20payments=20from=20the=20Soci?= =?UTF-8?q?=C3=A9t=C3=A9=20G=C3=A9n=C3=A9rale,=20closes=20#15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/member/forms.py | 2 +- apps/member/models.py | 10 +- apps/registration/forms.py | 10 +- apps/registration/views.py | 34 +++- .../registration/future_profile_detail.html | 167 +++++++++++------- 5 files changed, 152 insertions(+), 71 deletions(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 70ffcf5e..5c060f58 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -26,7 +26,7 @@ class ProfileForm(forms.ModelForm): class Meta: model = Profile fields = '__all__' - exclude = ('user', 'email_confirmed', 'registration_valid', ) + exclude = ('user', 'email_confirmed', 'registration_valid', 'soge', ) class ClubForm(forms.ModelForm): diff --git a/apps/member/models.py b/apps/member/models.py index 2cbb71eb..80681824 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -53,13 +53,19 @@ class Profile(models.Model): default=False, ) + email_confirmed = models.BooleanField( + verbose_name=_("email confirmed"), + default=False, + ) + registration_valid = models.BooleanField( verbose_name=_("registration valid"), default=False, ) - email_confirmed = models.BooleanField( - verbose_name=_("email confirmed"), + soge = models.BooleanField( + verbose_name=_("Société générale"), + help_text=_("Has the user ever be paid by the Société générale?"), default=False, ) diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 656f2243..3bd1cbbf 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -25,6 +25,12 @@ class SignUpForm(UserCreationForm): class ValidationForm(forms.Form): + soge = forms.BooleanField( + label=_("Inscription paid by Société Générale"), + required=False, + help_text=_("Check this case is the Société Générale paid the inscription."), + ) + credit_type = forms.ModelChoiceField( queryset=NoteSpecial.objects, label=_("Credit type"), @@ -55,13 +61,13 @@ class ValidationForm(forms.Form): ) join_BDE = forms.BooleanField( - label=_("Join BDE"), + label=_("Join BDE Club"), required=False, initial=True, ) join_Kfet = forms.BooleanField( - label=_("Join Kfet"), + label=_("Join Kfet Club"), required=False, initial=True, ) diff --git a/apps/registration/views.py b/apps/registration/views.py index 2ce349c4..f154b3e0 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -16,7 +16,7 @@ from django.views.generic import CreateView, TemplateView, DetailView, FormView from django_tables2 import SingleTableView from member.forms import ProfileForm from member.models import Membership, Club -from note.models import SpecialTransaction, Transaction +from note.models import SpecialTransaction, NoteSpecial from note.templatetags.pretty_money import pretty_money from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin @@ -66,7 +66,7 @@ class UserCreateView(CreateView): return super().form_valid(form) -class UserValidateView(LoginRequiredMixin, ProtectQuerysetMixin, TemplateView): +class UserValidateView(TemplateView): title = _("Account Activation") template_name = 'registration/email_validation_complete.html' @@ -89,6 +89,7 @@ class UserValidateView(LoginRequiredMixin, ProtectQuerysetMixin, TemplateView): user.is_active = True user.profile.email_confirmed = True user.save() + user.profile.save() return super().dispatch(*args, **kwargs) else: # Display the "Account Activation unsuccessful" page. @@ -116,7 +117,7 @@ class UserValidateView(LoginRequiredMixin, ProtectQuerysetMixin, TemplateView): return context -class UserValidationEmailSentView(LoginRequiredMixin, ProtectQuerysetMixin, TemplateView): +class UserValidationEmailSentView(TemplateView): template_name = 'registration/email_validation_email_sent.html' title = _('Account activation email sent') @@ -170,6 +171,19 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, """ return super().get_queryset().filter(profile__registration_valid=False) + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + + user = self.get_object() + fee = 0 + bde = Club.objects.get(name="BDE") + fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid + kfet = Club.objects.get(name="Kfet") + fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid + ctx["total_fee"] = "{:.02f}".format(fee / 100, ) + + return ctx + def get_form(self, form_class=None): form = super().get_form(form_class) user = self.get_object() @@ -180,7 +194,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, def form_valid(self, form): user = self.object = self.get_object() - print(form.cleaned_data) + soge = form.cleaned_data["soge"] credit_type = form.cleaned_data["credit_type"] credit_amount = form.cleaned_data["credit_amount"] last_name = form.cleaned_data["last_name"] @@ -189,6 +203,10 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, join_BDE = form.cleaned_data["join_BDE"] join_Kfet = form.cleaned_data["join_Kfet"] + if soge: + join_BDE = True + join_Kfet = True + fee = 0 bde = Club.objects.get(name="BDE") bde_fee = bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid @@ -199,6 +217,11 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, if join_Kfet: fee += kfet_fee + if soge: + credit_type = NoteSpecial.objects.get(special_type="Virement bancaire") + credit_amount = fee + bank = "Société générale" + if join_Kfet and not join_BDE: form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club.")) @@ -221,6 +244,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, ret = super().form_valid(form) user.is_active = True user.profile.registration_valid = True + user.profile.soge = soge user.save() user.profile.save() @@ -230,7 +254,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, destination=user.note, quantity=1, amount=credit_amount, - reason="Crédit " + credit_type.special_type + " (Inscription)", + reason="Crédit " + ("Société générale" if soge else credit_type.special_type) + " (Inscription)", last_name=last_name, first_name=first_name, bank=bank, diff --git a/templates/registration/future_profile_detail.html b/templates/registration/future_profile_detail.html index d422aec4..8c78fb8d 100644 --- a/templates/registration/future_profile_detail.html +++ b/templates/registration/future_profile_detail.html @@ -5,70 +5,115 @@ {% load perms %} {% block content %} -
        -
        -

        {% trans "Account #" %} {{ object.pk }}

        +
        + -
        -
        -
        {% trans 'name'|capfirst %}, {% trans 'first name' %}
        -
        {{ object.last_name }} {{ object.first_name }}
        - -
        {% trans 'username'|capfirst %}
        -
        {{ object.username }}
        - -
        {% trans 'email'|capfirst %}
        -
        {{ object.email }}
        - - {% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm:object.profile %} -
        -
        - {% trans "This user doesn't have confirmed his/her e-mail address." %} - {% trans "Click here to resend a validation link." %} -
        -
        - {% endif %} - -
        {% trans 'password'|capfirst %}
        -
        - - {% trans 'Change password' %} - -
        - -
        {% trans 'section'|capfirst %}
        -
        {{ object.profile.section }}
        - -
        {% trans 'address'|capfirst %}
        -
        {{ object.profile.address }}
        - -
        {% trans 'phone number'|capfirst %}
        -
        {{ object.profile.phone_number }}
        - -
        {% trans 'paid'|capfirst %}
        -
        {{ object.profile.paid|yesno }}
        -
        -
        - +{% endblock %} -
        +{% block extrajavascript %} + {% endblock %} \ No newline at end of file From ff820ef086f357e34bb5c431af17cc3ebca9c491 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 15:42:09 +0200 Subject: [PATCH 50/66] Update translations --- apps/registration/views.py | 6 +- locale/de/LC_MESSAGES/django.po | 408 ++++++++++++++++++++++--------- locale/fr/LC_MESSAGES/django.po | 416 ++++++++++++++++++++++---------- 3 files changed, 586 insertions(+), 244 deletions(-) diff --git a/apps/registration/views.py b/apps/registration/views.py index f154b3e0..8d303392 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -67,7 +67,7 @@ class UserCreateView(CreateView): class UserValidateView(TemplateView): - title = _("Account Activation") + title = _("Email validation") template_name = 'registration/email_validation_complete.html' @method_decorator(csrf_protect) @@ -111,7 +111,7 @@ class UserValidateView(TemplateView): context['validlink'] = True else: context.update({ - 'title': _('Account Activation unsuccessful'), + 'title': _('Email validation unsuccessful'), 'validlink': False, }) return context @@ -119,7 +119,7 @@ class UserValidateView(TemplateView): class UserValidationEmailSentView(TemplateView): template_name = 'registration/email_validation_email_sent.html' - title = _('Account activation email sent') + title = _('Email validation email sent') class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, DetailView): diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 16c73f35..41ed293d 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-01 18:39+0200\n" +"POT-Creation-Date: 2020-04-05 15:39+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,36 +18,37 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: apps/activity/apps.py:10 apps/activity/models.py:111 -#: apps/activity/models.py:120 +#: apps/activity/apps.py:10 apps/activity/models.py:102 +#: apps/activity/models.py:111 msgid "activity" msgstr "" -#: apps/activity/forms.py:45 apps/activity/models.py:217 +#: apps/activity/forms.py:45 apps/activity/models.py:206 msgid "You can't invite someone once the activity is started." msgstr "" -#: apps/activity/forms.py:48 apps/activity/models.py:220 +#: apps/activity/forms.py:48 apps/activity/models.py:209 msgid "This activity is not validated yet." msgstr "" -#: apps/activity/forms.py:58 apps/activity/models.py:228 +#: apps/activity/forms.py:58 apps/activity/models.py:217 msgid "This person has been already invited 5 times this year." msgstr "" -#: apps/activity/forms.py:62 apps/activity/models.py:232 +#: apps/activity/forms.py:62 apps/activity/models.py:221 msgid "This person is already invited." msgstr "" -#: apps/activity/forms.py:66 apps/activity/models.py:236 +#: apps/activity/forms.py:66 apps/activity/models.py:225 msgid "You can't invite more than 3 people to this activity." msgstr "" #: apps/activity/models.py:23 apps/activity/models.py:48 -#: apps/member/models.py:66 apps/member/models.py:169 +#: apps/member/models.py:100 apps/member/models.py:203 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 -#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:232 +#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 +#: templates/registration/future_profile_detail.html:16 msgid "name" msgstr "" @@ -67,18 +68,18 @@ msgstr "" msgid "activity types" msgstr "" -#: apps/activity/models.py:53 apps/note/models/transactions.py:69 +#: apps/activity/models.py:53 apps/note/models/transactions.py:74 #: apps/permission/models.py:103 templates/activity/activity_detail.html:16 msgid "description" msgstr "" #: apps/activity/models.py:60 apps/note/models/notes.py:164 -#: apps/note/models/transactions.py:62 +#: apps/note/models/transactions.py:64 #: templates/activity/activity_detail.html:19 msgid "type" msgstr "" -#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:190 +#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:224 #: apps/note/models/notes.py:117 msgid "user" msgstr "" @@ -87,73 +88,74 @@ msgstr "" msgid "organizer" msgstr "" -#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14 -#: apps/note/models/notes.py:58 -msgid "note" -msgstr "" - -#: apps/activity/models.py:89 templates/activity/activity_detail.html:36 +#: apps/activity/models.py:80 templates/activity/activity_detail.html:36 msgid "attendees club" msgstr "" -#: apps/activity/models.py:93 templates/activity/activity_detail.html:22 +#: apps/activity/models.py:84 templates/activity/activity_detail.html:22 msgid "start date" msgstr "" -#: apps/activity/models.py:97 templates/activity/activity_detail.html:25 +#: apps/activity/models.py:88 templates/activity/activity_detail.html:25 msgid "end date" msgstr "" -#: apps/activity/models.py:102 apps/note/models/transactions.py:134 +#: apps/activity/models.py:93 apps/note/models/transactions.py:139 #: templates/activity/activity_detail.html:47 msgid "valid" msgstr "" -#: apps/activity/models.py:107 templates/activity/activity_detail.html:61 +#: apps/activity/models.py:98 templates/activity/activity_detail.html:61 msgid "open" msgstr "" -#: apps/activity/models.py:112 +#: apps/activity/models.py:103 msgid "activities" msgstr "" -#: apps/activity/models.py:125 +#: apps/activity/models.py:116 msgid "entry time" msgstr "" -#: apps/activity/models.py:148 +#: apps/activity/models.py:122 apps/note/apps.py:14 +#: apps/note/models/notes.py:58 +msgid "note" +msgstr "" + +#: apps/activity/models.py:139 msgid "Already entered on " msgstr "" -#: apps/activity/models.py:148 apps/activity/tables.py:54 +#: apps/activity/models.py:139 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "" -#: apps/activity/models.py:156 +#: apps/activity/models.py:147 msgid "The balance is negative." msgstr "" -#: apps/activity/models.py:188 +#: apps/activity/models.py:177 msgid "last name" msgstr "" -#: apps/activity/models.py:193 templates/member/profile_info.html:14 +#: apps/activity/models.py:182 templates/member/profile_info.html:14 +#: templates/registration/future_profile_detail.html:16 msgid "first name" msgstr "" -#: apps/activity/models.py:200 +#: apps/activity/models.py:189 msgid "inviter" msgstr "" -#: apps/activity/models.py:241 +#: apps/activity/models.py:230 msgid "guest" msgstr "" -#: apps/activity/models.py:242 +#: apps/activity/models.py:231 msgid "guests" msgstr "" -#: apps/activity/models.py:254 +#: apps/activity/models.py:243 msgid "Invitation" msgstr "" @@ -169,12 +171,13 @@ msgstr "" msgid "Type" msgstr "" -#: apps/activity/tables.py:77 apps/treasury/forms.py:121 +#: apps/activity/tables.py:77 apps/registration/forms.py:49 +#: apps/treasury/forms.py:121 msgid "Last name" msgstr "" -#: apps/activity/tables.py:79 apps/treasury/forms.py:123 -#: templates/note/transaction_form.html:92 +#: apps/activity/tables.py:79 apps/registration/forms.py:54 +#: apps/treasury/forms.py:123 templates/note/transaction_form.html:92 msgid "First name" msgstr "" @@ -182,11 +185,11 @@ msgstr "" msgid "Note" msgstr "" -#: apps/activity/tables.py:83 +#: apps/activity/tables.py:83 apps/member/tables.py:35 msgid "Balance" msgstr "" -#: apps/activity/views.py:45 templates/base.html:94 +#: apps/activity/views.py:45 templates/base.html:106 msgid "Activities" msgstr "" @@ -226,12 +229,12 @@ msgstr "" msgid "create" msgstr "" -#: apps/logs/models.py:61 apps/note/tables.py:142 +#: apps/logs/models.py:61 apps/note/tables.py:144 #: templates/activity/activity_detail.html:67 msgid "edit" msgstr "" -#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146 +#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:149 msgid "delete" msgstr "" @@ -251,166 +254,200 @@ msgstr "" msgid "member" msgstr "" -#: apps/member/models.py:28 +#: apps/member/models.py:32 +#: templates/registration/future_profile_detail.html:47 msgid "phone number" msgstr "" -#: apps/member/models.py:34 templates/member/profile_info.html:27 +#: apps/member/models.py:38 templates/member/profile_info.html:27 +#: templates/registration/future_profile_detail.html:41 msgid "section" msgstr "" -#: apps/member/models.py:35 +#: apps/member/models.py:39 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "" -#: apps/member/models.py:41 templates/member/profile_info.html:30 +#: apps/member/models.py:45 templates/member/profile_info.html:30 +#: templates/registration/future_profile_detail.html:44 msgid "address" msgstr "" -#: apps/member/models.py:47 +#: apps/member/models.py:51 +#: templates/registration/future_profile_detail.html:50 msgid "paid" msgstr "" -#: apps/member/models.py:52 apps/member/models.py:53 +#: apps/member/models.py:52 +msgid "Tells if the user receive a salary." +msgstr "" + +#: apps/member/models.py:57 +msgid "email confirmed" +msgstr "" + +#: apps/member/models.py:62 +msgid "registration valid" +msgstr "" + +#: apps/member/models.py:67 +msgid "Société générale" +msgstr "" + +#: apps/member/models.py:68 +msgid "Has the user ever be paid by the Société générale?" +msgstr "" + +#: apps/member/models.py:73 apps/member/models.py:74 msgid "user profile" msgstr "" -#: apps/member/models.py:71 templates/member/club_info.html:46 +#: apps/member/models.py:105 templates/member/club_info.html:46 +#: templates/registration/future_profile_detail.html:22 msgid "email" msgstr "" -#: apps/member/models.py:78 +#: apps/member/models.py:112 msgid "parent club" msgstr "" -#: apps/member/models.py:87 +#: apps/member/models.py:121 msgid "require memberships" msgstr "" -#: apps/member/models.py:88 +#: apps/member/models.py:122 msgid "Uncheck if this club don't require memberships." msgstr "" -#: apps/member/models.py:93 templates/member/club_info.html:35 +#: apps/member/models.py:127 templates/member/club_info.html:35 msgid "membership fee (paid students)" msgstr "" -#: apps/member/models.py:98 templates/member/club_info.html:38 +#: apps/member/models.py:132 templates/member/club_info.html:38 msgid "membership fee (unpaid students)" msgstr "" -#: apps/member/models.py:104 templates/member/club_info.html:28 +#: apps/member/models.py:138 templates/member/club_info.html:28 msgid "membership duration" msgstr "" -#: apps/member/models.py:105 +#: apps/member/models.py:139 msgid "The longest time (in days) a membership can last (NULL = infinite)." msgstr "" -#: apps/member/models.py:112 templates/member/club_info.html:22 +#: apps/member/models.py:146 templates/member/club_info.html:22 msgid "membership start" msgstr "" -#: apps/member/models.py:113 +#: apps/member/models.py:147 msgid "How long after January 1st the members can renew their membership." msgstr "" -#: apps/member/models.py:120 templates/member/club_info.html:25 +#: apps/member/models.py:154 templates/member/club_info.html:25 msgid "membership end" msgstr "" -#: apps/member/models.py:121 +#: apps/member/models.py:155 msgid "" "How long the membership can last after January 1st of the next year after " "members can renew their membership." msgstr "" -#: apps/member/models.py:154 apps/member/models.py:196 +#: apps/member/models.py:188 apps/member/models.py:230 #: apps/note/models/notes.py:139 msgid "club" msgstr "" -#: apps/member/models.py:155 +#: apps/member/models.py:189 msgid "clubs" msgstr "" -#: apps/member/models.py:175 apps/permission/models.py:288 +#: apps/member/models.py:209 apps/permission/models.py:288 msgid "role" msgstr "" -#: apps/member/models.py:176 apps/member/models.py:201 +#: apps/member/models.py:210 apps/member/models.py:235 msgid "roles" msgstr "" -#: apps/member/models.py:205 +#: apps/member/models.py:240 msgid "membership starts on" msgstr "" -#: apps/member/models.py:209 +#: apps/member/models.py:244 msgid "membership ends on" msgstr "" -#: apps/member/models.py:214 +#: apps/member/models.py:249 msgid "fee" msgstr "" -#: apps/member/models.py:226 apps/member/views.py:383 +#: apps/member/models.py:261 apps/member/views.py:385 msgid "User is not a member of the parent club" msgstr "" -#: apps/member/models.py:236 apps/member/views.py:392 +#: apps/member/models.py:271 apps/member/views.py:394 msgid "User is already a member of the club" msgstr "" -#: apps/member/models.py:271 +#: apps/member/models.py:306 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "" -#: apps/member/models.py:274 +#: apps/member/models.py:309 msgid "membership" msgstr "" -#: apps/member/models.py:275 +#: apps/member/models.py:310 msgid "memberships" msgstr "" -#: apps/member/tables.py:73 +#: apps/member/tables.py:93 msgid "Renew" msgstr "" -#: apps/member/views.py:80 templates/member/profile_info.html:45 +#: apps/member/views.py:59 apps/registration/forms.py:20 +msgid "This address must be valid." +msgstr "" + +#: apps/member/views.py:62 templates/member/profile_info.html:45 +#: templates/registration/future_profile_detail.html:55 msgid "Update Profile" msgstr "" -#: apps/member/views.py:93 +#: apps/member/views.py:72 msgid "An alias with a similar name already exists." msgstr "" -#: apps/member/views.py:379 +#: apps/member/views.py:167 +msgid "Search user" +msgstr "" + +#: apps/member/views.py:381 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." msgstr "" -#: apps/member/views.py:396 apps/member/views.py:428 +#: apps/member/views.py:398 apps/member/views.py:430 msgid "The membership must start after {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:401 apps/member/views.py:433 +#: apps/member/views.py:403 apps/member/views.py:435 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:455 +#: apps/member/views.py:457 msgid "This membership is already renewed" msgstr "" -#: apps/note/admin.py:120 apps/note/models/transactions.py:94 +#: apps/note/admin.py:120 apps/note/models/transactions.py:99 msgid "source" msgstr "" -#: apps/note/admin.py:128 apps/note/admin.py:163 -#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107 +#: apps/note/admin.py:128 apps/note/admin.py:170 +#: apps/note/models/transactions.py:54 apps/note/models/transactions.py:112 msgid "destination" msgstr "" @@ -452,7 +489,7 @@ msgstr "" msgid "display image" msgstr "" -#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117 +#: apps/note/models/notes.py:53 apps/note/models/transactions.py:122 msgid "created at" msgstr "" @@ -535,78 +572,82 @@ msgstr "" msgid "A template with this name already exist" msgstr "" -#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:125 +#: apps/note/models/transactions.py:58 apps/note/models/transactions.py:130 msgid "amount" msgstr "" -#: apps/note/models/transactions.py:57 +#: apps/note/models/transactions.py:59 msgid "in centimes" msgstr "" -#: apps/note/models/transactions.py:75 +#: apps/note/models/transactions.py:70 +msgid "display" +msgstr "" + +#: apps/note/models/transactions.py:80 msgid "transaction template" msgstr "" -#: apps/note/models/transactions.py:76 +#: apps/note/models/transactions.py:81 msgid "transaction templates" msgstr "" -#: apps/note/models/transactions.py:100 apps/note/models/transactions.py:113 +#: apps/note/models/transactions.py:105 apps/note/models/transactions.py:118 #: apps/note/tables.py:33 apps/note/tables.py:42 msgid "used alias" msgstr "" -#: apps/note/models/transactions.py:121 +#: apps/note/models/transactions.py:126 msgid "quantity" msgstr "" -#: apps/note/models/transactions.py:129 +#: apps/note/models/transactions.py:134 msgid "reason" msgstr "" -#: apps/note/models/transactions.py:139 apps/note/tables.py:95 +#: apps/note/models/transactions.py:144 apps/note/tables.py:95 msgid "invalidity reason" msgstr "" -#: apps/note/models/transactions.py:147 +#: apps/note/models/transactions.py:152 msgid "transaction" msgstr "" -#: apps/note/models/transactions.py:148 +#: apps/note/models/transactions.py:153 msgid "transactions" msgstr "" -#: apps/note/models/transactions.py:202 templates/base.html:84 +#: apps/note/models/transactions.py:207 templates/base.html:84 #: templates/note/transaction_form.html:19 #: templates/note/transaction_form.html:140 msgid "Transfer" msgstr "" -#: apps/note/models/transactions.py:222 +#: apps/note/models/transactions.py:227 msgid "Template" msgstr "" -#: apps/note/models/transactions.py:237 +#: apps/note/models/transactions.py:242 msgid "first_name" msgstr "" -#: apps/note/models/transactions.py:242 +#: apps/note/models/transactions.py:247 msgid "bank" msgstr "" -#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:24 +#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:24 msgid "Credit" msgstr "" -#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:28 +#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:28 msgid "Debit" msgstr "" -#: apps/note/models/transactions.py:264 apps/note/models/transactions.py:269 +#: apps/note/models/transactions.py:269 apps/note/models/transactions.py:274 msgid "membership transaction" msgstr "" -#: apps/note/models/transactions.py:265 +#: apps/note/models/transactions.py:270 msgid "membership transactions" msgstr "" @@ -622,6 +663,15 @@ msgstr "" msgid "No reason specified" msgstr "" +#: apps/note/tables.py:122 apps/note/tables.py:151 +msgid "Delete" +msgstr "" + +#: apps/note/tables.py:146 templates/member/club_info.html:55 +#: templates/note/conso_form.html:121 +msgid "Edit" +msgstr "" + #: apps/note/views.py:39 msgid "Transfer money" msgstr "" @@ -648,7 +698,74 @@ msgstr "" msgid "Specifying field applies only to view and change permission types." msgstr "" -#: apps/treasury/apps.py:12 templates/base.html:99 +#: apps/registration/apps.py:10 +msgid "registration" +msgstr "" + +#: apps/registration/forms.py:29 +msgid "Inscription paid by Société Générale" +msgstr "" + +#: apps/registration/forms.py:31 +msgid "Check this case is the Société Générale paid the inscription." +msgstr "" + +#: apps/registration/forms.py:36 +msgid "Credit type" +msgstr "" + +#: apps/registration/forms.py:37 +msgid "No credit" +msgstr "" + +#: apps/registration/forms.py:42 +msgid "Credit amount" +msgstr "" + +#: apps/registration/forms.py:59 apps/treasury/forms.py:125 +#: templates/note/transaction_form.html:98 +msgid "Bank" +msgstr "" + +#: apps/registration/forms.py:64 +msgid "Join BDE Club" +msgstr "" + +#: apps/registration/forms.py:70 +msgid "Join Kfet Club" +msgstr "" + +#: apps/registration/views.py:70 +msgid "Email validation" +msgstr "" + +#: apps/registration/views.py:114 +msgid "Email validation unsuccessful" +msgstr "" + +#: apps/registration/views.py:122 +msgid "Email validation email sent" +msgstr "" + +#: apps/registration/views.py:154 +msgid "Unregistered users" +msgstr "" + +#: apps/registration/views.py:226 +msgid "You must join BDE club before joining Kfet club." +msgstr "" + +#: apps/registration/views.py:230 +msgid "" +"The entered amount is not enough for the memberships, should be at least {}" +msgstr "" + +#: apps/registration/views.py:237 apps/registration/views.py:239 +#: apps/registration/views.py:241 +msgid "This field is required." +msgstr "" + +#: apps/treasury/apps.py:12 templates/base.html:111 msgid "Treasury" msgstr "" @@ -673,10 +790,6 @@ msgstr "" msgid "You can't change the type of the remittance." msgstr "" -#: apps/treasury/forms.py:125 templates/note/transaction_form.html:98 -msgid "Bank" -msgstr "" - #: apps/treasury/forms.py:127 apps/treasury/tables.py:47 #: templates/note/transaction_form.html:128 #: templates/treasury/remittance_form.html:18 @@ -781,15 +894,15 @@ msgid "" "again unless your session expires or you logout." msgstr "" -#: note_kfet/settings/base.py:152 +#: note_kfet/settings/base.py:153 msgid "German" msgstr "" -#: note_kfet/settings/base.py:153 +#: note_kfet/settings/base.py:154 msgid "English" msgstr "" -#: note_kfet/settings/base.py:154 +#: note_kfet/settings/base.py:155 msgid "French" msgstr "" @@ -858,9 +971,23 @@ msgid "The ENS Paris-Saclay BDE note." msgstr "" #: templates/base.html:89 +msgid "Users" +msgstr "" + +#: templates/base.html:94 msgid "Clubs" msgstr "" +#: templates/base.html:100 +msgid "Registrations" +msgstr "" + +#: templates/base.html:150 +msgid "" +"Your e-mail address is not validated. Please check your mail inbox and click " +"on the validation link." +msgstr "" + #: templates/cas_server/base.html:7 msgid "Central Authentication Service" msgstr "" @@ -936,10 +1063,6 @@ msgstr "" msgid "Add member" msgstr "" -#: templates/member/club_info.html:55 templates/note/conso_form.html:121 -msgid "Edit" -msgstr "" - #: templates/member/club_info.html:59 templates/member/profile_info.html:48 msgid "View Profile" msgstr "" @@ -960,7 +1083,7 @@ msgstr "" msgid "Member of the Club" msgstr "" -#: templates/member/club_tables.html:22 templates/member/profile_tables.html:22 +#: templates/member/club_tables.html:22 templates/member/profile_tables.html:31 msgid "Transaction history" msgstr "" @@ -977,18 +1100,22 @@ msgid "Regenerate token" msgstr "" #: templates/member/profile_info.html:5 +#: templates/registration/future_profile_detail.html:12 msgid "Account #" msgstr "" #: templates/member/profile_info.html:17 +#: templates/registration/future_profile_detail.html:19 msgid "username" msgstr "" #: templates/member/profile_info.html:20 +#: templates/registration/future_profile_detail.html:34 msgid "password" msgstr "" #: templates/member/profile_info.html:23 +#: templates/registration/future_profile_detail.html:37 msgid "Change password" msgstr "" @@ -1000,7 +1127,17 @@ msgstr "" msgid "Manage auth token" msgstr "" -#: templates/member/profile_tables.html:9 +#: templates/member/profile_tables.html:7 +#: templates/registration/future_profile_detail.html:28 +msgid "This user doesn't have confirmed his/her e-mail address." +msgstr "" + +#: templates/member/profile_tables.html:8 +#: templates/registration/future_profile_detail.html:29 +msgid "Click here to resend a validation link." +msgstr "" + +#: templates/member/profile_tables.html:18 msgid "View my memberships" msgstr "" @@ -1008,11 +1145,6 @@ msgstr "" msgid "Save Changes" msgstr "" -#: templates/member/signup.html:5 templates/member/signup.html:8 -#: templates/member/signup.html:14 -msgid "Sign up" -msgstr "" - #: templates/note/conso_form.html:28 templates/note/transaction_form.html:50 msgid "Select emitters" msgstr "" @@ -1095,6 +1227,39 @@ msgstr "" msgid "Unable to delete button " msgstr "" +#: templates/registration/email_validation_complete.html:8 +#, python-format +msgid "" +"Your account have successfully been activated. You can now log in." +msgstr "" + +#: templates/registration/email_validation_complete.html:12 +msgid "" +"The link was invalid. The token may have expired. Please send us an email to " +"activate your account." +msgstr "" + +#: templates/registration/future_profile_detail.html:56 +msgid "Delete registration" +msgstr "" + +#: templates/registration/future_profile_detail.html:64 +msgid "Validate account" +msgstr "" + +#: templates/registration/future_profile_detail.html:71 +msgid "Validate registration" +msgstr "" + +#: templates/registration/future_user_list.html:7 +msgid "New user" +msgstr "" + +#: templates/registration/future_user_list.html:17 +msgid "There is no pending user." +msgstr "" + #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." msgstr "" @@ -1181,6 +1346,11 @@ msgstr "" msgid "Reset my password" msgstr "" +#: templates/registration/signup.html:5 templates/registration/signup.html:8 +#: templates/registration/signup.html:14 +msgid "Sign up" +msgstr "" + #: templates/treasury/invoice_form.html:6 msgid "Invoices list" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 9754fa9c..279a73ff 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-01 18:39+0200\n" +"POT-Creation-Date: 2020-04-05 15:31+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -13,37 +13,38 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: apps/activity/apps.py:10 apps/activity/models.py:111 -#: apps/activity/models.py:120 +#: apps/activity/apps.py:10 apps/activity/models.py:102 +#: apps/activity/models.py:111 msgid "activity" msgstr "activité" -#: apps/activity/forms.py:45 apps/activity/models.py:217 +#: apps/activity/forms.py:45 apps/activity/models.py:206 msgid "You can't invite someone once the activity is started." msgstr "" "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." -#: apps/activity/forms.py:48 apps/activity/models.py:220 +#: apps/activity/forms.py:48 apps/activity/models.py:209 msgid "This activity is not validated yet." msgstr "Cette activité n'est pas encore validée." -#: apps/activity/forms.py:58 apps/activity/models.py:228 +#: apps/activity/forms.py:58 apps/activity/models.py:217 msgid "This person has been already invited 5 times this year." msgstr "Cette personne a déjà été invitée 5 fois cette année." -#: apps/activity/forms.py:62 apps/activity/models.py:232 +#: apps/activity/forms.py:62 apps/activity/models.py:221 msgid "This person is already invited." msgstr "Cette personne est déjà invitée." -#: apps/activity/forms.py:66 apps/activity/models.py:236 +#: apps/activity/forms.py:66 apps/activity/models.py:225 msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/activity/models.py:23 apps/activity/models.py:48 -#: apps/member/models.py:66 apps/member/models.py:169 +#: apps/member/models.py:100 apps/member/models.py:203 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 -#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:232 +#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 +#: templates/registration/future_profile_detail.html:16 msgid "name" msgstr "nom" @@ -63,18 +64,18 @@ msgstr "type d'activité" msgid "activity types" msgstr "types d'activité" -#: apps/activity/models.py:53 apps/note/models/transactions.py:69 +#: apps/activity/models.py:53 apps/note/models/transactions.py:74 #: apps/permission/models.py:103 templates/activity/activity_detail.html:16 msgid "description" msgstr "description" #: apps/activity/models.py:60 apps/note/models/notes.py:164 -#: apps/note/models/transactions.py:62 +#: apps/note/models/transactions.py:64 #: templates/activity/activity_detail.html:19 msgid "type" msgstr "type" -#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:190 +#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:224 #: apps/note/models/notes.py:117 msgid "user" msgstr "utilisateur" @@ -83,73 +84,74 @@ msgstr "utilisateur" msgid "organizer" msgstr "organisateur" -#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14 -#: apps/note/models/notes.py:58 -msgid "note" -msgstr "note" - -#: apps/activity/models.py:89 templates/activity/activity_detail.html:36 +#: apps/activity/models.py:80 templates/activity/activity_detail.html:36 msgid "attendees club" msgstr "club attendu" -#: apps/activity/models.py:93 templates/activity/activity_detail.html:22 +#: apps/activity/models.py:84 templates/activity/activity_detail.html:22 msgid "start date" msgstr "date de début" -#: apps/activity/models.py:97 templates/activity/activity_detail.html:25 +#: apps/activity/models.py:88 templates/activity/activity_detail.html:25 msgid "end date" msgstr "date de fin" -#: apps/activity/models.py:102 apps/note/models/transactions.py:134 +#: apps/activity/models.py:93 apps/note/models/transactions.py:139 #: templates/activity/activity_detail.html:47 msgid "valid" msgstr "valide" -#: apps/activity/models.py:107 templates/activity/activity_detail.html:61 +#: apps/activity/models.py:98 templates/activity/activity_detail.html:61 msgid "open" msgstr "ouvrir" -#: apps/activity/models.py:112 +#: apps/activity/models.py:103 msgid "activities" msgstr "activités" -#: apps/activity/models.py:125 +#: apps/activity/models.py:116 msgid "entry time" msgstr "heure d'entrée" -#: apps/activity/models.py:148 +#: apps/activity/models.py:122 apps/note/apps.py:14 +#: apps/note/models/notes.py:58 +msgid "note" +msgstr "note" + +#: apps/activity/models.py:139 msgid "Already entered on " msgstr "Déjà rentré le " -#: apps/activity/models.py:148 apps/activity/tables.py:54 +#: apps/activity/models.py:139 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%d/%m/%Y %H:%M:%S}" -#: apps/activity/models.py:156 +#: apps/activity/models.py:147 msgid "The balance is negative." msgstr "La note est en négatif." -#: apps/activity/models.py:188 +#: apps/activity/models.py:177 msgid "last name" msgstr "nom de famille" -#: apps/activity/models.py:193 templates/member/profile_info.html:14 +#: apps/activity/models.py:182 templates/member/profile_info.html:14 +#: templates/registration/future_profile_detail.html:16 msgid "first name" msgstr "prénom" -#: apps/activity/models.py:200 +#: apps/activity/models.py:189 msgid "inviter" msgstr "hôte" -#: apps/activity/models.py:241 +#: apps/activity/models.py:230 msgid "guest" msgstr "invité" -#: apps/activity/models.py:242 +#: apps/activity/models.py:231 msgid "guests" msgstr "invités" -#: apps/activity/models.py:254 +#: apps/activity/models.py:243 msgid "Invitation" msgstr "Invitation" @@ -165,12 +167,13 @@ msgstr "supprimer" msgid "Type" msgstr "Type" -#: apps/activity/tables.py:77 apps/treasury/forms.py:121 +#: apps/activity/tables.py:77 apps/registration/forms.py:49 +#: apps/treasury/forms.py:121 msgid "Last name" msgstr "Nom de famille" -#: apps/activity/tables.py:79 apps/treasury/forms.py:123 -#: templates/note/transaction_form.html:92 +#: apps/activity/tables.py:79 apps/registration/forms.py:54 +#: apps/treasury/forms.py:123 templates/note/transaction_form.html:92 msgid "First name" msgstr "Prénom" @@ -178,11 +181,11 @@ msgstr "Prénom" msgid "Note" msgstr "Note" -#: apps/activity/tables.py:83 +#: apps/activity/tables.py:83 apps/member/tables.py:35 msgid "Balance" msgstr "Solde du compte" -#: apps/activity/views.py:45 templates/base.html:94 +#: apps/activity/views.py:45 templates/base.html:106 msgid "Activities" msgstr "Activités" @@ -222,12 +225,12 @@ msgstr "Nouvelles données" msgid "create" msgstr "Créer" -#: apps/logs/models.py:61 apps/note/tables.py:142 +#: apps/logs/models.py:61 apps/note/tables.py:144 #: templates/activity/activity_detail.html:67 msgid "edit" msgstr "Modifier" -#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146 +#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:149 msgid "delete" msgstr "Supprimer" @@ -247,77 +250,102 @@ msgstr "Les logs ne peuvent pas être détruits." msgid "member" msgstr "adhérent" -#: apps/member/models.py:28 +#: apps/member/models.py:32 +#: templates/registration/future_profile_detail.html:47 msgid "phone number" msgstr "numéro de téléphone" -#: apps/member/models.py:34 templates/member/profile_info.html:27 +#: apps/member/models.py:38 templates/member/profile_info.html:27 +#: templates/registration/future_profile_detail.html:41 msgid "section" msgstr "section" -#: apps/member/models.py:35 +#: apps/member/models.py:39 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" -#: apps/member/models.py:41 templates/member/profile_info.html:30 +#: apps/member/models.py:45 templates/member/profile_info.html:30 +#: templates/registration/future_profile_detail.html:44 msgid "address" msgstr "adresse" -#: apps/member/models.py:47 +#: apps/member/models.py:51 +#: templates/registration/future_profile_detail.html:50 msgid "paid" msgstr "payé" -#: apps/member/models.py:52 apps/member/models.py:53 +#: apps/member/models.py:52 +msgid "Tells if the user receive a salary." +msgstr "Indique si l'utilisateur perçoit un salaire." + +#: apps/member/models.py:57 +msgid "email confirmed" +msgstr "adresse email confirmée" + +#: apps/member/models.py:62 +msgid "registration valid" +msgstr "inscription valid" + +#: apps/member/models.py:67 +msgid "Société générale" +msgstr "Société générale" + +#: apps/member/models.py:68 +msgid "Has the user ever be paid by the Société générale?" +msgstr "Est-ce que l'utilisateur a déjà été payé par la Société Générale ?" + +#: apps/member/models.py:73 apps/member/models.py:74 msgid "user profile" msgstr "profil utilisateur" -#: apps/member/models.py:71 templates/member/club_info.html:46 +#: apps/member/models.py:105 templates/member/club_info.html:46 +#: templates/registration/future_profile_detail.html:22 msgid "email" msgstr "courriel" -#: apps/member/models.py:78 +#: apps/member/models.py:112 msgid "parent club" msgstr "club parent" -#: apps/member/models.py:87 +#: apps/member/models.py:121 msgid "require memberships" msgstr "nécessite des adhésions" -#: apps/member/models.py:88 +#: apps/member/models.py:122 msgid "Uncheck if this club don't require memberships." msgstr "Décochez si ce club n'utilise pas d'adhésions." -#: apps/member/models.py:93 templates/member/club_info.html:35 +#: apps/member/models.py:127 templates/member/club_info.html:35 msgid "membership fee (paid students)" msgstr "cotisation pour adhérer (normalien élève)" -#: apps/member/models.py:98 templates/member/club_info.html:38 +#: apps/member/models.py:132 templates/member/club_info.html:38 msgid "membership fee (unpaid students)" msgstr "cotisation pour adhérer (normalien étudiant)" -#: apps/member/models.py:104 templates/member/club_info.html:28 +#: apps/member/models.py:138 templates/member/club_info.html:28 msgid "membership duration" msgstr "durée de l'adhésion" -#: apps/member/models.py:105 +#: apps/member/models.py:139 msgid "The longest time (in days) a membership can last (NULL = infinite)." msgstr "La durée maximale (en jours) d'une adhésion (NULL = infinie)." -#: apps/member/models.py:112 templates/member/club_info.html:22 +#: apps/member/models.py:146 templates/member/club_info.html:22 msgid "membership start" msgstr "début de l'adhésion" -#: apps/member/models.py:113 +#: apps/member/models.py:147 msgid "How long after January 1st the members can renew their membership." msgstr "" "Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur " "adhésion." -#: apps/member/models.py:120 templates/member/club_info.html:25 +#: apps/member/models.py:154 templates/member/club_info.html:25 msgid "membership end" msgstr "fin de l'adhésion" -#: apps/member/models.py:121 +#: apps/member/models.py:155 msgid "" "How long the membership can last after January 1st of the next year after " "members can renew their membership." @@ -325,92 +353,103 @@ msgstr "" "Combien de temps l'adhésion peut durer après le 1er Janvier de l'année " "suivante avant que les adhérents peuvent renouveler leur adhésion." -#: apps/member/models.py:154 apps/member/models.py:196 +#: apps/member/models.py:188 apps/member/models.py:230 #: apps/note/models/notes.py:139 msgid "club" msgstr "club" -#: apps/member/models.py:155 +#: apps/member/models.py:189 msgid "clubs" msgstr "clubs" -#: apps/member/models.py:175 apps/permission/models.py:288 +#: apps/member/models.py:209 apps/permission/models.py:288 msgid "role" msgstr "rôle" -#: apps/member/models.py:176 apps/member/models.py:201 +#: apps/member/models.py:210 apps/member/models.py:235 msgid "roles" msgstr "rôles" -#: apps/member/models.py:205 +#: apps/member/models.py:240 msgid "membership starts on" msgstr "l'adhésion commence le" -#: apps/member/models.py:209 +#: apps/member/models.py:244 msgid "membership ends on" msgstr "l'adhésion finit le" -#: apps/member/models.py:214 +#: apps/member/models.py:249 msgid "fee" msgstr "cotisation" -#: apps/member/models.py:226 apps/member/views.py:383 +#: apps/member/models.py:261 apps/member/views.py:385 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" -#: apps/member/models.py:236 apps/member/views.py:392 +#: apps/member/models.py:271 apps/member/views.py:394 msgid "User is already a member of the club" msgstr "L'utilisateur est déjà membre du club" -#: apps/member/models.py:271 +#: apps/member/models.py:306 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Adhésion de {user} pour le club {club}" -#: apps/member/models.py:274 +#: apps/member/models.py:309 msgid "membership" msgstr "adhésion" -#: apps/member/models.py:275 +#: apps/member/models.py:310 msgid "memberships" msgstr "adhésions" -#: apps/member/tables.py:73 +#: apps/member/tables.py:93 msgid "Renew" -msgstr "" +msgstr "Renouveler" -#: apps/member/views.py:80 templates/member/profile_info.html:45 +#: apps/member/views.py:59 apps/registration/forms.py:20 +msgid "This address must be valid." +msgstr "Cette adresse doit être valide." + +#: apps/member/views.py:62 templates/member/profile_info.html:45 +#: templates/registration/future_profile_detail.html:55 msgid "Update Profile" msgstr "Modifier le profil" -#: apps/member/views.py:93 +#: apps/member/views.py:72 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." -#: apps/member/views.py:379 +#: apps/member/views.py:167 +msgid "Search user" +msgstr "Chercher un utilisateur" + +#: apps/member/views.py:381 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." msgstr "" +"Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne " +"peut pas avoir un solde négatif." -#: apps/member/views.py:396 apps/member/views.py:428 +#: apps/member/views.py:398 apps/member/views.py:430 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:401 apps/member/views.py:433 +#: apps/member/views.py:403 apps/member/views.py:435 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:455 +#: apps/member/views.py:457 msgid "This membership is already renewed" msgstr "Cette adhésion est déjà renouvelée" -#: apps/note/admin.py:120 apps/note/models/transactions.py:94 +#: apps/note/admin.py:120 apps/note/models/transactions.py:99 msgid "source" msgstr "source" -#: apps/note/admin.py:128 apps/note/admin.py:163 -#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107 +#: apps/note/admin.py:128 apps/note/admin.py:170 +#: apps/note/models/transactions.py:54 apps/note/models/transactions.py:112 msgid "destination" msgstr "destination" @@ -453,7 +492,7 @@ msgstr "" msgid "display image" msgstr "image affichée" -#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117 +#: apps/note/models/notes.py:53 apps/note/models/transactions.py:122 msgid "created at" msgstr "créée le" @@ -536,80 +575,84 @@ msgstr "catégories de transaction" msgid "A template with this name already exist" msgstr "Un modèle de transaction avec un nom similaire existe déjà." -#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:125 +#: apps/note/models/transactions.py:58 apps/note/models/transactions.py:130 msgid "amount" msgstr "montant" -#: apps/note/models/transactions.py:57 +#: apps/note/models/transactions.py:59 msgid "in centimes" msgstr "en centimes" -#: apps/note/models/transactions.py:75 +#: apps/note/models/transactions.py:70 +msgid "display" +msgstr "afficher" + +#: apps/note/models/transactions.py:80 msgid "transaction template" msgstr "modèle de transaction" -#: apps/note/models/transactions.py:76 +#: apps/note/models/transactions.py:81 msgid "transaction templates" msgstr "modèles de transaction" -#: apps/note/models/transactions.py:100 apps/note/models/transactions.py:113 +#: apps/note/models/transactions.py:105 apps/note/models/transactions.py:118 #: apps/note/tables.py:33 apps/note/tables.py:42 msgid "used alias" msgstr "alias utilisé" -#: apps/note/models/transactions.py:121 +#: apps/note/models/transactions.py:126 msgid "quantity" msgstr "quantité" -#: apps/note/models/transactions.py:129 +#: apps/note/models/transactions.py:134 msgid "reason" msgstr "raison" -#: apps/note/models/transactions.py:139 apps/note/tables.py:95 +#: apps/note/models/transactions.py:144 apps/note/tables.py:95 msgid "invalidity reason" msgstr "Motif d'invalidité" -#: apps/note/models/transactions.py:147 +#: apps/note/models/transactions.py:152 msgid "transaction" msgstr "transaction" -#: apps/note/models/transactions.py:148 +#: apps/note/models/transactions.py:153 msgid "transactions" msgstr "transactions" -#: apps/note/models/transactions.py:202 templates/base.html:84 +#: apps/note/models/transactions.py:207 templates/base.html:84 #: templates/note/transaction_form.html:19 #: templates/note/transaction_form.html:140 msgid "Transfer" msgstr "Virement" -#: apps/note/models/transactions.py:222 +#: apps/note/models/transactions.py:227 msgid "Template" msgstr "Bouton" -#: apps/note/models/transactions.py:237 +#: apps/note/models/transactions.py:242 msgid "first_name" msgstr "prénom" -#: apps/note/models/transactions.py:242 +#: apps/note/models/transactions.py:247 msgid "bank" msgstr "banque" -#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:24 +#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:24 msgid "Credit" msgstr "Crédit" -#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:28 +#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:28 msgid "Debit" msgstr "Débit" -#: apps/note/models/transactions.py:264 apps/note/models/transactions.py:269 +#: apps/note/models/transactions.py:269 apps/note/models/transactions.py:274 msgid "membership transaction" -msgstr "transaction d'adhésion" +msgstr "Transaction d'adhésion" -#: apps/note/models/transactions.py:265 +#: apps/note/models/transactions.py:270 msgid "membership transactions" -msgstr "transactions d'adhésion" +msgstr "Transactions d'adhésion" #: apps/note/tables.py:57 msgid "Click to invalidate" @@ -623,6 +666,15 @@ msgstr "Cliquez pour valider" msgid "No reason specified" msgstr "Pas de motif spécifié" +#: apps/note/tables.py:122 apps/note/tables.py:151 +msgid "Delete" +msgstr "Supprimer" + +#: apps/note/tables.py:146 templates/member/club_info.html:55 +#: templates/note/conso_form.html:121 +msgid "Edit" +msgstr "Éditer" + #: apps/note/views.py:39 msgid "Transfer money" msgstr "Transférer de l'argent" @@ -649,7 +701,74 @@ msgstr "Rang" msgid "Specifying field applies only to view and change permission types." msgstr "" -#: apps/treasury/apps.py:12 templates/base.html:99 +#: apps/registration/apps.py:10 +msgid "registration" +msgstr "inscription" + +#: apps/registration/forms.py:29 +msgid "Inscription paid by Société Générale" +msgstr "Inscription payée par la Société générale" + +#: apps/registration/forms.py:31 +msgid "Check this case is the Société Générale paid the inscription." +msgstr "Cochez cette case si la Société Générale a payé l'inscription." + +#: apps/registration/forms.py:36 +msgid "Credit type" +msgstr "Type de rechargement" + +#: apps/registration/forms.py:37 +msgid "No credit" +msgstr "Pas de rechargement" + +#: apps/registration/forms.py:42 +msgid "Credit amount" +msgstr "Montant à créditer" + +#: apps/registration/forms.py:59 apps/treasury/forms.py:125 +#: templates/note/transaction_form.html:98 +msgid "Bank" +msgstr "Banque" + +#: apps/registration/forms.py:64 +msgid "Join BDE Club" +msgstr "Adhérer au club BDE" + +#: apps/registration/forms.py:70 +msgid "Join Kfet Club" +msgstr "Adhérer au club Kfet" + +#: apps/registration/views.py:70 +msgid "Email validation" +msgstr "Validation de l'adresse mail" + +#: apps/registration/views.py:114 +msgid "Email validation unsuccessful" +msgstr " La validation de l'adresse mail a échoué" + +#: apps/registration/views.py:122 +msgid "Email validation email sent" +msgstr "L'email de vérification de l'adresse email a bien été envoyé." + +#: apps/registration/views.py:154 +msgid "Unregistered users" +msgstr "Utilisateurs en attente d'inscription" + +#: apps/registration/views.py:226 +msgid "You must join BDE club before joining Kfet club." +msgstr "Vous devez adhérer au club BDE avant d'adhérer au club Kfet." + +#: apps/registration/views.py:230 +msgid "" +"The entered amount is not enough for the memberships, should be at least {}" +msgstr "Le montant crédité est trop faible pour adhérer, il doit être au minimum de {}" + +#: apps/registration/views.py:237 apps/registration/views.py:239 +#: apps/registration/views.py:241 +msgid "This field is required." +msgstr "Ce champ est requis." + +#: apps/treasury/apps.py:12 templates/base.html:111 msgid "Treasury" msgstr "Trésorerie" @@ -674,10 +793,6 @@ msgstr "La remise est déjà fermée." msgid "You can't change the type of the remittance." msgstr "Vous ne pouvez pas changer le type de la remise." -#: apps/treasury/forms.py:125 templates/note/transaction_form.html:98 -msgid "Bank" -msgstr "Banque" - #: apps/treasury/forms.py:127 apps/treasury/tables.py:47 #: templates/note/transaction_form.html:128 #: templates/treasury/remittance_form.html:18 @@ -782,15 +897,15 @@ msgid "" "again unless your session expires or you logout." msgstr "" -#: note_kfet/settings/base.py:152 +#: note_kfet/settings/base.py:153 msgid "German" msgstr "" -#: note_kfet/settings/base.py:153 +#: note_kfet/settings/base.py:154 msgid "English" msgstr "" -#: note_kfet/settings/base.py:154 +#: note_kfet/settings/base.py:155 msgid "French" msgstr "" @@ -859,9 +974,23 @@ msgid "The ENS Paris-Saclay BDE note." msgstr "La note du BDE de l'ENS Paris-Saclay." #: templates/base.html:89 +msgid "Users" +msgstr "" + +#: templates/base.html:94 msgid "Clubs" msgstr "Clubs" +#: templates/base.html:100 +msgid "Registrations" +msgstr "Inscriptions" + +#: templates/base.html:150 +msgid "" +"Your e-mail address is not validated. Please check your mail inbox and click " +"on the validation link." +msgstr "" + #: templates/cas_server/base.html:7 msgid "Central Authentication Service" msgstr "" @@ -939,10 +1068,6 @@ msgstr "cotisation pour adhérer" msgid "Add member" msgstr "Ajouter un membre" -#: templates/member/club_info.html:55 templates/note/conso_form.html:121 -msgid "Edit" -msgstr "Éditer" - #: templates/member/club_info.html:59 templates/member/profile_info.html:48 msgid "View Profile" msgstr "Voir le profil" @@ -963,7 +1088,7 @@ msgstr "Liste des clubs" msgid "Member of the Club" msgstr "Membre du club" -#: templates/member/club_tables.html:22 templates/member/profile_tables.html:22 +#: templates/member/club_tables.html:22 templates/member/profile_tables.html:31 msgid "Transaction history" msgstr "Historique des transactions" @@ -980,18 +1105,22 @@ msgid "Regenerate token" msgstr "Regénérer le jeton" #: templates/member/profile_info.html:5 +#: templates/registration/future_profile_detail.html:12 msgid "Account #" msgstr "Compte n°" #: templates/member/profile_info.html:17 +#: templates/registration/future_profile_detail.html:19 msgid "username" msgstr "pseudo" #: templates/member/profile_info.html:20 +#: templates/registration/future_profile_detail.html:34 msgid "password" msgstr "mot de passe" #: templates/member/profile_info.html:23 +#: templates/registration/future_profile_detail.html:37 msgid "Change password" msgstr "Changer le mot de passe" @@ -1003,7 +1132,17 @@ msgstr "solde du compte" msgid "Manage auth token" msgstr "Gérer les jetons d'authentification" -#: templates/member/profile_tables.html:9 +#: templates/member/profile_tables.html:7 +#: templates/registration/future_profile_detail.html:28 +msgid "This user doesn't have confirmed his/her e-mail address." +msgstr "" + +#: templates/member/profile_tables.html:8 +#: templates/registration/future_profile_detail.html:29 +msgid "Click here to resend a validation link." +msgstr "" + +#: templates/member/profile_tables.html:18 msgid "View my memberships" msgstr "Voir mes adhésions" @@ -1011,11 +1150,6 @@ msgstr "Voir mes adhésions" msgid "Save Changes" msgstr "Sauvegarder les changements" -#: templates/member/signup.html:5 templates/member/signup.html:8 -#: templates/member/signup.html:14 -msgid "Sign up" -msgstr "Inscription" - #: templates/note/conso_form.html:28 templates/note/transaction_form.html:50 msgid "Select emitters" msgstr "Sélection des émetteurs" @@ -1098,6 +1232,39 @@ msgstr "Le bouton a bien été supprimé" msgid "Unable to delete button " msgstr "Impossible de supprimer le bouton " +#: templates/registration/email_validation_complete.html:8 +#, python-format +msgid "" +"Your account have successfully been activated. You can now log in." +msgstr "" + +#: templates/registration/email_validation_complete.html:12 +msgid "" +"The link was invalid. The token may have expired. Please send us an email to " +"activate your account." +msgstr "" + +#: templates/registration/future_profile_detail.html:56 +msgid "Delete registration" +msgstr "" + +#: templates/registration/future_profile_detail.html:64 +msgid "Validate account" +msgstr "" + +#: templates/registration/future_profile_detail.html:71 +msgid "Validate registration" +msgstr "" + +#: templates/registration/future_user_list.html:7 +msgid "New user" +msgstr "Nouvel utilisateur" + +#: templates/registration/future_user_list.html:17 +msgid "There is no pending user." +msgstr "Il n'y a pas d'inscription en attente." + #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." msgstr "" @@ -1184,6 +1351,11 @@ msgstr "" msgid "Reset my password" msgstr "" +#: templates/registration/signup.html:5 templates/registration/signup.html:8 +#: templates/registration/signup.html:14 +msgid "Sign up" +msgstr "Inscription" + #: templates/treasury/invoice_form.html:6 msgid "Invoices list" msgstr "Liste des factures" From f2baeedc013d5c5b1261abbe7fc1632e0730520e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 16:05:49 +0200 Subject: [PATCH 51/66] Add basic roles for BDE and Kfet --- apps/registration/views.py | 10 +++++++--- locale/fr/LC_MESSAGES/django.po | 10 +++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/registration/views.py b/apps/registration/views.py index 8d303392..9f003dac 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -15,7 +15,7 @@ from django.views.decorators.csrf import csrf_protect from django.views.generic import CreateView, TemplateView, DetailView, FormView from django_tables2 import SingleTableView from member.forms import ProfileForm -from member.models import Membership, Club +from member.models import Membership, Club, Role from note.models import SpecialTransaction, NoteSpecial from note.templatetags.pretty_money import pretty_money from permission.backends import PermissionBackend @@ -262,18 +262,22 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, ) if join_BDE: - Membership.objects.create( + membership = Membership.objects.create( club=bde, user=user, fee=bde_fee, ) + membership.roles.add(Role.objects.get(name="Adhérent BDE")) + membership.save() if join_Kfet: - Membership.objects.create( + membership = Membership.objects.create( club=kfet, user=user, fee=kfet_fee, ) + membership.roles.add(Role.objects.get(name="Adhérent Kfet")) + membership.save() return ret diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 279a73ff..dca8fd69 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -1135,12 +1135,12 @@ msgstr "Gérer les jetons d'authentification" #: templates/member/profile_tables.html:7 #: templates/registration/future_profile_detail.html:28 msgid "This user doesn't have confirmed his/her e-mail address." -msgstr "" +msgstr "Cet utilisateur n'a pas encore confirmé son adresse e-mail." #: templates/member/profile_tables.html:8 #: templates/registration/future_profile_detail.html:29 msgid "Click here to resend a validation link." -msgstr "" +msgstr "Cliquez ici pour renvoyer un lien de validation." #: templates/member/profile_tables.html:18 msgid "View my memberships" @@ -1247,15 +1247,15 @@ msgstr "" #: templates/registration/future_profile_detail.html:56 msgid "Delete registration" -msgstr "" +msgstr "Supprimer l'inscription" #: templates/registration/future_profile_detail.html:64 msgid "Validate account" -msgstr "" +msgstr "Valider le compte" #: templates/registration/future_profile_detail.html:71 msgid "Validate registration" -msgstr "" +msgstr "Valider l'inscription" #: templates/registration/future_user_list.html:7 msgid "New user" From e8133805f52ae03a6d7baaf1aa784fe82651b29f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 16:07:31 +0200 Subject: [PATCH 52/66] Users must be member of the BDE club --- apps/registration/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 3bd1cbbf..3ca14ca1 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -62,7 +62,7 @@ class ValidationForm(forms.Form): join_BDE = forms.BooleanField( label=_("Join BDE Club"), - required=False, + required=True, initial=True, ) From 5b61db882168eeea998a80c9c88a8444d63285ea Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 16:18:56 +0200 Subject: [PATCH 53/66] We adhere other users, not ourself... --- apps/member/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/member/views.py b/apps/member/views.py index ed5826ef..5391413b 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -361,7 +361,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): def form_valid(self, form): club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ .get(pk=self.kwargs["pk"]) - user = self.request.user + user = form.instance.user form.instance.club = club if user.profile.paid: @@ -379,6 +379,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): # TODO Send a notification to the user (with a mail?) to tell her/him to credit her/his note form.add_error('user', _("This user don't have enough money to join this club, and can't have a negative balance.")) + return super().form_invalid(form) if club.parent_club is not None: if not Membership.objects.filter(user=form.instance.user, club=club.parent_club).exists(): From 515edc445938036fae23aa45beeb04f1c360ace9 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 18:37:04 +0200 Subject: [PATCH 54/66] Credit note for new memberships --- apps/member/forms.py | 33 ++++++++++++++++++++++++++++ apps/member/views.py | 36 +++++++++++++++++++++++++++++-- static/js/autocomplete_model.js | 3 +++ templates/member/add_members.html | 8 +++++++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 5c060f58..71335dce 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -4,6 +4,9 @@ from django import forms from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.models import User +from django.utils.translation import gettext_lazy as _ + +from note.models import NoteSpecial from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput from permission.models import PermissionMask @@ -48,6 +51,36 @@ class ClubForm(forms.ModelForm): class MembershipForm(forms.ModelForm): + credit_type = forms.ModelChoiceField( + queryset=NoteSpecial.objects, + label=_("Credit type"), + empty_label=_("No credit"), + required=False, + help_text=_("You can credit the note of the user."), + ) + + credit_amount = forms.IntegerField( + label=_("Credit amount"), + required=False, + initial=0, + widget=AmountInput(), + ) + + last_name = forms.CharField( + label=_("Last name"), + required=False, + ) + + first_name = forms.CharField( + label=_("First name"), + required=False, + ) + + bank = forms.CharField( + label=_("Bank"), + required=False, + ) + class Meta: model = Membership fields = ('user', 'roles', 'date_start') diff --git a/apps/member/views.py b/apps/member/views.py index 5391413b..b2befe37 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -22,7 +22,7 @@ from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token from note.forms import ImageForm from note.models import Alias, NoteUser -from note.models.transactions import Transaction +from note.models.transactions import Transaction, SpecialTransaction from note.tables import HistoryTable, AliasTable from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin @@ -355,6 +355,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): .get(pk=self.kwargs["pk"]) context = super().get_context_data(**kwargs) context['club'] = club + context['form'].fields['credit_amount'].initial = club.membership_fee_paid return context @@ -364,11 +365,20 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): user = form.instance.user form.instance.club = club + credit_type = form.cleaned_data["credit_type"] + credit_amount = form.cleaned_data["credit_amount"] + last_name = form.cleaned_data["last_name"] + first_name = form.cleaned_data["first_name"] + bank = form.cleaned_data["bank"] + + if credit_type is None: + credit_amount = 0 + if user.profile.paid: fee = club.membership_fee_paid else: fee = club.membership_fee_unpaid - if user.note.balance < fee and not Membership.objects.filter( + if user.note.balance + credit_amount < fee and not Membership.objects.filter( club__name="Kfet", user=user, date_start__lte=datetime.now().date(), @@ -405,6 +415,28 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): .format(form.instance.club.membership_start)) return super().form_invalid(form) + if credit_amount > 0: + if not last_name or not first_name or not bank: + if not last_name: + form.add_error('last_name', _("This field is required.")) + if not first_name: + form.add_error('first_name', _("This field is required.")) + if not bank: + form.add_error('bank', _("This field is required.")) + return self.form_invalid(form) + + SpecialTransaction.objects.create( + source=credit_type, + destination=user.note, + quantity=1, + amount=credit_amount, + reason="Crédit " + credit_type.special_type + " (Adhésion " + club.name + ")", + last_name=last_name, + first_name=first_name, + bank=bank, + valid=True, + ) + return super().form_valid(form) def get_success_url(self): diff --git a/static/js/autocomplete_model.js b/static/js/autocomplete_model.js index e2a3f0cb..8c3f6f09 100644 --- a/static/js/autocomplete_model.js +++ b/static/js/autocomplete_model.js @@ -24,6 +24,9 @@ $(document).ready(function () { $("#" + prefix + "_" + obj.id).click(function() { target.val(obj[name_field]); $("#" + prefix + "_pk").val(obj.id); + + if (typeof autocompleted != 'undefined') + autocompleted(obj, prefix) }); if (input === obj[name_field]) diff --git a/templates/member/add_members.html b/templates/member/add_members.html index c44440bf..f9b2276d 100644 --- a/templates/member/add_members.html +++ b/templates/member/add_members.html @@ -17,5 +17,13 @@ {% block extrajavascript %} {% endblock %} From c513759515653af173f48d394006f90026152d3f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 21:56:56 +0200 Subject: [PATCH 55/66] Credit note for membership renewals --- apps/member/forms.py | 1 - apps/member/urls.py | 4 +- apps/member/views.py | 96 +++++++++++++++------------------ apps/note/views.py | 2 +- apps/registration/views.py | 4 +- templates/member/club_info.html | 2 +- tox.ini | 2 +- 7 files changed, 49 insertions(+), 62 deletions(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 71335dce..e567fbd5 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -5,7 +5,6 @@ from django import forms from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.models import User from django.utils.translation import gettext_lazy as _ - from note.models import NoteSpecial from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput from permission.models import PermissionMask diff --git a/apps/member/urls.py b/apps/member/urls.py index 37ff8d2b..4be4ceae 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -10,9 +10,9 @@ urlpatterns = [ path('club/', views.ClubListView.as_view(), name="club_list"), path('club/create/', views.ClubCreateView.as_view(), name="club_create"), path('club//', views.ClubDetailView.as_view(), name="club_detail"), - path('club//add_member/', views.ClubAddMemberView.as_view(), name="club_add_member"), + path('club//add_member/', views.ClubAddMemberView.as_view(), name="club_add_member"), path('club/manage_roles//', views.ClubManageRolesView.as_view(), name="club_manage_roles"), - path('club/renew_membership//', views.ClubRenewMembershipView.as_view(), name="club_renew_membership"), + path('club/renew_membership//', views.ClubAddMemberView.as_view(), name="club_renew_membership"), path('club//update/', views.ClubUpdateView.as_view(), name="club_update"), path('club//update_pic/', views.ClubPictureUpdateView.as_view(), name="club_update_pic"), path('club//aliases/', views.ClubAliasView.as_view(), name="club_alias"), diff --git a/apps/member/views.py b/apps/member/views.py index b2befe37..f9b76135 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -9,14 +9,11 @@ from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.contrib.auth.views import LoginView -from django.core.exceptions import ValidationError from django.db.models import Q -from django.forms import HiddenInput from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, UpdateView, TemplateView -from django.views.generic.base import View from django.views.generic.edit import FormMixin from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token @@ -351,18 +348,40 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): template_name = 'member/add_members.html' def get_context_data(self, **kwargs): - club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ - .get(pk=self.kwargs["pk"]) context = super().get_context_data(**kwargs) + form = context['form'] + + if "club_pk" in self.kwargs: + club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ + .get(pk=self.kwargs["club_pk"]) + form.fields['credit_amount'].initial = club.membership_fee_paid + else: + old_membership = self.get_queryset().get(pk=self.kwargs["pk"]) + club = old_membership.club + user = old_membership.user + form.fields['user'].initial = user + form.fields['user'].disabled = True + form.fields['roles'].initial = old_membership.roles.all() + form.fields['date_start'].initial = old_membership.date_end + timedelta(days=1) + form.fields['credit_amount'].initial = club.membership_fee_paid if user.profile.paid \ + else club.membership_fee_unpaid + form.fields['last_name'].initial = user.last_name + form.fields['first_name'].initial = user.first_name + context['club'] = club - context['form'].fields['credit_amount'].initial = club.membership_fee_paid return context def form_valid(self, form): - club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ - .get(pk=self.kwargs["pk"]) - user = form.instance.user + if "club_pk" in self.kwargs: + club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \ + .get(pk=self.kwargs["club_pk"]) + user = form.instance.user + else: + old_membership = self.get_queryset().get(pk=self.kwargs["pk"]) + club = old_membership.club + user = old_membership.user + form.instance.club = club credit_type = form.cleaned_data["credit_type"] @@ -405,23 +424,23 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): form.add_error('user', _('User is already a member of the club')) return super().form_invalid(form) - if form.instance.club.membership_start and form.instance.date_start < form.instance.club.membership_start: + if club.membership_start and form.instance.date_start < club.membership_start: form.add_error('user', _("The membership must start after {:%m-%d-%Y}.") .format(form.instance.club.membership_start)) return super().form_invalid(form) - if form.instance.club.membership_end and form.instance.date_start > form.instance.club.membership_end: + if club.membership_end and form.instance.date_start > club.membership_end: form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.") .format(form.instance.club.membership_start)) return super().form_invalid(form) if credit_amount > 0: - if not last_name or not first_name or not bank: + if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"): if not last_name: form.add_error('last_name', _("This field is required.")) if not first_name: form.add_error('first_name', _("This field is required.")) - if not bank: + if not bank and credit_type.special_type == "Chèque": form.add_error('bank', _("This field is required.")) return self.form_invalid(form) @@ -452,49 +471,18 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): context = super().get_context_data(**kwargs) club = self.object.club context['club'] = club - form = context['form'] - form.fields['user'].disabled = True - form.fields['date_start'].widget = HiddenInput() - return context - def form_valid(self, form): - if form.instance.club.membership_start and form.instance.date_start < form.instance.club.membership_start: - form.add_error('user', _("The membership must start after {:%m-%d-%Y}.") - .format(form.instance.club.membership_start)) - return super().form_invalid(form) - - if form.instance.club.membership_end and form.instance.date_start > form.instance.club.membership_end: - form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.") - .format(form.instance.club.membership_start)) - return super().form_invalid(form) - - return super().form_valid(form) + def get_form(self, form_class=None): + form = super().get_form(form_class) + form.fields['user'].disabled = True + del form.fields['date_start'] + del form.fields['credit_type'] + del form.fields['credit_amount'] + del form.fields['last_name'] + del form.fields['first_name'] + del form.fields['bank'] + return form def get_success_url(self): return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id}) - - -class ClubRenewMembershipView(ProtectQuerysetMixin, LoginRequiredMixin, View): - def get(self, *args, **kwargs): - user = self.request.user - membership = Membership.objects.filter(PermissionBackend.filter_queryset(user, Membership, "change"))\ - .filter(pk=self.kwargs["pk"]).get() - - if Membership.objects.filter( - club=membership.club, - user=membership.user, - date_start__gte=membership.club.membership_start, - date_end__lte=membership.club.membership_end, - ).exists(): - raise ValidationError(_("This membership is already renewed")) - - new_membership = Membership.objects.create( - user=user, - club=membership.club, - date_start=membership.date_end + timedelta(days=1), - ) - new_membership.roles.set(membership.roles.all()) - new_membership.save() - - return redirect(reverse_lazy('member:club_detail', kwargs={'pk': membership.club.pk})) diff --git a/apps/note/views.py b/apps/note/views.py index ac9b3e40..4f6575e3 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -84,7 +84,7 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): table_class = HistoryTable def get_queryset(self, **kwargs): - return super().get_queryset(**kwargs).order_by("-id").all()[:50] + return super().get_queryset(**kwargs).order_by("-id").all()[:20] def get_context_data(self, **kwargs): """ diff --git a/apps/registration/views.py b/apps/registration/views.py index 9f003dac..d5a6be60 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -232,12 +232,12 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, return self.form_invalid(form) if credit_type is not None and credit_amount > 0: - if not last_name or not first_name or not bank: + if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"): if not last_name: form.add_error('last_name', _("This field is required.")) if not first_name: form.add_error('first_name', _("This field is required.")) - if not bank: + if not bank and credit_type.special_type == "Chèque": form.add_error('bank', _("This field is required.")) return self.form_invalid(form) diff --git a/templates/member/club_info.html b/templates/member/club_info.html index a781bea8..de0192b4 100644 --- a/templates/member/club_info.html +++ b/templates/member/club_info.html @@ -49,7 +49,7 @@
        @@ -137,7 +142,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
        - +
        From f833f1c46cc1a11f115c1d7418ac0c8d2b00adce Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Apr 2020 08:58:39 +0200 Subject: [PATCH 59/66] Comment code --- apps/member/models.py | 9 +++ apps/member/tables.py | 20 ++++++ apps/member/views.py | 54 +++++++++++++- apps/note/views.py | 6 +- apps/registration/api/__init__.py | 0 apps/registration/forms.py | 7 ++ apps/registration/tables.py | 3 + apps/registration/views.py | 76 +++++++++++++++----- locale/de/LC_MESSAGES/django.po | 2 +- locale/fr/LC_MESSAGES/django.po | 4 +- static/js/transfer.js | 2 - templates/activity/activity_detail.html | 1 - templates/member/club_list.html | 1 - templates/member/user_list.html | 8 ++- templates/note/transactiontemplate_list.html | 1 - templates/registration/future_user_list.html | 47 +++++++++--- 16 files changed, 201 insertions(+), 40 deletions(-) delete mode 100644 apps/registration/api/__init__.py diff --git a/apps/member/models.py b/apps/member/models.py index 80681824..c51a95c8 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -250,12 +250,18 @@ class Membership(models.Model): ) def valid(self): + """ + A membership is valid if today is between the start and the end date. + """ if self.date_end is not None: return self.date_start.toordinal() <= datetime.datetime.now().toordinal() < self.date_end.toordinal() else: return self.date_start.toordinal() <= datetime.datetime.now().toordinal() def save(self, *args, **kwargs): + """ + Calculate fee and end date before saving the membership and creating the transaction if needed. + """ if self.club.parent_club is not None: if not Membership.objects.filter(user=self.user, club=self.club.parent_club).exists(): raise ValidationError(_('User is not a member of the parent club') + ' ' + self.club.parent_club.name) @@ -287,6 +293,9 @@ class Membership(models.Model): self.make_transaction() def make_transaction(self): + """ + Create Membership transaction associated to this membership. + """ if not self.fee or MembershipTransaction.objects.filter(membership=self).exists(): return diff --git a/apps/member/tables.py b/apps/member/tables.py index c8a510ff..515d7836 100644 --- a/apps/member/tables.py +++ b/apps/member/tables.py @@ -15,6 +15,9 @@ from .models import Club, Membership class ClubTable(tables.Table): + """ + List all clubs. + """ class Meta: attrs = { 'class': 'table table-condensed table-striped table-hover' @@ -30,6 +33,9 @@ class ClubTable(tables.Table): class UserTable(tables.Table): + """ + List all users. + """ section = tables.Column(accessor='profile.section') balance = tables.Column(accessor='note.balance', verbose_name=_("Balance")) @@ -51,6 +57,9 @@ class UserTable(tables.Table): class MembershipTable(tables.Table): + """ + List all memberships. + """ roles = tables.Column( attrs={ "td": { @@ -59,7 +68,17 @@ class MembershipTable(tables.Table): } ) + def render_user(self, value): + # If the user has the right, link the displayed user with the page of its detail. + s = value.username + if PermissionBackend.check_perm(get_current_authenticated_user(), "auth.view_user", value): + s = format_html("{name}", + url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s) + + return s + def render_club(self, value): + # If the user has the right, link the displayed club with the page of its detail. s = value.name if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value): s = format_html("{name}", @@ -94,6 +113,7 @@ class MembershipTable(tables.Table): return t def render_roles(self, record): + # If the user has the right to manage the roles, display the link to manage them roles = record.roles.all() s = ", ".join(str(role) for role in roles) if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record): diff --git a/apps/member/views.py b/apps/member/views.py index 50c3b813..b8048d6e 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -30,6 +30,9 @@ from .tables import ClubTable, UserTable, MembershipTable class CustomLoginView(LoginView): + """ + Login view, where the user can select its permission mask. + """ form_class = CustomAuthenticationForm def form_valid(self, form): @@ -38,6 +41,9 @@ class CustomLoginView(LoginView): class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): + """ + Update the user information. + """ model = User fields = ['first_name', 'last_name', 'username', 'email'] template_name = 'member/profile_update.html' @@ -93,6 +99,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): user.save() if olduser.email != user.email: + # If the user changed her/his email, then it is unvalidated and a confirmation link is sent. user.profile.email_confirmed = False user.profile.send_email_validation_link() @@ -132,13 +139,16 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ - Affiche la liste des utilisateurs, avec une fonction de recherche statique + Display user list, with a search bar """ model = User table_class = UserTable template_name = 'member/user_list.html' def get_queryset(self, **kwargs): + """ + Filter the user list with the given pattern. + """ qs = super().get_queryset().filter(profile__registration_valid=True) if "search" in self.request.GET: pattern = self.request.GET["search"] @@ -150,6 +160,7 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): Q(first_name__iregex=pattern) | Q(last_name__iregex=pattern) | Q(profile__section__iregex=pattern) + | Q(profile__username__iregex="^" + pattern) | Q(note__alias__name__iregex="^" + pattern) | Q(note__alias__normalized_name__iregex=Alias.normalize("^" + pattern)) ) @@ -167,6 +178,9 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + View and manage user aliases. + """ model = User template_name = 'member/profile_alias.html' context_object_name = 'user_object' @@ -179,6 +193,9 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView): + """ + Update profile picture of the user note. + """ form_class = ImageForm def get_context_data(self, **kwargs): @@ -278,6 +295,9 @@ class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + Display details of a club + """ model = Club context_object_name = "club" @@ -298,6 +318,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): context['member_list'] = MembershipTable(data=club_member) + # Check if the user has the right to create a membership, to display the button. empty_membership = Membership( club=club, user=User.objects.first(), @@ -312,6 +333,9 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + Manage aliases of a club. + """ model = Club template_name = 'member/club_alias.html' context_object_name = 'club' @@ -324,6 +348,9 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): + """ + Update the information of a club. + """ model = Club context_object_name = "club" form_class = ClubForm @@ -334,6 +361,9 @@ class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class ClubPictureUpdateView(PictureUpdateView): + """ + Update the profile picture of a club. + """ model = Club template_name = 'member/club_picture_update.html' context_object_name = 'club' @@ -343,6 +373,9 @@ class ClubPictureUpdateView(PictureUpdateView): class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): + """ + Add a membership to a club. + """ model = Membership form_class = MembershipForm template_name = 'member/add_members.html' @@ -352,10 +385,12 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): form = context['form'] if "club_pk" in self.kwargs: + # We create a new membership. club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ .get(pk=self.kwargs["club_pk"]) form.fields['credit_amount'].initial = club.membership_fee_paid + # If the concerned club is the BDE, then we add the option that Société générale pays the membership. if club.name != "BDE": del form.fields['soge'] else: @@ -366,6 +401,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): fee += kfet.membership_fee_paid context["total_fee"] = "{:.02f}".format(fee / 100, ) else: + # This is a renewal. Fields can be pre-completed. old_membership = self.get_queryset().get(pk=self.kwargs["pk"]) club = old_membership.club user = old_membership.user @@ -378,6 +414,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): form.fields['last_name'].initial = user.last_name form.fields['first_name'].initial = user.first_name + # If this is a renewal of a BDE membership, Société générale can pays, if it is not yet done if club.name != "BDE" or user.profile.soge: del form.fields['soge'] else: @@ -393,6 +430,10 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): return context def form_valid(self, form): + """ + Create membership, check that all is good, make transactions + """ + # Get the club that is concerned by the membership if "club_pk" in self.kwargs: club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \ .get(pk=self.kwargs["club_pk"]) @@ -404,6 +445,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): form.instance.club = club + # Get form data credit_type = form.cleaned_data["credit_type"] credit_amount = form.cleaned_data["credit_amount"] last_name = form.cleaned_data["last_name"] @@ -411,6 +453,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): bank = form.cleaned_data["bank"] soge = form.cleaned_data["soge"] and not user.profile.soge and club.name == "BDE" + # If Société générale pays, then we auto-fill some data if soge: credit_type = NoteSpecial.objects.get(special_type="Virement bancaire") bde = club @@ -466,6 +509,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): .format(form.instance.club.membership_start)) return super().form_invalid(form) + # Now, all is fine, the membership can be created. + + # Credit note before the membership is created. if credit_amount > 0: if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"): if not last_name: @@ -488,6 +534,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): valid=True, ) + # If Société générale pays, then we store the information: the bank can't pay twice to a same person. if soge: user.profile.soge = True user.profile.save() @@ -495,6 +542,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): kfet = Club.objects.get(name="Kfet") kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid + # Get current membership, to get the end date old_membership = Membership.objects.filter( club__name="Kfet", user=user, @@ -522,6 +570,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): + """ + Manage the roles of a user in a club + """ model = Membership form_class = MembershipForm template_name = 'member/add_members.html' @@ -534,6 +585,7 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): def get_form(self, form_class=None): form = super().get_form(form_class) + # We don't create a full membership, we only update one field form.fields['user'].disabled = True del form.fields['date_start'] del form.fields['credit_type'] diff --git a/apps/note/views.py b/apps/note/views.py index 4f2321fb..88d47847 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -45,6 +45,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl .filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\ .order_by("special_type").all() + # Add a shortcut for entry page for open activities if "activity" in settings.INSTALLED_APPS: from activity.models import Activity context["activities_open"] = Activity.objects.filter(open=True).filter( @@ -56,7 +57,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): """ - Create TransactionTemplate + Create Transaction template """ model = TransactionTemplate form_class = TransactionTemplateForm @@ -65,7 +66,7 @@ class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, Cr class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ - List TransactionsTemplates + List Transaction templates """ model = TransactionTemplate table_class = ButtonTable @@ -73,6 +74,7 @@ class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, Sing class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ + Update Transaction template """ model = TransactionTemplate form_class = TransactionTemplateForm diff --git a/apps/registration/api/__init__.py b/apps/registration/api/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 3ca14ca1..4955c4a8 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -10,6 +10,9 @@ from note_kfet.inputs import AmountInput class SignUpForm(UserCreationForm): + """ + Pre-register users with all information + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['username'].widget.attrs.pop("autofocus", None) @@ -25,6 +28,9 @@ class SignUpForm(UserCreationForm): class ValidationForm(forms.Form): + """ + Validate the inscription of the new users and pay memberships. + """ soge = forms.BooleanField( label=_("Inscription paid by Société Générale"), required=False, @@ -66,6 +72,7 @@ class ValidationForm(forms.Form): initial=True, ) + # The user can join the Kfet club at the inscription join_Kfet = forms.BooleanField( label=_("Join Kfet Club"), required=False, diff --git a/apps/registration/tables.py b/apps/registration/tables.py index 7fd7537f..7068f6ca 100644 --- a/apps/registration/tables.py +++ b/apps/registration/tables.py @@ -6,6 +6,9 @@ from django.contrib.auth.models import User class FutureUserTable(tables.Table): + """ + Display the list of pre-registered users + """ phone_number = tables.Column(accessor='profile.phone_number') section = tables.Column(accessor='profile.section') diff --git a/apps/registration/views.py b/apps/registration/views.py index b3d4766c..2daca037 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -5,13 +5,12 @@ from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.core.exceptions import ValidationError +from django.db.models import Q from django.shortcuts import resolve_url, redirect from django.urls import reverse_lazy -from django.utils.decorators import method_decorator from django.utils.http import urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ from django.views import View -from django.views.decorators.csrf import csrf_protect from django.views.generic import CreateView, TemplateView, DetailView, FormView from django_tables2 import SingleTableView from member.forms import ProfileForm @@ -46,11 +45,13 @@ class UserCreateView(CreateView): """ If the form is valid, then the user is created with is_active set to False so that the user cannot log in until the email has been validated. + The user must also wait that someone validate her/his account. """ profile_form = ProfileForm(data=self.request.POST) if not profile_form.is_valid(): return self.form_invalid(form) + # Save the user and the profile user = form.save(commit=False) user.is_active = False profile_form.instance.user = user @@ -67,16 +68,15 @@ class UserCreateView(CreateView): class UserValidateView(TemplateView): + """ + A view to validate the email address. + """ title = _("Email validation") template_name = 'registration/email_validation_complete.html' - @method_decorator(csrf_protect) - def dispatch(self, *args, **kwargs): + def get(self, *args, **kwargs): """ - The dispatch method looks at the request to determine whether it is a GET, POST, etc, - and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed - if not. We chose to check the token in the dispatch method to mimic PasswordReset from - django.contrib.auth + With a given token and user id (in params), validate the email address. """ assert 'uidb64' in kwargs and 'token' in kwargs @@ -84,18 +84,23 @@ class UserValidateView(TemplateView): user = self.get_user(kwargs['uidb64']) token = kwargs['token'] + # Validate the token if user is not None and email_validation_token.check_token(user, token): self.validlink = True + # The user must wait that someone validates the account before the user can be active and login. user.is_active = user.profile.registration_valid user.profile.email_confirmed = True user.save() user.profile.save() return super().dispatch(*args, **kwargs) else: - # Display the "Account Activation unsuccessful" page. + # Display the "Email validation unsuccessful" page. return self.render_to_response(self.get_context_data()) def get_user(self, uidb64): + """ + Get user from the base64-encoded string. + """ try: # urlsafe_base64_decode() decodes to bytestring uid = urlsafe_base64_decode(uidb64).decode() @@ -118,16 +123,19 @@ class UserValidateView(TemplateView): class UserValidationEmailSentView(TemplateView): + """ + Display the information that the validation link has been sent. + """ template_name = 'registration/email_validation_email_sent.html' title = _('Email validation email sent') class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, DetailView): + """ + Rensend the email validation link. + """ model = User - def get_queryset(self, **kwargs): - return super().get_queryset(**kwargs).filter(profile__email_confirmed=False) - def get(self, request, *args, **kwargs): user = self.get_object() @@ -139,14 +147,35 @@ class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, De class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ - Affiche la liste des utilisateurs, avec une fonction de recherche statique + Display pre-registered users, with a search bar """ model = User table_class = FutureUserTable template_name = 'registration/future_user_list.html' def get_queryset(self, **kwargs): - return super().get_queryset().filter(profile__registration_valid=False) + """ + Filter the table with the given parameter. + :param kwargs: + :return: + """ + qs = super().get_queryset().filter(profile__registration_valid=False) + if "search" in self.request.GET: + pattern = self.request.GET["search"] + + if not pattern: + return qs.none() + + qs = qs.filter( + Q(first_name__iregex=pattern) + | Q(last_name__iregex=pattern) + | Q(profile__section__iregex=pattern) + | Q(username__iregex="^" + pattern) + ) + else: + qs = qs.none() + + return qs[:20] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -158,7 +187,7 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, FormView): """ - Affiche les informations sur un utilisateur, sa note, ses clubs... + Display information about a pre-registered user, in order to complete the registration. """ model = User form_class = ValidationForm @@ -194,6 +223,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, def form_valid(self, form): user = self.object = self.get_object() + # Get form data soge = form.cleaned_data["soge"] credit_type = form.cleaned_data["credit_type"] credit_amount = form.cleaned_data["credit_amount"] @@ -204,6 +234,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, join_Kfet = form.cleaned_data["join_Kfet"] if soge: + # If Société Générale pays the inscription, the user joins the two clubs join_BDE = True join_Kfet = True @@ -218,6 +249,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, fee += kfet_fee if soge: + # Fill payment information if Société Générale pays the inscription credit_type = NoteSpecial.objects.get(special_type="Virement bancaire") credit_amount = fee bank = "Société générale" @@ -226,6 +258,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club.")) if fee > credit_amount: + # Check if the user credits enough money form.add_error('credit_type', _("The entered amount is not enough for the memberships, should be at least {}") .format(pretty_money(fee))) @@ -241,14 +274,18 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, form.add_error('bank', _("This field is required.")) return self.form_invalid(form) + # Save the user and finally validate the registration + # Saving the user creates the associated note ret = super().form_valid(form) user.is_active = user.profile.email_confirmed user.profile.registration_valid = True + # Store if Société générale paid for next years user.profile.soge = soge user.save() user.profile.save() if credit_type is not None and credit_amount > 0: + # Credit the note SpecialTransaction.objects.create( source=credit_type, destination=user.note, @@ -262,6 +299,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, ) if join_BDE: + # Create membership for the user to the BDE starting today membership = Membership.objects.create( club=bde, user=user, @@ -271,6 +309,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, membership.save() if join_Kfet: + # Create membership for the user to the Kfet starting today membership = Membership.objects.create( club=kfet, user=user, @@ -287,10 +326,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View): """ - Affiche les informations sur un utilisateur, sa note, ses clubs... + Delete a pre-registered user. """ - def dispatch(self, request, *args, **kwargs): + def get(self, request, *args, **kwargs): + """ + Delete the pre-registered user which id is given in the URL. + """ user = User.objects.filter(profile__registration_valid=False)\ .filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\ .get(pk=self.kwargs["pk"]) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 5fe4ad5a..1e791220 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -1267,7 +1267,7 @@ msgid "New user" msgstr "" #: templates/registration/future_user_list.html:17 -msgid "There is no pending user." +msgid "There is no pending user with this pattern." msgstr "" #: templates/registration/logged_out.html:8 diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index c666c12a..4e282bea 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -1274,8 +1274,8 @@ msgid "New user" msgstr "Nouvel utilisateur" #: templates/registration/future_user_list.html:17 -msgid "There is no pending user." -msgstr "Il n'y a pas d'inscription en attente." +msgid "There is no pending user with this pattern." +msgstr "Il n'y a pas d'inscription en attente avec cette entrée." #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." diff --git a/static/js/transfer.js b/static/js/transfer.js index 9aff4649..e5aafc39 100644 --- a/static/js/transfer.js +++ b/static/js/transfer.js @@ -28,7 +28,6 @@ function reset() { } $(document).ready(function() { - console.log(42); autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display, "source_alias", "source_note", "user_note", "profile_pic"); autoCompleteNote("dest_note", "dest_alias_matched", "dest_note_list", dests, dests_notes_display, @@ -72,7 +71,6 @@ $(document).ready(function() { $("label[for='type_credit']").attr('class', 'btn btn-sm btn-outline-primary'); $("label[for='type_debit']").attr('class', 'btn btn-sm btn-outline-primary'); - console.log("#type_" + location.hash.substr(1)); if (location.hash) $("#type_" + location.hash.substr(1)).click(); else diff --git a/templates/activity/activity_detail.html b/templates/activity/activity_detail.html index 84182065..7ee9f7c0 100644 --- a/templates/activity/activity_detail.html +++ b/templates/activity/activity_detail.html @@ -118,7 +118,6 @@ }); $("#validate_activity").click(function () { - console.log(42); $.ajax({ url: "/api/activity/activity/{{ activity.pk }}/", type: "PATCH", diff --git a/templates/member/club_list.html b/templates/member/club_list.html index 2653ace8..4682164c 100644 --- a/templates/member/club_list.html +++ b/templates/member/club_list.html @@ -36,7 +36,6 @@ function getInfo() { if (asked.length >= 1) { $.getJSON("/api/members/club/?format=json&search="+asked, function(buttons){ let selected_id = buttons.results.map((a => "#row-"+a.id)); - console.log(selected_id.join()); $(".table-row,"+selected_id.join()).show(); $(".table-row").not(selected_id.join()).hide(); diff --git a/templates/member/user_list.html b/templates/member/user_list.html index d0eaaedb..0bcd7e89 100644 --- a/templates/member/user_list.html +++ b/templates/member/user_list.html @@ -7,7 +7,13 @@
        - {% render_table table %} + {% if table.data %} + {% render_table table %} + {% else %} +
        + {% trans "There is no pending user with this pattern." %} +
        + {% endif %}
        {% endblock %} diff --git a/templates/note/transactiontemplate_list.html b/templates/note/transactiontemplate_list.html index af0a02b4..cf9bc5ed 100644 --- a/templates/note/transactiontemplate_list.html +++ b/templates/note/transactiontemplate_list.html @@ -37,7 +37,6 @@ function getInfo() { if (asked.length >= 1) { $.getJSON("/api/note/transaction/template/?format=json&search="+asked, function(buttons){ let selected_id = buttons.results.map((a => "#row-"+a.id)); - console.log(selected_id.join()); $(".table-row,"+selected_id.join()).show(); $(".table-row").not(selected_id.join()).hide(); diff --git a/templates/registration/future_user_list.html b/templates/registration/future_user_list.html index 490fe2ab..1e10dcbb 100644 --- a/templates/registration/future_user_list.html +++ b/templates/registration/future_user_list.html @@ -5,24 +5,49 @@ {% block content %} - +
        +
        - {% if table.data %} -
        +
        + {% if table.data %} {% render_table table %} -
        - {% else %} -
        - {% trans "There is no pending user." %} -
        - {% endif %} + {% else %} +
        + {% trans "There is no pending user with this pattern." %} +
        + {% endif %} +
        {% endblock %} {% block extrajavascript %} {% endblock %} From bd41560f45da5f4a161d3bf7ca3179c4cb74077a Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Apr 2020 10:45:32 +0200 Subject: [PATCH 60/66] Update permission fixtures --- apps/activity/fixtures/initial.json | 20 + apps/member/views.py | 1 + apps/note/signals.py | 2 +- apps/permission/fixtures/initial.json | 703 ++++++++++++++++---------- apps/registration/forms.py | 2 +- apps/registration/views.py | 23 +- 6 files changed, 478 insertions(+), 273 deletions(-) create mode 100644 apps/activity/fixtures/initial.json diff --git a/apps/activity/fixtures/initial.json b/apps/activity/fixtures/initial.json new file mode 100644 index 00000000..1856bce4 --- /dev/null +++ b/apps/activity/fixtures/initial.json @@ -0,0 +1,20 @@ +[ + { + "model": "activity.activitytype", + "pk": 1, + "fields": { + "name": "Pot", + "can_invite": true, + "guest_entry_fee": 500 + } + }, + { + "model": "activity.activitytype", + "pk": 2, + "fields": { + "name": "Soir\u00e9e de club", + "can_invite": false, + "guest_entry_fee": 0 + } + } +] \ No newline at end of file diff --git a/apps/member/views.py b/apps/member/views.py index b8048d6e..e03c5ff5 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -389,6 +389,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ .get(pk=self.kwargs["club_pk"]) form.fields['credit_amount'].initial = club.membership_fee_paid + form.fields['roles'].initial = Role.objects.filter(name="Membre de club").all() # If the concerned club is the BDE, then we add the option that Société générale pays the membership. if club.name != "BDE": diff --git a/apps/note/signals.py b/apps/note/signals.py index 78312682..37737a45 100644 --- a/apps/note/signals.py +++ b/apps/note/signals.py @@ -10,7 +10,7 @@ def save_user_note(instance, raw, **_kwargs): # When provisionning data, do not try to autocreate return - if instance.profile.registration_valid and instance.is_active: + if (instance.is_superuser or instance.profile.registration_valid) and instance.is_active: # Create note only when the registration is validated from note.models import NoteUser NoteUser.objects.get_or_create(user=instance) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 4cf3ecfa..d7eca508 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -17,54 +17,61 @@ "model": "member.role", "pk": 3, "fields": { - "name": "Pr\u00e9sident\u00b7e BDE" + "name": "Membre de club" } }, { "model": "member.role", "pk": 4, "fields": { - "name": "Tr\u00e9sorier\u00b7\u00e8re BDE" + "name": "Bureau de club" } }, { "model": "member.role", "pk": 5, "fields": { - "name": "Respo info" + "name": "Pr\u00e9sident\u00b7e de club" } }, { "model": "member.role", "pk": 6, "fields": { - "name": "GC Kfet" + "name": "Tr\u00e9sorier\u00b7\u00e8re de club" } }, { "model": "member.role", "pk": 7, "fields": { - "name": "Pr\u00e9sident\u00b7e de club" + "name": "Pr\u00e9sident\u00b7e BDE" } }, { "model": "member.role", "pk": 8, "fields": { - "name": "Tr\u00e9sorier\u00b7\u00e8re de club" - } - }, - { - "model": "member.role", - "pk": 8, - "fields": { - "name": "Tr\u00e9sorier\u00b7\u00e8re de club" + "name": "Tr\u00e9sorier\u00b7\u00e8re BDE" } }, { "model": "member.role", "pk": 9, + "fields": { + "name": "Respo info" + } + }, + { + "model": "member.role", + "pk": 10, + "fields": { + "name": "GC Kfet" + } + }, + { + "model": "member.role", + "pk": 11, "fields": { "name": "Res[pot]" } @@ -97,10 +104,7 @@ "model": "permission.permission", "pk": 1, "fields": { - "model": [ - "auth", - "user" - ], + "model": 4, "query": "{\"pk\": [\"user\", \"pk\"]}", "type": "view", "mask": 1, @@ -112,10 +116,7 @@ "model": "permission.permission", "pk": 2, "fields": { - "model": [ - "member", - "profile" - ], + "model": 17, "query": "{\"user\": [\"user\"]}", "type": "view", "mask": 1, @@ -127,10 +128,7 @@ "model": "permission.permission", "pk": 3, "fields": { - "model": [ - "note", - "noteuser" - ], + "model": 27, "query": "{\"pk\": [\"user\", \"note\", \"pk\"]}", "type": "view", "mask": 1, @@ -142,10 +140,7 @@ "model": "permission.permission", "pk": 4, "fields": { - "model": [ - "authtoken", - "token" - ], + "model": 8, "query": "{\"user\": [\"user\"]}", "type": "view", "mask": 1, @@ -157,10 +152,7 @@ "model": "permission.permission", "pk": 5, "fields": { - "model": [ - "note", - "transaction" - ], + "model": 22, "query": "[\"OR\", {\"source\": [\"user\", \"note\"]}, {\"destination\": [\"user\", \"note\"]}]", "type": "view", "mask": 1, @@ -172,10 +164,7 @@ "model": "permission.permission", "pk": 6, "fields": { - "model": [ - "note", - "alias" - ], + "model": 19, "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}]", "type": "view", "mask": 1, @@ -187,10 +176,7 @@ "model": "permission.permission", "pk": 7, "fields": { - "model": [ - "auth", - "user" - ], + "model": 4, "query": "{\"pk\": [\"user\", \"pk\"]}", "type": "change", "mask": 1, @@ -202,10 +188,7 @@ "model": "permission.permission", "pk": 8, "fields": { - "model": [ - "auth", - "user" - ], + "model": 4, "query": "{\"pk\": [\"user\", \"pk\"]}", "type": "change", "mask": 1, @@ -217,10 +200,7 @@ "model": "permission.permission", "pk": 9, "fields": { - "model": [ - "auth", - "user" - ], + "model": 4, "query": "{\"pk\": [\"user\", \"pk\"]}", "type": "change", "mask": 1, @@ -232,10 +212,7 @@ "model": "permission.permission", "pk": 10, "fields": { - "model": [ - "auth", - "user" - ], + "model": 4, "query": "{\"pk\": [\"user\", \"pk\"]}", "type": "change", "mask": 1, @@ -247,10 +224,7 @@ "model": "permission.permission", "pk": 11, "fields": { - "model": [ - "auth", - "user" - ], + "model": 4, "query": "{\"pk\": [\"user\", \"pk\"]}", "type": "change", "mask": 1, @@ -262,10 +236,7 @@ "model": "permission.permission", "pk": 12, "fields": { - "model": [ - "authtoken", - "token" - ], + "model": 8, "query": "{\"user\": [\"user\"]}", "type": "delete", "mask": 1, @@ -277,10 +248,7 @@ "model": "permission.permission", "pk": 13, "fields": { - "model": [ - "authtoken", - "token" - ], + "model": 8, "query": "{\"user\": [\"user\"]}", "type": "add", "mask": 1, @@ -292,10 +260,7 @@ "model": "permission.permission", "pk": 14, "fields": { - "model": [ - "note", - "alias" - ], + "model": 19, "query": "{\"note\": [\"user\", \"note\"]}", "type": "delete", "mask": 1, @@ -307,10 +272,7 @@ "model": "permission.permission", "pk": 15, "fields": { - "model": [ - "note", - "alias" - ], + "model": 19, "query": "{\"note\": [\"user\", \"note\"]}", "type": "add", "mask": 1, @@ -322,10 +284,7 @@ "model": "permission.permission", "pk": 16, "fields": { - "model": [ - "note", - "noteuser" - ], + "model": 27, "query": "{\"pk\": [\"user\", \"note\", \"pk\"]}", "type": "change", "mask": 1, @@ -337,10 +296,7 @@ "model": "permission.permission", "pk": 17, "fields": { - "model": [ - "note", - "transaction" - ], + "model": 22, "query": "[\"AND\", {\"source\": [\"user\", \"note\"]}, [\"OR\", {\"amount__lte\": [\"user\", \"note\", \"balance\"]}, {\"valid\": false}]]", "type": "add", "mask": 1, @@ -352,10 +308,7 @@ "model": "permission.permission", "pk": 18, "fields": { - "model": [ - "note", - "note" - ], + "model": 20, "query": "{}", "type": "change", "mask": 1, @@ -367,10 +320,7 @@ "model": "permission.permission", "pk": 19, "fields": { - "model": [ - "note", - "note" - ], + "model": 20, "query": "[\"OR\", {\"pk\": [\"club\", \"note\", \"pk\"]}, {\"pk__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club\": [\"club\"]}], [\"all\"]]}]", "type": "view", "mask": 2, @@ -382,10 +332,7 @@ "model": "permission.permission", "pk": 20, "fields": { - "model": [ - "note", - "transaction" - ], + "model": 22, "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]", "type": "add", "mask": 2, @@ -397,10 +344,7 @@ "model": "permission.permission", "pk": 21, "fields": { - "model": [ - "note", - "recurrenttransaction" - ], + "model": 28, "query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]", "type": "add", "mask": 2, @@ -412,10 +356,7 @@ "model": "permission.permission", "pk": 22, "fields": { - "model": [ - "member", - "club" - ], + "model": 15, "query": "{\"pk\": [\"club\", \"pk\"]}", "type": "view", "mask": 1, @@ -427,10 +368,7 @@ "model": "permission.permission", "pk": 23, "fields": { - "model": [ - "note", - "transaction" - ], + "model": 22, "query": "{}", "type": "change", "mask": 1, @@ -442,10 +380,7 @@ "model": "permission.permission", "pk": 24, "fields": { - "model": [ - "note", - "transaction" - ], + "model": 22, "query": "{}", "type": "view", "mask": 2, @@ -457,10 +392,7 @@ "model": "permission.permission", "pk": 25, "fields": { - "model": [ - "note", - "notespecial" - ], + "model": 26, "query": "{}", "type": "view", "mask": 2, @@ -472,10 +404,7 @@ "model": "permission.permission", "pk": 26, "fields": { - "model": [ - "note", - "specialtransaction" - ], + "model": 29, "query": "{}", "type": "add", "mask": 2, @@ -487,10 +416,7 @@ "model": "permission.permission", "pk": 27, "fields": { - "model": [ - "note", - "templatecategory" - ], + "model": 21, "query": "{}", "type": "view", "mask": 2, @@ -502,10 +428,7 @@ "model": "permission.permission", "pk": 28, "fields": { - "model": [ - "note", - "templatecategory" - ], + "model": 21, "query": "{}", "type": "change", "mask": 3, @@ -517,10 +440,7 @@ "model": "permission.permission", "pk": 29, "fields": { - "model": [ - "note", - "templatecategory" - ], + "model": 21, "query": "{}", "type": "add", "mask": 3, @@ -532,10 +452,7 @@ "model": "permission.permission", "pk": 30, "fields": { - "model": [ - "note", - "transactiontemplate" - ], + "model": 23, "query": "{}", "type": "view", "mask": 2, @@ -547,10 +464,7 @@ "model": "permission.permission", "pk": 31, "fields": { - "model": [ - "note", - "transactiontemplate" - ], + "model": 23, "query": "{}", "type": "add", "mask": 3, @@ -562,10 +476,7 @@ "model": "permission.permission", "pk": 32, "fields": { - "model": [ - "note", - "transactiontemplate" - ], + "model": 23, "query": "{}", "type": "change", "mask": 3, @@ -577,10 +488,7 @@ "model": "permission.permission", "pk": 33, "fields": { - "model": [ - "note", - "transaction" - ], + "model": 22, "query": "{}", "type": "add", "mask": 2, @@ -592,10 +500,7 @@ "model": "permission.permission", "pk": 34, "fields": { - "model": [ - "activity", - "activity" - ], + "model": 9, "query": "[\"OR\", {\"valid\": true}, {\"creater\": [\"user\"]}]", "type": "view", "mask": 1, @@ -607,10 +512,7 @@ "model": "permission.permission", "pk": 35, "fields": { - "model": [ - "activity", - "activity" - ], + "model": 9, "query": "[\"AND\", {\"valid\": false}, {\"creater\": [\"user\"]}]", "type": "change", "mask": 1, @@ -622,10 +524,7 @@ "model": "permission.permission", "pk": 36, "fields": { - "model": [ - "activity", - "activity" - ], + "model": 9, "query": "{\"creater\": [\"user\"], \"valid\": false}", "type": "add", "mask": 1, @@ -637,10 +536,7 @@ "model": "permission.permission", "pk": 37, "fields": { - "model": [ - "activity", - "activity" - ], + "model": 9, "query": "{}", "type": "change", "mask": 2, @@ -652,10 +548,7 @@ "model": "permission.permission", "pk": 38, "fields": { - "model": [ - "activity", - "activity" - ], + "model": 9, "query": "{}", "type": "change", "mask": 2, @@ -667,10 +560,7 @@ "model": "permission.permission", "pk": 39, "fields": { - "model": [ - "activity", - "guest" - ], + "model": 12, "query": "{\"inviter\": [\"user\", \"note\"], \"activity__activity_type__can_invite\": true}", "type": "add", "mask": 1, @@ -682,10 +572,7 @@ "model": "permission.permission", "pk": 40, "fields": { - "model": [ - "activity", - "guest" - ], + "model": 12, "query": "{\"inviter\": [\"user\", \"note\"]}", "type": "view", "mask": 1, @@ -697,10 +584,7 @@ "model": "permission.permission", "pk": 41, "fields": { - "model": [ - "activity", - "activity" - ], + "model": 9, "query": "{}", "type": "view", "mask": 2, @@ -712,10 +596,7 @@ "model": "permission.permission", "pk": 42, "fields": { - "model": [ - "activity", - "guest" - ], + "model": 12, "query": "{}", "type": "view", "mask": 2, @@ -727,10 +608,7 @@ "model": "permission.permission", "pk": 43, "fields": { - "model": [ - "activity", - "entry" - ], + "model": 11, "query": "{}", "type": "add", "mask": 2, @@ -742,10 +620,7 @@ "model": "permission.permission", "pk": 44, "fields": { - "model": [ - "activity", - "guesttransaction" - ], + "model": 13, "query": "{}", "type": "add", "mask": 2, @@ -757,10 +632,7 @@ "model": "permission.permission", "pk": 45, "fields": { - "model": [ - "activity", - "guesttransaction" - ], + "model": 13, "query": "{}", "type": "view", "mask": 1, @@ -772,10 +644,7 @@ "model": "permission.permission", "pk": 46, "fields": { - "model": [ - "activity", - "guesttransaction" - ], + "model": 13, "query": "{}", "type": "change", "mask": 2, @@ -787,10 +656,7 @@ "model": "permission.permission", "pk": 47, "fields": { - "model": [ - "member", - "club" - ], + "model": 15, "query": "{\"pk\": [\"club\", \"pk\"]}", "type": "change", "mask": 1, @@ -802,10 +668,7 @@ "model": "permission.permission", "pk": 48, "fields": { - "model": [ - "member", - "membership" - ], + "model": 16, "query": "{\"user\": [\"user\"]}", "type": "view", "mask": 1, @@ -817,10 +680,7 @@ "model": "permission.permission", "pk": 49, "fields": { - "model": [ - "member", - "membership" - ], + "model": 16, "query": "{\"club\": [\"club\"]}", "type": "view", "mask": 1, @@ -832,10 +692,7 @@ "model": "permission.permission", "pk": 50, "fields": { - "model": [ - "member", - "membership" - ], + "model": 16, "query": "{\"club\": [\"club\"]}", "type": "add", "mask": 2, @@ -843,6 +700,234 @@ "description": "Add a membership to a club" } }, + { + "model": "permission.permission", + "pk": 51, + "fields": { + "model": 16, + "query": "{\"club\": [\"club\"]}", + "type": "change", + "mask": 2, + "field": "roles", + "description": "Update user roles" + } + }, + { + "model": "permission.permission", + "pk": 52, + "fields": { + "model": 17, + "query": "{\"user\": [\"user\"]}", + "type": "change", + "mask": 1, + "field": "", + "description": "Change own profile" + } + }, + { + "model": "permission.permission", + "pk": 53, + "fields": { + "model": 17, + "query": "{}", + "type": "change", + "mask": 2, + "field": "", + "description": "Change any profile" + } + }, + { + "model": "permission.permission", + "pk": 54, + "fields": { + "model": 4, + "query": "{}", + "type": "change", + "mask": 2, + "field": "", + "description": "Change any user" + } + }, + { + "model": "permission.permission", + "pk": 55, + "fields": { + "model": 4, + "query": "{}", + "type": "add", + "mask": 1, + "field": "", + "description": "Add user" + } + }, + { + "model": "permission.permission", + "pk": 56, + "fields": { + "model": 17, + "query": "{\"email_confirmed\": false, \"registration_valid\": false}", + "type": "add", + "mask": 1, + "field": "", + "description": "Add profile" + } + }, + { + "model": "permission.permission", + "pk": 57, + "fields": { + "model": 4, + "query": "{\"profile__registration_valid\": false}", + "type": "delete", + "mask": 2, + "field": "", + "description": "Delete pre-registered user" + } + }, + { + "model": "permission.permission", + "pk": 58, + "fields": { + "model": 17, + "query": "{\"registration_valid\": false}", + "type": "delete", + "mask": 2, + "field": "", + "description": "Delete pre-registered user profile" + } + }, + { + "model": "permission.permission", + "pk": 59, + "fields": { + "model": 23, + "query": "{\"destination\": [\"club\", \"note\"]}", + "type": "view", + "mask": 2, + "field": "", + "description": "New club button" + } + }, + { + "model": "permission.permission", + "pk": 60, + "fields": { + "model": 23, + "query": "{\"destination\": [\"club\", \"note\"]}", + "type": "add", + "mask": 2, + "field": "", + "description": "Create club button" + } + }, + { + "model": "permission.permission", + "pk": 61, + "fields": { + "model": 23, + "query": "{\"destination\": [\"club\", \"note\"]}", + "type": "change", + "mask": 2, + "field": "", + "description": "Update club button" + } + }, + { + "model": "permission.permission", + "pk": 62, + "fields": { + "model": 22, + "query": "[\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}]", + "type": "view", + "mask": 1, + "field": "", + "description": "View transactions of a club" + } + }, + { + "model": "permission.permission", + "pk": 63, + "fields": { + "model": 33, + "query": "{}", + "type": "view", + "mask": 3, + "field": "", + "description": "View invoices" + } + }, + { + "model": "permission.permission", + "pk": 64, + "fields": { + "model": 33, + "query": "{}", + "type": "add", + "mask": 3, + "field": "", + "description": "Add invoice" + } + }, + { + "model": "permission.permission", + "pk": 65, + "fields": { + "model": 33, + "query": "{}", + "type": "change", + "mask": 3, + "field": "", + "description": "Change invoice" + } + }, + { + "model": "permission.permission", + "pk": 66, + "fields": { + "model": 34, + "query": "{}", + "type": "view", + "mask": 3, + "field": "", + "description": "View products" + } + }, + { + "model": "permission.permission", + "pk": 67, + "fields": { + "model": 34, + "query": "{}", + "type": "add", + "mask": 3, + "field": "", + "description": "Add products" + } + }, + { + "model": "permission.permission", + "pk": 68, + "fields": { + "model": 34, + "query": "{}", + "type": "change", + "mask": 3, + "field": "", + "description": "Change product" + } + }, + { + "model": "permission.permission", + "pk": 69, + "fields": { + "model": 34, + "query": "{}", + "type": "delete", + "mask": 3, + "field": "", + "description": "Delete product" + } + }, { "model": "permission.rolepermissions", "pk": 1, @@ -851,12 +936,18 @@ "permissions": [ 1, 2, + 3, + 4, + 5, 7, 8, 9, 10, 11, - 48 + 12, + 13, + 48, + 52 ] } }, @@ -866,19 +957,7 @@ "fields": { "role": 2, "permissions": [ - 1, - 2, - 3, - 4, - 5, 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, 14, 15, 16, @@ -894,23 +973,73 @@ }, { "model": "permission.rolepermissions", - "pk": 3, + "pk": 4, "fields": { - "role": 8, + "role": 4, "permissions": [ - 19, - 20, - 21, - 22 + 22, + 47, + 49 ] } }, { "model": "permission.rolepermissions", - "pk": 4, + "pk": 5, "fields": { - "role": 4, + "role": 5, "permissions": [ + 50, + 51, + 62 + ] + } + }, + { + "model": "permission.rolepermissions", + "pk": 6, + "fields": { + "role": 6, + "permissions": [ + 19, + 21, + 27, + 59, + 60, + 61, + 20, + 62 + ] + } + }, + { + "model": "permission.rolepermissions", + "pk": 7, + "fields": { + "role": 7, + "permissions": [ + 33, + 24, + 25, + 26, + 27 + ] + } + }, + { + "model": "permission.rolepermissions", + "pk": 8, + "fields": { + "role": 8, + "permissions": [ + 32, + 33, + 56, + 58, + 55, + 57, + 53, + 54, 23, 24, 25, @@ -920,44 +1049,21 @@ 29, 30, 31, - 32, - 33 + 64, + 65, + 66, + 67, + 68, + 69, + 63 ] } }, { "model": "permission.rolepermissions", - "pk": 5, + "pk": 9, "fields": { "role": 9, - "permissions": [ - 37, - 38, - 41, - 42, - 43, - 44, - 45, - 46 - ] - } - }, - { - "model": "permission.rolepermissions", - "pk": 6, - "fields": { - "role": 7, - "permissions": [ - 22, - 47 - ] - } - }, - { - "model": "permission.rolepermissions", - "pk": 7, - "fields": { - "role": 5, "permissions": [ 1, 2, @@ -978,7 +1084,6 @@ 17, 18, 19, - 20, 21, 22, 23, @@ -1008,7 +1113,71 @@ 47, 48, 49, - 50 + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 20, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69 + ] + } + }, + { + "model": "permission.rolepermissions", + "pk": 10, + "fields": { + "role": 10, + "permissions": [ + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 52, + 53, + 54, + 55, + 56, + 57, + 58 + ] + } + }, + { + "model": "permission.rolepermissions", + "pk": 11, + "fields": { + "role": 11, + "permissions": [ + 37, + 38, + 41, + 42, + 43, + 44, + 45, + 46 ] } } diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 4955c4a8..cba5c2ae 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -68,7 +68,7 @@ class ValidationForm(forms.Form): join_BDE = forms.BooleanField( label=_("Join BDE Club"), - required=True, + required=False, initial=True, ) diff --git a/apps/registration/views.py b/apps/registration/views.py index 2daca037..575ada8f 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -12,6 +12,7 @@ from django.utils.http import urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ from django.views import View from django.views.generic import CreateView, TemplateView, DetailView, FormView +from django.views.generic.edit import FormMixin from django_tables2 import SingleTableView from member.forms import ProfileForm from member.models import Membership, Club, Role @@ -88,7 +89,7 @@ class UserValidateView(TemplateView): if user is not None and email_validation_token.check_token(user, token): self.validlink = True # The user must wait that someone validates the account before the user can be active and login. - user.is_active = user.profile.registration_valid + user.is_active = user.profile.registration_valid or user.is_superuser user.profile.email_confirmed = True user.save() user.profile.save() @@ -185,7 +186,7 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi return context -class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, FormView): +class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView): """ Display information about a pre-registered user, in order to complete the registration. """ @@ -194,6 +195,14 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, context_object_name = "user_object" template_name = "registration/future_profile_detail.html" + def post(self, request, *args, **kwargs): + form = self.get_form() + self.object = self.get_object() + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + def get_queryset(self, **kwargs): """ We only display information of a not registered user. @@ -221,7 +230,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, return form def form_valid(self, form): - user = self.object = self.get_object() + user = self.get_object() # Get form data soge = form.cleaned_data["soge"] @@ -238,6 +247,10 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, join_BDE = True join_Kfet = True + if not join_BDE: + form.add_error('join_BDE', _("You must join the BDE.")) + return super().form_invalid(form) + fee = 0 bde = Club.objects.get(name="BDE") bde_fee = bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid @@ -254,6 +267,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, credit_amount = fee bank = "Société générale" + print("OK") + if join_Kfet and not join_BDE: form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club.")) @@ -277,7 +292,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, # Save the user and finally validate the registration # Saving the user creates the associated note ret = super().form_valid(form) - user.is_active = user.profile.email_confirmed + user.is_active = user.profile.email_confirmed or user.is_superuser user.profile.registration_valid = True # Store if Société générale paid for next years user.profile.soge = soge From 6fedbe2a2aa08585df735d4548c7f490e2961bc1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Apr 2020 10:58:16 +0200 Subject: [PATCH 61/66] Some model translations were missing --- apps/activity/models.py | 2 + apps/logs/models.py | 4 + apps/permission/models.py | 10 ++ apps/treasury/models.py | 20 +++ locale/de/LC_MESSAGES/django.po | 223 +++++++++++++++++++++----------- locale/fr/LC_MESSAGES/django.po | 223 +++++++++++++++++++++----------- 6 files changed, 332 insertions(+), 150 deletions(-) diff --git a/apps/activity/models.py b/apps/activity/models.py index ed2d94c9..215aa0cc 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -130,6 +130,8 @@ class Entry(models.Model): class Meta: unique_together = (('activity', 'note', 'guest', ), ) + verbose_name = _("entry") + verbose_name_plural = _("entries") def save(self, force_insert=False, force_update=False, using=None, update_fields=None): diff --git a/apps/logs/models.py b/apps/logs/models.py index 10e2651f..94e2b4ce 100644 --- a/apps/logs/models.py +++ b/apps/logs/models.py @@ -75,3 +75,7 @@ class Changelog(models.Model): def delete(self, using=None, keep_parents=False): raise ValidationError(_("Logs cannot be destroyed.")) + + class Meta: + verbose_name = _("changelog") + verbose_name_plural = _("changelogs") diff --git a/apps/permission/models.py b/apps/permission/models.py index 8aaf416c..81174389 100644 --- a/apps/permission/models.py +++ b/apps/permission/models.py @@ -106,6 +106,10 @@ class PermissionMask(models.Model): def __str__(self): return self.description + class Meta: + verbose_name = _("permission mask") + verbose_name_plural = _("permission masks") + class Permission(models.Model): @@ -153,6 +157,8 @@ class Permission(models.Model): class Meta: unique_together = ('model', 'query', 'type', 'field') + verbose_name = _("permission") + verbose_name_plural = _("permissions") def clean(self): self.query = json.dumps(json.loads(self.query)) @@ -293,3 +299,7 @@ class RolePermissions(models.Model): def __str__(self): return str(self.role) + + class Meta: + verbose_name = _("role permissions") + verbose_name_plural = _("role permissions") diff --git a/apps/treasury/models.py b/apps/treasury/models.py index bcd89db9..ca1da3a4 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -59,6 +59,10 @@ class Invoice(models.Model): verbose_name=_("Acquitted"), ) + class Meta: + verbose_name = _("invoice") + verbose_name_plural = _("invoices") + class Product(models.Model): """ @@ -95,6 +99,10 @@ class Product(models.Model): def total_euros(self): return self.total / 100 + class Meta: + verbose_name = _("product") + verbose_name_plural = _("products") + class RemittanceType(models.Model): """ @@ -109,6 +117,10 @@ class RemittanceType(models.Model): def __str__(self): return str(self.note) + class Meta: + verbose_name = _("remittance type") + verbose_name_plural = _("remittance types") + class Remittance(models.Model): """ @@ -136,6 +148,10 @@ class Remittance(models.Model): verbose_name=_("Closed"), ) + class Meta: + verbose_name = _("remittance") + verbose_name_plural = _("remittances") + @property def transactions(self): """ @@ -187,3 +203,7 @@ class SpecialTransactionProxy(models.Model): null=True, verbose_name=_("Remittance"), ) + + class Meta: + verbose_name = _("special transaction proxy") + verbose_name_plural = _("special transaction proxies") diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 1e791220..ff98db5b 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-06 06:43+0200\n" +"POT-Creation-Date: 2020-04-06 10:51+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,23 +23,23 @@ msgstr "" msgid "activity" msgstr "" -#: apps/activity/forms.py:45 apps/activity/models.py:206 +#: apps/activity/forms.py:45 apps/activity/models.py:208 msgid "You can't invite someone once the activity is started." msgstr "" -#: apps/activity/forms.py:48 apps/activity/models.py:209 +#: apps/activity/forms.py:48 apps/activity/models.py:211 msgid "This activity is not validated yet." msgstr "" -#: apps/activity/forms.py:58 apps/activity/models.py:217 +#: apps/activity/forms.py:58 apps/activity/models.py:219 msgid "This person has been already invited 5 times this year." msgstr "" -#: apps/activity/forms.py:62 apps/activity/models.py:221 +#: apps/activity/forms.py:62 apps/activity/models.py:223 msgid "This person is already invited." msgstr "" -#: apps/activity/forms.py:66 apps/activity/models.py:225 +#: apps/activity/forms.py:66 apps/activity/models.py:227 msgid "You can't invite more than 3 people to this activity." msgstr "" @@ -122,40 +122,48 @@ msgstr "" msgid "note" msgstr "" -#: apps/activity/models.py:139 +#: apps/activity/models.py:133 templates/activity/activity_entry.html:38 +msgid "entry" +msgstr "" + +#: apps/activity/models.py:134 templates/activity/activity_entry.html:38 +msgid "entries" +msgstr "" + +#: apps/activity/models.py:141 msgid "Already entered on " msgstr "" -#: apps/activity/models.py:139 apps/activity/tables.py:54 +#: apps/activity/models.py:141 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "" -#: apps/activity/models.py:147 +#: apps/activity/models.py:149 msgid "The balance is negative." msgstr "" -#: apps/activity/models.py:177 +#: apps/activity/models.py:179 msgid "last name" msgstr "" -#: apps/activity/models.py:182 templates/member/profile_info.html:14 +#: apps/activity/models.py:184 templates/member/profile_info.html:14 #: templates/registration/future_profile_detail.html:16 msgid "first name" msgstr "" -#: apps/activity/models.py:189 +#: apps/activity/models.py:191 msgid "inviter" msgstr "" -#: apps/activity/models.py:230 +#: apps/activity/models.py:232 msgid "guest" msgstr "" -#: apps/activity/models.py:231 +#: apps/activity/models.py:233 msgid "guests" msgstr "" -#: apps/activity/models.py:243 +#: apps/activity/models.py:245 msgid "Invitation" msgstr "" @@ -167,17 +175,17 @@ msgstr "" msgid "remove" msgstr "" -#: apps/activity/tables.py:75 apps/treasury/models.py:126 +#: apps/activity/tables.py:75 apps/treasury/models.py:138 msgid "Type" msgstr "" #: apps/activity/tables.py:77 apps/member/forms.py:75 -#: apps/registration/forms.py:49 apps/treasury/forms.py:121 +#: apps/registration/forms.py:55 apps/treasury/forms.py:121 msgid "Last name" msgstr "" #: apps/activity/tables.py:79 apps/member/forms.py:80 -#: apps/registration/forms.py:54 apps/treasury/forms.py:123 +#: apps/registration/forms.py:60 apps/treasury/forms.py:123 #: templates/note/transaction_form.html:97 msgid "First name" msgstr "" @@ -186,7 +194,7 @@ msgstr "" msgid "Note" msgstr "" -#: apps/activity/tables.py:83 apps/member/tables.py:35 +#: apps/activity/tables.py:83 apps/member/tables.py:41 msgid "Balance" msgstr "" @@ -251,23 +259,31 @@ msgstr "" msgid "Logs cannot be destroyed." msgstr "" +#: apps/logs/models.py:80 +msgid "changelog" +msgstr "" + +#: apps/logs/models.py:81 +msgid "changelogs" +msgstr "" + #: apps/member/apps.py:14 msgid "member" msgstr "" -#: apps/member/forms.py:54 apps/registration/forms.py:29 +#: apps/member/forms.py:54 apps/registration/forms.py:35 msgid "Inscription paid by Société Générale" msgstr "" -#: apps/member/forms.py:56 apps/registration/forms.py:31 +#: apps/member/forms.py:56 apps/registration/forms.py:37 msgid "Check this case is the Société Générale paid the inscription." msgstr "" -#: apps/member/forms.py:61 apps/registration/forms.py:36 +#: apps/member/forms.py:61 apps/registration/forms.py:42 msgid "Credit type" msgstr "" -#: apps/member/forms.py:62 apps/registration/forms.py:37 +#: apps/member/forms.py:62 apps/registration/forms.py:43 msgid "No credit" msgstr "" @@ -275,11 +291,11 @@ msgstr "" msgid "You can credit the note of the user." msgstr "" -#: apps/member/forms.py:68 apps/registration/forms.py:42 +#: apps/member/forms.py:68 apps/registration/forms.py:48 msgid "Credit amount" msgstr "" -#: apps/member/forms.py:85 apps/registration/forms.py:59 +#: apps/member/forms.py:85 apps/registration/forms.py:65 #: apps/treasury/forms.py:125 templates/note/transaction_form.html:103 msgid "Bank" msgstr "" @@ -392,7 +408,7 @@ msgstr "" msgid "clubs" msgstr "" -#: apps/member/models.py:209 apps/permission/models.py:288 +#: apps/member/models.py:209 apps/permission/models.py:294 msgid "role" msgstr "" @@ -412,65 +428,65 @@ msgstr "" msgid "fee" msgstr "" -#: apps/member/models.py:261 apps/member/views.py:447 +#: apps/member/models.py:267 apps/member/views.py:491 msgid "User is not a member of the parent club" msgstr "" -#: apps/member/models.py:271 apps/member/views.py:456 +#: apps/member/models.py:277 apps/member/views.py:500 msgid "User is already a member of the club" msgstr "" -#: apps/member/models.py:306 +#: apps/member/models.py:315 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "" -#: apps/member/models.py:309 +#: apps/member/models.py:318 msgid "membership" msgstr "" -#: apps/member/models.py:310 +#: apps/member/models.py:319 msgid "memberships" msgstr "" -#: apps/member/tables.py:93 +#: apps/member/tables.py:112 msgid "Renew" msgstr "" -#: apps/member/views.py:56 apps/registration/forms.py:20 +#: apps/member/views.py:62 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "" -#: apps/member/views.py:59 templates/member/profile_info.html:45 +#: apps/member/views.py:65 templates/member/profile_info.html:45 #: templates/registration/future_profile_detail.html:55 msgid "Update Profile" msgstr "" -#: apps/member/views.py:69 +#: apps/member/views.py:75 msgid "An alias with a similar name already exists." msgstr "" -#: apps/member/views.py:164 +#: apps/member/views.py:175 msgid "Search user" msgstr "" -#: apps/member/views.py:442 +#: apps/member/views.py:486 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." msgstr "" -#: apps/member/views.py:460 +#: apps/member/views.py:504 msgid "The membership must start after {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:465 +#: apps/member/views.py:509 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:472 apps/member/views.py:474 apps/member/views.py:476 -#: apps/registration/views.py:237 apps/registration/views.py:239 -#: apps/registration/views.py:241 +#: apps/member/views.py:519 apps/member/views.py:521 apps/member/views.py:523 +#: apps/registration/views.py:285 apps/registration/views.py:287 +#: apps/registration/views.py:289 msgid "This field is required." msgstr "" @@ -711,16 +727,16 @@ msgstr "" msgid "Transfer money" msgstr "" -#: apps/note/views.py:107 templates/base.html:79 +#: apps/note/views.py:109 templates/base.html:79 msgid "Consumptions" msgstr "" -#: apps/permission/models.py:82 apps/permission/models.py:275 +#: apps/permission/models.py:82 apps/permission/models.py:281 #, python-brace-format msgid "Can {type} {model}.{field} in {query}" msgstr "" -#: apps/permission/models.py:84 apps/permission/models.py:277 +#: apps/permission/models.py:84 apps/permission/models.py:283 #, python-brace-format msgid "Can {type} {model} in {query}" msgstr "" @@ -729,43 +745,67 @@ msgstr "" msgid "rank" msgstr "" +#: apps/permission/models.py:110 +msgid "permission mask" +msgstr "" + +#: apps/permission/models.py:111 +msgid "permission masks" +msgstr "" + #: apps/permission/models.py:160 +msgid "permission" +msgstr "" + +#: apps/permission/models.py:161 +msgid "permissions" +msgstr "" + +#: apps/permission/models.py:166 msgid "Specifying field applies only to view and change permission types." msgstr "" +#: apps/permission/models.py:304 apps/permission/models.py:305 +msgid "role permissions" +msgstr "" + #: apps/registration/apps.py:10 msgid "registration" msgstr "" -#: apps/registration/forms.py:64 +#: apps/registration/forms.py:70 msgid "Join BDE Club" msgstr "" -#: apps/registration/forms.py:70 +#: apps/registration/forms.py:77 msgid "Join Kfet Club" msgstr "" -#: apps/registration/views.py:70 +#: apps/registration/views.py:75 msgid "Email validation" msgstr "" -#: apps/registration/views.py:114 +#: apps/registration/views.py:120 msgid "Email validation unsuccessful" msgstr "" -#: apps/registration/views.py:122 +#: apps/registration/views.py:131 msgid "Email validation email sent" msgstr "" -#: apps/registration/views.py:154 +#: apps/registration/views.py:184 msgid "Unregistered users" msgstr "" -#: apps/registration/views.py:226 +#: apps/registration/views.py:251 +msgid "You must join the BDE." +msgstr "" + +#: apps/registration/views.py:273 msgid "You must join BDE club before joining Kfet club." msgstr "" -#: apps/registration/views.py:230 +#: apps/registration/views.py:278 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" @@ -833,40 +873,80 @@ msgstr "" msgid "Acquitted" msgstr "" -#: apps/treasury/models.py:75 -msgid "Designation" +#: apps/treasury/models.py:63 +msgid "invoice" +msgstr "" + +#: apps/treasury/models.py:64 +msgid "invoices" msgstr "" #: apps/treasury/models.py:79 -msgid "Quantity" +msgid "Designation" msgstr "" #: apps/treasury/models.py:83 +msgid "Quantity" +msgstr "" + +#: apps/treasury/models.py:87 msgid "Unit price" msgstr "" -#: apps/treasury/models.py:120 +#: apps/treasury/models.py:103 +msgid "product" +msgstr "" + +#: apps/treasury/models.py:104 +msgid "products" +msgstr "" + +#: apps/treasury/models.py:121 +msgid "remittance type" +msgstr "" + +#: apps/treasury/models.py:122 +msgid "remittance types" +msgstr "" + +#: apps/treasury/models.py:132 msgid "Date" msgstr "" -#: apps/treasury/models.py:131 +#: apps/treasury/models.py:143 msgid "Comment" msgstr "" -#: apps/treasury/models.py:136 +#: apps/treasury/models.py:148 msgid "Closed" msgstr "" -#: apps/treasury/models.py:169 +#: apps/treasury/models.py:152 +msgid "remittance" +msgstr "" + +#: apps/treasury/models.py:153 +msgid "remittances" +msgstr "" + +#: apps/treasury/models.py:185 msgid "Remittance #{:d}: {}" msgstr "" -#: apps/treasury/models.py:188 apps/treasury/tables.py:76 +#: apps/treasury/models.py:204 apps/treasury/tables.py:76 #: apps/treasury/tables.py:84 templates/treasury/invoice_list.html:13 #: templates/treasury/remittance_list.html:13 msgid "Remittance" msgstr "" +#: apps/treasury/models.py:208 +msgid "special transaction proxy" +msgstr "" + +#: apps/treasury/models.py:209 +msgid "special transaction proxies" +msgstr "" + #: apps/treasury/tables.py:19 msgid "Invoice #{:d}" msgstr "" @@ -952,14 +1032,6 @@ msgstr "" msgid "Return to activity page" msgstr "" -#: templates/activity/activity_entry.html:38 -msgid "entries" -msgstr "" - -#: templates/activity/activity_entry.html:38 -msgid "entry" -msgstr "" - #: templates/activity/activity_list.html:5 msgid "Upcoming activities" msgstr "" @@ -1155,6 +1227,11 @@ msgstr "" msgid "Save Changes" msgstr "" +#: templates/member/user_list.html:14 +#: templates/registration/future_user_list.html:17 +msgid "There is no pending user with this pattern." +msgstr "" + #: templates/note/conso_form.html:28 templates/note/transaction_form.html:55 msgid "Select emitters" msgstr "" @@ -1229,11 +1306,11 @@ msgstr "" msgid "buttons listing " msgstr "" -#: templates/note/transactiontemplate_list.html:71 +#: templates/note/transactiontemplate_list.html:70 msgid "button successfully deleted " msgstr "" -#: templates/note/transactiontemplate_list.html:75 +#: templates/note/transactiontemplate_list.html:74 msgid "Unable to delete button " msgstr "" @@ -1266,10 +1343,6 @@ msgstr "" msgid "New user" msgstr "" -#: templates/registration/future_user_list.html:17 -msgid "There is no pending user with this pattern." -msgstr "" - #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 4e282bea..be518ebf 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-06 06:43+0200\n" +"POT-Creation-Date: 2020-04-06 10:51+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,24 +18,24 @@ msgstr "" msgid "activity" msgstr "activité" -#: apps/activity/forms.py:45 apps/activity/models.py:206 +#: apps/activity/forms.py:45 apps/activity/models.py:208 msgid "You can't invite someone once the activity is started." msgstr "" "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." -#: apps/activity/forms.py:48 apps/activity/models.py:209 +#: apps/activity/forms.py:48 apps/activity/models.py:211 msgid "This activity is not validated yet." msgstr "Cette activité n'est pas encore validée." -#: apps/activity/forms.py:58 apps/activity/models.py:217 +#: apps/activity/forms.py:58 apps/activity/models.py:219 msgid "This person has been already invited 5 times this year." msgstr "Cette personne a déjà été invitée 5 fois cette année." -#: apps/activity/forms.py:62 apps/activity/models.py:221 +#: apps/activity/forms.py:62 apps/activity/models.py:223 msgid "This person is already invited." msgstr "Cette personne est déjà invitée." -#: apps/activity/forms.py:66 apps/activity/models.py:225 +#: apps/activity/forms.py:66 apps/activity/models.py:227 msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." @@ -118,40 +118,48 @@ msgstr "heure d'entrée" msgid "note" msgstr "note" -#: apps/activity/models.py:139 +#: apps/activity/models.py:133 templates/activity/activity_entry.html:38 +msgid "entry" +msgstr "entrée" + +#: apps/activity/models.py:134 templates/activity/activity_entry.html:38 +msgid "entries" +msgstr "entrées" + +#: apps/activity/models.py:141 msgid "Already entered on " msgstr "Déjà rentré le " -#: apps/activity/models.py:139 apps/activity/tables.py:54 +#: apps/activity/models.py:141 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%d/%m/%Y %H:%M:%S}" -#: apps/activity/models.py:147 +#: apps/activity/models.py:149 msgid "The balance is negative." msgstr "La note est en négatif." -#: apps/activity/models.py:177 +#: apps/activity/models.py:179 msgid "last name" msgstr "nom de famille" -#: apps/activity/models.py:182 templates/member/profile_info.html:14 +#: apps/activity/models.py:184 templates/member/profile_info.html:14 #: templates/registration/future_profile_detail.html:16 msgid "first name" msgstr "prénom" -#: apps/activity/models.py:189 +#: apps/activity/models.py:191 msgid "inviter" msgstr "hôte" -#: apps/activity/models.py:230 +#: apps/activity/models.py:232 msgid "guest" msgstr "invité" -#: apps/activity/models.py:231 +#: apps/activity/models.py:233 msgid "guests" msgstr "invités" -#: apps/activity/models.py:243 +#: apps/activity/models.py:245 msgid "Invitation" msgstr "Invitation" @@ -163,17 +171,17 @@ msgstr "Entré le " msgid "remove" msgstr "supprimer" -#: apps/activity/tables.py:75 apps/treasury/models.py:126 +#: apps/activity/tables.py:75 apps/treasury/models.py:138 msgid "Type" msgstr "Type" #: apps/activity/tables.py:77 apps/member/forms.py:75 -#: apps/registration/forms.py:49 apps/treasury/forms.py:121 +#: apps/registration/forms.py:55 apps/treasury/forms.py:121 msgid "Last name" msgstr "Nom de famille" #: apps/activity/tables.py:79 apps/member/forms.py:80 -#: apps/registration/forms.py:54 apps/treasury/forms.py:123 +#: apps/registration/forms.py:60 apps/treasury/forms.py:123 #: templates/note/transaction_form.html:97 msgid "First name" msgstr "Prénom" @@ -182,7 +190,7 @@ msgstr "Prénom" msgid "Note" msgstr "Note" -#: apps/activity/tables.py:83 apps/member/tables.py:35 +#: apps/activity/tables.py:83 apps/member/tables.py:41 msgid "Balance" msgstr "Solde du compte" @@ -247,23 +255,31 @@ msgstr "Date" msgid "Logs cannot be destroyed." msgstr "Les logs ne peuvent pas être détruits." +#: apps/logs/models.py:80 +msgid "changelog" +msgstr "" + +#: apps/logs/models.py:81 +msgid "changelogs" +msgstr "" + #: apps/member/apps.py:14 msgid "member" msgstr "adhérent" -#: apps/member/forms.py:54 apps/registration/forms.py:29 +#: apps/member/forms.py:54 apps/registration/forms.py:35 msgid "Inscription paid by Société Générale" msgstr "Inscription payée par la Société générale" -#: apps/member/forms.py:56 apps/registration/forms.py:31 +#: apps/member/forms.py:56 apps/registration/forms.py:37 msgid "Check this case is the Société Générale paid the inscription." msgstr "Cochez cette case si la Société Générale a payé l'inscription." -#: apps/member/forms.py:61 apps/registration/forms.py:36 +#: apps/member/forms.py:61 apps/registration/forms.py:42 msgid "Credit type" msgstr "Type de rechargement" -#: apps/member/forms.py:62 apps/registration/forms.py:37 +#: apps/member/forms.py:62 apps/registration/forms.py:43 msgid "No credit" msgstr "Pas de rechargement" @@ -271,11 +287,11 @@ msgstr "Pas de rechargement" msgid "You can credit the note of the user." msgstr "Vous pouvez créditer la note de l'utisateur avant l'adhésion." -#: apps/member/forms.py:68 apps/registration/forms.py:42 +#: apps/member/forms.py:68 apps/registration/forms.py:48 msgid "Credit amount" msgstr "Montant à créditer" -#: apps/member/forms.py:85 apps/registration/forms.py:59 +#: apps/member/forms.py:85 apps/registration/forms.py:65 #: apps/treasury/forms.py:125 templates/note/transaction_form.html:103 msgid "Bank" msgstr "Banque" @@ -392,7 +408,7 @@ msgstr "club" msgid "clubs" msgstr "clubs" -#: apps/member/models.py:209 apps/permission/models.py:288 +#: apps/member/models.py:209 apps/permission/models.py:294 msgid "role" msgstr "rôle" @@ -412,49 +428,49 @@ msgstr "l'adhésion finit le" msgid "fee" msgstr "cotisation" -#: apps/member/models.py:261 apps/member/views.py:447 +#: apps/member/models.py:267 apps/member/views.py:491 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" -#: apps/member/models.py:271 apps/member/views.py:456 +#: apps/member/models.py:277 apps/member/views.py:500 msgid "User is already a member of the club" msgstr "L'utilisateur est déjà membre du club" -#: apps/member/models.py:306 +#: apps/member/models.py:315 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Adhésion de {user} pour le club {club}" -#: apps/member/models.py:309 +#: apps/member/models.py:318 msgid "membership" msgstr "adhésion" -#: apps/member/models.py:310 +#: apps/member/models.py:319 msgid "memberships" msgstr "adhésions" -#: apps/member/tables.py:93 +#: apps/member/tables.py:112 msgid "Renew" msgstr "Renouveler" -#: apps/member/views.py:56 apps/registration/forms.py:20 +#: apps/member/views.py:62 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "Cette adresse doit être valide." -#: apps/member/views.py:59 templates/member/profile_info.html:45 +#: apps/member/views.py:65 templates/member/profile_info.html:45 #: templates/registration/future_profile_detail.html:55 msgid "Update Profile" msgstr "Modifier le profil" -#: apps/member/views.py:69 +#: apps/member/views.py:75 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." -#: apps/member/views.py:164 +#: apps/member/views.py:175 msgid "Search user" msgstr "Chercher un utilisateur" -#: apps/member/views.py:442 +#: apps/member/views.py:486 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -462,17 +478,17 @@ msgstr "" "Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas " "avoir un solde négatif." -#: apps/member/views.py:460 +#: apps/member/views.py:504 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:465 +#: apps/member/views.py:509 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:472 apps/member/views.py:474 apps/member/views.py:476 -#: apps/registration/views.py:237 apps/registration/views.py:239 -#: apps/registration/views.py:241 +#: apps/member/views.py:519 apps/member/views.py:521 apps/member/views.py:523 +#: apps/registration/views.py:285 apps/registration/views.py:287 +#: apps/registration/views.py:289 msgid "This field is required." msgstr "Ce champ est requis." @@ -714,16 +730,16 @@ msgstr "Éditer" msgid "Transfer money" msgstr "Transférer de l'argent" -#: apps/note/views.py:107 templates/base.html:79 +#: apps/note/views.py:109 templates/base.html:79 msgid "Consumptions" msgstr "Consommations" -#: apps/permission/models.py:82 apps/permission/models.py:275 +#: apps/permission/models.py:82 apps/permission/models.py:281 #, python-brace-format msgid "Can {type} {model}.{field} in {query}" msgstr "" -#: apps/permission/models.py:84 apps/permission/models.py:277 +#: apps/permission/models.py:84 apps/permission/models.py:283 #, python-brace-format msgid "Can {type} {model} in {query}" msgstr "" @@ -732,43 +748,67 @@ msgstr "" msgid "rank" msgstr "Rang" +#: apps/permission/models.py:110 +msgid "permission mask" +msgstr "masque de permissions" + +#: apps/permission/models.py:111 +msgid "permission masks" +msgstr "masques de permissions" + #: apps/permission/models.py:160 +msgid "permission" +msgstr "permission" + +#: apps/permission/models.py:161 +msgid "permissions" +msgstr "permissions" + +#: apps/permission/models.py:166 msgid "Specifying field applies only to view and change permission types." msgstr "" +#: apps/permission/models.py:304 apps/permission/models.py:305 +msgid "role permissions" +msgstr "Permissions par rôles" + #: apps/registration/apps.py:10 msgid "registration" msgstr "inscription" -#: apps/registration/forms.py:64 +#: apps/registration/forms.py:70 msgid "Join BDE Club" msgstr "Adhérer au club BDE" -#: apps/registration/forms.py:70 +#: apps/registration/forms.py:77 msgid "Join Kfet Club" msgstr "Adhérer au club Kfet" -#: apps/registration/views.py:70 +#: apps/registration/views.py:75 msgid "Email validation" msgstr "Validation de l'adresse mail" -#: apps/registration/views.py:114 +#: apps/registration/views.py:120 msgid "Email validation unsuccessful" msgstr " La validation de l'adresse mail a échoué" -#: apps/registration/views.py:122 +#: apps/registration/views.py:131 msgid "Email validation email sent" msgstr "L'email de vérification de l'adresse email a bien été envoyé." -#: apps/registration/views.py:154 +#: apps/registration/views.py:184 msgid "Unregistered users" msgstr "Utilisateurs en attente d'inscription" -#: apps/registration/views.py:226 +#: apps/registration/views.py:251 +msgid "You must join the BDE." +msgstr "Vous devez adhérer au BDE." + +#: apps/registration/views.py:273 msgid "You must join BDE club before joining Kfet club." msgstr "Vous devez adhérer au club BDE avant d'adhérer au club Kfet." -#: apps/registration/views.py:230 +#: apps/registration/views.py:278 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" @@ -838,40 +878,80 @@ msgstr "Lieu" msgid "Acquitted" msgstr "Acquittée" -#: apps/treasury/models.py:75 +#: apps/treasury/models.py:63 +msgid "invoice" +msgstr "facture" + +#: apps/treasury/models.py:64 +msgid "invoices" +msgstr "factures" + +#: apps/treasury/models.py:79 msgid "Designation" msgstr "Désignation" -#: apps/treasury/models.py:79 +#: apps/treasury/models.py:83 msgid "Quantity" msgstr "Quantité" -#: apps/treasury/models.py:83 +#: apps/treasury/models.py:87 msgid "Unit price" msgstr "Prix unitaire" -#: apps/treasury/models.py:120 +#: apps/treasury/models.py:103 +msgid "product" +msgstr "produit" + +#: apps/treasury/models.py:104 +msgid "products" +msgstr "produits" + +#: apps/treasury/models.py:121 +msgid "remittance type" +msgstr "type de remise" + +#: apps/treasury/models.py:122 +msgid "remittance types" +msgstr "types de remises" + +#: apps/treasury/models.py:132 msgid "Date" msgstr "Date" -#: apps/treasury/models.py:131 +#: apps/treasury/models.py:143 msgid "Comment" msgstr "Commentaire" -#: apps/treasury/models.py:136 +#: apps/treasury/models.py:148 msgid "Closed" msgstr "Fermée" -#: apps/treasury/models.py:169 +#: apps/treasury/models.py:152 +msgid "remittance" +msgstr "remise" + +#: apps/treasury/models.py:153 +msgid "remittances" +msgstr "remises" + +#: apps/treasury/models.py:185 msgid "Remittance #{:d}: {}" msgstr "Remise n°{:d} : {}" -#: apps/treasury/models.py:188 apps/treasury/tables.py:76 +#: apps/treasury/models.py:204 apps/treasury/tables.py:76 #: apps/treasury/tables.py:84 templates/treasury/invoice_list.html:13 #: templates/treasury/remittance_list.html:13 msgid "Remittance" msgstr "Remise" +#: apps/treasury/models.py:208 +msgid "special transaction proxy" +msgstr "Proxy de transaction spéciale" + +#: apps/treasury/models.py:209 +msgid "special transaction proxies" +msgstr "Proxys de transactions spéciales" + #: apps/treasury/tables.py:19 msgid "Invoice #{:d}" msgstr "Facture n°{:d}" @@ -957,14 +1037,6 @@ msgstr "Entrées" msgid "Return to activity page" msgstr "Retour à la page de l'activité" -#: templates/activity/activity_entry.html:38 -msgid "entries" -msgstr "entrées" - -#: templates/activity/activity_entry.html:38 -msgid "entry" -msgstr "entrée" - #: templates/activity/activity_list.html:5 msgid "Upcoming activities" msgstr "Activités à venir" @@ -1162,6 +1234,11 @@ msgstr "Voir mes adhésions" msgid "Save Changes" msgstr "Sauvegarder les changements" +#: templates/member/user_list.html:14 +#: templates/registration/future_user_list.html:17 +msgid "There is no pending user with this pattern." +msgstr "Il n'y a pas d'inscription en attente avec cette entrée." + #: templates/note/conso_form.html:28 templates/note/transaction_form.html:55 msgid "Select emitters" msgstr "Sélection des émetteurs" @@ -1236,11 +1313,11 @@ msgstr "Nouveau bouton" msgid "buttons listing " msgstr "Liste des boutons" -#: templates/note/transactiontemplate_list.html:71 +#: templates/note/transactiontemplate_list.html:70 msgid "button successfully deleted " msgstr "Le bouton a bien été supprimé" -#: templates/note/transactiontemplate_list.html:75 +#: templates/note/transactiontemplate_list.html:74 msgid "Unable to delete button " msgstr "Impossible de supprimer le bouton " @@ -1273,10 +1350,6 @@ msgstr "Valider l'inscription" msgid "New user" msgstr "Nouvel utilisateur" -#: templates/registration/future_user_list.html:17 -msgid "There is no pending user with this pattern." -msgstr "Il n'y a pas d'inscription en attente avec cette entrée." - #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." msgstr "" From 9fbfac7bdb71d861d167fb99cf0dfdbb1af10158 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Mon, 6 Apr 2020 11:43:17 +0200 Subject: [PATCH 62/66] distinct on field not supported by sqlite --- apps/activity/views.py | 2 +- apps/permission/backends.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/activity/views.py b/apps/activity/views.py index 51e2ebf5..34670d36 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -139,7 +139,7 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): | Q(name__regex=pattern) | Q(normalized_name__regex=Alias.normalize(pattern)))) \ .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))\ - .distinct("username")[:20] + .distinct()[:20] for note in note_qs: note.type = "Adhérent" note.activity = activity diff --git a/apps/permission/backends.py b/apps/permission/backends.py index 4fb7b577..04d93528 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -43,7 +43,7 @@ class PermissionBackend(ModelBackend): rolepermissions__role__membership__date_end__gte=datetime.date.today(), type=t, mask__rank__lte=get_current_session().get("permission_mask", 0), - ).distinct('club', 'pk',) + ).distinct() @staticmethod def permissions(user, model, type): From 61b32f9aec5f007d7374ced073934d7ae933bb20 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Mon, 6 Apr 2020 12:13:12 +0200 Subject: [PATCH 63/66] explicit context is better than implicit ctx --- apps/activity/views.py | 32 ++++++++++++++++---------------- apps/treasury/views.py | 34 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/apps/activity/views.py b/apps/activity/views.py index 51e2ebf5..1fe2d97a 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -40,15 +40,15 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView return super().get_queryset().reverse() def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) - ctx['title'] = _("Activities") + context['title'] = _("Activities") upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now()) - ctx['upcoming'] = ActivityTable(data=upcoming_activities + context['upcoming'] = ActivityTable(data=upcoming_activities .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) - return ctx + return context class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): @@ -56,15 +56,15 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): context_object_name = "activity" def get_context_data(self, **kwargs): - ctx = super().get_context_data() + context = super().get_context_data() table = GuestTable(data=Guest.objects.filter(activity=self.object) .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))) - ctx["guests"] = table + context["guests"] = table - ctx["activity_started"] = datetime.now(timezone.utc) > self.object.date_start + context["activity_started"] = datetime.now(timezone.utc) > self.object.date_start - return ctx + return context class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): @@ -99,11 +99,11 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): template_name = "activity/activity_entry.html" def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ .get(pk=self.kwargs["pk"]) - ctx["activity"] = activity + context["activity"] = activity matched = [] @@ -146,12 +146,12 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): matched.append(note) table = EntryTable(data=matched) - ctx["table"] = table + context["table"] = table - ctx["entries"] = Entry.objects.filter(activity=activity) + context["entries"] = Entry.objects.filter(activity=activity) - ctx["title"] = _('Entry for activity "{}"').format(activity.name) - ctx["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk - ctx["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk + context["title"] = _('Entry for activity "{}"').format(activity.name) + context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk + context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - return ctx + return context diff --git a/apps/treasury/views.py b/apps/treasury/views.py index f564ccb2..7361d1d2 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -201,14 +201,14 @@ class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView) return reverse_lazy('treasury:remittance_list') def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) - ctx["table"] = RemittanceTable(data=Remittance.objects + context["table"] = RemittanceTable(data=Remittance.objects .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) .all()) - ctx["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) + context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) - return ctx + return context class RemittanceListView(LoginRequiredMixin, TemplateView): @@ -218,27 +218,27 @@ class RemittanceListView(LoginRequiredMixin, TemplateView): template_name = "treasury/remittance_list.html" def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) - ctx["opened_remittances"] = RemittanceTable( + context["opened_remittances"] = RemittanceTable( data=Remittance.objects.filter(closed=False).filter( PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) - ctx["closed_remittances"] = RemittanceTable( + context["closed_remittances"] = RemittanceTable( data=Remittance.objects.filter(closed=True).filter( PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).reverse().all()) - ctx["special_transactions_no_remittance"] = SpecialTransactionTable( + context["special_transactions_no_remittance"] = SpecialTransactionTable( data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)), specialtransactionproxy__remittance=None).filter( PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), exclude=('remittance_remove', )) - ctx["special_transactions_with_remittance"] = SpecialTransactionTable( + context["special_transactions_with_remittance"] = SpecialTransactionTable( data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)), specialtransactionproxy__remittance__closed=False).filter( PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), exclude=('remittance_add', )) - return ctx + return context class RemittanceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): @@ -252,17 +252,17 @@ class RemittanceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView) return reverse_lazy('treasury:remittance_list') def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) - ctx["table"] = RemittanceTable(data=Remittance.objects.filter( + context["table"] = RemittanceTable(data=Remittance.objects.filter( PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).filter( PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all() - ctx["special_transactions"] = SpecialTransactionTable( + context["special_transactions"] = SpecialTransactionTable( data=data, exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', )) - return ctx + return context class LinkTransactionToRemittanceView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): @@ -277,9 +277,9 @@ class LinkTransactionToRemittanceView(ProtectQuerysetMixin, LoginRequiredMixin, return reverse_lazy('treasury:remittance_list') def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) - form = ctx["form"] + form = context["form"] form.fields["last_name"].initial = self.object.transaction.last_name form.fields["first_name"].initial = self.object.transaction.first_name form.fields["bank"].initial = self.object.transaction.bank @@ -287,7 +287,7 @@ class LinkTransactionToRemittanceView(ProtectQuerysetMixin, LoginRequiredMixin, form.fields["remittance"].queryset = form.fields["remittance"] \ .queryset.filter(remittance_type__note=self.object.transaction.source) - return ctx + return context class UnlinkTransactionToRemittanceView(LoginRequiredMixin, View): From 7e0aac0125239df717fb7a7c09bd92ea2d346b26 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Mon, 6 Apr 2020 12:32:44 +0200 Subject: [PATCH 64/66] add docstring for entry --- apps/activity/models.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/activity/models.py b/apps/activity/models.py index ed2d94c9..60653b3c 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -104,6 +104,12 @@ class Activity(models.Model): class Entry(models.Model): + """ + Register the entry of someone: + - a member with a :model:`note.NoteUser` + - or a :model:`activity.Guest` + In the case of a Guest Entry, the inviter note is also save. + """ activity = models.ForeignKey( Activity, on_delete=models.PROTECT, @@ -131,8 +137,7 @@ class Entry(models.Model): class Meta: unique_together = (('activity', 'note', 'guest', ), ) - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): + def save(self, *args,**kwargs): qs = Entry.objects.filter(~Q(pk=self.pk), activity=self.activity, note=self.note, guest=self.guest) if qs.exists(): @@ -146,7 +151,7 @@ class Entry(models.Model): if self.note.balance < 0: raise ValidationError(_("The balance is negative.")) - ret = super().save(force_insert, force_update, using, update_fields) + ret = super().save(*args,**kwargs) if insert and self.guest: GuestTransaction.objects.create( From e067b19d413c538f09342c2e4cc431e193159922 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Apr 2020 19:51:39 +0200 Subject: [PATCH 65/66] Paginate memberships and transactions --- apps/member/views.py | 17 +++++++--- templates/member/club_detail.html | 10 +++--- templates/member/club_info.html | 2 +- templates/member/club_tables.html | 46 ++++++++++++---------------- templates/member/profile_detail.html | 11 +++++++ templates/member/profile_info.html | 2 +- templates/member/profile_tables.html | 44 ++++++++++++-------------- 7 files changed, 70 insertions(+), 62 deletions(-) diff --git a/apps/member/views.py b/apps/member/views.py index e03c5ff5..381314b2 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -130,10 +130,15 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): history_list = \ Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")\ .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")) - context['history_list'] = HistoryTable(history_list) + history_table = HistoryTable(history_list, prefix='transaction-') + history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1)) + context['history_list'] = history_table + club_list = Membership.objects.filter(user=user, date_end__gte=datetime.today())\ .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")) - context['club_list'] = MembershipTable(data=club_list) + membership_table = MembershipTable(data=club_list, prefix='membership-') + membership_table.paginate(per_page=10, page=self.request.GET.get("membership-page", 1)) + context['club_list'] = membership_table return context @@ -310,13 +315,17 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\ .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id') - context['history_list'] = HistoryTable(club_transactions) + history_table = HistoryTable(club_transactions, prefix="history-") + history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1)) + context['history_list'] = history_table club_member = Membership.objects.filter( club=club, date_end__gte=datetime.today(), ).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")) - context['member_list'] = MembershipTable(data=club_member) + membership_table = MembershipTable(data=club_member, prefix="membership-") + membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1)) + context['member_list'] = membership_table # Check if the user has the right to create a membership, to display the button. empty_membership = Membership( diff --git a/templates/member/club_detail.html b/templates/member/club_detail.html index 3ad29901..fedd43fa 100644 --- a/templates/member/club_detail.html +++ b/templates/member/club_detail.html @@ -10,9 +10,11 @@ {% block extrajavascript %} {% endblock %} diff --git a/templates/member/club_info.html b/templates/member/club_info.html index de0192b4..93c76d59 100644 --- a/templates/member/club_info.html +++ b/templates/member/club_info.html @@ -55,7 +55,7 @@ {% trans "Edit" %} {% endif %} {% url 'member:club_detail' club.pk as club_detail_url %} - {%if request.get_full_path != club_detail_url %} + {%if request.path_info != club_detail_url %} {% trans 'View Profile' %} {% endif %}
        diff --git a/templates/member/club_tables.html b/templates/member/club_tables.html index fbded9c3..32be9bd4 100644 --- a/templates/member/club_tables.html +++ b/templates/member/club_tables.html @@ -1,31 +1,23 @@ {% load render_table from django_tables2 %} {% load i18n %} -
        -
        - -
        - {% render_table member_list %} -
        -
        - -
        - -
        -
        - {% render_table history_list %} -
        -
        +
        + + {% render_table member_list %} +
        + +
        + +
        + +
        + {% render_table history_list %} +
        diff --git a/templates/member/profile_detail.html b/templates/member/profile_detail.html index 42d03d8b..04b15d3b 100644 --- a/templates/member/profile_detail.html +++ b/templates/member/profile_detail.html @@ -7,3 +7,14 @@ {% block profile_content %} {% include "member/profile_tables.html" %} {% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/templates/member/profile_info.html b/templates/member/profile_info.html index 9ff20385..74856355 100644 --- a/templates/member/profile_info.html +++ b/templates/member/profile_info.html @@ -44,7 +44,7 @@ diff --git a/templates/member/profile_tables.html b/templates/member/profile_tables.html index 9016289a..9629ff14 100644 --- a/templates/member/profile_tables.html +++ b/templates/member/profile_tables.html @@ -9,32 +9,26 @@
        {% endif %} -
        -
        - -
        - {% render_table club_list %} -
        +
        + + {% render_table club_list %} +
        -
        - -
        -
        - {% render_table history_list %} -
        -
        +
        + +
        + +
        + {% render_table history_list %}
        From fc511689b0d98a3c83f9225b7917afc02675a98f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Apr 2020 22:03:26 +0200 Subject: [PATCH 66/66] Update mail translation --- apps/member/models.py | 7 +- apps/registration/views.py | 1 + locale/de/LC_MESSAGES/django.po | 157 ++++++++++------- locale/fr/LC_MESSAGES/django.po | 162 +++++++++++------- .../email_validation_complete.html | 21 ++- .../registration/email_validation_email.html | 11 -- .../mails/email_validation_email.html | 15 ++ 7 files changed, 228 insertions(+), 146 deletions(-) delete mode 100644 templates/registration/email_validation_email.html create mode 100644 templates/registration/mails/email_validation_email.html diff --git a/apps/member/models.py b/apps/member/models.py index c51a95c8..3a022434 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later import datetime +import os from django.conf import settings from django.contrib.auth.models import User @@ -79,12 +80,10 @@ class Profile(models.Model): def send_email_validation_link(self): subject = "Activate your Note Kfet account" - message = loader.render_to_string('registration/email_validation_email.html', + message = loader.render_to_string('registration/mails/email_validation_email.html', { 'user': self.user, - 'domain': "nk20.ynerant.fr", - 'site_name': "La Note Kfet", - 'protocol': 'https', + 'domain': os.getenv("NOTE_URL", "note.example.com"), 'token': email_validation_token.make_token(self.user), 'uid': urlsafe_base64_encode(force_bytes(self.user.pk)).decode('UTF-8'), }) diff --git a/apps/registration/views.py b/apps/registration/views.py index 575ada8f..35391b05 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -112,6 +112,7 @@ class UserValidateView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + context['user'] = self.get_user(self.kwargs["uidb64"]) context['login_url'] = resolve_url(settings.LOGIN_URL) if self.validlink: context['validlink'] = True diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index ff98db5b..f616ffd6 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-06 10:51+0200\n" +"POT-Creation-Date: 2020-04-09 21:59+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -44,7 +44,7 @@ msgid "You can't invite more than 3 people to this activity." msgstr "" #: apps/activity/models.py:23 apps/activity/models.py:48 -#: apps/member/models.py:100 apps/member/models.py:203 +#: apps/member/models.py:99 apps/member/models.py:202 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 @@ -79,7 +79,7 @@ msgstr "" msgid "type" msgstr "" -#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:224 +#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:223 #: apps/note/models/notes.py:117 msgid "user" msgstr "" @@ -300,152 +300,152 @@ msgstr "" msgid "Bank" msgstr "" -#: apps/member/models.py:32 +#: apps/member/models.py:33 #: templates/registration/future_profile_detail.html:47 msgid "phone number" msgstr "" -#: apps/member/models.py:38 templates/member/profile_info.html:27 +#: apps/member/models.py:39 templates/member/profile_info.html:27 #: templates/registration/future_profile_detail.html:41 msgid "section" msgstr "" -#: apps/member/models.py:39 +#: apps/member/models.py:40 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "" -#: apps/member/models.py:45 templates/member/profile_info.html:30 +#: apps/member/models.py:46 templates/member/profile_info.html:30 #: templates/registration/future_profile_detail.html:44 msgid "address" msgstr "" -#: apps/member/models.py:51 +#: apps/member/models.py:52 #: templates/registration/future_profile_detail.html:50 msgid "paid" msgstr "" -#: apps/member/models.py:52 +#: apps/member/models.py:53 msgid "Tells if the user receive a salary." msgstr "" -#: apps/member/models.py:57 +#: apps/member/models.py:58 msgid "email confirmed" msgstr "" -#: apps/member/models.py:62 +#: apps/member/models.py:63 msgid "registration valid" msgstr "" -#: apps/member/models.py:67 +#: apps/member/models.py:68 msgid "Société générale" msgstr "" -#: apps/member/models.py:68 +#: apps/member/models.py:69 msgid "Has the user ever be paid by the Société générale?" msgstr "" -#: apps/member/models.py:73 apps/member/models.py:74 +#: apps/member/models.py:74 apps/member/models.py:75 msgid "user profile" msgstr "" -#: apps/member/models.py:105 templates/member/club_info.html:46 +#: apps/member/models.py:104 templates/member/club_info.html:46 #: templates/registration/future_profile_detail.html:22 msgid "email" msgstr "" -#: apps/member/models.py:112 +#: apps/member/models.py:111 msgid "parent club" msgstr "" -#: apps/member/models.py:121 +#: apps/member/models.py:120 msgid "require memberships" msgstr "" -#: apps/member/models.py:122 +#: apps/member/models.py:121 msgid "Uncheck if this club don't require memberships." msgstr "" -#: apps/member/models.py:127 templates/member/club_info.html:35 +#: apps/member/models.py:126 templates/member/club_info.html:35 msgid "membership fee (paid students)" msgstr "" -#: apps/member/models.py:132 templates/member/club_info.html:38 +#: apps/member/models.py:131 templates/member/club_info.html:38 msgid "membership fee (unpaid students)" msgstr "" -#: apps/member/models.py:138 templates/member/club_info.html:28 +#: apps/member/models.py:137 templates/member/club_info.html:28 msgid "membership duration" msgstr "" -#: apps/member/models.py:139 +#: apps/member/models.py:138 msgid "The longest time (in days) a membership can last (NULL = infinite)." msgstr "" -#: apps/member/models.py:146 templates/member/club_info.html:22 +#: apps/member/models.py:145 templates/member/club_info.html:22 msgid "membership start" msgstr "" -#: apps/member/models.py:147 +#: apps/member/models.py:146 msgid "How long after January 1st the members can renew their membership." msgstr "" -#: apps/member/models.py:154 templates/member/club_info.html:25 +#: apps/member/models.py:153 templates/member/club_info.html:25 msgid "membership end" msgstr "" -#: apps/member/models.py:155 +#: apps/member/models.py:154 msgid "" "How long the membership can last after January 1st of the next year after " "members can renew their membership." msgstr "" -#: apps/member/models.py:188 apps/member/models.py:230 +#: apps/member/models.py:187 apps/member/models.py:229 #: apps/note/models/notes.py:139 msgid "club" msgstr "" -#: apps/member/models.py:189 +#: apps/member/models.py:188 msgid "clubs" msgstr "" -#: apps/member/models.py:209 apps/permission/models.py:294 +#: apps/member/models.py:208 apps/permission/models.py:294 msgid "role" msgstr "" -#: apps/member/models.py:210 apps/member/models.py:235 +#: apps/member/models.py:209 apps/member/models.py:234 msgid "roles" msgstr "" -#: apps/member/models.py:240 +#: apps/member/models.py:239 msgid "membership starts on" msgstr "" -#: apps/member/models.py:244 +#: apps/member/models.py:243 msgid "membership ends on" msgstr "" -#: apps/member/models.py:249 +#: apps/member/models.py:248 msgid "fee" msgstr "" -#: apps/member/models.py:267 apps/member/views.py:491 +#: apps/member/models.py:266 apps/member/views.py:500 msgid "User is not a member of the parent club" msgstr "" -#: apps/member/models.py:277 apps/member/views.py:500 +#: apps/member/models.py:276 apps/member/views.py:509 msgid "User is already a member of the club" msgstr "" -#: apps/member/models.py:315 +#: apps/member/models.py:314 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "" -#: apps/member/models.py:318 +#: apps/member/models.py:317 msgid "membership" msgstr "" -#: apps/member/models.py:319 +#: apps/member/models.py:318 msgid "memberships" msgstr "" @@ -466,27 +466,27 @@ msgstr "" msgid "An alias with a similar name already exists." msgstr "" -#: apps/member/views.py:175 +#: apps/member/views.py:180 msgid "Search user" msgstr "" -#: apps/member/views.py:486 +#: apps/member/views.py:495 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." msgstr "" -#: apps/member/views.py:504 +#: apps/member/views.py:513 msgid "The membership must start after {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:509 +#: apps/member/views.py:518 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:519 apps/member/views.py:521 apps/member/views.py:523 -#: apps/registration/views.py:285 apps/registration/views.py:287 -#: apps/registration/views.py:289 +#: apps/member/views.py:528 apps/member/views.py:530 apps/member/views.py:532 +#: apps/registration/views.py:286 apps/registration/views.py:288 +#: apps/registration/views.py:290 msgid "This field is required." msgstr "" @@ -785,27 +785,27 @@ msgstr "" msgid "Email validation" msgstr "" -#: apps/registration/views.py:120 +#: apps/registration/views.py:121 msgid "Email validation unsuccessful" msgstr "" -#: apps/registration/views.py:131 +#: apps/registration/views.py:132 msgid "Email validation email sent" msgstr "" -#: apps/registration/views.py:184 +#: apps/registration/views.py:185 msgid "Unregistered users" msgstr "" -#: apps/registration/views.py:251 +#: apps/registration/views.py:252 msgid "You must join the BDE." msgstr "" -#: apps/registration/views.py:273 +#: apps/registration/views.py:274 msgid "You must join BDE club before joining Kfet club." msgstr "" -#: apps/registration/views.py:278 +#: apps/registration/views.py:279 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" @@ -1161,11 +1161,11 @@ msgstr "" msgid "club listing " msgstr "" -#: templates/member/club_tables.html:9 +#: templates/member/club_tables.html:6 msgid "Member of the Club" msgstr "" -#: templates/member/club_tables.html:22 templates/member/profile_tables.html:31 +#: templates/member/club_tables.html:17 templates/member/profile_tables.html:28 msgid "Transaction history" msgstr "" @@ -1219,7 +1219,7 @@ msgstr "" msgid "Click here to resend a validation link." msgstr "" -#: templates/member/profile_tables.html:18 +#: templates/member/profile_tables.html:15 msgid "View my memberships" msgstr "" @@ -1314,14 +1314,21 @@ msgstr "" msgid "Unable to delete button " msgstr "" -#: templates/registration/email_validation_complete.html:8 -#, python-format -msgid "" -"Your account have successfully been activated. You can now log in." +#: templates/registration/email_validation_complete.html:6 +msgid "Your email have successfully been validated." msgstr "" -#: templates/registration/email_validation_complete.html:12 +#: templates/registration/email_validation_complete.html:8 +#, python-format +msgid "You can now log in." +msgstr "" + +#: templates/registration/email_validation_complete.html:10 +msgid "" +"You must pay now your membership in the Kfet to complete your registration." +msgstr "" + +#: templates/registration/email_validation_complete.html:13 msgid "" "The link was invalid. The token may have expired. Please send us an email to " "activate your account." @@ -1376,6 +1383,36 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "" +#: templates/registration/mails/email_validation_email.html:3 +msgid "Hi" +msgstr "" + +#: templates/registration/mails/email_validation_email.html:5 +msgid "" +"You recently registered on the Note Kfet. Please click on the link below to " +"confirm your registration." +msgstr "" + +#: templates/registration/mails/email_validation_email.html:9 +msgid "" +"This link is only valid for a couple of days, after that you will need to " +"contact us to validate your email." +msgstr "" + +#: templates/registration/mails/email_validation_email.html:11 +msgid "" +"After that, you'll have to wait that someone validates your account before " +"you can log in. You will need to pay your membership in the Kfet." +msgstr "" + +#: templates/registration/mails/email_validation_email.html:13 +msgid "Thanks" +msgstr "" + +#: templates/registration/mails/email_validation_email.html:15 +msgid "The Note Kfet team." +msgstr "" + #: templates/registration/password_change_done.html:8 msgid "Your password was changed." msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index be518ebf..27636a65 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-06 10:51+0200\n" +"POT-Creation-Date: 2020-04-09 21:59+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -40,7 +40,7 @@ msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/activity/models.py:23 apps/activity/models.py:48 -#: apps/member/models.py:100 apps/member/models.py:203 +#: apps/member/models.py:99 apps/member/models.py:202 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 @@ -75,7 +75,7 @@ msgstr "description" msgid "type" msgstr "type" -#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:224 +#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:223 #: apps/note/models/notes.py:117 msgid "user" msgstr "utilisateur" @@ -296,102 +296,102 @@ msgstr "Montant à créditer" msgid "Bank" msgstr "Banque" -#: apps/member/models.py:32 +#: apps/member/models.py:33 #: templates/registration/future_profile_detail.html:47 msgid "phone number" msgstr "numéro de téléphone" -#: apps/member/models.py:38 templates/member/profile_info.html:27 +#: apps/member/models.py:39 templates/member/profile_info.html:27 #: templates/registration/future_profile_detail.html:41 msgid "section" msgstr "section" -#: apps/member/models.py:39 +#: apps/member/models.py:40 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" -#: apps/member/models.py:45 templates/member/profile_info.html:30 +#: apps/member/models.py:46 templates/member/profile_info.html:30 #: templates/registration/future_profile_detail.html:44 msgid "address" msgstr "adresse" -#: apps/member/models.py:51 +#: apps/member/models.py:52 #: templates/registration/future_profile_detail.html:50 msgid "paid" msgstr "payé" -#: apps/member/models.py:52 +#: apps/member/models.py:53 msgid "Tells if the user receive a salary." msgstr "Indique si l'utilisateur perçoit un salaire." -#: apps/member/models.py:57 +#: apps/member/models.py:58 msgid "email confirmed" msgstr "adresse email confirmée" -#: apps/member/models.py:62 +#: apps/member/models.py:63 msgid "registration valid" msgstr "inscription valid" -#: apps/member/models.py:67 +#: apps/member/models.py:68 msgid "Société générale" msgstr "Société générale" -#: apps/member/models.py:68 +#: apps/member/models.py:69 msgid "Has the user ever be paid by the Société générale?" msgstr "Est-ce que l'utilisateur a déjà été payé par la Société Générale ?" -#: apps/member/models.py:73 apps/member/models.py:74 +#: apps/member/models.py:74 apps/member/models.py:75 msgid "user profile" msgstr "profil utilisateur" -#: apps/member/models.py:105 templates/member/club_info.html:46 +#: apps/member/models.py:104 templates/member/club_info.html:46 #: templates/registration/future_profile_detail.html:22 msgid "email" msgstr "courriel" -#: apps/member/models.py:112 +#: apps/member/models.py:111 msgid "parent club" msgstr "club parent" -#: apps/member/models.py:121 +#: apps/member/models.py:120 msgid "require memberships" msgstr "nécessite des adhésions" -#: apps/member/models.py:122 +#: apps/member/models.py:121 msgid "Uncheck if this club don't require memberships." msgstr "Décochez si ce club n'utilise pas d'adhésions." -#: apps/member/models.py:127 templates/member/club_info.html:35 +#: apps/member/models.py:126 templates/member/club_info.html:35 msgid "membership fee (paid students)" msgstr "cotisation pour adhérer (normalien élève)" -#: apps/member/models.py:132 templates/member/club_info.html:38 +#: apps/member/models.py:131 templates/member/club_info.html:38 msgid "membership fee (unpaid students)" msgstr "cotisation pour adhérer (normalien étudiant)" -#: apps/member/models.py:138 templates/member/club_info.html:28 +#: apps/member/models.py:137 templates/member/club_info.html:28 msgid "membership duration" msgstr "durée de l'adhésion" -#: apps/member/models.py:139 +#: apps/member/models.py:138 msgid "The longest time (in days) a membership can last (NULL = infinite)." msgstr "La durée maximale (en jours) d'une adhésion (NULL = infinie)." -#: apps/member/models.py:146 templates/member/club_info.html:22 +#: apps/member/models.py:145 templates/member/club_info.html:22 msgid "membership start" msgstr "début de l'adhésion" -#: apps/member/models.py:147 +#: apps/member/models.py:146 msgid "How long after January 1st the members can renew their membership." msgstr "" "Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur " "adhésion." -#: apps/member/models.py:154 templates/member/club_info.html:25 +#: apps/member/models.py:153 templates/member/club_info.html:25 msgid "membership end" msgstr "fin de l'adhésion" -#: apps/member/models.py:155 +#: apps/member/models.py:154 msgid "" "How long the membership can last after January 1st of the next year after " "members can renew their membership." @@ -399,53 +399,53 @@ msgstr "" "Combien de temps l'adhésion peut durer après le 1er Janvier de l'année " "suivante avant que les adhérents peuvent renouveler leur adhésion." -#: apps/member/models.py:188 apps/member/models.py:230 +#: apps/member/models.py:187 apps/member/models.py:229 #: apps/note/models/notes.py:139 msgid "club" msgstr "club" -#: apps/member/models.py:189 +#: apps/member/models.py:188 msgid "clubs" msgstr "clubs" -#: apps/member/models.py:209 apps/permission/models.py:294 +#: apps/member/models.py:208 apps/permission/models.py:294 msgid "role" msgstr "rôle" -#: apps/member/models.py:210 apps/member/models.py:235 +#: apps/member/models.py:209 apps/member/models.py:234 msgid "roles" msgstr "rôles" -#: apps/member/models.py:240 +#: apps/member/models.py:239 msgid "membership starts on" msgstr "l'adhésion commence le" -#: apps/member/models.py:244 +#: apps/member/models.py:243 msgid "membership ends on" msgstr "l'adhésion finit le" -#: apps/member/models.py:249 +#: apps/member/models.py:248 msgid "fee" msgstr "cotisation" -#: apps/member/models.py:267 apps/member/views.py:491 +#: apps/member/models.py:266 apps/member/views.py:500 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" -#: apps/member/models.py:277 apps/member/views.py:500 +#: apps/member/models.py:276 apps/member/views.py:509 msgid "User is already a member of the club" msgstr "L'utilisateur est déjà membre du club" -#: apps/member/models.py:315 +#: apps/member/models.py:314 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Adhésion de {user} pour le club {club}" -#: apps/member/models.py:318 +#: apps/member/models.py:317 msgid "membership" msgstr "adhésion" -#: apps/member/models.py:319 +#: apps/member/models.py:318 msgid "memberships" msgstr "adhésions" @@ -466,11 +466,11 @@ msgstr "Modifier le profil" msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." -#: apps/member/views.py:175 +#: apps/member/views.py:180 msgid "Search user" msgstr "Chercher un utilisateur" -#: apps/member/views.py:486 +#: apps/member/views.py:495 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -478,17 +478,17 @@ msgstr "" "Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas " "avoir un solde négatif." -#: apps/member/views.py:504 +#: apps/member/views.py:513 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:509 +#: apps/member/views.py:518 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:519 apps/member/views.py:521 apps/member/views.py:523 -#: apps/registration/views.py:285 apps/registration/views.py:287 -#: apps/registration/views.py:289 +#: apps/member/views.py:528 apps/member/views.py:530 apps/member/views.py:532 +#: apps/registration/views.py:286 apps/registration/views.py:288 +#: apps/registration/views.py:290 msgid "This field is required." msgstr "Ce champ est requis." @@ -788,27 +788,27 @@ msgstr "Adhérer au club Kfet" msgid "Email validation" msgstr "Validation de l'adresse mail" -#: apps/registration/views.py:120 +#: apps/registration/views.py:121 msgid "Email validation unsuccessful" msgstr " La validation de l'adresse mail a échoué" -#: apps/registration/views.py:131 +#: apps/registration/views.py:132 msgid "Email validation email sent" msgstr "L'email de vérification de l'adresse email a bien été envoyé." -#: apps/registration/views.py:184 +#: apps/registration/views.py:185 msgid "Unregistered users" msgstr "Utilisateurs en attente d'inscription" -#: apps/registration/views.py:251 +#: apps/registration/views.py:252 msgid "You must join the BDE." msgstr "Vous devez adhérer au BDE." -#: apps/registration/views.py:273 +#: apps/registration/views.py:274 msgid "You must join BDE club before joining Kfet club." msgstr "Vous devez adhérer au club BDE avant d'adhérer au club Kfet." -#: apps/registration/views.py:278 +#: apps/registration/views.py:279 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" @@ -1168,11 +1168,11 @@ msgstr "Créer un club" msgid "club listing " msgstr "Liste des clubs" -#: templates/member/club_tables.html:9 +#: templates/member/club_tables.html:6 msgid "Member of the Club" msgstr "Membre du club" -#: templates/member/club_tables.html:22 templates/member/profile_tables.html:31 +#: templates/member/club_tables.html:17 templates/member/profile_tables.html:28 msgid "Transaction history" msgstr "Historique des transactions" @@ -1226,7 +1226,7 @@ msgstr "Cet utilisateur n'a pas encore confirmé son adresse e-mail." msgid "Click here to resend a validation link." msgstr "Cliquez ici pour renvoyer un lien de validation." -#: templates/member/profile_tables.html:18 +#: templates/member/profile_tables.html:15 msgid "View my memberships" msgstr "Voir mes adhésions" @@ -1321,18 +1321,28 @@ msgstr "Le bouton a bien été supprimé" msgid "Unable to delete button " msgstr "Impossible de supprimer le bouton " +#: templates/registration/email_validation_complete.html:6 +msgid "Your email have successfully been validated." +msgstr "Votre adresse e-mail a bien été validée." + #: templates/registration/email_validation_complete.html:8 #, python-format -msgid "" -"Your account have successfully been activated. You can now log in." -msgstr "" +msgid "You can now log in." +msgstr "Vous pouvez désormais vous connecter." -#: templates/registration/email_validation_complete.html:12 +#: templates/registration/email_validation_complete.html:10 +msgid "" +"You must pay now your membership in the Kfet to complete your registration." +msgstr "" +"Vous devez désormais payer votre adhésion à la Kfet pour compléter votre inscription." + +#: templates/registration/email_validation_complete.html:13 msgid "" "The link was invalid. The token may have expired. Please send us an email to " "activate your account." msgstr "" +"Le lien est invalide. Le jeton a sans doute expiré. Merci de nous contacter pour " +"activer votre compte." #: templates/registration/future_profile_detail.html:56 msgid "Delete registration" @@ -1383,9 +1393,41 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "" +#: templates/registration/mails/email_validation_email.html:3 +msgid "Hi" +msgstr "" + +#: templates/registration/mails/email_validation_email.html:5 +msgid "" +"You recently registered on the Note Kfet. Please click on the link below to " +"confirm your registration." +msgstr "" + +#: templates/registration/mails/email_validation_email.html:9 +msgid "" +"This link is only valid for a couple of days, after that you will need to " +"contact us to validate your email." +msgstr "" + +#: templates/registration/mails/email_validation_email.html:11 +msgid "" +"After that, you'll have to wait that someone validates your account before " +"you can log in. You will need to pay your membership in the Kfet." +msgstr "" +"Après cela, vous devrez attendre que quelqu'un valide votre compte avant " +"de pouvoir vous connecter. Vous devrez payer votre adhésion à la Kfet." + +#: templates/registration/mails/email_validation_email.html:13 +msgid "Thanks" +msgstr "Merci" + +#: templates/registration/mails/email_validation_email.html:15 +msgid "The Note Kfet team." +msgstr "L'équipe de la Note Kfet." + #: templates/registration/password_change_done.html:8 msgid "Your password was changed." -msgstr "" +msgstr "Votre mot de passe a bien été changé." #: templates/registration/password_change_form.html:9 msgid "" diff --git a/templates/registration/email_validation_complete.html b/templates/registration/email_validation_complete.html index 185fbfb0..4835cfa1 100644 --- a/templates/registration/email_validation_complete.html +++ b/templates/registration/email_validation_complete.html @@ -2,15 +2,14 @@ {% load i18n %} {% block content %} -

        {% trans Activation %}

        - -{% if validlink %} -{% blocktrans trimmed %} -Your account have successfully been activated. You can now log in. -{% endblocktrans %} -{% else %} -{% blocktrans trimmed %} -The link was invalid. The token may have expired. Please send us an email to activate your account. -{% endblocktrans %} -{% endif %} + {% if validlink %} + {% trans "Your email have successfully been validated." %} + {% if user.profile.registration_valid %} + {% blocktrans %}You can now log in.{% endblocktrans %} + {% else %} + {% trans "You must pay now your membership in the Kfet to complete your registration." %} + {% endif %} + {% else %} + {% trans "The link was invalid. The token may have expired. Please send us an email to activate your account." %} + {% endif %} {% endblock %} diff --git a/templates/registration/email_validation_email.html b/templates/registration/email_validation_email.html deleted file mode 100644 index feb46876..00000000 --- a/templates/registration/email_validation_email.html +++ /dev/null @@ -1,11 +0,0 @@ -Hi {{ user.username }}, - -Welcome to {{ site_name }}. Please click on the link below to confirm your registration. - -{{ protocol }}://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %} - -This link is only valid for a couple of days, after that you will need to contact us to validate your email. - -Thanks, - -{{ site_name }} team. diff --git a/templates/registration/mails/email_validation_email.html b/templates/registration/mails/email_validation_email.html new file mode 100644 index 00000000..577c1220 --- /dev/null +++ b/templates/registration/mails/email_validation_email.html @@ -0,0 +1,15 @@ +{% load i18n %} + +{% trans "Hi" %} {{ user.username }}, + +{% trans "You recently registered on the Note Kfet. Please click on the link below to confirm your registration." %} + +https://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %} + +{% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %} + +{% trans "After that, you'll have to wait that someone validates your account before you can log in. You will need to pay your membership in the Kfet." %} + +{% trans "Thanks" %}, + +{% trans "The Note Kfet team." %}