diff --git a/README.rst b/README.rst index 8717e7086..568439157 100644 --- a/README.rst +++ b/README.rst @@ -49,6 +49,12 @@ Orientações gerais sobre o GitHub + +Logo dos Partidos +=================================== + `Logo dos Partidos `_ + + Issues ------ diff --git a/docs/instacao31.rst b/docs/instacao31.rst index b9457c5b0..4307a5d6c 100644 --- a/docs/instacao31.rst +++ b/docs/instacao31.rst @@ -143,6 +143,9 @@ Criação da `SECRET_KEY =1.9,<1.10 -django-admin-bootstrapped==2.5.7 +# TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite +# a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma +# nova versão do django-admin-bootstrapped for lançada +git+git://github.com/django-admin-bootstrapped/django-admin-bootstrapped.git django-bootstrap3==7.0.1 django-bower==5.1.0 django-braces==1.9.0 diff --git a/sapl/base/admin.py b/sapl/base/admin.py index ae581e031..02ccd3c60 100644 --- a/sapl/base/admin.py +++ b/sapl/base/admin.py @@ -8,6 +8,9 @@ register_all_models_in_admin(__name__) admin.site.unregister(ProblemaMigracao) +admin.site.site_title = 'Administração - SAPL' +admin.site.site_header = 'Administração - SAPL' + @admin.register(ProblemaMigracao) class ProblemaMigracaoAdmin(admin.ModelAdmin): diff --git a/sapl/base/forms.py b/sapl/base/forms.py index ea30726ae..7f1eef180 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -4,8 +4,9 @@ from crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row from django import forms from django.conf import settings from django.contrib.auth import get_user_model -from django.contrib.auth.forms import AuthenticationForm -from django.contrib.auth.models import Group +from django.contrib.auth.forms import (AuthenticationForm, PasswordResetForm, + SetPasswordForm) +from django.contrib.auth.models import Group, User from django.contrib.auth.password_validation import validate_password from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError @@ -695,3 +696,42 @@ class ConfiguracoesAppForm(ModelForm): 'texto_articulado_materia', 'texto_articulado_norma', 'proposicao_incorporacao_obrigatoria'] + + +class RecuperarSenhaForm(PasswordResetForm): + def __init__(self, *args, **kwargs): + row1 = to_row( + [('email', 12)]) + self.helper = FormHelper() + self.helper.layout = Layout( + Fieldset(_('Insira o e-mail cadastrado com a sua conta'), + row1, + form_actions(save_label='Enviar')) + ) + + super(RecuperarSenhaForm, self).__init__(*args, **kwargs) + + def clean(self): + email_existente = User.objects.filter( + email=self.data['email']).exists() + + if not email_existente: + msg = 'Não existe nenhum usuário cadastrado com este e-mail.' + raise ValidationError(msg) + + return self.cleaned_data + + +class NovaSenhaForm(SetPasswordForm): + def __init__(self, user, *args, **kwargs): + self.user = user + super(NovaSenhaForm, self).__init__(user, *args, **kwargs) + + row1 = to_row( + [('new_password1', 6), + ('new_password2', 6)]) + + self.helper = FormHelper() + self.helper.layout = Layout( + row1, + form_actions(save_label='Enviar')) diff --git a/sapl/base/urls.py b/sapl/base/urls.py index c2a9fdc4a..08f456f3b 100644 --- a/sapl/base/urls.py +++ b/sapl/base/urls.py @@ -1,12 +1,17 @@ from django.conf.urls import include, url from django.contrib.auth import views +from django.contrib.auth.views import (password_reset, + password_reset_done, + password_reset_confirm, + password_reset_complete) from django.contrib.auth.decorators import permission_required from django.views.generic.base import TemplateView from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud from .apps import AppConfig -from .forms import LoginForm +from .forms import LoginForm, NovaSenhaForm, RecuperarSenhaForm +from sapl.settings import EMAIL_SEND_USER from .views import (AppConfigCrud, CasaLegislativaCrud, HelpView, RelatorioAtasView, RelatorioHistoricoTramitacaoView, RelatorioMateriasPorAnoAutorTipoView, @@ -16,6 +21,35 @@ from .views import (AppConfigCrud, CasaLegislativaCrud, HelpView, app_name = AppConfig.name +recuperar_senha = [ + url(r'^recuperar-senha/email/$', + password_reset, + {'post_reset_redirect': 'sapl.base:recuperar_senha_finalizado', + 'email_template_name': 'base/recuperar_senha_email.html', + 'html_email_template_name': 'base/recuperar_senha_email.html', + 'template_name': 'base/recuperar_senha_email_form.html', + 'from_email': EMAIL_SEND_USER, + 'password_reset_form': RecuperarSenhaForm}, + name='recuperar_senha_email'), + + url(r'^recuperar-senha/finalizado/$', + password_reset_done, + {'template_name': 'base/recupera_senha_email_enviado.html'}, + name='recuperar_senha_finalizado'), + + url(r'^recuperar-senha/(?P[0-9A-Za-z_\-]+)/(?P.+)/$', + password_reset_confirm, + {'post_reset_redirect': 'sapl.base:recuperar_senha_completo', + 'template_name': 'base/nova_senha_form.html', + 'set_password_form': NovaSenhaForm}, + name='recuperar_senha_confirma'), + + url(r'^recuperar-senha/completo/$', + password_reset_complete, + {'template_name': 'base/recuperar_senha_completo.html'}, + name='recuperar_senha_completo'), +] + urlpatterns = [ url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())), @@ -66,4 +100,4 @@ urlpatterns = [ name='login'), url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'), -] +] + recuperar_senha diff --git a/sapl/materia/tests/test_materia.py b/sapl/materia/tests/test_materia.py index a8aaaf2ec..4f88a8b79 100644 --- a/sapl/materia/tests/test_materia.py +++ b/sapl/materia/tests/test_materia.py @@ -159,6 +159,7 @@ def test_despacho_inicial_submit(admin_client): comissao = mommy.make(Comissao, tipo=tipo_comissao, nome='Teste', + ativa=True, sigla='T', data_criacao='2016-03-18') @@ -172,6 +173,7 @@ def test_despacho_inicial_submit(admin_client): # Verifica se o despacho foi criado despacho = DespachoInicial.objects.first() + assert despacho.comissao == comissao assert despacho.materia == materia_principal diff --git a/sapl/parlamentares/models.py b/sapl/parlamentares/models.py index 2703b2416..ea8c972c9 100644 --- a/sapl/parlamentares/models.py +++ b/sapl/parlamentares/models.py @@ -86,7 +86,15 @@ class Coligacao(models.Model): def __str__(self): return self.nome + +def get_logo_media_path(instance, subpath, filename): + return './sapl/partidos/%s/%s/%s' % (instance, subpath, filename) + +def logo_upload_path(instance, filename): + return get_logo_media_path(instance, 'logo', filename) + + class Partido(models.Model): sigla = models.CharField(max_length=9, verbose_name=_('Sigla')) nome = models.CharField(max_length=50, verbose_name=_('Nome')) @@ -94,6 +102,12 @@ class Partido(models.Model): blank=True, null=True, verbose_name=_('Data Criação')) data_extincao = models.DateField( blank=True, null=True, verbose_name=_('Data Extinção')) + logo_partido = models.ImageField( + blank=True, + null=True, + upload_to=logo_upload_path, + verbose_name=_('Logo Partido'), + validators=[restringe_tipos_de_arquivo_img]) class Meta: verbose_name = _('Partido') @@ -294,7 +308,7 @@ class Parlamentar(models.Model): @property def avatar_html(self): return ''if self.fotografia else '' + + self.fotografia.url + '>'if self.fotografia else '' class TipoDependente(models.Model): diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index d069e5ce9..f0856330c 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -93,7 +93,7 @@ class ExpedienteMateriaForm(ModelForm): return self.cleaned_data['numero_ordem'] def clean_data_ordem(self): - return datetime.now() + return self.instance.sessao_plenaria.data_inicio def clean(self): cleaned_data = self.cleaned_data @@ -136,7 +136,7 @@ class OrdemDiaForm(ExpedienteMateriaForm): 'numero_materia', 'ano_materia', 'tipo_votacao'] def clean_data_ordem(self): - return datetime.now() + return self.instance.sessao_plenaria.data_inicio def clean(self): cleaned_data = self.cleaned_data diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 99a992bba..3ccd0f4b0 100644 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -133,6 +133,11 @@ class MateriaOrdemDiaCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = OrdemDiaForm + def get_initial(self): + self.initial['data_ordem'] = SessaoPlenaria.objects.get( + pk=self.kwargs['pk']).data_inicio.strftime('%d/%m/%Y') + return self.initial + def get_success_url(self): return reverse('sapl.sessao:ordemdia_list', kwargs={'pk': self.kwargs['pk']}) @@ -331,6 +336,11 @@ class ExpedienteMateriaCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = ExpedienteMateriaForm + def get_initial(self): + self.initial['data_ordem'] = SessaoPlenaria.objects.get( + pk=self.kwargs['pk']).data_inicio.strftime('%d/%m/%Y') + return self.initial + def get_success_url(self): return reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': self.kwargs['pk']}) diff --git a/sapl/settings.py b/sapl/settings.py index 3a2212de2..bcf61fd56 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -167,6 +167,8 @@ EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='') EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='') EMAIL_USE_TLS = config('EMAIL_USE_TLS', cast=bool, default=True) EMAIL_SEND_USER = config('EMAIL_SEND_USER', cast=str, default='') +DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', cast=str, default='') +SERVER_EMAIL = config('SERVER_EMAIL', cast=str, default='') MAX_DOC_UPLOAD_SIZE = 5 * 1024 * 1024 # 5MB MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB diff --git a/sapl/templates/base/login.html b/sapl/templates/base/login.html index 78450f96a..c2c7aa2c6 100644 --- a/sapl/templates/base/login.html +++ b/sapl/templates/base/login.html @@ -18,7 +18,7 @@