Commit f0c5784dcec874d8bcf462de22567f828e2062ca

Authored by Matheus Lins
2 parents 253d8132 df375701

Merge branch 'dev' of https://github.com/amadeusproject/amadeuslms into dev

amadeus/settings.py
... ... @@ -48,6 +48,7 @@ INSTALLED_APPS = [
48 48 'courses',
49 49 'users',
50 50 'forum',
  51 + 'poll',
51 52 ]
52 53  
53 54 MIDDLEWARE_CLASSES = [
... ...
core/context_processors.py
... ... @@ -5,6 +5,6 @@ def notifications(request):
5 5 context['notifications'] = None
6 6 if request.user.is_authenticated:
7 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 10 return context
... ...
core/forms.py
... ... @@ -15,31 +15,7 @@ class RegisterUserForm(forms.ModelForm):
15 15  
16 16 def validate_cpf(self, cpf):
17 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 19 if cpfcnpj.validate(cpf):
44 20 return True
45 21 return False
... ...
core/static/css/base/amadeus.css
... ... @@ -266,7 +266,6 @@ li.alert_li:hover{background-color:#eee}
266 266 a.alert_message{color : grey}
267 267 a.alert_message:hover{color : grey}
268 268  
269   -/*=================== Ailson - Please Don't touch*/
270 269 .breadcrumb .divider{
271 270 display: none;
272 271 }
... ...
core/static/js/base/amadeus.js
... ... @@ -126,4 +126,14 @@ function validarCpfSemAlert(campo,nome,idElementoMensagemErro){
126 126 return true;
127 127 }
128 128 return retorno;
129   -}
130 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 + console.log(data);
  139 + });
  140 +}
... ...
core/static/js/base/header.js
1 1 $(document).ready(function(){
2 2 $('[data-toggle="tooltip"]').tooltip(); //activate tooltip on all elements that has attribute data-toggle
3   -});
4 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 27 \ No newline at end of file
... ...
core/templates/base.html
... ... @@ -8,7 +8,7 @@
8 8  
9 9 <meta http-equiv="Cache-Control" content="no-cache, no-store" />
10 10 <link href="{% static 'img/favicon.ico' %}" rel="shortcut icon" />
11   -
  11 +
12 12 <!-- Roboto font -->
13 13 <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
14 14 <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
... ... @@ -18,11 +18,11 @@
18 18 <script type="text/javascript" src="{% static 'js/vendor/jquery-ui.js' %}"></script>
19 19  
20 20 <!-- Bootstrap and themes (material) -->
21   - <link rel="stylesheet" type="text/css" href="{% static 'css/vendor/bootstrap.min.css' %}">
  21 + <link rel="stylesheet" type="text/css" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
22 22 <link rel="stylesheet" type="text/css" href="{% static 'css/vendor/material.min.css' %}">
23 23 <link rel="stylesheet" type="text/css" href="{% static 'css/vendor/ripples.min.css' %}">
24   - <link rel="stylesheet" type="text/css" href="{% static 'css/vendor/datepicker.css' %}">
25   - <script type="text/javascript" src="{% static 'js/vendor/bootstrap.min.js' %}"></script>
  24 + <link rel="stylesheet" type="text/css" href="{% static 'css/datepicker.css' %}">
  25 + <script type="text/javascript" src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script>
26 26 <script type="text/javascript" src="{% static 'js/vendor/bootstrap-acessibility.min.js' %}"></script>
27 27 <script type="text/javascript" src="{% static 'js/vendor/material.min.js' %}"></script>
28 28 <script type="text/javascript" src="{% static 'js/vendor/ripples.min.js' %}"></script>
... ... @@ -38,6 +38,7 @@
38 38 <script type="text/javascript">$.material.init()</script>
39 39  
40 40 <!--Javascript block for specific-app ones -->
  41 + <script src="{% static 'js/base/amadeus.js' %}"></script>
41 42 {% block style %}
42 43 {% endblock %}
43 44 </head>
... ... @@ -60,40 +61,10 @@
60 61 <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 62 <ul id="notification-dropdown" class="dropdown-menu">
62 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 66 <li>
96   - <a>
  67 + <a onclick="getNotifications(5)">
97 68 <div class="list-group-item">
98 69 <div class="row-content">
99 70 <p class="list-group-item-text">See More</p>
... ... @@ -141,8 +112,6 @@
141 112 </div>
142 113 </div>
143 114 </div>
144   -
145   -
146 115 </div>
147 116 </div>
148 117 </body>
... ...
core/templates/notifications.html 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +{% for notification in notifications %}
  2 + {% if notification.actor %} <!-- if the notification has a user-->
  3 + <li>
  4 + <a href="{% url 'core:notification_read' notification.id %}"><div class="list-group-item">
  5 + <div class="row-picture">
  6 + <img class="circle" src="http://lorempixel.com/56/56/people/1" alt="icon">
  7 + <div class="least-content pull-right">{{ notification.datetime }}</div>
  8 + </div>
  9 + <div class="row-content">
  10 + <p class="list-group-item-text">{{ notification.message }}</p>
  11 + </div>
  12 + </div>
  13 + </a>
  14 + </li>
  15 + {% else %}
  16 + <li>
  17 + <a href="{% url 'core:notification_read' notification.id %}">
  18 + <div class="list-group-item">
  19 + <div class="row-action-primary">
  20 + <i class="material-icons">folder</i>
  21 + </div>
  22 + <div class="row-content">
  23 +
  24 + <div class="least-content pull-right">{{ notification.datetime }}</div>
  25 +
  26 + <p class="list-group-item-text">{{ notification.message }}</p>
  27 + </div>
  28 + </a>
  29 + </li>
  30 + {% endif %}
  31 +{% endfor %}
0 32 \ No newline at end of file
... ...
core/templates/register_user.html
... ... @@ -8,11 +8,16 @@
8 8  
9 9  
10 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">&times;</span>
  16 + </button>
  17 + <p>{{ message }}</p>
  18 + </div>
  19 + {% endfor %}
  20 + {% endif %}
16 21 <div class="row logo-row">
17 22 <div class="col-lg-offset-2 col-lg-9">
18 23 <img src="{% static 'img/amadeus.png' %}" class="img-responsive center-block " alt="logo amadeus" id="logo">
... ... @@ -29,7 +34,11 @@
29 34 <legend>{% trans 'User Register' %}</legend>
30 35 {% for field in form %}
31 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 42 <div class="col-md-8">
34 43 {% if field.auto_id == 'id_birth_date' %}
35 44 {% render_field field class='form-control input-sm' type='date' %}
... ... @@ -81,5 +90,4 @@
81 90 </div>
82 91  
83 92 <br clear="all" />
84   - <script src="{% static 'js/base/amadeus.js' %}"></script>
85 93 {% endblock %}
... ...
core/tests.py
... ... @@ -89,7 +89,7 @@ class RegisterUserTestCase(TestCase):
89 89 }
90 90  
91 91 response = self.client.post(self.url, data)
92   - self.assertFormError(response, 'form', 'email', 'Insira um endereço de email válido.')
  92 + self.assertFormError(response, 'form', 'email', 'Enter a valid email address.')
93 93  
94 94 data = {
95 95 'username': '',
... ... @@ -102,7 +102,7 @@ class RegisterUserTestCase(TestCase):
102 102 'gender': 'F',
103 103 }
104 104 response = self.client.post(self.url, data)
105   - self.assertFormError(response, 'form', 'username', 'Este campo é obrigatório.')
  105 + self.assertFormError(response, 'form', 'username', 'This field is required.')
106 106  
107 107 class RememberPasswordTestCase(TestCase):
108 108  
... ... @@ -194,7 +194,7 @@ class UpdateUserTestCase(TestCase):
194 194 'gender': 'F',
195 195 }
196 196 response = self.client.post(self.url, data)
197   - self.assertFormError(response, 'form', 'username', 'Este campo é obrigatório.')
  197 + self.assertFormError(response, 'form', 'username', 'This field is required.')
198 198  
199 199  
200 200 class DeleteUserTestCase(TestCase):
... ...
core/urls.py
... ... @@ -9,5 +9,6 @@ urlpatterns = [
9 9 url(r'^register/$', views.RegisterUser.as_view(), name='register'),
10 10 url(r'^remember_password/$', views.remember_password, name='remember_password'),
11 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 5 from .decorators import log_decorator
6 6 from django.contrib import messages
7 7 from django.shortcuts import render, redirect
  8 +from django.template.loader import render_to_string
8 9 from django.views.generic import CreateView, UpdateView
9   -from django.http import HttpResponse
  10 +from django.http import HttpResponse, JsonResponse
10 11 from django.core.mail import send_mail,BadHeaderError
11 12 from django.conf import settings
12 13 from core.mixins import NotificationMixin
... ... @@ -95,6 +96,28 @@ def processNotification(self, notificationId):
95 96 notification.save()
96 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 121 # class LoginClass(LoginView):
99 122 # template_name='index.html'
100 123 #
... ...
courses/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-22 05:46
  2 +# Generated by Django 1.10 on 2016-09-28 20:27
3 3 from __future__ import unicode_literals
4 4  
5 5 import autoslug.fields
... ... @@ -22,7 +22,6 @@ class Migration(migrations.Migration):
22 22 name='Activity',
23 23 fields=[
24 24 ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Resource')),
25   - ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')),
26 25 ('limit_date', models.DateTimeField(verbose_name='Deliver Date')),
27 26 ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='student')),
28 27 ],
... ... @@ -37,8 +36,8 @@ class Migration(migrations.Migration):
37 36 ('create_date', models.DateField(auto_now_add=True, verbose_name='Creation Date')),
38 37 ],
39 38 options={
40   - 'verbose_name_plural': 'Categories',
41 39 'verbose_name': 'Category',
  40 + 'verbose_name_plural': 'Categories',
42 41 },
43 42 ),
44 43 migrations.CreateModel(
... ... @@ -57,20 +56,19 @@ class Migration(migrations.Migration):
57 56 ('end_date', models.DateField(verbose_name='End of Course Date')),
58 57 ('image', models.ImageField(blank=True, upload_to='courses/', verbose_name='Image')),
59 58 ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Category', verbose_name='Category')),
60   - ('professors', models.ManyToManyField(related_name='courses', to=settings.AUTH_USER_MODEL, verbose_name='Professors')),
  59 + ('professors', models.ManyToManyField(related_name='courses_professors', to=settings.AUTH_USER_MODEL, verbose_name='Professors')),
61 60 ('students', models.ManyToManyField(related_name='courses_student', to=settings.AUTH_USER_MODEL, verbose_name='Students')),
62 61 ],
63 62 options={
  63 + 'verbose_name': 'Course',
64 64 'verbose_name_plural': 'Courses',
65 65 'ordering': ('create_date', 'name'),
66   - 'verbose_name': 'Course',
67 66 },
68 67 ),
69 68 migrations.CreateModel(
70 69 name='Material',
71 70 fields=[
72 71 ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Resource')),
73   - ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')),
74 72 ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='student')),
75 73 ],
76 74 bases=('core.resource',),
... ... @@ -91,9 +89,23 @@ class Migration(migrations.Migration):
91 89 ('professors', models.ManyToManyField(related_name='subjects', to=settings.AUTH_USER_MODEL, verbose_name='Professors')),
92 90 ],
93 91 options={
  92 + 'verbose_name': 'Subject',
94 93 'verbose_name_plural': 'Subjects',
95 94 'ordering': ('create_date', 'name'),
96   - 'verbose_name': 'Subject',
  95 + },
  96 + ),
  97 + migrations.CreateModel(
  98 + name='SubjectCategory',
  99 + fields=[
  100 + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
  101 + ('name', models.CharField(max_length=100, verbose_name='Name')),
  102 + ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')),
  103 + ('description', models.TextField(blank=True, verbose_name='Description')),
  104 + ('subjects', models.ManyToManyField(to='courses.Subject')),
  105 + ],
  106 + options={
  107 + 'verbose_name': 'subject category',
  108 + 'verbose_name_plural': 'subject categories',
97 109 },
98 110 ),
99 111 migrations.CreateModel(
... ... @@ -110,9 +122,9 @@ class Migration(migrations.Migration):
110 122 ('subject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Subject', verbose_name='Subject')),
111 123 ],
112 124 options={
  125 + 'verbose_name': 'Topic',
113 126 'verbose_name_plural': 'Topics',
114 127 'ordering': ('create_date', 'name'),
115   - 'verbose_name': 'Topic',
116 128 },
117 129 ),
118 130 migrations.AddField(
... ...
courses/migrations/0002_auto_20160926_0026.py
... ... @@ -1,38 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-26 03:26
3   -from __future__ import unicode_literals
4   -
5   -import autoslug.fields
6   -from django.db import migrations, models
7   -
8   -
9   -class Migration(migrations.Migration):
10   -
11   - dependencies = [
12   - ('courses', '0001_initial'),
13   - ]
14   -
15   - operations = [
16   - migrations.CreateModel(
17   - name='SubjectCategory',
18   - fields=[
19   - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20   - ('name', models.CharField(max_length=100, verbose_name='Name')),
21   - ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')),
22   - ('description', models.TextField(blank=True, verbose_name='Description')),
23   - ('subjects', models.ManyToManyField(to='courses.Subject')),
24   - ],
25   - options={
26   - 'verbose_name_plural': 'subject categories',
27   - 'verbose_name': 'subject category',
28   - },
29   - ),
30   - migrations.RemoveField(
31   - model_name='activity',
32   - name='create_date',
33   - ),
34   - migrations.RemoveField(
35   - model_name='material',
36   - name='create_date',
37   - ),
38   - ]
courses/migrations/0003_auto_20160927_1945.py
... ... @@ -1,52 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-27 22:45
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   - ('courses', '0002_auto_20160926_0026'),
14   - ]
15   -
16   - operations = [
17   - migrations.CreateModel(
18   - name='Answer',
19   - fields=[
20   - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21   - ('answer', models.CharField(max_length=200, verbose_name='Answer')),
22   - ('order', models.PositiveSmallIntegerField(verbose_name='Order')),
23   - ],
24   - options={
25   - 'verbose_name_plural': 'Answers',
26   - 'verbose_name': 'Answer',
27   - 'ordering': ('order',),
28   - },
29   - ),
30   - migrations.CreateModel(
31   - name='Poll',
32   - fields=[
33   - ('activity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='courses.Activity')),
34   - ('question', models.CharField(max_length=300, verbose_name='Question')),
35   - ],
36   - options={
37   - 'verbose_name_plural': 'Polls',
38   - 'verbose_name': 'Poll',
39   - },
40   - bases=('courses.activity',),
41   - ),
42   - migrations.AlterField(
43   - model_name='course',
44   - name='professors',
45   - field=models.ManyToManyField(related_name='courses_professors', to=settings.AUTH_USER_MODEL, verbose_name='Professors'),
46   - ),
47   - migrations.AddField(
48   - model_name='answer',
49   - name='poll',
50   - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='courses.Poll', verbose_name='Answers'),
51   - ),
52   - ]
courses/models.py
... ... @@ -105,35 +105,11 @@ It is a category for each subject.
105 105 """
106 106 class SubjectCategory(models.Model):
107 107 name = models.CharField(_('Name'), max_length= 100)
108   - slug = AutoSlugField(_("Slug"),populate_from='name',unique=True)
  108 + slug = AutoSlugField(_("Slug"),populate_from='name',unique=True)
109 109 description = models.TextField(_('Description'), blank = True)
110 110 subjects = models.ManyToManyField(Subject)
111 111  
112 112 class Meta:
113 113 verbose_name = _('subject category')
114 114 verbose_name_plural = _('subject categories')
115   -
116   -class Poll(Activity):
117   - question = models.CharField(_('Question'), max_length = 300)
118   -
119   - class Meta:
120   - #ordering = ('create_date','name')
121   - verbose_name = _('Poll')
122   - verbose_name_plural = _('Polls')
123   -
124   - def __str__(self):
125   - return str(self.question) + str("/") + str(self.topic)
126   -
127   -class Answer(models.Model):
128   - answer = models.CharField(_("Answer"), max_length = 200)
129   - order = models.PositiveSmallIntegerField(_("Order"))
130   - poll = models.ForeignKey(Poll, verbose_name = _('Answers'), related_name='answers')
131   -
132   - class Meta:
133   - ordering = ('order',)
134   - verbose_name = _('Answer')
135   - verbose_name_plural = _('Answers')
136   -
137   - def __str__(self):
138   - return str(self.question) + str("/") + str(self.topic)
139   -
  115 +
... ...
courses/templates/poll/poll.html
... ... @@ -1,53 +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><span class="glyphicon glyphicon-menu-hamburger"></span></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">Please enter a valid email address</span>
27   - </div>
28   - </div>
29   - <div class="col-md-1">
30   - </br>
31   - <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></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   - <a href="#" class="small">View Result</a>
42   - </div>
43   - </div>
44   -<script type="text/javascript">
45   -$( "#form" ).sortable({
46   - delay: 100,
47   - distance: 5,
48   -});
49   -$("#add").click(function() {
50   - $("#form").append('<div class="row form-group"><div class="col-md-1"></br><label><span class="glyphicon glyphicon-menu-hamburger"></span></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><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label></div></br></div>');
51   -});
52   -</script>
53   -{% endblock content %}
courses/templates/subject/form_view_teacher.html
... ... @@ -32,16 +32,6 @@
32 32 <div class="modal-body">
33 33 <section>
34 34 <div class="forum_topics"></div>
35   - <div class="form-group">
36   - <div class="input-group">
37   - <textarea type="text" id="addon3a" class="form-control" placeholder="{% trans 'Post a comment...' %}"></textarea>
38   - <span class="input-group-btn">
39   - <button type="button" class="btn btn-fab btn-fab-mini">
40   - <i class="material-icons">send</i>
41   - </button>
42   - </span>
43   - </div>
44   - </div>
45 35 </section>
46 36 </div>
47 37 <div class="modal-footer">
... ...
courses/urls.py
1   -from django.conf.urls import url
  1 +from django.conf.urls import url, include
2 2  
3 3 from . import views
4 4  
... ... @@ -22,6 +22,8 @@ urlpatterns = [
22 22 url(r'^topics/update/(?P<slug>[\w_-]+)/$', views.UpdateTopicView.as_view(), name='update_topic'),
23 23 url(r'^topics/(?P<slug>[\w_-]+)/$', views.TopicsView.as_view(), name='view_topic'),
24 24 url(r'^subjects/categories$',views.IndexSubjectCategoryView.as_view(), name='subject_category_index'),
25   - url(r'^to/poll/to/$', views.Poll.as_view(), name='poll'),
  25 +
  26 +
  27 + url(r'^poll/', include('poll.urls', namespace = 'poll'))
26 28  
27 29 ]
... ...
courses/views.py
... ... @@ -455,27 +455,3 @@ class IndexSubjectCategoryView(LoginRequiredMixin, generic.ListView):
455 455 context = super(IndexSubjectCategoryView, self).get_context_data(**kwargs)
456 456 context['subject_categories'] = SubjectCategory.objects.all()
457 457 return context
458   -
459   -
460   -class Poll(generic.TemplateView):
461   -
462   - # login_url = reverse_lazy("core:home")
463   - # redirect_field_name = 'next'
464   - # model = Course
465   - # context_object_name = 'course'
466   - template_name = 'poll/poll.html'
467   - # queryset = Course.objects.all()
468   -
469   - # def get_queryset(self):
470   - # return Course.objects.all()[0]
471   -
472   - def get_context_data(self, **kwargs):
473   - context = super(Poll, self).get_context_data(**kwargs)
474   - course = Course.objects.all()[0]
475   - context['course'] = course
476   - context['subject'] = course.subjects.all()[0]
477   - context['subjects'] = course.subjects.all()
478   - # if (has_role(self.request.user,'system_admin')):
479   - # context['subjects'] = self.object.course.subjects.all()
480   - return context
481   -
... ...
forum/forms.py
1 1 from django import forms
2 2 from django.utils.translation import ugettext_lazy as _
3   -from .models import Forum, PostAnswer
  3 +from .models import Forum, Post, PostAnswer
4 4  
5 5 class ForumForm(forms.ModelForm):
6 6  
... ... @@ -19,6 +19,19 @@ class ForumForm(forms.ModelForm):
19 19 'description': forms.Textarea(attrs={'cols': 80, 'rows': 5}),
20 20 }
21 21  
  22 +class PostForm(forms.ModelForm):
  23 +
  24 + class Meta:
  25 + model = Post
  26 + fields = ('message', 'forum', )
  27 + labels = {
  28 + 'message': _('Message')
  29 + }
  30 + widgets = {
  31 + 'message': forms.Textarea(attrs={'cols': 80, 'rows': 3}),
  32 + 'forum': forms.HiddenInput(),
  33 + }
  34 +
22 35 class PostAnswerForm(forms.ModelForm):
23 36  
24 37 class Meta:
... ...
forum/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-22 20:41
  2 +# Generated by Django 1.10 on 2016-09-28 20:27
3 3 from __future__ import unicode_literals
4 4  
5 5 from django.conf import settings
... ... @@ -21,8 +21,9 @@ class Migration(migrations.Migration):
21 21 name='Forum',
22 22 fields=[
23 23 ('activity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='courses.Activity')),
24   - ('title', models.CharField(max_length=100, verbose_name='Title')),
25 24 ('description', models.TextField(blank=True, verbose_name='Description')),
  25 + ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')),
  26 + ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Create Date')),
26 27 ],
27 28 options={
28 29 'verbose_name': 'Forum',
... ... @@ -35,6 +36,7 @@ class Migration(migrations.Migration):
35 36 fields=[
36 37 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
37 38 ('message', models.TextField(verbose_name='Post message')),
  39 + ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')),
38 40 ('post_date', models.DateTimeField(auto_now_add=True, verbose_name='Post Date')),
39 41 ('forum', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Forum', verbose_name='Forum')),
40 42 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor')),
... ... @@ -49,6 +51,7 @@ class Migration(migrations.Migration):
49 51 fields=[
50 52 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
51 53 ('message', models.TextField(verbose_name='Answer message')),
  54 + ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')),
52 55 ('answer_date', models.DateTimeField(auto_now_add=True, verbose_name='Answer Date')),
53 56 ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Post', verbose_name='Post')),
54 57 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor')),
... ...
forum/migrations/0002_remove_forum_title.py
... ... @@ -1,19 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-22 20:43
3   -from __future__ import unicode_literals
4   -
5   -from django.db import migrations
6   -
7   -
8   -class Migration(migrations.Migration):
9   -
10   - dependencies = [
11   - ('forum', '0001_initial'),
12   - ]
13   -
14   - operations = [
15   - migrations.RemoveField(
16   - model_name='forum',
17   - name='title',
18   - ),
19   - ]
forum/migrations/0003_forum_create_date.py
... ... @@ -1,22 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-28 02:17
3   -from __future__ import unicode_literals
4   -
5   -from django.db import migrations, models
6   -import django.utils.timezone
7   -
8   -
9   -class Migration(migrations.Migration):
10   -
11   - dependencies = [
12   - ('forum', '0002_remove_forum_title'),
13   - ]
14   -
15   - operations = [
16   - migrations.AddField(
17   - model_name='forum',
18   - name='create_date',
19   - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Create Date'),
20   - preserve_default=False,
21   - ),
22   - ]
forum/migrations/0004_auto_20160928_1558.py
... ... @@ -1,30 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-28 18:58
3   -from __future__ import unicode_literals
4   -
5   -from django.db import migrations, models
6   -
7   -
8   -class Migration(migrations.Migration):
9   -
10   - dependencies = [
11   - ('forum', '0003_forum_create_date'),
12   - ]
13   -
14   - operations = [
15   - migrations.AddField(
16   - model_name='forum',
17   - name='modification_date',
18   - field=models.DateTimeField(auto_now=True, verbose_name='Modification Date'),
19   - ),
20   - migrations.AddField(
21   - model_name='post',
22   - name='modification_date',
23   - field=models.DateTimeField(auto_now=True, verbose_name='Modification Date'),
24   - ),
25   - migrations.AddField(
26   - model_name='postanswer',
27   - name='modification_date',
28   - field=models.DateTimeField(auto_now=True, verbose_name='Modification Date'),
29   - ),
30   - ]
forum/static/js/forum.js
  1 +function getCookie(name) {
  2 + var cookieValue = null;
  3 + if (document.cookie && document.cookie !== '') {
  4 + var cookies = document.cookie.split(';');
  5 + for (var i = 0; i < cookies.length; i++) {
  6 + var cookie = jQuery.trim(cookies[i]);
  7 + // Does this cookie string begin with the name we want?
  8 + if (cookie.substring(0, name.length + 1) === (name + '=')) {
  9 + cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
  10 + break;
  11 + }
  12 + }
  13 + }
  14 + return cookieValue;
  15 +}
  16 +
  17 +
1 18 /*
2 19 *
3 20 * Function to load forum to modal
... ... @@ -9,12 +26,45 @@ function showForum(url, forum_id) {
9 26 data: {'forum_id': forum_id},
10 27 success: function(data) {
11 28 $(".forum_topics").html(data);
  29 +
  30 + var frm = $('#form_post');
  31 + frm.submit(function () {
  32 + $.ajax({
  33 + type: frm.attr('method'),
  34 + url: frm.attr('action'),
  35 + data: frm.serialize(),
  36 + success: function (data) {
  37 + $("#posts_list").append(data);
  38 + frm[0].reset();
  39 + },
  40 + error: function(data) {
  41 + console.log(frm.serialize());
  42 + console.log('Error');
  43 + }
  44 + });
  45 + return false;
  46 + });
12 47 }
13 48 });
14 49  
15 50 $('#forumModal').modal();
16 51 }
17 52  
  53 +function delete_post(url, post) {
  54 + var csrftoken = getCookie('csrftoken');
  55 +
  56 + $.ajax({
  57 + method: 'post',
  58 + beforeSend: function (request) {
  59 + request.setRequestHeader('X-CSRFToken', csrftoken);
  60 + },
  61 + url: url,
  62 + success: function(data) {
  63 + $("#post_"+post).remove();
  64 + }
  65 + });
  66 +}
  67 +
18 68 function answer(id, url) {
19 69 $.ajax({
20 70 url: url,
... ...
forum/templates/forum/forum_list.html
1 1 {% load i18n permission_tags list_post %}
  2 +{% load widget_tweaks %}
2 3  
3 4 <div class="comments-list">
4 5 <div class="section-heading">
... ... @@ -8,7 +9,45 @@
8 9 </div>
9 10 </div>
10 11  
11   -{% list_posts request forum %}
  12 +<div id="posts_list">
  13 + {% list_posts request forum %}
  14 +</div>
  15 +
  16 +<form id="form_post" method="post" action="{% url 'forum:create_post' %}" enctype="multipart/form-data">
  17 + {% csrf_token %}
  18 + {% for field in form %}
  19 + {% if field.field.widget.input_type == 'hidden' %}
  20 + {% render_field field class='form-control' value=forum.id %}
  21 + {% else %}
  22 + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput">
  23 + <div class="input-group">
  24 + {% render_field field class='form-control' placeholder="Post a message" %}
  25 + <span class="help-block">{{ field.help_text }}</span>
  26 + {% if field.errors %}
  27 + <div class="row">
  28 + <br />
  29 + <div class="alert alert-danger alert-dismissible" role="alert">
  30 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  31 + <span aria-hidden="true">&times;</span>
  32 + </button>
  33 + <ul>
  34 + {% for error in field.errors %}
  35 + <li>{{ error }}</li>
  36 + {% endfor %}
  37 + </ul>
  38 + </div>
  39 + </div>
  40 + {% endif %}
  41 + <span class="input-group-btn">
  42 + <button type="submit" class="btn btn-fab btn-fab-mini">
  43 + <i class="material-icons">send</i>
  44 + </button>
  45 + </span>
  46 + </div>
  47 + </div>
  48 + {% endif %}
  49 + {% endfor %}
  50 +</form>
12 51  
13 52 <!--{% if foruns|length > 0 %}
14 53 {% for forum in foruns %}
... ...
forum/templates/post/post_list.html
... ... @@ -11,13 +11,14 @@
11 11 <i class="material-icons">reply</i>
12 12 </a>
13 13 {% if request.user|has_role:'system_admin' or request.user|has_role:'professor' and request.user == post.user %}
  14 + {% csrf_token %}
14 15 <div class="btn-group icon-more-horiz">
15 16 <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
16 17 <i class="material-icons">more_horiz</i>
17 18 </a>
18 19 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
19 20 <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li>
20   - <li><a href="javascript:void(0)"><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>
21 22 </ul>
22 23 </div>
23 24 {% endif %}
... ...
forum/templates/post/post_render.html 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +{% load i18n permission_tags %}
  2 +
  3 +<div class="row">
  4 + <div id="post_{{ post.id }}" class="col-sm-12 col-xs-12">
  5 + <h3 class="user-name">
  6 + {{ post.user }}
  7 + <div class="pull-right">
  8 + <a href="javascript:answer('{{ post.id }}', '{% url 'forum:reply_post' %}');">
  9 + <i class="material-icons">reply</i>
  10 + </a>
  11 + {% if request.user|has_role:'system_admin' or request.user|has_role:'professor' and request.user == post.user %}
  12 + {% csrf_token %}
  13 + <div class="btn-group icon-more-horiz">
  14 + <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
  15 + <i class="material-icons">more_horiz</i>
  16 + </a>
  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>
  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>
  21 + </div>
  22 + {% endif %}
  23 + </div>
  24 + </h3>
  25 + <div class="card-data">
  26 + <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p>
  27 + </div>
  28 + <p class="comment-text">{{ post.message|linebreaks }}</p>
  29 + <div class="answer_post"></div>
  30 + </div>
  31 +</div>
0 32 \ No newline at end of file
... ...
forum/templates/post_answers/post_answer_form.html
... ... @@ -25,7 +25,7 @@
25 25 </div>
26 26 {% endif %}
27 27 <span class="input-group-btn">
28   - <button type="button" class="btn btn-fab btn-fab-mini">
  28 + <button type="submit" class="btn btn-fab btn-fab-mini">
29 29 <i class="material-icons">send</i>
30 30 </button>
31 31 </span>
... ...
forum/urls.py
... ... @@ -7,6 +7,10 @@ urlpatterns = [
7 7 url(r'^$', views.ForumIndex.as_view(), name='index'),
8 8 url(r'^create$', views.CreateForumView.as_view(), name='create'),
9 9 url(r'^posts$', views.PostIndex.as_view(), name='posts'),
  10 + url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'),
  11 + url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'),
  12 + url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'),
  13 + url(r'^post_deleted/$', views.post_deleted, name='deleted_post'),
10 14 url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'),
11 15 url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'),
12 16 ]
... ...
forum/views.py
  1 +from django.http import HttpResponse
1 2 from django.shortcuts import render, get_object_or_404
2   -from django.core.urlresolvers import reverse_lazy
  3 +from django.core.urlresolvers import reverse, reverse_lazy
3 4 from django.utils.translation import ugettext_lazy as _
4 5 from django.views import generic
5 6 from django.contrib.auth.mixins import LoginRequiredMixin
... ... @@ -7,7 +8,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
7 8 from .models import Forum, Post, PostAnswer
8 9 from courses.models import Topic
9 10  
10   -from .forms import ForumForm, PostAnswerForm
  11 +from .forms import ForumForm, PostForm, PostAnswerForm
11 12  
12 13 class ForumIndex(LoginRequiredMixin, generic.ListView):
13 14 login_url = reverse_lazy("core:home")
... ... @@ -20,6 +21,12 @@ class ForumIndex(LoginRequiredMixin, generic.ListView):
20 21 forum_id = self.request.GET.get('forum_id', 0)
21 22  
22 23 context = Forum.objects.get(id = forum_id)
  24 +
  25 + return context
  26 +
  27 + def get_context_data(self, **kwargs):
  28 + context = super(ForumIndex, self).get_context_data(**kwargs)
  29 + context['form'] = PostForm()
23 30  
24 31 return context
25 32  
... ... @@ -43,6 +50,44 @@ class PostIndex(LoginRequiredMixin, generic.ListView):
43 50  
44 51 return context
45 52  
  53 +class CreatePostView(LoginRequiredMixin, generic.edit.CreateView):
  54 + login_url = reverse_lazy("core:home")
  55 + redirect_field_name = 'next'
  56 +
  57 + form_class = PostForm
  58 +
  59 + def form_valid(self, form):
  60 + self.object = form.save(commit = False)
  61 + self.object.user = self.request.user
  62 +
  63 + self.object.save()
  64 +
  65 + return super(CreatePostView, self).form_valid(form)
  66 +
  67 + def get_success_url(self):
  68 + self.success_url = reverse('forum:render_post', args = (self.object.id, ))
  69 +
  70 + return self.success_url
  71 +
  72 +def render_post(request, post):
  73 + last_post = get_object_or_404(Post, id = post)
  74 +
  75 + context = {}
  76 + context['post'] = last_post
  77 +
  78 + return render(request, "post/post_render.html", context)
  79 +
  80 +class PostDeleteView(LoginRequiredMixin, generic.DeleteView):
  81 + login_url = reverse_lazy("core:home")
  82 + redirect_field_name = 'next'
  83 +
  84 + model = Post
  85 + pk_url_kwarg = 'pk'
  86 + success_url = reverse_lazy('forum:deleted_post')
  87 +
  88 +def post_deleted(request):
  89 + return HttpResponse(_("Post deleted successfully."))
  90 +
46 91 class PostAnswerIndex(LoginRequiredMixin, generic.ListView):
47 92 login_url = reverse_lazy("core:home")
48 93 redirect_field_name = 'next'
... ...
poll/__init__.py 0 → 100644
poll/admin.py 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +from django.contrib import admin
  2 +
  3 +# Register your models here.
... ...
poll/apps.py 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class PollConfig(AppConfig):
  5 + name = 'poll'
... ...
poll/migrations/0001_initial.py 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +# -*- coding: utf-8 -*-
  2 +# Generated by Django 1.10 on 2016-09-28 20:27
  3 +from __future__ import unicode_literals
  4 +
  5 +from django.db import migrations, models
  6 +import django.db.models.deletion
  7 +
  8 +
  9 +class Migration(migrations.Migration):
  10 +
  11 + initial = True
  12 +
  13 + dependencies = [
  14 + ('courses', '0001_initial'),
  15 + ]
  16 +
  17 + operations = [
  18 + migrations.CreateModel(
  19 + name='Answer',
  20 + fields=[
  21 + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
  22 + ('answer', models.CharField(max_length=200, verbose_name='Answer')),
  23 + ('order', models.PositiveSmallIntegerField(verbose_name='Order')),
  24 + ],
  25 + options={
  26 + 'verbose_name': 'Answer',
  27 + 'verbose_name_plural': 'Answers',
  28 + 'ordering': ('order',),
  29 + },
  30 + ),
  31 + migrations.CreateModel(
  32 + name='Poll',
  33 + fields=[
  34 + ('activity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='courses.Activity')),
  35 + ('question', models.CharField(max_length=300, verbose_name='Question')),
  36 + ],
  37 + options={
  38 + 'verbose_name': 'Poll',
  39 + 'verbose_name_plural': 'Polls',
  40 + },
  41 + bases=('courses.activity',),
  42 + ),
  43 + migrations.AddField(
  44 + model_name='answer',
  45 + name='poll',
  46 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='poll.Poll', verbose_name='Answers'),
  47 + ),
  48 + ]
... ...
poll/migrations/__init__.py 0 → 100644
poll/models.py 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +from django.utils.translation import ugettext_lazy as _
  2 +from django.db import models
  3 +from autoslug.fields import AutoSlugField
  4 +from users.models import User
  5 +from core.models import Resource
  6 +from courses.models import Activity
  7 +
  8 +class Poll(Activity):
  9 + question = models.CharField(_('Question'), max_length = 300)
  10 +
  11 + class Meta:
  12 + #ordering = ('create_date','name')
  13 + verbose_name = _('Poll')
  14 + verbose_name_plural = _('Polls')
  15 +
  16 + def __str__(self):
  17 + return str(self.question) + str("/") + str(self.topic)
  18 +
  19 +class Answer(models.Model):
  20 + answer = models.CharField(_("Answer"), max_length = 200)
  21 + order = models.PositiveSmallIntegerField(_("Order"))
  22 + poll = models.ForeignKey(Poll, verbose_name = _('Answers'), related_name='answers')
  23 +
  24 + class Meta:
  25 + ordering = ('order',)
  26 + verbose_name = _('Answer')
  27 + verbose_name_plural = _('Answers')
  28 +
  29 + def __str__(self):
  30 + return str(self.question) + str("/") + str(self.topic)
... ...
poll/templates/poll/poll.html 0 → 100644
... ... @@ -0,0 +1,52 @@
  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 %}
... ...
poll/tests.py 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
... ...
poll/urls.py 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +from django.conf.urls import url
  2 +
  3 +from . import views
  4 +
  5 +urlpatterns = [
  6 + url(r'^to/poll/to/$', views.Poll.as_view(), name='poll'),
  7 +
  8 +]
... ...
poll/views.py 0 → 100644
... ... @@ -0,0 +1,38 @@
  1 +from django.shortcuts import render, get_object_or_404, redirect
  2 +from django.views import generic
  3 +from django.contrib.auth.decorators import login_required
  4 +from django.core.paginator import Paginator, EmptyPage
  5 +from django.contrib.auth.mixins import LoginRequiredMixin
  6 +from rolepermissions.mixins import HasRoleMixin
  7 +from django.core.urlresolvers import reverse_lazy
  8 +from django.utils.translation import ugettext_lazy as _
  9 +from rolepermissions.verifications import has_role
  10 +from rolepermissions.verifications import has_object_permission
  11 +
  12 +# from .forms import CourseForm, UpdateCourseForm, CategoryForm, SubjectForm,TopicForm
  13 +# from .models import Course, Subject, Category,Topic, SubjectCategory
  14 +from core.mixins import NotificationMixin
  15 +from users.models import User
  16 +from courses.models import Course
  17 +
  18 +class Poll(generic.TemplateView):
  19 +
  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()
  26 +
  27 + # def get_queryset(self):
  28 + # return Course.objects.all()[0]
  29 +
  30 + 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()
  38 + return context
... ...
requirements.txt
... ... @@ -10,7 +10,6 @@ itsdangerous==0.24
10 10 Jinja2==2.8
11 11 MarkupSafe==0.23
12 12 Pillow==3.3.1
13   -pkg-resources==0.0.0
14 13 pycpfcnpj==1.0.2
15 14 six==1.10.0
16 15 slugify==0.0.1
... ...
users/forms.py
... ... @@ -27,25 +27,6 @@ class ProfileForm(forms.ModelForm):
27 27 }
28 28  
29 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 31 class Meta:
51 32 model = User
... ... @@ -59,7 +40,8 @@ class EditUserForm(forms.ModelForm):
59 40  
60 41 # Ailson
61 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 48 \ No newline at end of file
... ...
users/templates/list_users.html
... ... @@ -63,9 +63,29 @@
63 63 <p>{% trans 'Contact' %}: {{ acc.phone }}</p>
64 64 <div align="right">
65 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 67 </div>
68 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">&times;</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 89 </div>
70 90 </div>
71 91 {% endfor %}
... ...
users/templates/users/create.html
... ... @@ -33,10 +33,18 @@
33 33 {% for field in form %}
34 34 <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput">
35 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 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 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 48 {% render_field field class='form-control' %}
41 49 <div class="input-group">
42 50 <input type="text" readonly="" class="form-control" placeholder="{% trans 'Choose your photo...' %}">
... ... @@ -53,15 +61,23 @@
53 61 </label>
54 62 </div>
55 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 69 {% render_field field class='form-control' onkeypress='campoNumerico(this,event); formatarCpf(this,event);' %}
58 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 76 {% render_field field class='form-control' %}
61 77 {% endif %}
62 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 81 <button type="button" class="close" data-dismiss="alert" aria-label="Close">
66 82 <span aria-hidden="true">&times;</span>
67 83 </button>
... ...
users/templates/users/profile.html
... ... @@ -28,10 +28,8 @@
28 28  
29 29 {% block content %}
30 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 33 </div>
36 34 </div>
37 35 <div class="row">
... ...
users/templates/users/update.html
... ... @@ -46,18 +46,14 @@
46 46 </button>
47 47 </span>
48 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 51 {% else %}
56 52 {% render_field field class='form-control' %}
57 53 <span id="helpBlock" class="help-block">{{ field.help_text }}</span>
58 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 57 <button type="button" class="close" data-dismiss="alert" aria-label="Close">
62 58 <span aria-hidden="true">&times;</span>
63 59 </button>
... ... @@ -67,7 +63,6 @@
67 63 {% endfor %}
68 64 </ul>
69 65 </div>
70   - </div>
71 66 {% endif %}
72 67 </div>
73 68 {% endfor %}
... ...
users/views.py
... ... @@ -10,6 +10,7 @@ from rolepermissions.shortcuts import assign_role
10 10 from .models import User
11 11 from .forms import UserForm, ProfileForm, UpdateUserForm
12 12  
  13 +# ================ ADMIN =======================
13 14 class UsersListView(HasRoleMixin, LoginRequiredMixin, generic.ListView):
14 15  
15 16 allowed_roles = ['system_admin']
... ... @@ -65,7 +66,7 @@ class Update(HasRoleMixin, LoginRequiredMixin, generic.UpdateView):
65 66 slug_url_kwarg = 'username'
66 67 context_object_name = 'acc'
67 68 model = User
68   - form_class = UserForm
  69 + form_class = UpdateUserForm
69 70 success_url = reverse_lazy('users:manage')
70 71  
71 72 def form_valid(self, form):
... ... @@ -94,6 +95,36 @@ class View(LoginRequiredMixin, generic.DetailView):
94 95 slug_field = 'username'
95 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 128 class Profile(LoginRequiredMixin, generic.DetailView):
98 129  
99 130 login_url = reverse_lazy("core:home")
... ... @@ -131,34 +162,4 @@ class EditProfile(LoginRequiredMixin, generic.UpdateView):
131 162  
132 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 166 \ No newline at end of file
... ...