Commit 726a3044bcdf1e7d568b72dec0f8acbe1b1a7d9d

Authored by filipecmedeiros
2 parents 8770943c 510f3e21

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

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/migrations/0002_auto_20160930_0124.py 0 → 100644
... ... @@ -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 6  
7 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 10 "visualize post", etc. It is supposed to be created everytime we want an aciton
11 11 """
12 12  
13 13 name = models.CharField(_('Name'), max_length = 100)
14 14 created_date = models.DateField(_('Created Date'), auto_now_add=True)
15   -
  15 +
16 16 class Meta:
17 17 verbose_name = "Action"
18 18 verbose_name_plural = "Actions"
19 19  
20 20 def __str__(self):
21 21 return self.name
22   -
  22 +
23 23  
24 24 class Resource(models.Model):
25 25 """
... ... @@ -27,38 +27,38 @@ class Resource(models.Model):
27 27 Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF).
28 28  
29 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 31 by a huge amount of actions
32 32 @created_date: The date the resource was created
33 33 @link: Which URL made that resource able to find
34 34 """
35 35  
36 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 38 created_date = models.DateField(_('Created Date'), auto_now_add=True)
39 39 url = models.CharField(_('URL'), max_length =100, default="")
40 40  
41 41  
42 42 class Meta:
43 43 verbose_name = "Resource"
44   - verbose_name_plural = "Resources"
  44 + verbose_name_plural = "Resources"
45 45  
46 46 def __str__(self):
47 47 return self.name
48 48  
49 49  
50 50 class Action_Resource(models.Model):
51   -
  51 +
52 52 action = models.ForeignKey(Action , verbose_name= _('Action_Applied'))
53 53 resource = models.ForeignKey(Resource, verbose_name = _('Resource'))
54   -
  54 +
55 55 class Meta:
56 56 verbose_name = "Action_Resource"
57 57 verbose_name_plural = "Action_Resources"
58 58  
59 59 def __str__(self):
60 60 return ''.join([self.action.name, " / ", self.resource.name])
61   -
  61 +
62 62  
63 63 class Notification(models.Model):
64 64 """
... ... @@ -77,7 +77,7 @@ class Notification(models.Model):
77 77 datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True)
78 78 action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource'))
79 79 actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True)
80   -
  80 +
81 81 class Meta:
82 82 verbose_name = _("Notification")
83 83 verbose_name_plural = _("Notifications")
... ...
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,18 @@ 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 + $("#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 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
... ... @@ -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>
... ... @@ -57,44 +58,14 @@
57 58 <div class="navbar-collapse collapse navbar-responsive-collapse">
58 59 <ul class="nav navbar-nav navbar-right notifications">
59 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 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>
97   - <div class="list-group-item">
  67 + <a onclick="getNotifications(5)">
  68 + <div id="notification-see-more" class="list-group-item">
98 69 <div class="row-content">
99 70 <p class="list-group-item-text">See More</p>
100 71 </div>
... ...
core/templates/notifications.html 0 → 100644
... ... @@ -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 34 \ 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/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/0002_auto_20160930_0124.py 0 → 100644
... ... @@ -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 88 Activity is something that has a deadline and has to be delivered by the student
89 89 """
90 90 class Activity(Resource):
91   - topic = models.ForeignKey(Topic, verbose_name = _('Topic'))
  91 + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='activities')
92 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 98 It represents any Material inside a topic, be it a file, a link, etc.
98 99 """
99 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 106 It is a category for each subject.
... ... @@ -112,4 +114,3 @@ class SubjectCategory(models.Model):
112 114 class Meta:
113 115 verbose_name = _('subject category')
114 116 verbose_name_plural = _('subject categories')
115   -
... ...
forum/models.py
... ... @@ -41,6 +41,15 @@ class Post(models.Model):
41 41 def __str__(self):
42 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 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 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 101 function delete_post(url, post) {
54 102 var csrftoken = getCookie('csrftoken');
55 103  
... ... @@ -74,36 +122,4 @@ function answer(id, url) {
74 122 });
75 123  
76 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 126 \ No newline at end of file
... ...
forum/templates/post/post_list.html
... ... @@ -17,17 +17,24 @@
17 17 <i class="material-icons">more_horiz</i>
18 18 </a>
19 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 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 22 </ul>
23 23 </div>
24 24 {% endif %}
25 25 </div>
26 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 37 </div>
30   - <p class="comment-text">{{ post.message|linebreaks }}</p>
31 38 <div class="answer_post"></div>
32 39 </div>
33 40 </div>
... ...
forum/templates/post/post_render.html
... ... @@ -15,17 +15,24 @@
15 15 <i class="material-icons">more_horiz</i>
16 16 </a>
17 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 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 20 </ul>
21 21 </div>
22 22 {% endif %}
23 23 </div>
24 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 35 </div>
28   - <p class="comment-text">{{ post.message|linebreaks }}</p>
29 36 <div class="answer_post"></div>
30 37 </div>
31 38 </div>
32 39 \ No newline at end of file
... ...
forum/templates/post/post_update_form.html 0 → 100644
... ... @@ -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">&times;</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 38 \ No newline at end of file
... ...
forum/templatetags/list_post.py
... ... @@ -14,6 +14,6 @@ def list_posts(request, forum):
14 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 19 return context
20 20 \ No newline at end of file
... ...
forum/urls.py
... ... @@ -6,10 +6,10 @@ from . import views
6 6 urlpatterns = [
7 7 url(r'^$', views.ForumIndex.as_view(), name='index'),
8 8 url(r'^create$', views.CreateForumView.as_view(), name='create'),
9   - url(r'^posts$', views.PostIndex.as_view(), name='posts'),
10 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 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 13 url(r'^post_deleted/$', views.post_deleted, name='deleted_post'),
14 14 url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'),
15 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 36 form_class = ForumForm
37 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 39 class CreatePostView(LoginRequiredMixin, generic.edit.CreateView):
54 40 login_url = reverse_lazy("core:home")
55 41 redirect_field_name = 'next'
... ... @@ -77,6 +63,19 @@ def render_post(request, post):
77 63  
78 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 79 class PostDeleteView(LoginRequiredMixin, generic.DeleteView):
81 80 login_url = reverse_lazy("core:home")
82 81 redirect_field_name = 'next'
... ...
poll/admin.py
1 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)
... ...
poll/forms.py 0 → 100644
... ... @@ -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 + }
... ...
poll/migrations/0002_remove_poll_question.py 0 → 100644
... ... @@ -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 6 from courses.models import Activity
7 7  
8 8 class Poll(Activity):
9   - question = models.CharField(_('Question'), max_length = 300)
10 9  
11 10 class Meta:
12 11 #ordering = ('create_date','name')
... ... @@ -14,7 +13,7 @@ class Poll(Activity):
14 13 verbose_name_plural = _('Polls')
15 14  
16 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 18 class Answer(models.Model):
20 19 answer = models.CharField(_("Answer"), max_length = 200)
... ... @@ -27,4 +26,4 @@ class Answer(models.Model):
27 26 verbose_name_plural = _('Answers')
28 27  
29 28 def __str__(self):
30   - return str(self.question) + str("/") + str(self.topic)
  29 + return str(self.answer) + str("/") + str(self.poll)
... ...
poll/permissions.py 0 → 100644
... ... @@ -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
... ...
poll/templates/poll/create_update.html 0 → 100644
... ... @@ -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">&times;</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">&times;</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">&times;</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   -{% 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/templates/poll/view.html 0 → 100644
... ... @@ -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">&times;</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/templatetags/__init__.py 0 → 100644
poll/templatetags/dict_access.py 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +from django import template
  2 +
  3 +from forum.models import Forum
  4 +
  5 +register = template.Library()
  6 +
  7 +"""
  8 + Template tag to load all the foruns of a post
  9 +"""
  10 +
  11 +@register.filter
  12 +def value(dictionary, key):
  13 + return dictionary[key]
... ...
poll/urls.py
... ... @@ -3,6 +3,7 @@ from django.conf.urls import url
3 3 from . import views
4 4  
5 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 8 from django.utils.translation import ugettext_lazy as _
9 9 from rolepermissions.verifications import has_role
10 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 15 from core.mixins import NotificationMixin
15 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 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 123 return context
... ...
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
... ...