Commit 726a3044bcdf1e7d568b72dec0f8acbe1b1a7d9d
Exists in
master
and in
5 other branches
Merge branch 'dev' of https://github.com/amadeusproject/amadeuslms into dev
Showing
40 changed files
with
934 additions
and
294 deletions
Show diff stats
core/context_processors.py
@@ -5,6 +5,6 @@ def notifications(request): | @@ -5,6 +5,6 @@ def notifications(request): | ||
5 | context['notifications'] = None | 5 | context['notifications'] = None |
6 | if request.user.is_authenticated: | 6 | if request.user.is_authenticated: |
7 | return { | 7 | return { |
8 | - 'notifications': Notification.objects.filter(user= request.user, read=False).order_by('-datetime') | 8 | + 'notifications': Notification.objects.filter(user= request.user, read=False).order_by('-datetime')[0:5] |
9 | } | 9 | } |
10 | return context | 10 | return context |
core/forms.py
@@ -15,31 +15,7 @@ class RegisterUserForm(forms.ModelForm): | @@ -15,31 +15,7 @@ class RegisterUserForm(forms.ModelForm): | ||
15 | 15 | ||
16 | def validate_cpf(self, cpf): | 16 | def validate_cpf(self, cpf): |
17 | cpf = ''.join(re.findall('\d', str(cpf))) | 17 | cpf = ''.join(re.findall('\d', str(cpf))) |
18 | - # print(cpf) | ||
19 | - | ||
20 | - # if (not cpf) or (len(cpf) < 11): | ||
21 | - # return False | ||
22 | - | ||
23 | - # #Get only the first 9 digits and generate other 2 | ||
24 | - # _int = map(int, cpf) | ||
25 | - # integer = list(map(int, cpf)) | ||
26 | - # new = integer[:9] | ||
27 | - | ||
28 | - # while len(new) < 11: | ||
29 | - # r = sum([(len(new) + 1 - i)* v for i, v in enumerate(new)]) % 11 | ||
30 | - | ||
31 | - # if r > 1: | ||
32 | - # f = 11 - r | ||
33 | - # else: | ||
34 | - # f = 0 | ||
35 | - # new.append(f) | ||
36 | - | ||
37 | - # #if generated number is the same(original) the cpf is valid | ||
38 | - # new2 = list(new) | ||
39 | - # if new2 == _int: | ||
40 | - # return cpf | ||
41 | - # else: | ||
42 | - # return False | 18 | + |
43 | if cpfcnpj.validate(cpf): | 19 | if cpfcnpj.validate(cpf): |
44 | return True | 20 | return True |
45 | return False | 21 | return False |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2016-09-30 04:24 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +import autoslug.fields | ||
6 | +from django.db import migrations | ||
7 | + | ||
8 | + | ||
9 | +class Migration(migrations.Migration): | ||
10 | + | ||
11 | + dependencies = [ | ||
12 | + ('core', '0001_initial'), | ||
13 | + ] | ||
14 | + | ||
15 | + operations = [ | ||
16 | + migrations.AlterField( | ||
17 | + model_name='resource', | ||
18 | + name='slug', | ||
19 | + field=autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug'), | ||
20 | + ), | ||
21 | + ] |
core/models.py
@@ -6,20 +6,20 @@ from autoslug.fields import AutoSlugField | @@ -6,20 +6,20 @@ from autoslug.fields import AutoSlugField | ||
6 | 6 | ||
7 | class Action(models.Model): | 7 | class Action(models.Model): |
8 | """ | 8 | """ |
9 | - It represents an Action on the program by a User such as "create post", | 9 | + It represents an Action on the program by a User such as "create post", |
10 | "visualize post", etc. It is supposed to be created everytime we want an aciton | 10 | "visualize post", etc. It is supposed to be created everytime we want an aciton |
11 | """ | 11 | """ |
12 | 12 | ||
13 | name = models.CharField(_('Name'), max_length = 100) | 13 | name = models.CharField(_('Name'), max_length = 100) |
14 | created_date = models.DateField(_('Created Date'), auto_now_add=True) | 14 | created_date = models.DateField(_('Created Date'), auto_now_add=True) |
15 | - | 15 | + |
16 | class Meta: | 16 | class Meta: |
17 | verbose_name = "Action" | 17 | verbose_name = "Action" |
18 | verbose_name_plural = "Actions" | 18 | verbose_name_plural = "Actions" |
19 | 19 | ||
20 | def __str__(self): | 20 | def __str__(self): |
21 | return self.name | 21 | return self.name |
22 | - | 22 | + |
23 | 23 | ||
24 | class Resource(models.Model): | 24 | class Resource(models.Model): |
25 | """ | 25 | """ |
@@ -27,38 +27,38 @@ class Resource(models.Model): | @@ -27,38 +27,38 @@ class Resource(models.Model): | ||
27 | Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF). | 27 | Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF). |
28 | 28 | ||
29 | Attributes: | 29 | Attributes: |
30 | - @name: name of the resource affected, it will be unique because a resource can be affecte | 30 | + @name: name of the resource affected, it will be unique because a resource can be affecte |
31 | by a huge amount of actions | 31 | by a huge amount of actions |
32 | @created_date: The date the resource was created | 32 | @created_date: The date the resource was created |
33 | @link: Which URL made that resource able to find | 33 | @link: Which URL made that resource able to find |
34 | """ | 34 | """ |
35 | 35 | ||
36 | name = models.CharField(_('Name'), max_length =100) | 36 | name = models.CharField(_('Name'), max_length =100) |
37 | - slug = AutoSlugField(_("Slug"), populate_from='name', unique=True, null=True) | 37 | + slug = AutoSlugField(_("Slug"), populate_from='name', unique=True) |
38 | created_date = models.DateField(_('Created Date'), auto_now_add=True) | 38 | created_date = models.DateField(_('Created Date'), auto_now_add=True) |
39 | url = models.CharField(_('URL'), max_length =100, default="") | 39 | url = models.CharField(_('URL'), max_length =100, default="") |
40 | 40 | ||
41 | 41 | ||
42 | class Meta: | 42 | class Meta: |
43 | verbose_name = "Resource" | 43 | verbose_name = "Resource" |
44 | - verbose_name_plural = "Resources" | 44 | + verbose_name_plural = "Resources" |
45 | 45 | ||
46 | def __str__(self): | 46 | def __str__(self): |
47 | return self.name | 47 | return self.name |
48 | 48 | ||
49 | 49 | ||
50 | class Action_Resource(models.Model): | 50 | class Action_Resource(models.Model): |
51 | - | 51 | + |
52 | action = models.ForeignKey(Action , verbose_name= _('Action_Applied')) | 52 | action = models.ForeignKey(Action , verbose_name= _('Action_Applied')) |
53 | resource = models.ForeignKey(Resource, verbose_name = _('Resource')) | 53 | resource = models.ForeignKey(Resource, verbose_name = _('Resource')) |
54 | - | 54 | + |
55 | class Meta: | 55 | class Meta: |
56 | verbose_name = "Action_Resource" | 56 | verbose_name = "Action_Resource" |
57 | verbose_name_plural = "Action_Resources" | 57 | verbose_name_plural = "Action_Resources" |
58 | 58 | ||
59 | def __str__(self): | 59 | def __str__(self): |
60 | return ''.join([self.action.name, " / ", self.resource.name]) | 60 | return ''.join([self.action.name, " / ", self.resource.name]) |
61 | - | 61 | + |
62 | 62 | ||
63 | class Notification(models.Model): | 63 | class Notification(models.Model): |
64 | """ | 64 | """ |
@@ -77,7 +77,7 @@ class Notification(models.Model): | @@ -77,7 +77,7 @@ class Notification(models.Model): | ||
77 | datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True) | 77 | datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True) |
78 | action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource')) | 78 | action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource')) |
79 | actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True) | 79 | actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True) |
80 | - | 80 | + |
81 | class Meta: | 81 | class Meta: |
82 | verbose_name = _("Notification") | 82 | verbose_name = _("Notification") |
83 | verbose_name_plural = _("Notifications") | 83 | verbose_name_plural = _("Notifications") |
core/static/css/base/amadeus.css
@@ -266,7 +266,6 @@ li.alert_li:hover{background-color:#eee} | @@ -266,7 +266,6 @@ li.alert_li:hover{background-color:#eee} | ||
266 | a.alert_message{color : grey} | 266 | a.alert_message{color : grey} |
267 | a.alert_message:hover{color : grey} | 267 | a.alert_message:hover{color : grey} |
268 | 268 | ||
269 | -/*=================== Ailson - Please Don't touch*/ | ||
270 | .breadcrumb .divider{ | 269 | .breadcrumb .divider{ |
271 | display: none; | 270 | display: none; |
272 | } | 271 | } |
core/static/js/base/amadeus.js
@@ -126,4 +126,18 @@ function validarCpfSemAlert(campo,nome,idElementoMensagemErro){ | @@ -126,4 +126,18 @@ function validarCpfSemAlert(campo,nome,idElementoMensagemErro){ | ||
126 | return true; | 126 | return true; |
127 | } | 127 | } |
128 | return retorno; | 128 | return retorno; |
129 | -} | ||
130 | \ No newline at end of file | 129 | \ No newline at end of file |
130 | +} | ||
131 | + | ||
132 | +/* | ||
133 | +This functions get the next 5 notifications from the user given a "step"(an amount) of previous notifications | ||
134 | +*/ | ||
135 | +function getNotifications(step){ | ||
136 | + $.get('/getNotifications', | ||
137 | + {'steps':step, 'amount': 5}, function(data){ | ||
138 | + $("#notification-dropdown").append(data); | ||
139 | + $('#notification-see-more').remove(); | ||
140 | + var seemore = '<li><a onclick="getNotifications('+(step+5)+')"> <div id="notification-see-more" class="list-group-item"> <div class="row-content"><p class="list-group-item-text">See More</p> </div> </a></li>'; | ||
141 | + $("#notification-dropdown").append(seemore); | ||
142 | + $("#notification-count").text(step+5); | ||
143 | + }); | ||
144 | +} |
core/static/js/base/header.js
1 | $(document).ready(function(){ | 1 | $(document).ready(function(){ |
2 | $('[data-toggle="tooltip"]').tooltip(); //activate tooltip on all elements that has attribute data-toggle | 2 | $('[data-toggle="tooltip"]').tooltip(); //activate tooltip on all elements that has attribute data-toggle |
3 | -}); | ||
4 | \ No newline at end of file | 3 | \ No newline at end of file |
4 | +}); | ||
5 | + | ||
6 | + | ||
7 | +/* | ||
8 | + | ||
9 | +*/ | ||
10 | +function getNotifications(step){ | ||
11 | + $.ajax('/getNotifications',{ | ||
12 | + steps: step, | ||
13 | + amount: 5, | ||
14 | + sucess: function(response){ | ||
15 | + | ||
16 | + } | ||
17 | + }); | ||
18 | +} | ||
19 | + | ||
20 | + | ||
21 | +/* | ||
22 | + | ||
23 | +*/ | ||
24 | +function checkIfNewNotification(){ | ||
25 | + | ||
26 | +} | ||
5 | \ No newline at end of file | 27 | \ No newline at end of file |
core/templates/base.html
@@ -38,6 +38,7 @@ | @@ -38,6 +38,7 @@ | ||
38 | <script type="text/javascript">$.material.init()</script> | 38 | <script type="text/javascript">$.material.init()</script> |
39 | 39 | ||
40 | <!--Javascript block for specific-app ones --> | 40 | <!--Javascript block for specific-app ones --> |
41 | + <script src="{% static 'js/base/amadeus.js' %}"></script> | ||
41 | {% block style %} | 42 | {% block style %} |
42 | {% endblock %} | 43 | {% endblock %} |
43 | </head> | 44 | </head> |
@@ -57,44 +58,14 @@ | @@ -57,44 +58,14 @@ | ||
57 | <div class="navbar-collapse collapse navbar-responsive-collapse"> | 58 | <div class="navbar-collapse collapse navbar-responsive-collapse"> |
58 | <ul class="nav navbar-nav navbar-right notifications"> | 59 | <ul class="nav navbar-nav navbar-right notifications"> |
59 | <li class="" data-toggle="tooltip" data-placement="bottom" title data-original-title="notifications"> | 60 | <li class="" data-toggle="tooltip" data-placement="bottom" title data-original-title="notifications"> |
60 | - <a class="dropdown-toggle" data-toggle="dropdown"> <span class="badge notification-count">{{notifications.count}}</span><i class="fa fa-bell" aria-hidden="true"></i></a> | 61 | + <a class="dropdown-toggle" data-toggle="dropdown"> <span id="notification-count" class="badge notification-count">{{notifications.count}}</span><i class="fa fa-bell" aria-hidden="true"></i></a> |
61 | <ul id="notification-dropdown" class="dropdown-menu"> | 62 | <ul id="notification-dropdown" class="dropdown-menu"> |
62 | <li class="dropdown-header">Notifications</li> | 63 | <li class="dropdown-header">Notifications</li> |
63 | - {% for notification in notifications %} | ||
64 | - {% if notification.actor %} <!-- if the notification has a user--> | ||
65 | - <li> | ||
66 | - <a href="{% url 'core:notification_read' notification.id %}"><div class="list-group-item"> | ||
67 | - <div class="row-picture"> | ||
68 | - <img class="circle" src="http://lorempixel.com/56/56/people/1" alt="icon"> | ||
69 | - <div class="least-content pull-right">{{ notification.datetime }}</div> | ||
70 | - </div> | ||
71 | - <div class="row-content"> | ||
72 | - <p class="list-group-item-text">{{ notification.message }}</p> | ||
73 | - </div> | ||
74 | - </div> | ||
75 | - </a> | ||
76 | - </li> | ||
77 | - {% else %} | ||
78 | - <li> | ||
79 | - <a href="{% url 'core:notification_read' notification.id %}"> | ||
80 | - <div class="list-group-item"> | ||
81 | - <div class="row-action-primary"> | ||
82 | - <i class="material-icons">folder</i> | ||
83 | - </div> | ||
84 | - <div class="row-content"> | ||
85 | - | ||
86 | - <div class="least-content pull-right">{{ notification.datetime }}</div> | ||
87 | - | ||
88 | - <p class="list-group-item-text">{{ notification.message }}</p> | ||
89 | - </div> | ||
90 | - </a> | ||
91 | - </li> | ||
92 | - {% endif %} | ||
93 | - {% endfor %} | 64 | + {% include "notifications.html" %} |
94 | 65 | ||
95 | <li> | 66 | <li> |
96 | - <a> | ||
97 | - <div class="list-group-item"> | 67 | + <a onclick="getNotifications(5)"> |
68 | + <div id="notification-see-more" class="list-group-item"> | ||
98 | <div class="row-content"> | 69 | <div class="row-content"> |
99 | <p class="list-group-item-text">See More</p> | 70 | <p class="list-group-item-text">See More</p> |
100 | </div> | 71 | </div> |
@@ -0,0 +1,33 @@ | @@ -0,0 +1,33 @@ | ||
1 | + | ||
2 | +<!-- This templates is responsible to display the list of notifications at the top of the page. --> | ||
3 | +{% for notification in notifications %} | ||
4 | + {% if notification.actor %} <!-- if the notification has a user--> | ||
5 | + <li> | ||
6 | + <a href="{% url 'core:notification_read' notification.id %}"><div class="list-group-item"> | ||
7 | + <div class="row-picture"> | ||
8 | + <img class="circle" src="http://lorempixel.com/56/56/people/1" alt="icon"> | ||
9 | + <div class="least-content pull-right">{{ notification.datetime }}</div> | ||
10 | + </div> | ||
11 | + <div class="row-content"> | ||
12 | + <p class="list-group-item-text">{{ notification.message }}</p> | ||
13 | + </div> | ||
14 | + </div> | ||
15 | + </a> | ||
16 | + </li> | ||
17 | + {% else %} | ||
18 | + <li> | ||
19 | + <a href="{% url 'core:notification_read' notification.id %}"> | ||
20 | + <div class="list-group-item"> | ||
21 | + <div class="row-action-primary"> | ||
22 | + <i class="material-icons">folder</i> | ||
23 | + </div> | ||
24 | + <div class="row-content"> | ||
25 | + | ||
26 | + <div class="least-content pull-right">{{ notification.datetime }}</div> | ||
27 | + | ||
28 | + <p class="list-group-item-text">{{ notification.message }}</p> | ||
29 | + </div> | ||
30 | + </a> | ||
31 | + </li> | ||
32 | + {% endif %} | ||
33 | +{% endfor %} | ||
0 | \ No newline at end of file | 34 | \ No newline at end of file |
core/templates/register_user.html
@@ -8,11 +8,16 @@ | @@ -8,11 +8,16 @@ | ||
8 | 8 | ||
9 | 9 | ||
10 | {% block content %} | 10 | {% block content %} |
11 | - {% if message %} | ||
12 | - <div class="alert alert-danger"> | ||
13 | - {{message}} | ||
14 | - </div> | ||
15 | - {% endif %} | 11 | + {% if messages %} |
12 | + {% for message in messages %} | ||
13 | + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert"> | ||
14 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
15 | + <span aria-hidden="true">×</span> | ||
16 | + </button> | ||
17 | + <p>{{ message }}</p> | ||
18 | + </div> | ||
19 | + {% endfor %} | ||
20 | + {% endif %} | ||
16 | <div class="row logo-row"> | 21 | <div class="row logo-row"> |
17 | <div class="col-lg-offset-2 col-lg-9"> | 22 | <div class="col-lg-offset-2 col-lg-9"> |
18 | <img src="{% static 'img/amadeus.png' %}" class="img-responsive center-block " alt="logo amadeus" id="logo"> | 23 | <img src="{% static 'img/amadeus.png' %}" class="img-responsive center-block " alt="logo amadeus" id="logo"> |
@@ -29,7 +34,11 @@ | @@ -29,7 +34,11 @@ | ||
29 | <legend>{% trans 'User Register' %}</legend> | 34 | <legend>{% trans 'User Register' %}</legend> |
30 | {% for field in form %} | 35 | {% for field in form %} |
31 | <div class="form-group is-empy{% if form.has_error %} has-error {% endif %} is-fileinput"> | 36 | <div class="form-group is-empy{% if form.has_error %} has-error {% endif %} is-fileinput"> |
32 | - <label for="{{ field.auto_id }}" class="col-md-4 control-label">{{ field.label }}</label> | 37 | + {% if field.field.required %} |
38 | + <label for="{{ field.auto_id }}" class="col-md-4 control-label">{{ field.label }}<span>*</span></label> | ||
39 | + {% else %} | ||
40 | + <label for="{{ field.auto_id }}" class="col-md-4 control-label">{{ field.label }}</label> | ||
41 | + {% endif %} | ||
33 | <div class="col-md-8"> | 42 | <div class="col-md-8"> |
34 | {% if field.auto_id == 'id_birth_date' %} | 43 | {% if field.auto_id == 'id_birth_date' %} |
35 | {% render_field field class='form-control input-sm' type='date' %} | 44 | {% render_field field class='form-control input-sm' type='date' %} |
@@ -81,5 +90,4 @@ | @@ -81,5 +90,4 @@ | ||
81 | </div> | 90 | </div> |
82 | 91 | ||
83 | <br clear="all" /> | 92 | <br clear="all" /> |
84 | - <script src="{% static 'js/base/amadeus.js' %}"></script> | ||
85 | {% endblock %} | 93 | {% endblock %} |
core/urls.py
@@ -9,5 +9,6 @@ urlpatterns = [ | @@ -9,5 +9,6 @@ urlpatterns = [ | ||
9 | url(r'^register/$', views.RegisterUser.as_view(), name='register'), | 9 | url(r'^register/$', views.RegisterUser.as_view(), name='register'), |
10 | url(r'^remember_password/$', views.remember_password, name='remember_password'), | 10 | url(r'^remember_password/$', views.remember_password, name='remember_password'), |
11 | url(r'^logout/$', auth_views.logout, {'next_page': 'core:home'}, name='logout'), | 11 | url(r'^logout/$', auth_views.logout, {'next_page': 'core:home'}, name='logout'), |
12 | - url(r'^notification/([0-9]+)/$', views.processNotification, name='notification_read') | 12 | + url(r'^notification/([0-9]+)/$', views.processNotification, name='notification_read'), |
13 | + url(r'^getNotifications/$', views.getNotifications, name='getNotifications'), | ||
13 | ] | 14 | ] |
core/views.py
@@ -5,8 +5,9 @@ from django.contrib.auth.mixins import LoginRequiredMixin | @@ -5,8 +5,9 @@ from django.contrib.auth.mixins import LoginRequiredMixin | ||
5 | from .decorators import log_decorator | 5 | from .decorators import log_decorator |
6 | from django.contrib import messages | 6 | from django.contrib import messages |
7 | from django.shortcuts import render, redirect | 7 | from django.shortcuts import render, redirect |
8 | +from django.template.loader import render_to_string | ||
8 | from django.views.generic import CreateView, UpdateView | 9 | from django.views.generic import CreateView, UpdateView |
9 | -from django.http import HttpResponse | 10 | +from django.http import HttpResponse, JsonResponse |
10 | from django.core.mail import send_mail,BadHeaderError | 11 | from django.core.mail import send_mail,BadHeaderError |
11 | from django.conf import settings | 12 | from django.conf import settings |
12 | from core.mixins import NotificationMixin | 13 | from core.mixins import NotificationMixin |
@@ -95,6 +96,28 @@ def processNotification(self, notificationId): | @@ -95,6 +96,28 @@ def processNotification(self, notificationId): | ||
95 | notification.save() | 96 | notification.save() |
96 | return redirect(notification.action_resource.resource.url) | 97 | return redirect(notification.action_resource.resource.url) |
97 | 98 | ||
99 | + | ||
100 | + | ||
101 | + | ||
102 | +def getNotifications(request): | ||
103 | + context = {} | ||
104 | + if request.user.is_authenticated: | ||
105 | + | ||
106 | + steps = int(request.GET['steps']) | ||
107 | + amount = int(request.GET['amount']) | ||
108 | + notifications = Notification.objects.filter(user= request.user, read=False).order_by('-datetime')[steps:steps+amount] | ||
109 | + context['notifications'] = notifications | ||
110 | + else: #go to login page | ||
111 | + return HttpResponse('teste') | ||
112 | + | ||
113 | + | ||
114 | + html = render_to_string("notifications.html", context) | ||
115 | + print(html) | ||
116 | + return HttpResponse(html) | ||
117 | + | ||
118 | + | ||
119 | + | ||
120 | + | ||
98 | # class LoginClass(LoginView): | 121 | # class LoginClass(LoginView): |
99 | # template_name='index.html' | 122 | # template_name='index.html' |
100 | # | 123 | # |
@@ -0,0 +1,56 @@ | @@ -0,0 +1,56 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2016-09-30 04:24 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.conf import settings | ||
6 | +from django.db import migrations, models | ||
7 | +import django.db.models.deletion | ||
8 | + | ||
9 | + | ||
10 | +class Migration(migrations.Migration): | ||
11 | + | ||
12 | + dependencies = [ | ||
13 | + migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
14 | + ('courses', '0001_initial'), | ||
15 | + ] | ||
16 | + | ||
17 | + operations = [ | ||
18 | + migrations.RemoveField( | ||
19 | + model_name='activity', | ||
20 | + name='student', | ||
21 | + ), | ||
22 | + migrations.RemoveField( | ||
23 | + model_name='material', | ||
24 | + name='student', | ||
25 | + ), | ||
26 | + migrations.AddField( | ||
27 | + model_name='activity', | ||
28 | + name='all_students', | ||
29 | + field=models.BooleanField(default=False, verbose_name='All Students'), | ||
30 | + ), | ||
31 | + migrations.AddField( | ||
32 | + model_name='activity', | ||
33 | + name='students', | ||
34 | + field=models.ManyToManyField(related_name='activities', to=settings.AUTH_USER_MODEL, verbose_name='Students'), | ||
35 | + ), | ||
36 | + migrations.AddField( | ||
37 | + model_name='material', | ||
38 | + name='all_students', | ||
39 | + field=models.BooleanField(default=False, verbose_name='All Students'), | ||
40 | + ), | ||
41 | + migrations.AddField( | ||
42 | + model_name='material', | ||
43 | + name='students', | ||
44 | + field=models.ManyToManyField(related_name='materials', to=settings.AUTH_USER_MODEL, verbose_name='Students'), | ||
45 | + ), | ||
46 | + migrations.AlterField( | ||
47 | + model_name='activity', | ||
48 | + name='topic', | ||
49 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='courses.Topic', verbose_name='Topic'), | ||
50 | + ), | ||
51 | + migrations.AlterField( | ||
52 | + model_name='material', | ||
53 | + name='topic', | ||
54 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='materials', to='courses.Topic', verbose_name='Topic'), | ||
55 | + ), | ||
56 | + ] |
courses/models.py
@@ -88,17 +88,19 @@ It is one kind of possible resources available inside a Topic. | @@ -88,17 +88,19 @@ It is one kind of possible resources available inside a Topic. | ||
88 | Activity is something that has a deadline and has to be delivered by the student | 88 | Activity is something that has a deadline and has to be delivered by the student |
89 | """ | 89 | """ |
90 | class Activity(Resource): | 90 | class Activity(Resource): |
91 | - topic = models.ForeignKey(Topic, verbose_name = _('Topic')) | 91 | + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='activities') |
92 | limit_date = models.DateTimeField(_('Deliver Date')) | 92 | limit_date = models.DateTimeField(_('Deliver Date')) |
93 | - student = models.ForeignKey(User, verbose_name = _('student')) | 93 | + students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='activities') |
94 | + all_students = models.BooleanField(_('All Students'), default=False) | ||
94 | 95 | ||
95 | 96 | ||
96 | """ | 97 | """ |
97 | It represents any Material inside a topic, be it a file, a link, etc. | 98 | It represents any Material inside a topic, be it a file, a link, etc. |
98 | """ | 99 | """ |
99 | class Material(Resource): | 100 | class Material(Resource): |
100 | - topic = models.ForeignKey(Topic, verbose_name = _('Topic')) | ||
101 | - student = models.ForeignKey(User, verbose_name = _('student')) | 101 | + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='materials') |
102 | + students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='materials') | ||
103 | + all_students = models.BooleanField(_('All Students'), default=False) | ||
102 | 104 | ||
103 | """ | 105 | """ |
104 | It is a category for each subject. | 106 | It is a category for each subject. |
@@ -112,4 +114,3 @@ class SubjectCategory(models.Model): | @@ -112,4 +114,3 @@ class SubjectCategory(models.Model): | ||
112 | class Meta: | 114 | class Meta: |
113 | verbose_name = _('subject category') | 115 | verbose_name = _('subject category') |
114 | verbose_name_plural = _('subject categories') | 116 | verbose_name_plural = _('subject categories') |
115 | - |
forum/models.py
@@ -41,6 +41,15 @@ class Post(models.Model): | @@ -41,6 +41,15 @@ class Post(models.Model): | ||
41 | def __str__(self): | 41 | def __str__(self): |
42 | return ''.join([self.user.name, " / ", str(self.post_date)]) | 42 | return ''.join([self.user.name, " / ", str(self.post_date)]) |
43 | 43 | ||
44 | + def is_modified(self): | ||
45 | + create = self.post_date.strftime("%Y-%m-%d %H:%M:%S") | ||
46 | + edit = self.modification_date.strftime("%Y-%m-%d %H:%M:%S") | ||
47 | + | ||
48 | + if create != edit: | ||
49 | + return True | ||
50 | + | ||
51 | + return False | ||
52 | + | ||
44 | """ | 53 | """ |
45 | It represents an answer to a forum's post | 54 | It represents an answer to a forum's post |
46 | """ | 55 | """ |
forum/static/js/forum.js
@@ -50,6 +50,54 @@ function showForum(url, forum_id) { | @@ -50,6 +50,54 @@ function showForum(url, forum_id) { | ||
50 | $('#forumModal').modal(); | 50 | $('#forumModal').modal(); |
51 | } | 51 | } |
52 | 52 | ||
53 | +/* | ||
54 | +* | ||
55 | +* Function to load form to edit post | ||
56 | +* | ||
57 | +*/ | ||
58 | +function edit_post(url, post_id) { | ||
59 | + $.ajax({ | ||
60 | + url: url, | ||
61 | + success: function(data) { | ||
62 | + $("#post_"+post_id).find(".post_content").hide(); | ||
63 | + $("#post_"+post_id).find(".post_content").after(data); | ||
64 | + | ||
65 | + var frm = $("#post_"+post_id).find(".edit_post_form"); | ||
66 | + frm.submit(function () { | ||
67 | + $.ajax({ | ||
68 | + type: frm.attr('method'), | ||
69 | + url: frm.attr('action'), | ||
70 | + data: frm.serialize(), | ||
71 | + success: function (data) { | ||
72 | + $("#post_"+post_id).parent().after(data); | ||
73 | + frm.parent().parent().remove(); | ||
74 | + }, | ||
75 | + error: function(data) { | ||
76 | + console.log(frm.serialize()); | ||
77 | + console.log('Error'); | ||
78 | + } | ||
79 | + }); | ||
80 | + return false; | ||
81 | + }); | ||
82 | + } | ||
83 | + }); | ||
84 | +} | ||
85 | + | ||
86 | +/* | ||
87 | +* | ||
88 | +* Function to cancel post edition | ||
89 | +* | ||
90 | +*/ | ||
91 | +function cancelEditPost(post_id) { | ||
92 | + $("#post_"+post_id).find(".post_content").show(); | ||
93 | + $("#post_"+post_id).find(".edit_post_form").remove(); | ||
94 | +} | ||
95 | + | ||
96 | +/* | ||
97 | +* | ||
98 | +* Function to delete a post | ||
99 | +* | ||
100 | +*/ | ||
53 | function delete_post(url, post) { | 101 | function delete_post(url, post) { |
54 | var csrftoken = getCookie('csrftoken'); | 102 | var csrftoken = getCookie('csrftoken'); |
55 | 103 | ||
@@ -74,36 +122,4 @@ function answer(id, url) { | @@ -74,36 +122,4 @@ function answer(id, url) { | ||
74 | }); | 122 | }); |
75 | 123 | ||
76 | $("#post_"+id).find(".answer_post").show(); | 124 | $("#post_"+id).find(".answer_post").show(); |
77 | -} | ||
78 | - | ||
79 | -function showPosts(url, forum) { | ||
80 | - if ($("#collapse" + forum).hasClass('in')) { | ||
81 | - $("#collapse" + forum).collapse('hide'); | ||
82 | - } else { | ||
83 | - $.ajax({ | ||
84 | - url: url, | ||
85 | - data: {'forum': forum}, | ||
86 | - success: function(data) { | ||
87 | - $("#collapse" + forum).find(".well").html(data); | ||
88 | - } | ||
89 | - }); | ||
90 | - | ||
91 | - $("#collapse" + forum).collapse('show'); | ||
92 | - } | ||
93 | -} | ||
94 | - | ||
95 | -function showPostsAnswers(url, post) { | ||
96 | - if ($("#collapse" + post).hasClass('in')) { | ||
97 | - $("#collapse" + post).collapse('hide'); | ||
98 | - } else { | ||
99 | - $.ajax({ | ||
100 | - url: url, | ||
101 | - data: {'post': post}, | ||
102 | - success: function(data) { | ||
103 | - $("#collapse" + post).find(".well").html(data); | ||
104 | - } | ||
105 | - }); | ||
106 | - | ||
107 | - $("#collapse" + post).collapse('show'); | ||
108 | - } | ||
109 | } | 125 | } |
110 | \ No newline at end of file | 126 | \ No newline at end of file |
forum/templates/post/post_list.html
@@ -17,17 +17,24 @@ | @@ -17,17 +17,24 @@ | ||
17 | <i class="material-icons">more_horiz</i> | 17 | <i class="material-icons">more_horiz</i> |
18 | </a> | 18 | </a> |
19 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | 19 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> |
20 | - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | 20 | + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> |
21 | <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | 21 | <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> |
22 | </ul> | 22 | </ul> |
23 | </div> | 23 | </div> |
24 | {% endif %} | 24 | {% endif %} |
25 | </div> | 25 | </div> |
26 | </h3> | 26 | </h3> |
27 | - <div class="card-data"> | ||
28 | - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> | 27 | + <div class="post_content"> |
28 | + <div class="card-data"> | ||
29 | + <p class="comment-date"> | ||
30 | + <i class="fa fa-clock-o"></i> {{ post.post_date }} | ||
31 | + {% if post.is_modified %} | ||
32 | + <em> - {% trans 'Edited' %}</em> | ||
33 | + {% endif %} | ||
34 | + </p> | ||
35 | + </div> | ||
36 | + <p class="comment-text">{{ post.message|linebreaks }}</p> | ||
29 | </div> | 37 | </div> |
30 | - <p class="comment-text">{{ post.message|linebreaks }}</p> | ||
31 | <div class="answer_post"></div> | 38 | <div class="answer_post"></div> |
32 | </div> | 39 | </div> |
33 | </div> | 40 | </div> |
forum/templates/post/post_render.html
@@ -15,17 +15,24 @@ | @@ -15,17 +15,24 @@ | ||
15 | <i class="material-icons">more_horiz</i> | 15 | <i class="material-icons">more_horiz</i> |
16 | </a> | 16 | </a> |
17 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | 17 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> |
18 | - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | 18 | + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"></li> |
19 | <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | 19 | <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> |
20 | </ul> | 20 | </ul> |
21 | </div> | 21 | </div> |
22 | {% endif %} | 22 | {% endif %} |
23 | </div> | 23 | </div> |
24 | </h3> | 24 | </h3> |
25 | - <div class="card-data"> | ||
26 | - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> | 25 | + <div class="post_content"> |
26 | + <div class="card-data"> | ||
27 | + <p class="comment-date"> | ||
28 | + <i class="fa fa-clock-o"></i> {{ post.post_date }} | ||
29 | + {% if post.post_date != post.modifiction_date %} | ||
30 | + <em> - {% trans 'Edited' %}</em> | ||
31 | + {% endif %} | ||
32 | + </p> | ||
33 | + </div> | ||
34 | + <p class="comment-text">{{ post.message|linebreaks }}</p> | ||
27 | </div> | 35 | </div> |
28 | - <p class="comment-text">{{ post.message|linebreaks }}</p> | ||
29 | <div class="answer_post"></div> | 36 | <div class="answer_post"></div> |
30 | </div> | 37 | </div> |
31 | </div> | 38 | </div> |
32 | \ No newline at end of file | 39 | \ No newline at end of file |
@@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
1 | +{% load i18n permission_tags list_post %} | ||
2 | +{% load widget_tweaks %} | ||
3 | + | ||
4 | +<form class="edit_post_form" method="post" action="{% url 'forum:update_post' post.id %}" enctype="multipart/form-data"> | ||
5 | + {% csrf_token %} | ||
6 | + {% for field in form %} | ||
7 | + {% if field.field.widget.input_type == 'hidden' %} | ||
8 | + {% render_field field class='form-control' %} | ||
9 | + {% else %} | ||
10 | + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
11 | + <div class="input-group"> | ||
12 | + {% render_field field class='form-control' placeholder="Post a message" %} | ||
13 | + <span class="help-block">{{ field.help_text }}</span> | ||
14 | + {% if field.errors %} | ||
15 | + <div class="row"> | ||
16 | + <br /> | ||
17 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
18 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
19 | + <span aria-hidden="true">×</span> | ||
20 | + </button> | ||
21 | + <ul> | ||
22 | + {% for error in field.errors %} | ||
23 | + <li>{{ error }}</li> | ||
24 | + {% endfor %} | ||
25 | + </ul> | ||
26 | + </div> | ||
27 | + </div> | ||
28 | + {% endif %} | ||
29 | + </div> | ||
30 | + </div> | ||
31 | + <div class="pull-right"> | ||
32 | + <button type="button" onclick="cancelEditPost('{{ post.id }}')" class="btn btn-danger btn-raised">{% trans 'Cancel' %}</button> | ||
33 | + <button type="submit" class="btn btn-primary btn-raised">{% trans 'Save changes' %}</button> | ||
34 | + </div> | ||
35 | + {% endif %} | ||
36 | + {% endfor %} | ||
37 | +</form> | ||
0 | \ No newline at end of file | 38 | \ No newline at end of file |
forum/templatetags/list_post.py
@@ -14,6 +14,6 @@ def list_posts(request, forum): | @@ -14,6 +14,6 @@ def list_posts(request, forum): | ||
14 | 'request': request, | 14 | 'request': request, |
15 | } | 15 | } |
16 | 16 | ||
17 | - context['posts'] = Post.objects.filter(forum = forum) | 17 | + context['posts'] = Post.objects.filter(forum = forum).order_by('post_date') |
18 | 18 | ||
19 | return context | 19 | return context |
20 | \ No newline at end of file | 20 | \ No newline at end of file |
forum/urls.py
@@ -6,10 +6,10 @@ from . import views | @@ -6,10 +6,10 @@ from . import views | ||
6 | urlpatterns = [ | 6 | urlpatterns = [ |
7 | url(r'^$', views.ForumIndex.as_view(), name='index'), | 7 | url(r'^$', views.ForumIndex.as_view(), name='index'), |
8 | url(r'^create$', views.CreateForumView.as_view(), name='create'), | 8 | url(r'^create$', views.CreateForumView.as_view(), name='create'), |
9 | - url(r'^posts$', views.PostIndex.as_view(), name='posts'), | ||
10 | url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'), | 9 | url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'), |
11 | - url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'), | 10 | + url(r'^update_post/(?P<pk>[\w_-]+)/$', views.PostUpdateView.as_view(), name='update_post'), |
12 | url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'), | 11 | url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'), |
12 | + url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'), | ||
13 | url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), | 13 | url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), |
14 | url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'), | 14 | url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'), |
15 | url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'), | 15 | url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'), |
forum/views.py
@@ -36,20 +36,6 @@ class CreateForumView(LoginRequiredMixin, generic.edit.CreateView): | @@ -36,20 +36,6 @@ class CreateForumView(LoginRequiredMixin, generic.edit.CreateView): | ||
36 | form_class = ForumForm | 36 | form_class = ForumForm |
37 | success_url = reverse_lazy('forum:index') | 37 | success_url = reverse_lazy('forum:index') |
38 | 38 | ||
39 | -class PostIndex(LoginRequiredMixin, generic.ListView): | ||
40 | - login_url = reverse_lazy("core:home") | ||
41 | - redirect_field_name = 'next' | ||
42 | - | ||
43 | - template_name = "post/post_list.html" | ||
44 | - context_object_name = 'posts' | ||
45 | - | ||
46 | - def get_queryset(self): | ||
47 | - forum = get_object_or_404(Forum, slug = self.request.GET.get('forum', '')) | ||
48 | - | ||
49 | - context = Post.objects.filter(forum = forum) | ||
50 | - | ||
51 | - return context | ||
52 | - | ||
53 | class CreatePostView(LoginRequiredMixin, generic.edit.CreateView): | 39 | class CreatePostView(LoginRequiredMixin, generic.edit.CreateView): |
54 | login_url = reverse_lazy("core:home") | 40 | login_url = reverse_lazy("core:home") |
55 | redirect_field_name = 'next' | 41 | redirect_field_name = 'next' |
@@ -77,6 +63,19 @@ def render_post(request, post): | @@ -77,6 +63,19 @@ def render_post(request, post): | ||
77 | 63 | ||
78 | return render(request, "post/post_render.html", context) | 64 | return render(request, "post/post_render.html", context) |
79 | 65 | ||
66 | +class PostUpdateView(LoginRequiredMixin, generic.UpdateView): | ||
67 | + login_url = reverse_lazy("core:home") | ||
68 | + redirect_field_name = 'next' | ||
69 | + | ||
70 | + form_class = PostForm | ||
71 | + model = Post | ||
72 | + template_name = "post/post_update_form.html" | ||
73 | + | ||
74 | + def get_success_url(self): | ||
75 | + self.success_url = reverse('forum:render_post', args = (self.object.id, )) | ||
76 | + | ||
77 | + return self.success_url | ||
78 | + | ||
80 | class PostDeleteView(LoginRequiredMixin, generic.DeleteView): | 79 | class PostDeleteView(LoginRequiredMixin, generic.DeleteView): |
81 | login_url = reverse_lazy("core:home") | 80 | login_url = reverse_lazy("core:home") |
82 | redirect_field_name = 'next' | 81 | redirect_field_name = 'next' |
poll/admin.py
1 | from django.contrib import admin | 1 | from django.contrib import admin |
2 | 2 | ||
3 | -# Register your models here. | 3 | +from .models import Poll, Answer |
4 | + | ||
5 | +class PollAdmin(admin.ModelAdmin): | ||
6 | + list_display = ['name', 'slug','limit_date'] | ||
7 | + search_fields = ['name','slug'] | ||
8 | + | ||
9 | +class AnswerAdmin(admin.ModelAdmin): | ||
10 | + list_display = ['answer','order'] | ||
11 | + search_fields = ['answer'] | ||
12 | + | ||
13 | +admin.site.register(Poll, PollAdmin) | ||
14 | +admin.site.register(Answer, AnswerAdmin) |
@@ -0,0 +1,35 @@ | @@ -0,0 +1,35 @@ | ||
1 | +from django import forms | ||
2 | +from django.utils.translation import ugettext_lazy as _ | ||
3 | +from users.models import User | ||
4 | +from .models import Poll | ||
5 | + | ||
6 | +class PollForm(forms.ModelForm): | ||
7 | + | ||
8 | + def __init__(self, *args, **kwargs): | ||
9 | + super(PollForm, self).__init__(*args, **kwargs) | ||
10 | + self.fields["all_students"].required = False | ||
11 | + self.fields["all_students"].initial = False | ||
12 | + self.fields["students"].required = False | ||
13 | + | ||
14 | + def clean_all_students(self): | ||
15 | + if('all_students' not in self.data): | ||
16 | + if('students' in self.data): | ||
17 | + return False | ||
18 | + raise forms.ValidationError(_('It is required one these fields.')) | ||
19 | + else: | ||
20 | + all_students = self.data['all_students'] | ||
21 | + if(not all_students): | ||
22 | + raise forms.ValidationError(_('It is required one these fields.')) | ||
23 | + return True | ||
24 | + | ||
25 | + | ||
26 | + class Meta: | ||
27 | + model = Poll | ||
28 | + fields = ['name','limit_date','students','all_students'] | ||
29 | + | ||
30 | + widgets = { | ||
31 | + 'name': forms.TextInput(attrs={'placeholder': 'Question?'}), | ||
32 | + 'limit_date': forms.DateTimeInput( | ||
33 | + attrs={'placeholder': 'Maximum date permited to resolve the poll'}), | ||
34 | + 'student': forms.Select(), | ||
35 | + } |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2016-09-30 04:24 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations | ||
6 | + | ||
7 | + | ||
8 | +class Migration(migrations.Migration): | ||
9 | + | ||
10 | + dependencies = [ | ||
11 | + ('poll', '0001_initial'), | ||
12 | + ] | ||
13 | + | ||
14 | + operations = [ | ||
15 | + migrations.RemoveField( | ||
16 | + model_name='poll', | ||
17 | + name='question', | ||
18 | + ), | ||
19 | + ] |
poll/models.py
@@ -6,7 +6,6 @@ from core.models import Resource | @@ -6,7 +6,6 @@ from core.models import Resource | ||
6 | from courses.models import Activity | 6 | from courses.models import Activity |
7 | 7 | ||
8 | class Poll(Activity): | 8 | class Poll(Activity): |
9 | - question = models.CharField(_('Question'), max_length = 300) | ||
10 | 9 | ||
11 | class Meta: | 10 | class Meta: |
12 | #ordering = ('create_date','name') | 11 | #ordering = ('create_date','name') |
@@ -14,7 +13,7 @@ class Poll(Activity): | @@ -14,7 +13,7 @@ class Poll(Activity): | ||
14 | verbose_name_plural = _('Polls') | 13 | verbose_name_plural = _('Polls') |
15 | 14 | ||
16 | def __str__(self): | 15 | def __str__(self): |
17 | - return str(self.question) + str("/") + str(self.topic) | 16 | + return str(self.name) + str("/") + str(self.topic) |
18 | 17 | ||
19 | class Answer(models.Model): | 18 | class Answer(models.Model): |
20 | answer = models.CharField(_("Answer"), max_length = 200) | 19 | answer = models.CharField(_("Answer"), max_length = 200) |
@@ -27,4 +26,4 @@ class Answer(models.Model): | @@ -27,4 +26,4 @@ class Answer(models.Model): | ||
27 | verbose_name_plural = _('Answers') | 26 | verbose_name_plural = _('Answers') |
28 | 27 | ||
29 | def __str__(self): | 28 | def __str__(self): |
30 | - return str(self.question) + str("/") + str(self.topic) | 29 | + return str(self.answer) + str("/") + str(self.poll) |
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +from rolepermissions.permissions import register_object_checker | ||
2 | +from amadeus.roles import SystemAdmin | ||
3 | + | ||
4 | +@register_object_checker() | ||
5 | +def edit_poll(role, user, poll): | ||
6 | + if (role == SystemAdmin): | ||
7 | + return True | ||
8 | + | ||
9 | + if (user in poll.topic.subject.professors.all()): | ||
10 | + return True | ||
11 | + | ||
12 | + return False |
@@ -0,0 +1,206 @@ | @@ -0,0 +1,206 @@ | ||
1 | +{% extends "topic/index.html" %} | ||
2 | + | ||
3 | +{% load i18n widget_tweaks dict_access static%} | ||
4 | + | ||
5 | +{% block style %} | ||
6 | + <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> | ||
7 | +{% endblock %} | ||
8 | + | ||
9 | +{% block content %} | ||
10 | +<!-- Modal (remember to change the ids!!!) --> | ||
11 | +<div class="modal fade" id="poll" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> | ||
12 | + <div class="modal-dialog" role="document"> | ||
13 | + <div class="modal-content"> | ||
14 | + | ||
15 | + <!-- Modal Header --> | ||
16 | + <div class="modal-header"> | ||
17 | + | ||
18 | + <!-- Put your title here!!! --> | ||
19 | + <h4 class="modal-title" id="myModalLabel">{% trans "Create a Poll" %}</h4> | ||
20 | + | ||
21 | + </div> | ||
22 | + <!-- Modal Body --> | ||
23 | + <div class="modal-body"> | ||
24 | + | ||
25 | + <!-- Put ONLY your content here!!! --> | ||
26 | + <div class="conteiner"> | ||
27 | + <div class="row form-group"> | ||
28 | + <div class="col-md-1"> | ||
29 | + </br> | ||
30 | + <label><span class="glyphicon glyphicon-hand-right"></span></label> | ||
31 | + </div> | ||
32 | + <div class="col-md-10"> | ||
33 | + <div class="has-success"> | ||
34 | + <input form="form" type="text" name="{{form.name.name}}" {% if form.name.value != None %}value="{{form.name.value}}" {% endif %} class="form-control" placeholder='{% trans "Question?" %}'> | ||
35 | + <span class="help-block">{% trans "A Question to be answered" %}</span> | ||
36 | + </div> | ||
37 | + </div> | ||
38 | + {% if form.name.errors %} | ||
39 | + <div class="col-md-10 not_submited"> | ||
40 | + </br> | ||
41 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
42 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
43 | + <span aria-hidden="true">×</span> | ||
44 | + </button> | ||
45 | + <ul> | ||
46 | + {% for error in form.name.errors %} | ||
47 | + <li>{{ error }}</li> | ||
48 | + {% endfor %} | ||
49 | + </ul> | ||
50 | + </div> | ||
51 | + </div> | ||
52 | + {% endif %} | ||
53 | + </div> | ||
54 | + <form id="form" class="" action="" method="post"> | ||
55 | + {% csrf_token %} | ||
56 | + {% for key in keys %} | ||
57 | + <div class="row form-group"> | ||
58 | + <div class="col-md-1"> | ||
59 | + </br> | ||
60 | + <label><span class="glyphicon glyphicon-move"></span></label> | ||
61 | + </div> | ||
62 | + <div class="col-md-10"> | ||
63 | + <div class="has-success is-empty"> | ||
64 | + <input type="text" name="{{key}}" class="form-control" placeholder='{% trans "Answer" %}' value="{{ answers|value:key }}"> | ||
65 | + <span class="help-block">{% trans "Possible answer for the question" %}</span> | ||
66 | + </div> | ||
67 | + </div> | ||
68 | + <div class="col-md-1"> | ||
69 | + </br> | ||
70 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label> | ||
71 | + </div> | ||
72 | + </div> | ||
73 | + {% empty %} | ||
74 | + <div class="row form-group"> | ||
75 | + <div class="col-md-1"> | ||
76 | + </br> | ||
77 | + <label><span class="glyphicon glyphicon-move"></span></label> | ||
78 | + </div> | ||
79 | + <div class="col-md-10"> | ||
80 | + <div class="has-success is-empty"> | ||
81 | + <input type="text" name="1" class="form-control" placeholder='{% trans "Answer" %}'> | ||
82 | + <span class="help-block">{% trans "Possible answer for the question" %}</span> | ||
83 | + </div> | ||
84 | + </div> | ||
85 | + <div class="col-md-1"> | ||
86 | + </br> | ||
87 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label> | ||
88 | + </div> | ||
89 | + </div> | ||
90 | + {% endfor %} | ||
91 | + </form> | ||
92 | + </br> | ||
93 | + </div> | ||
94 | + <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button> | ||
95 | + <div class="row form-group"> | ||
96 | + <label for="{{ form.limit_date.auto_id }}">{{ form.limit_date.label }}</label> | ||
97 | + {% render_field form.limit_date class="form-control" form="form"%} | ||
98 | + {# <input form="form" class="form-control" type="date" name="{{form.limit_date.name}}" {% if form.limit_date.value != None %}value="{% if form.limit_date.value.year %}{{form.limit_date.value|date:'Y-m-d'}}{% else %}{{form.limit_date.value}}{% endif %}"{% endif %}>#} | ||
99 | + {% if form.limit_date.errors %} | ||
100 | + <div class="not_submited"> | ||
101 | + </br> | ||
102 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
103 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
104 | + <span aria-hidden="true">×</span> | ||
105 | + </button> | ||
106 | + <ul> | ||
107 | + {% for error in form.limit_date.errors %} | ||
108 | + <li>{{ error }}</li> | ||
109 | + {% endfor %} | ||
110 | + </ul> | ||
111 | + </div> | ||
112 | + </div> | ||
113 | + {% endif %} | ||
114 | + </div> | ||
115 | + | ||
116 | + <div class="row form-group"> | ||
117 | + <label for="{{ form.students.auto_id }}">{{ form.students.label }}</label> | ||
118 | + {% render_field form.students class="form-control" form="form"%} | ||
119 | + </div> | ||
120 | + <div class="row form-group"> | ||
121 | + <div class="checkbox"> | ||
122 | + <label> | ||
123 | + {% render_field form.all_students class="form-control" form="form" %}<span class="checkbox-material"><span class="check"></span></span> {{form.all_students.label }} | ||
124 | + {# <input form="form" type="checkbox" name="{{form.all_students.name}}"><span class="checkbox-material"><span class="check"></span></span> {{ form.all_students.label }}#} | ||
125 | + </label> | ||
126 | + </div> | ||
127 | + {% if form.all_students.errors %} | ||
128 | + <div class="not_submited"> | ||
129 | + </br> | ||
130 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
131 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
132 | + <span aria-hidden="true">×</span> | ||
133 | + </button> | ||
134 | + <ul> | ||
135 | + {% for error in form.all_students.errors %} | ||
136 | + <li>{{ error }}</li> | ||
137 | + {% endfor %} | ||
138 | + </ul> | ||
139 | + </div> | ||
140 | + </div> | ||
141 | + {% endif %} | ||
142 | + | ||
143 | + {# <label for="{{ form.all_students.auto_id }}">{{ form.all_students.label }}</label>#} | ||
144 | + {# {% render_field form.all_students class="form-control" form="form"%}#} | ||
145 | + </div> | ||
146 | + | ||
147 | + </div> | ||
148 | + | ||
149 | + <!-- Modal Footer --> | ||
150 | + <div class="modal-footer"> | ||
151 | + | ||
152 | + <!-- Don't remove that!!! --> | ||
153 | + <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button> | ||
154 | + | ||
155 | + <!-- Put curtom buttons here!!! --> | ||
156 | + <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button> | ||
157 | + </div> | ||
158 | + | ||
159 | + </div> | ||
160 | + </div> | ||
161 | +</div> | ||
162 | +<script type="text/javascript"> | ||
163 | +// Este js tem que ficar aqui se não a tag "trans" não vai funcionar | ||
164 | +$(window).ready(function() { // utilizado para abrir o modal quando tiver tido algum erro no preenchimento do formulario | ||
165 | + if($('.not_submited').length){ | ||
166 | + $('#poll').modal('show'); | ||
167 | + } | ||
168 | +}); | ||
169 | +$( "#form" ).sortable({ // utilizado para fazer a re-organização das respostas | ||
170 | + delay: 100, | ||
171 | + distance: 5, | ||
172 | + update: function( event, ui ) { | ||
173 | + var cont = 1; | ||
174 | + $("#form div div div input").each(function(){ | ||
175 | + $(this).attr('name',cont++); | ||
176 | + }); | ||
177 | + }, | ||
178 | +}); | ||
179 | +name = 2; | ||
180 | +$("#add").click(function() { // utilizado para adicionar um novo campo de resposta | ||
181 | + //Obs: não funcionar se estiver importado no head, só funciona se estiver no final do arquivo | ||
182 | + $("#form").append('\ | ||
183 | + <div class="row form-group">\ | ||
184 | + <div class="col-md-1">\ | ||
185 | + </br>\ | ||
186 | + <label><span class="glyphicon glyphicon-move"></span></label>\ | ||
187 | + </div>\ | ||
188 | + <div class="col-md-10">\ | ||
189 | + <div class="has-success is-empty">\ | ||
190 | + <input type="text" name="1" class="form-control" placeholder="{% trans "Answer" %}">\ | ||
191 | + <span class="help-block">{% trans "Possible answer for the question" %}</span>\ | ||
192 | + </div>\ | ||
193 | + </div>\ | ||
194 | + <div class="col-md-1">\ | ||
195 | + </br>\ | ||
196 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>\ | ||
197 | + </div>\ | ||
198 | + </div>'); | ||
199 | + var cont = 1; | ||
200 | + $("#form div div div input").each(function(){ | ||
201 | + $(this).attr('name',cont++); | ||
202 | + }); | ||
203 | +}); | ||
204 | +</script> | ||
205 | +<a href="" data-toggle="modal" data-target="#poll">modal</a> | ||
206 | +{% endblock content %} |
poll/templates/poll/poll.html
@@ -1,52 +0,0 @@ | @@ -1,52 +0,0 @@ | ||
1 | -{% extends "topic/index.html" %} | ||
2 | - | ||
3 | -{% load i18n %} | ||
4 | - | ||
5 | -{% block style %} | ||
6 | - <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> | ||
7 | -{% endblock %} | ||
8 | - | ||
9 | -{% block content %} | ||
10 | -<div class="col-md-8 col-md-offset-2"> | ||
11 | - <div class="panel panel-primary"> | ||
12 | - <div class="panel-heading"> | ||
13 | - <h3 class="panel-title"> | ||
14 | - <span class="glyphicon glyphicon-hand-right"></span> Question?</h3> | ||
15 | - </div> | ||
16 | - <div class="container-fluid"> | ||
17 | - <form id="form" class="" action="" method="post"> | ||
18 | - <div class="row form-group"> | ||
19 | - <div class="col-md-1"> | ||
20 | - </br> | ||
21 | - <label><a href=""><span class="glyphicon glyphicon-move"></span></a></label> | ||
22 | - </div> | ||
23 | - <div class="col-md-10"> | ||
24 | - <div class="form-control-md has-success is-empty"> | ||
25 | - <input type="text" class="form-control" placeholder="Email address default size"> | ||
26 | - <span class="help-block">{% trans "Possible Answer" %}</span> | ||
27 | - </div> | ||
28 | - </div> | ||
29 | - <div class="col-md-1"> | ||
30 | - </br> | ||
31 | - <label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label> | ||
32 | - </div> | ||
33 | - </div> | ||
34 | - </form> | ||
35 | - </br> | ||
36 | - </div> | ||
37 | - | ||
38 | - </div> | ||
39 | - <div class="panel-footer text-center"> | ||
40 | - <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button> | ||
41 | - </div> | ||
42 | - </div> | ||
43 | -<script type="text/javascript"> | ||
44 | -$( "#form" ).sortable({ | ||
45 | - delay: 100, | ||
46 | - distance: 5, | ||
47 | -}); | ||
48 | -$("#add").click(function() { | ||
49 | - $("#form").append('<div class="row form-group"><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-move"></span></a></label></div><div class="col-md-10"><div class="form-group-md has-success is-empty"><input type="text" class="form-control" placeholder="Email address default size"><span class="help-block">Please enter a valid email address</span></div></div><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label></div></br></div>'); | ||
50 | -}); | ||
51 | -</script> | ||
52 | -{% endblock content %} |
@@ -0,0 +1,99 @@ | @@ -0,0 +1,99 @@ | ||
1 | +{% extends "topic/index.html" %} | ||
2 | + | ||
3 | +{% load i18n widget_tweaks dict_access static%} | ||
4 | +{% block content %} | ||
5 | +<!-- Modal (remember to change the ids!!!) --> | ||
6 | +<div class="modal fade" id="poll" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> | ||
7 | + <div class="modal-dialog" role="document"> | ||
8 | + <div class="modal-content"> | ||
9 | + | ||
10 | + <!-- Modal Header --> | ||
11 | + <div class="modal-header"> | ||
12 | + | ||
13 | + <!-- Put your title here!!! --> | ||
14 | + <h4 class="modal-title" id="myModalLabel">{{form.question}}</h4> | ||
15 | + | ||
16 | + </div> | ||
17 | + <!-- Modal Body --> | ||
18 | + <div class="modal-body"> | ||
19 | + | ||
20 | + <!-- Put ONLY your content here!!! --> | ||
21 | + <div class="conteiner"> | ||
22 | + </div> | ||
23 | + <form id="form" method="post"> | ||
24 | + {% csrf_token %} | ||
25 | + {% for key in keys %} | ||
26 | + <div class="row form-group"> | ||
27 | + <div class="col-md-1"> | ||
28 | + </br> | ||
29 | + <label><span class="glyphicon glyphicon-move"></span></label> | ||
30 | + </div> | ||
31 | + <div class="col-md-10"> | ||
32 | + <div class="has-success is-empty"> | ||
33 | + <input type="text" name="{{key}}" class="form-control" placeholder='{% trans "Answer" %}' value="{{ answers|value:key }}"> | ||
34 | + <span class="help-block">{% trans "Possible answer for the question" %}</span> | ||
35 | + </div> | ||
36 | + </div> | ||
37 | + <div class="col-md-1"> | ||
38 | + </br> | ||
39 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label> | ||
40 | + </div> | ||
41 | + </div> | ||
42 | + {% empty %} | ||
43 | + <div class="row form-group"> | ||
44 | + <div class="col-md-1"> | ||
45 | + </br> | ||
46 | + <label><span class="glyphicon glyphicon-move"></span></label> | ||
47 | + </div> | ||
48 | + <div class="col-md-10"> | ||
49 | + <div class="has-success is-empty"> | ||
50 | + <input type="text" name="1" class="form-control" placeholder='{% trans "Answer" %}'> | ||
51 | + <span class="help-block">{% trans "Possible answer for the question" %}</span> | ||
52 | + </div> | ||
53 | + </div> | ||
54 | + <div class="col-md-1"> | ||
55 | + </br> | ||
56 | + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label> | ||
57 | + </div> | ||
58 | + </div> | ||
59 | + {% endfor %} | ||
60 | + </form> | ||
61 | + </br> | ||
62 | + </div> | ||
63 | + <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button> | ||
64 | + <div class="row form-group"> | ||
65 | + <input form="form" class="form-control" type="date" name="{{form.limit_date.name}}" {% if form.limit_date.value != None %}value="{% if form.limit_date.value.year %}{{form.limit_date.value|date:'Y-m-d'}}{% else %}{{form.limit_date.value}}{% endif %}"{% endif %}> | ||
66 | + {% if form.limit_date.errors %} | ||
67 | + <div class="not_submited"> | ||
68 | + </br> | ||
69 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
70 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
71 | + <span aria-hidden="true">×</span> | ||
72 | + </button> | ||
73 | + <ul> | ||
74 | + {% for error in form.limit_date.errors %} | ||
75 | + <li>{{ error }}</li> | ||
76 | + {% endfor %} | ||
77 | + </ul> | ||
78 | + </div> | ||
79 | + </div> | ||
80 | + {% endif %} | ||
81 | + </div> | ||
82 | + | ||
83 | + </div> | ||
84 | + | ||
85 | + <!-- Modal Footer --> | ||
86 | + <div class="modal-footer"> | ||
87 | + | ||
88 | + <!-- Don't remove that!!! --> | ||
89 | + <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button> | ||
90 | + | ||
91 | + <!-- Put curtom buttons here!!! --> | ||
92 | + <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button> | ||
93 | + </div> | ||
94 | + | ||
95 | + </div> | ||
96 | + </div> | ||
97 | +</div> | ||
98 | +<a href="" data-toggle="modal" data-target="#poll">modal</a> | ||
99 | +{% endblock content %} |
poll/urls.py
@@ -3,6 +3,7 @@ from django.conf.urls import url | @@ -3,6 +3,7 @@ from django.conf.urls import url | ||
3 | from . import views | 3 | from . import views |
4 | 4 | ||
5 | urlpatterns = [ | 5 | urlpatterns = [ |
6 | - url(r'^to/poll/to/$', views.Poll.as_view(), name='poll'), | 6 | + url(r'^create/(?P<slug>[\w\-_]+)/$', views.CreatePoll.as_view(), name='create_poll'), # topic slug |
7 | + url(r'^update/(?P<slug>[\w\-_]+)/$', views.UpdatePoll.as_view(), name='update_poll'), # poll slug | ||
7 | 8 | ||
8 | ] | 9 | ] |
poll/views.py
@@ -8,31 +8,116 @@ from django.core.urlresolvers import reverse_lazy | @@ -8,31 +8,116 @@ from django.core.urlresolvers import reverse_lazy | ||
8 | from django.utils.translation import ugettext_lazy as _ | 8 | from django.utils.translation import ugettext_lazy as _ |
9 | from rolepermissions.verifications import has_role | 9 | from rolepermissions.verifications import has_role |
10 | from rolepermissions.verifications import has_object_permission | 10 | from rolepermissions.verifications import has_object_permission |
11 | +# from django.views.generic.edit import FormMixin | ||
11 | 12 | ||
12 | -# from .forms import CourseForm, UpdateCourseForm, CategoryForm, SubjectForm,TopicForm | ||
13 | -# from .models import Course, Subject, Category,Topic, SubjectCategory | 13 | +from .forms import PollForm |
14 | +from .models import Poll, Answer | ||
14 | from core.mixins import NotificationMixin | 15 | from core.mixins import NotificationMixin |
15 | from users.models import User | 16 | from users.models import User |
16 | -from courses.models import Course | 17 | +from courses.models import Course, Topic |
17 | 18 | ||
18 | -class Poll(generic.TemplateView): | 19 | +class CreatePoll(LoginRequiredMixin,generic.CreateView): |
19 | 20 | ||
20 | - # login_url = reverse_lazy("core:home") | ||
21 | - # redirect_field_name = 'next' | ||
22 | - # model = Course | ||
23 | - # context_object_name = 'course' | ||
24 | - template_name = 'poll/poll.html' | ||
25 | - # queryset = Course.objects.all() | 21 | + login_url = reverse_lazy("core:home") |
22 | + redirect_field_name = 'next' | ||
23 | + model = Poll | ||
24 | + form_class = PollForm | ||
25 | + context_object_name = 'poll' | ||
26 | + template_name = 'poll/create_update.html' | ||
27 | + success_url = reverse_lazy('core:home') | ||
26 | 28 | ||
27 | - # def get_queryset(self): | ||
28 | - # return Course.objects.all()[0] | 29 | + def form_invalid(self, form,**kwargs): |
30 | + context = super(CreatePoll, self).form_invalid(form) | ||
31 | + answers = {} | ||
32 | + for key in self.request.POST: | ||
33 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | ||
34 | + answers[key] = self.request.POST[key] | ||
35 | + | ||
36 | + keys = sorted(answers) | ||
37 | + context.context_data['answers'] = answers | ||
38 | + context.context_data['keys'] = keys | ||
39 | + return context | ||
40 | + | ||
41 | + def form_valid(self, form): | ||
42 | + self.object = form.save(commit = False) | ||
43 | + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | ||
44 | + self.object.topic = topic | ||
45 | + self.object.save() | ||
46 | + | ||
47 | + for key in self.request.POST: | ||
48 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | ||
49 | + answer = Answer(answer=self.request.POST[key],order=key,poll=self.object) | ||
50 | + answer.save() | ||
51 | + | ||
52 | + return super(CreatePoll, self).form_valid(form) | ||
53 | + | ||
54 | + def get_context_data(self, **kwargs): | ||
55 | + context = super(CreatePoll, self).get_context_data(**kwargs) | ||
56 | + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | ||
57 | + context['course'] = topic.subject.course | ||
58 | + context['subject'] = topic.subject | ||
59 | + context['subjects'] = topic.subject.course.subjects.all() | ||
60 | + return context | ||
61 | + | ||
62 | +class UpdatePoll(LoginRequiredMixin,generic.UpdateView): | ||
63 | + | ||
64 | + login_url = reverse_lazy("core:home") | ||
65 | + redirect_field_name = 'next' | ||
66 | + model = Poll | ||
67 | + form_class = PollForm | ||
68 | + context_object_name = 'poll' | ||
69 | + template_name = 'poll/create_update.html' | ||
70 | + success_url = reverse_lazy('core:home') | ||
71 | + | ||
72 | + def dispatch(self, *args, **kwargs): | ||
73 | + poll = get_object_or_404(Poll, slug = self.kwargs.get('slug')) | ||
74 | + if(not has_object_permission('edit_poll', self.request.user, poll)): | ||
75 | + return self.handle_no_permission() | ||
76 | + return super(UpdatePoll, self).dispatch(*args, **kwargs) | ||
77 | + | ||
78 | + def get_object(self, queryset=None): | ||
79 | + return get_object_or_404(Poll, slug = self.kwargs.get('slug')) | ||
80 | + | ||
81 | + def form_invalid(self, form,**kwargs): | ||
82 | + context = super(UpdatePoll, self).form_invalid(form) | ||
83 | + answers = {} | ||
84 | + for key in self.request.POST: | ||
85 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | ||
86 | + answers[key] = self.request.POST[key] | ||
87 | + | ||
88 | + keys = sorted(answers) | ||
89 | + context.context_data['answers'] = answers | ||
90 | + context.context_data['keys'] = keys | ||
91 | + return context | ||
92 | + | ||
93 | + def form_valid(self, form): | ||
94 | + poll = self.object | ||
95 | + poll = form.save(commit = False) | ||
96 | + poll.answers.all().delete() | ||
97 | + poll.save() | ||
98 | + | ||
99 | + | ||
100 | + for key in self.request.POST: | ||
101 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'): | ||
102 | + answer = Answer(answer=self.request.POST[key],order=key,poll=poll) | ||
103 | + answer.save() | ||
104 | + | ||
105 | + return super(UpdatePoll, self).form_valid(form) | ||
29 | 106 | ||
30 | def get_context_data(self, **kwargs): | 107 | def get_context_data(self, **kwargs): |
31 | - context = super(Poll, self).get_context_data(**kwargs) | ||
32 | - course = Course.objects.all()[0] | ||
33 | - context['course'] = course | ||
34 | - context['subject'] = course.subjects.all()[0] | ||
35 | - context['subjects'] = course.subjects.all() | ||
36 | - # if (has_role(self.request.user,'system_admin')): | ||
37 | - # context['subjects'] = self.object.course.subjects.all() | 108 | + context = super(UpdatePoll, self).get_context_data(**kwargs) |
109 | + poll = self.object | ||
110 | + context['course'] = poll.topic.subject.course | ||
111 | + context['subject'] = poll.topic.subject | ||
112 | + context['subjects'] = poll.topic.subject.course.subjects.all() | ||
113 | + | ||
114 | + answers = {} | ||
115 | + for answer in poll.answers.all(): | ||
116 | + # print (key.answer) | ||
117 | + answers[answer.order] = answer.answer | ||
118 | + | ||
119 | + keys = sorted(answers) | ||
120 | + context['answers'] = answers | ||
121 | + context['keys'] = keys | ||
122 | + | ||
38 | return context | 123 | return context |
users/forms.py
@@ -27,25 +27,6 @@ class ProfileForm(forms.ModelForm): | @@ -27,25 +27,6 @@ class ProfileForm(forms.ModelForm): | ||
27 | } | 27 | } |
28 | 28 | ||
29 | class UserForm(RegisterUserForm): | 29 | class UserForm(RegisterUserForm): |
30 | - def save(self, commit=True): | ||
31 | - super(UserForm, self).save() | ||
32 | - | ||
33 | - if not self.instance.image: | ||
34 | - self.instance.image = os.path.join(os.path.dirname(settings.BASE_DIR), 'uploads', 'no_image.jpg') | ||
35 | - | ||
36 | - self.instance.set_password(self.cleaned_data['password1']) | ||
37 | - self.instance.save() | ||
38 | - | ||
39 | - if self.instance.is_staff: | ||
40 | - assign_role(self.instance, 'system_admin') | ||
41 | - elif self.instance.type_profile == 2: | ||
42 | - assign_role(self.instance, 'student') | ||
43 | - elif self.instance.type_profile == 1: | ||
44 | - assign_role(self.instance, 'professor') | ||
45 | - | ||
46 | - self.instance.save() | ||
47 | - | ||
48 | - return self.instance | ||
49 | 30 | ||
50 | class Meta: | 31 | class Meta: |
51 | model = User | 32 | model = User |
@@ -59,7 +40,8 @@ class EditUserForm(forms.ModelForm): | @@ -59,7 +40,8 @@ class EditUserForm(forms.ModelForm): | ||
59 | 40 | ||
60 | # Ailson | 41 | # Ailson |
61 | class UpdateUserForm(forms.ModelForm): | 42 | class UpdateUserForm(forms.ModelForm): |
43 | + company_logo = forms.ImageField(label=_('Company Logo'),required=False, error_messages = {'invalid':_("Image files only")}) | ||
62 | 44 | ||
63 | - class Meta: | ||
64 | - model = User | ||
65 | - fields = ['username', 'name', 'email', 'city', 'state', 'birth_date', 'gender', 'cpf', 'phone', 'image'] | 45 | + class Meta: |
46 | + model = User | ||
47 | + fields = ['username', 'name', 'email', 'city', 'state', 'birth_date', 'gender', 'cpf', 'phone', 'image'] | ||
66 | \ No newline at end of file | 48 | \ No newline at end of file |
users/templates/list_users.html
@@ -63,9 +63,29 @@ | @@ -63,9 +63,29 @@ | ||
63 | <p>{% trans 'Contact' %}: {{ acc.phone }}</p> | 63 | <p>{% trans 'Contact' %}: {{ acc.phone }}</p> |
64 | <div align="right"> | 64 | <div align="right"> |
65 | <a href="{% url 'users:update' acc.username %}" class="btn btn-raised btn-success">{% trans 'Edit' %}</a> | 65 | <a href="{% url 'users:update' acc.username %}" class="btn btn-raised btn-success">{% trans 'Edit' %}</a> |
66 | - <a href="javascript:void(0)" class="btn btn-raised btn-primary">{% trans 'Delete' %}</a> | 66 | + <a href="javascript:void(0)" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#DeleteModal">{% trans 'Delete' %}</a> |
67 | </div> | 67 | </div> |
68 | </div> | 68 | </div> |
69 | + | ||
70 | + | ||
71 | + <!-- Modal --> | ||
72 | + <div class="modal fade" id="DeleteModal" tabindex="-1" role="dialog" aria-labelledby="DeleteModalLabel"> | ||
73 | + <div class="modal-dialog" role="document"> | ||
74 | + <div class="modal-content"> | ||
75 | + <div class="modal-header"> | ||
76 | + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||
77 | + <h4 class="modal-title" id="DeleteModalLabel">Confirm delete</h4> | ||
78 | + </div> | ||
79 | + <div class="modal-body"> | ||
80 | + Are you sure you want to delete? | ||
81 | + </div> | ||
82 | + <div class="modal-footer"> | ||
83 | + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> | ||
84 | + <button type="button" class="btn btn-primary">Delete</button> | ||
85 | + </div> | ||
86 | + </div> | ||
87 | + </div> | ||
88 | + </div> | ||
69 | </div> | 89 | </div> |
70 | </div> | 90 | </div> |
71 | {% endfor %} | 91 | {% endfor %} |
users/templates/users/create.html
@@ -33,10 +33,18 @@ | @@ -33,10 +33,18 @@ | ||
33 | {% for field in form %} | 33 | {% for field in form %} |
34 | <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | 34 | <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> |
35 | {% if field.auto_id == 'id_birth_date' %} | 35 | {% if field.auto_id == 'id_birth_date' %} |
36 | - <label for="{{ field.auto_id }}">{{ field.label }}</label> | 36 | + {% if field.field.required %} |
37 | + <label for="{{ field.auto_id }}">{{ field.label }}<span>*</span></label> | ||
38 | + {% else %} | ||
39 | + <label for="{{ field.auto_id }}">{{ field.label }}</label> | ||
40 | + {% endif %} | ||
37 | <input type="date" class="form-control"name="{{field.name}}" value="{% if field.value.year %}{{field.value|date:'Y-m-d'}}{% else %}{{field.value}}{% endif %}"> | 41 | <input type="date" class="form-control"name="{{field.name}}" value="{% if field.value.year %}{{field.value|date:'Y-m-d'}}{% else %}{{field.value}}{% endif %}"> |
38 | {% elif field.auto_id == 'id_image' %} | 42 | {% elif field.auto_id == 'id_image' %} |
39 | - <label for="{{ field.auto_id }}">{{ field.label }}</label> | 43 | + {% if field.field.required %} |
44 | + <label for="{{ field.auto_id }}">{{ field.label }}<span>*</span></label> | ||
45 | + {% else %} | ||
46 | + <label for="{{ field.auto_id }}">{{ field.label }}</label> | ||
47 | + {% endif %} | ||
40 | {% render_field field class='form-control' %} | 48 | {% render_field field class='form-control' %} |
41 | <div class="input-group"> | 49 | <div class="input-group"> |
42 | <input type="text" readonly="" class="form-control" placeholder="{% trans 'Choose your photo...' %}"> | 50 | <input type="text" readonly="" class="form-control" placeholder="{% trans 'Choose your photo...' %}"> |
@@ -53,15 +61,23 @@ | @@ -53,15 +61,23 @@ | ||
53 | </label> | 61 | </label> |
54 | </div> | 62 | </div> |
55 | {% elif field.auto_id == 'id_cpf' %} | 63 | {% elif field.auto_id == 'id_cpf' %} |
56 | - <label for="{{ field.auto_id }}">{{ field.label }}</label> | 64 | + {% if field.field.required %} |
65 | + <label for="{{ field.auto_id }}">{{ field.label }}<span>*</span></label> | ||
66 | + {% else %} | ||
67 | + <label for="{{ field.auto_id }}">{{ field.label }}</label> | ||
68 | + {% endif %} | ||
57 | {% render_field field class='form-control' onkeypress='campoNumerico(this,event); formatarCpf(this,event);' %} | 69 | {% render_field field class='form-control' onkeypress='campoNumerico(this,event); formatarCpf(this,event);' %} |
58 | {% else %} | 70 | {% else %} |
59 | - <label for="{{ field.auto_id }}">{{ field.label }}</label> | 71 | + {% if field.field.required %} |
72 | + <label for="{{ field.auto_id }}">{{ field.label }}<span>*</span></label> | ||
73 | + {% else %} | ||
74 | + <label for="{{ field.auto_id }}">{{ field.label }}</label> | ||
75 | + {% endif %} | ||
60 | {% render_field field class='form-control' %} | 76 | {% render_field field class='form-control' %} |
61 | {% endif %} | 77 | {% endif %} |
62 | <span id="helpBlock" class="help-block">{{ field.help_text }}</span> | 78 | <span id="helpBlock" class="help-block">{{ field.help_text }}</span> |
63 | - {% if field.errors.length > 0 %} | ||
64 | - <div class="alert alert-danger alert-dismissible" role="alert"> | 79 | + {% if field.errors %} |
80 | + <div class="alert alert-danger alert-dismissible col-md-offset-4 col-md-8" role="alert"> | ||
65 | <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | 81 | <button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
66 | <span aria-hidden="true">×</span> | 82 | <span aria-hidden="true">×</span> |
67 | </button> | 83 | </button> |
users/templates/users/profile.html
@@ -28,10 +28,8 @@ | @@ -28,10 +28,8 @@ | ||
28 | 28 | ||
29 | {% block content %} | 29 | {% block content %} |
30 | <div class="row"> | 30 | <div class="row"> |
31 | - <div class="row"> | ||
32 | - <div class="col-lg-offset-4 col-lg-2"> | ||
33 | - <img src="" class="img-responsive center-block " alt="logo amadeus"> | ||
34 | - </div> | 31 | + <div class="col-lg-offset-4 col-lg-2"> |
32 | + <img src="" class="img-responsive center-block " alt="logo amadeus"> | ||
35 | </div> | 33 | </div> |
36 | </div> | 34 | </div> |
37 | <div class="row"> | 35 | <div class="row"> |
users/templates/users/update.html
@@ -46,18 +46,14 @@ | @@ -46,18 +46,14 @@ | ||
46 | </button> | 46 | </button> |
47 | </span> | 47 | </span> |
48 | </div> | 48 | </div> |
49 | - {% elif field.auto_id == 'id_is_staff' or field.auto_id == 'id_is_active' %} | ||
50 | - <div class="checkbox"> | ||
51 | - <label> | ||
52 | - {% render_field field type='checkbox' %} | ||
53 | - </label> | ||
54 | - </div> | 49 | + {% elif field.auto_id == 'id_cpf' %} |
50 | + {% render_field field class='form-control' onkeypress='campoNumerico(this,event); formatarCpf(this,event);' %} | ||
55 | {% else %} | 51 | {% else %} |
56 | {% render_field field class='form-control' %} | 52 | {% render_field field class='form-control' %} |
57 | <span id="helpBlock" class="help-block">{{ field.help_text }}</span> | 53 | <span id="helpBlock" class="help-block">{{ field.help_text }}</span> |
58 | {% endif %} | 54 | {% endif %} |
59 | - {% if field.errors.length > 0 %} | ||
60 | - <div class="alert alert-danger alert-dismissible" role="alert"> | 55 | + {% if field.errors %} |
56 | + <div class="alert alert-danger alert-dismissible col-md-offset-4 col-md-8" role="alert"> | ||
61 | <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | 57 | <button type="button" class="close" data-dismiss="alert" aria-label="Close"> |
62 | <span aria-hidden="true">×</span> | 58 | <span aria-hidden="true">×</span> |
63 | </button> | 59 | </button> |
@@ -67,7 +63,6 @@ | @@ -67,7 +63,6 @@ | ||
67 | {% endfor %} | 63 | {% endfor %} |
68 | </ul> | 64 | </ul> |
69 | </div> | 65 | </div> |
70 | - </div> | ||
71 | {% endif %} | 66 | {% endif %} |
72 | </div> | 67 | </div> |
73 | {% endfor %} | 68 | {% endfor %} |
users/views.py
@@ -10,6 +10,7 @@ from rolepermissions.shortcuts import assign_role | @@ -10,6 +10,7 @@ from rolepermissions.shortcuts import assign_role | ||
10 | from .models import User | 10 | from .models import User |
11 | from .forms import UserForm, ProfileForm, UpdateUserForm | 11 | from .forms import UserForm, ProfileForm, UpdateUserForm |
12 | 12 | ||
13 | +# ================ ADMIN ======================= | ||
13 | class UsersListView(HasRoleMixin, LoginRequiredMixin, generic.ListView): | 14 | class UsersListView(HasRoleMixin, LoginRequiredMixin, generic.ListView): |
14 | 15 | ||
15 | allowed_roles = ['system_admin'] | 16 | allowed_roles = ['system_admin'] |
@@ -65,7 +66,7 @@ class Update(HasRoleMixin, LoginRequiredMixin, generic.UpdateView): | @@ -65,7 +66,7 @@ class Update(HasRoleMixin, LoginRequiredMixin, generic.UpdateView): | ||
65 | slug_url_kwarg = 'username' | 66 | slug_url_kwarg = 'username' |
66 | context_object_name = 'acc' | 67 | context_object_name = 'acc' |
67 | model = User | 68 | model = User |
68 | - form_class = UserForm | 69 | + form_class = UpdateUserForm |
69 | success_url = reverse_lazy('users:manage') | 70 | success_url = reverse_lazy('users:manage') |
70 | 71 | ||
71 | def form_valid(self, form): | 72 | def form_valid(self, form): |
@@ -94,6 +95,36 @@ class View(LoginRequiredMixin, generic.DetailView): | @@ -94,6 +95,36 @@ class View(LoginRequiredMixin, generic.DetailView): | ||
94 | slug_field = 'username' | 95 | slug_field = 'username' |
95 | slug_url_kwarg = 'username' | 96 | slug_url_kwarg = 'username' |
96 | 97 | ||
98 | +class UpdateUser(LoginRequiredMixin, generic.edit.UpdateView): | ||
99 | + | ||
100 | + allowed_roles = ['student'] | ||
101 | + login_url = reverse_lazy("core:home") | ||
102 | + template_name = 'users/edit_profile.html' | ||
103 | + form_class = UpdateUserForm | ||
104 | + success_url = reverse_lazy('users:update_profile') | ||
105 | + | ||
106 | + def get_object(self): | ||
107 | + user = get_object_or_404(User, username = self.request.user.username) | ||
108 | + return user | ||
109 | + | ||
110 | + def form_valid(self, form): | ||
111 | + form.save() | ||
112 | + messages.success(self.request, _('Profile edited successfully!')) | ||
113 | + | ||
114 | + return super(UpdateUser, self).form_valid(form) | ||
115 | + | ||
116 | +class DeleteUser(LoginRequiredMixin, generic.edit.DeleteView): | ||
117 | + allowed_roles = ['student'] | ||
118 | + login_url = reverse_lazy("core:home") | ||
119 | + model = User | ||
120 | + success_url = reverse_lazy('core:index') | ||
121 | + success_message = "Deleted Successfully" | ||
122 | + | ||
123 | + def get_queryset(self): | ||
124 | + user = get_object_or_404(User, username = self.request.user.username) | ||
125 | + return user | ||
126 | + | ||
127 | + | ||
97 | class Profile(LoginRequiredMixin, generic.DetailView): | 128 | class Profile(LoginRequiredMixin, generic.DetailView): |
98 | 129 | ||
99 | login_url = reverse_lazy("core:home") | 130 | login_url = reverse_lazy("core:home") |
@@ -131,34 +162,4 @@ class EditProfile(LoginRequiredMixin, generic.UpdateView): | @@ -131,34 +162,4 @@ class EditProfile(LoginRequiredMixin, generic.UpdateView): | ||
131 | 162 | ||
132 | messages.success(self.request, _('Profile edited successfully!')) | 163 | messages.success(self.request, _('Profile edited successfully!')) |
133 | 164 | ||
134 | - return super(EditProfile, self).form_valid(form) | ||
135 | - | ||
136 | - | ||
137 | -class UpdateUser(LoginRequiredMixin, generic.edit.UpdateView): | ||
138 | - | ||
139 | - allowed_roles = ['student'] | ||
140 | - login_url = reverse_lazy("core:home") | ||
141 | - template_name = 'users/edit_profile.html' | ||
142 | - form_class = UpdateUserForm | ||
143 | - success_url = reverse_lazy('users:update_profile') | ||
144 | - | ||
145 | - def get_object(self): | ||
146 | - user = get_object_or_404(User, username = self.request.user.username) | ||
147 | - return user | ||
148 | - | ||
149 | - def form_valid(self, form): | ||
150 | - form.save() | ||
151 | - messages.success(self.request, _('Profile edited successfully!')) | ||
152 | - | ||
153 | - return super(UpdateUser, self).form_valid(form) | ||
154 | - | ||
155 | -class DeleteUser(LoginRequiredMixin, generic.edit.DeleteView): | ||
156 | - allowed_roles = ['student'] | ||
157 | - login_url = reverse_lazy("core:home") | ||
158 | - model = User | ||
159 | - success_url = reverse_lazy('core:index') | ||
160 | - success_message = "Deleted Successfully" | ||
161 | - | ||
162 | - def get_queryset(self): | ||
163 | - user = get_object_or_404(User, username = self.request.user.username) | ||
164 | - return user | 165 | + return super(EditProfile, self).form_valid(form) |
165 | \ No newline at end of file | 166 | \ No newline at end of file |