Browse Source

Add procedimento na alteração de username de Autor

Na edição de Autores foi adicionado o tratamento por opção do usuário do
que deve ser feito com o usuário que está sendo desvinculado no caso de
uma alteração do username de um Autor.

Foram dadas três opções:

1) Apenas retirar Perfil de Autor do Usuário que está sendo desvinculado
2) Retirar Perfil de Autor e desativar Usuário que está sendo desvinculado
3) Excluir Usuário
pull/739/head
LeandroRoberto 8 years ago
parent
commit
9db646df86
  1. 84
      sapl/base/forms.py
  2. 6
      sapl/materia/views.py
  3. 5
      sapl/templates/base.html
  4. 46
      sapl/templates/base/autor_form.html
  5. 15
      sapl/templates/bootstrap3/layout/radioselect.html
  6. 12
      sapl/templates/bootstrap3/layout/radioselect_inline.html
  7. 12
      scripts/inicializa_grupos_autorizacoes.py

84
sapl/base/forms.py

@ -12,7 +12,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models, transaction from django.db import models, transaction
from django.forms import ModelForm from django.forms import ModelForm
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _, string_concat
import django_filters import django_filters
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
@ -34,6 +34,15 @@ ACTION_CREATE_USERS_AUTOR_CHOICE = [
] ]
STATUS_USER_CHOICE = [
('R', _('Apenas retirar Perfil de Autor do Usuário que está sendo'
' desvinculado')),
('D', _('Retirar Perfil de Autor e desativar Usuário que está sendo'
' desvinculado')),
('X', _('Excluir Usuário')),
]
class TipoAutorForm(ModelForm): class TipoAutorForm(ModelForm):
content_type = forms.ModelChoiceField( content_type = forms.ModelChoiceField(
@ -97,7 +106,8 @@ class AutorForm(ModelForm):
required=False, required=False,
label=_('Confirmar Email')) label=_('Confirmar Email'))
username = forms.CharField( username = forms.CharField(label=get_user_model()._meta.get_field(
'username').verbose_name.capitalize(),
required=False, required=False,
max_length=50) max_length=50)
@ -111,10 +121,19 @@ class AutorForm(ModelForm):
widget=forms.RadioSelect()) widget=forms.RadioSelect())
action_user = forms.ChoiceField( action_user = forms.ChoiceField(
label=_('Usuário de acesso ao Sistema para este Autor'), label=_('Usuário com acesso ao Sistema para este Autor'),
choices=ACTION_CREATE_USERS_AUTOR_CHOICE, choices=ACTION_CREATE_USERS_AUTOR_CHOICE,
widget=forms.RadioSelect()) widget=forms.RadioSelect())
status_user = forms.ChoiceField(
label=_('Bloqueio do Usuário Existente'),
choices=STATUS_USER_CHOICE,
widget=forms.RadioSelect(),
required=False,
help_text=_('Se vc está trocando ou removendo o usuário deste Autor, '
'como o Sistema deve proceder com o usuário que está sendo'
' desvinculado?'))
class Meta: class Meta:
model = Autor model = Autor
fields = ['tipo', fields = ['tipo',
@ -150,16 +169,19 @@ class AutorForm(ModelForm):
12))) 12)))
row2 = Row(to_column((InlineRadios('action_user'), 8)), row2 = Row(to_column((InlineRadios('action_user'), 8)),
to_column(('username', 4))) to_column((Div('username'), 4)))
row3 = Row(to_column(('senha', 3)), row3 = Row(to_column(('senha', 3)),
to_column(('senha_confirma', 3)), to_column(('senha_confirma', 3)),
to_column(('email', 3)), to_column(('email', 3)),
to_column(('confirma_email', 3)), to_column(('confirma_email', 3)),
css_class='new_user_fields hidden') css_class='new_user_fields hidden')
row4 = Row(to_column((Div(InlineRadios('status_user'),
css_class='radiogroup-status hidden'), 12)))
controle_acesso = Fieldset( controle_acesso = Fieldset(
_('Controle de Acesso do Autor'), _('Controle de Acesso do Autor'),
row2, row3 row2, row3, row4
) )
self.helper = FormHelper() self.helper = FormHelper()
@ -168,7 +190,6 @@ class AutorForm(ModelForm):
super(AutorForm, self).__init__(*args, **kwargs) super(AutorForm, self).__init__(*args, **kwargs)
self.fields['action_user'].initial = 'N' self.fields['action_user'].initial = 'N'
self.fields['action_user'].inline_class = True
if self.instance.pk: if self.instance.pk:
if self.instance.autor_related: if self.instance.autor_related:
@ -180,6 +201,13 @@ class AutorForm(ModelForm):
if self.instance.user: if self.instance.user:
self.fields['username'].initial = self.instance.user.username self.fields['username'].initial = self.instance.user.username
self.fields['action_user'].initial = 'A' self.fields['action_user'].initial = 'A'
self.fields['status_user'].initial = 'R'
self.fields['username'].label = string_concat(
self.fields['username'].label,
' (', self.instance.user.username, ')')
self.fields['username'].widget.attrs.update({
'data': self.instance.user.username
if self.instance.user else ''})
def valida_igualdade(self, texto1, texto2, msg): def valida_igualdade(self, texto1, texto2, msg):
if texto1 != texto2: if texto1 != texto2:
@ -190,12 +218,20 @@ class AutorForm(ModelForm):
User = get_user_model() User = get_user_model()
cd = self.cleaned_data cd = self.cleaned_data
if 'username' not in cd: if 'username' not in cd or not cd['username']:
raise ValidationError(_('O username deve ser informado.')) raise ValidationError(_('O username deve ser informado.'))
if 'action_user' not in cd: if 'action_user' not in cd or not cd['action_user']:
raise ValidationError(_('Informe se o Autor terá usuário ' raise ValidationError(_('Informe se o Autor terá usuário '
'para acesso ao Sistema.')) 'vinculado para acesso ao Sistema.'))
if self.instance.pk and self.instance.user_id:
if self.instance.user.username != cd['username']:
if 'status_user' not in cd or cd['status_user']:
raise ValidationError(
_('Foi trocado ou removido o usuário deste Autor, '
'mas não foi informado com se deve proceder com o '
'usuário que está sendo desvinculado?'))
qs_user = User.objects.all() qs_user = User.objects.all()
qs_autor = Autor.objects.all() qs_autor = Autor.objects.all()
@ -209,7 +245,7 @@ class AutorForm(ModelForm):
if User.objects.filter(username=cd['username']).exists(): if User.objects.filter(username=cd['username']).exists():
raise ValidationError( raise ValidationError(
_('Já existe usuário com o username "%s". ' _('Já existe usuário com o username "%s". '
'Para usá-lo você deve selecionar ' 'Para utilizar esse username você deve selecionar '
'"Associar um usuário existente".') % cd['username']) '"Associar um usuário existente".') % cd['username'])
if ('senha' not in cd or 'senha_confirma' not in cd or if ('senha' not in cd or 'senha_confirma' not in cd or
@ -242,7 +278,7 @@ class AutorForm(ModelForm):
if not User.objects.filter(username=cd['username']).exists(): if not User.objects.filter(username=cd['username']).exists():
raise ValidationError( raise ValidationError(
_('Não existe usuário com username "%s". ' _('Não existe usuário com username "%s". '
'Para usá-lo você deve selecionar ' 'Para utilizar esse username você deve selecionar '
'"Criar novo Usuário".') % cd['username']) '"Criar novo Usuário".') % cd['username'])
if cd['action_user'] != 'N': if cd['action_user'] != 'N':
@ -292,10 +328,10 @@ class AutorForm(ModelForm):
user_old = autor.user if autor.user_id else None user_old = autor.user if autor.user_id else None
u = None
if self.cleaned_data['action_user'] == 'A': if self.cleaned_data['action_user'] == 'A':
u = get_user_model().objects.get( u = get_user_model().objects.get(
username=self.cleaned_data['username']) username=self.cleaned_data['username'])
autor.user = u
elif self.cleaned_data['action_user'] == 'C': elif self.cleaned_data['action_user'] == 'C':
u = get_user_model().objects.create( u = get_user_model().objects.create(
username=self.cleaned_data['username'], username=self.cleaned_data['username'],
@ -305,7 +341,7 @@ class AutorForm(ModelForm):
# pode logar sem a necessidade de passar pela validação de email # pode logar sem a necessidade de passar pela validação de email
u.is_active = settings.DEBUG u.is_active = settings.DEBUG
u.save() u.save()
autor.user = u autor.user = u
if not autor.tipo.content_type: if not autor.tipo.content_type:
autor.content_type = None autor.content_type = None
@ -318,15 +354,29 @@ class AutorForm(ModelForm):
autor.save() autor.save()
# FIXME melhorar captura de grupo de Autor, levando em conta,
# no mínimo, a tradução.
grupo = Group.objects.filter(name='Autor')[0]
if self.cleaned_data['action_user'] != 'N': if self.cleaned_data['action_user'] != 'N':
# FIXME melhorar captura de grupo de Autor, levando em conta
# tradução
grupo = Group.objects.filter(name='Autor')[0]
autor.user.groups.add(grupo) autor.user.groups.add(grupo)
if user_old and user_old != autor.user: if user_old and user_old != autor.user:
user_old.groups.remove(grupo) user_old.groups.remove(grupo)
else:
if 'status_user' in self.cleaned_data and user_old:
if self.cleaned_data['status_user'] == 'X':
user_old.delete()
elif self.cleaned_data['status_user'] == 'D':
user_old.groups.remove(grupo)
user_old.is_active = False
user_old.save()
elif self.cleaned_data['status_user'] == 'R':
user_old.groups.remove(grupo)
else:
user_old.groups.remove(grupo)
return autor return autor

6
sapl/materia/views.py

@ -313,6 +313,12 @@ class ConfirmarProposicao(PermissionRequiredMixin, CreateView):
class ProposicaoCrud(Crud): class ProposicaoCrud(Crud):
"""
TODO: Entre outros comportamento gerais, mesmo que um usuário tenha
Perfil de Autor o Crud de proposição não deverá permitir acesso a
proposições. O acesso deve ser permitido se existe um Autor registrado
e vinculado ao usuário. Essa tarefa deve ser realizada nas Tabelas Aux.
"""
model = Proposicao model = Proposicao
help_path = '' help_path = ''

5
sapl/templates/base.html

@ -277,6 +277,9 @@
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" src="{% static 'jquery/dist/jquery.min.js' %}"></script> <script type="text/javascript" src="{% static 'jquery/dist/jquery.min.js' %}"></script>
<script type="text/javascript" src="{% static 'bootstrap-sass/assets/javascripts/bootstrap.min.js' %}"></script> <script type="text/javascript" src="{% static 'bootstrap-sass/assets/javascripts/bootstrap.min.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/checkbox.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/radio.js' %}"></script>
<script type="text/javascript" src="{% static 'jquery-ui/jquery-ui.min.js' %}"></script> <script type="text/javascript" src="{% static 'jquery-ui/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'jquery-ui/ui/i18n/datepicker-pt-BR.js' %}"></script> <script type="text/javascript" src="{% static 'jquery-ui/ui/i18n/datepicker-pt-BR.js' %}"></script>
@ -284,8 +287,6 @@
<script type="text/javascript" src="{% static 'js/jquery.runner.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery.runner.js' %}"></script>
<script type="text/javascript" src="{% static 'jquery-mask-plugin/dist/jquery.mask.js' %}"></script> <script type="text/javascript" src="{% static 'jquery-mask-plugin/dist/jquery.mask.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/checkbox.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/radio.js' %}"></script>
<script src="{% static 'tinymce/tinymce.min.js' %}"></script> <script src="{% static 'tinymce/tinymce.min.js' %}"></script>
<script type="text/javascript" src="{% static 'jsdiff/diff.min.js' %}"></script> <script type="text/javascript" src="{% static 'jsdiff/diff.min.js' %}"></script>

46
sapl/templates/base/autor_form.html

@ -5,6 +5,11 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function(){ $(document).ready(function(){
var flag_create = false;
if (location.href.indexOf('create') > 0) {
$('.radiogroup-status').remove();
flag_create = true
}
var active = function(str) { var active = function(str) {
if (str == 'nome') { if (str == 'nome') {
$('#id_nome, #id_q').val(''); $('#id_nome, #id_q').val('');
@ -77,14 +82,49 @@ $(document).ready(function(){
update_search(pk); update_search(pk);
}); });
$('input[name=action_user]').change(function(event) { $('input[name=action_user]').change(function(event) {
if (event.target.value == 'C') if (!this.checked)
return;
$('#div_id_username input').prop('readonly', '');
if (event.target.value == 'C') {
$('.new_user_fields, #div_id_username').removeClass('hidden'); $('.new_user_fields, #div_id_username').removeClass('hidden');
else if (event.target.value == 'N') $('input[name=username]').val('');
$('.new_user_fields, #div_id_username').addClass('hidden'); }
else if (event.target.value == 'N') {
$('.new_user_fields').addClass('hidden');
if ($('input[name=username]').attr('data') != '')
$('.radiogroup-status').removeClass('hidden');
if (flag_create) {
$('#div_id_username').addClass('hidden');
}
else {
$('#div_id_username input').prop('readonly', 'readonly');
}
}
else { else {
$('.radiogroup-status').addClass('hidden');
$('#div_id_username').removeClass('hidden'); $('#div_id_username').removeClass('hidden');
$('.new_user_fields').addClass('hidden'); $('.new_user_fields').addClass('hidden');
} }
if (!flag_create) {
var username = $('input[name=username]');
if (username.length == 1) {
if ((event.target.value == 'A' && username.attr('data') != '' && username[0].value != username.attr('data'))
|| (event.target.value == 'C' && username.attr('data') != ''))
$('.radiogroup-status').removeClass('hidden');
}
}
});
$('input[name=username]').keyup(function(event) {
if (!flag_create)
if (this.getAttribute('data') != '' && this.value != this.getAttribute('data'))
$('.radiogroup-status').removeClass('hidden');
else
$('.radiogroup-status').addClass('hidden');
}); });
$('input[name=action_user]:checked').trigger('change'); $('input[name=action_user]:checked').trigger('change');

15
sapl/templates/bootstrap3/layout/radioselect.html

@ -3,16 +3,15 @@
<div class="controls controls-radio {{ field_class }}"{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}> <div class="controls controls-radio {{ field_class }}"{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}>
{% include 'bootstrap3/layout/field_errors_block.html' %} {% include 'bootstrap3/layout/field_errors_block.html' %}
{% for choice in field.field.choices %} {% for choice in field.field.choices %}
<label class="radio{% if field.field.inline_class %} radio-inline{% endif %}{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked{% endif %}" for="id_{{ field.html_name }}_{{ forloop.counter }}">
<span class="icons">
<span class="first-icon"></span>
<span class="second-icon"></span>
</span>
<input type="radio"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }} <label class="radio {% if inline_class %}radio-{{ inline_class }}{% endif %}{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked{% endif %}" for="id_{{ field.html_name }}_{{ forloop.counter }}">
</label> <span class="icons">
<span class="first-icon"></span>
<span class="second-icon"></span>
</span>
<input type="radio"{% if choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
</label>
{% endfor %} {% endfor %}
{% include 'bootstrap3/layout/help_text.html' %} {% include 'bootstrap3/layout/help_text.html' %}

12
sapl/templates/bootstrap3/layout/radioselect_inline.html

@ -0,0 +1,12 @@
{% if field.is_hidden %}
{{ field }}
{% else %}
<div id="div_{{ field.auto_id }}" class="form-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors and field.errors %} has-error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label %}
<label for="{{ field.auto_id }}" class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}
{% include 'bootstrap3/layout/radioselect.html' %}
</div>
{% endif %}

12
scripts/inicializa_grupos_autorizacoes.py

@ -120,8 +120,16 @@ class InicializaGruposAutorizacoes():
for p in perms_autor: for p in perms_autor:
grupo.permissions.add(p) grupo.permissions.add(p)
nome_usuario = 'operador_autor' """
self.cria_usuario(nome_usuario, grupo) Mesmo para teste, um usuário com perfil Autor criado via /admin
não deverá ser criado pois esse é um papel do operador_geral fazer
nas tabelas auxiliares.
A tentativa de acesso a qualquer container (hoje apenas proposições)
do SAPL de Usuários com perfil Autor mas sem um Autor cadastrado
nas tabelas auxiliares será negado e notificado via front-end.
"""
# nome_usuario = 'operador_autor'
# self.cria_usuario(nome_usuario, grupo)
def __call__(self): def __call__(self):
self.cria_grupos_permissoes() self.cria_grupos_permissoes()

Loading…
Cancel
Save