Commit d92060185e92fb3174ab61517f8f926bf45d087b

Authored by fbormann
2 parents 7fa7ebc6 dbb6ae08

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

core/models.py
@@ -6,20 +6,20 @@ from autoslug.fields import AutoSlugField @@ -6,20 +6,20 @@ from autoslug.fields import AutoSlugField
6 6
7 class Action(models.Model): 7 class Action(models.Model):
8 """ 8 """
9 - It represents an Action on the program by a User such as "create post", 9 + It represents an Action on the program by a User such as "create post",
10 "visualize post", etc. It is supposed to be created everytime we want an aciton 10 "visualize post", etc. It is supposed to be created everytime we want an aciton
11 """ 11 """
12 12
13 name = models.CharField(_('Name'), max_length = 100) 13 name = models.CharField(_('Name'), max_length = 100)
14 created_date = models.DateField(_('Created Date'), auto_now_add=True) 14 created_date = models.DateField(_('Created Date'), auto_now_add=True)
15 - 15 +
16 class Meta: 16 class Meta:
17 verbose_name = "Action" 17 verbose_name = "Action"
18 verbose_name_plural = "Actions" 18 verbose_name_plural = "Actions"
19 19
20 def __str__(self): 20 def __str__(self):
21 return self.name 21 return self.name
22 - 22 +
23 23
24 class Resource(models.Model): 24 class Resource(models.Model):
25 """ 25 """
@@ -27,38 +27,38 @@ class Resource(models.Model): @@ -27,38 +27,38 @@ class Resource(models.Model):
27 Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF). 27 Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF).
28 28
29 Attributes: 29 Attributes:
30 - @name: name of the resource affected, it will be unique because a resource can be affecte 30 + @name: name of the resource affected, it will be unique because a resource can be affecte
31 by a huge amount of actions 31 by a huge amount of actions
32 @created_date: The date the resource was created 32 @created_date: The date the resource was created
33 @link: Which URL made that resource able to find 33 @link: Which URL made that resource able to find
34 """ 34 """
35 35
36 name = models.CharField(_('Name'), max_length =100) 36 name = models.CharField(_('Name'), max_length =100)
37 - slug = AutoSlugField(_("Slug"), populate_from='name', unique=True, null=True) 37 + slug = AutoSlugField(_("Slug"), populate_from='name', unique=True)
38 created_date = models.DateField(_('Created Date'), auto_now_add=True) 38 created_date = models.DateField(_('Created Date'), auto_now_add=True)
39 url = models.CharField(_('URL'), max_length =100, default="") 39 url = models.CharField(_('URL'), max_length =100, default="")
40 40
41 41
42 class Meta: 42 class Meta:
43 verbose_name = "Resource" 43 verbose_name = "Resource"
44 - verbose_name_plural = "Resources" 44 + verbose_name_plural = "Resources"
45 45
46 def __str__(self): 46 def __str__(self):
47 return self.name 47 return self.name
48 48
49 49
50 class Action_Resource(models.Model): 50 class Action_Resource(models.Model):
51 - 51 +
52 action = models.ForeignKey(Action , verbose_name= _('Action_Applied')) 52 action = models.ForeignKey(Action , verbose_name= _('Action_Applied'))
53 resource = models.ForeignKey(Resource, verbose_name = _('Resource')) 53 resource = models.ForeignKey(Resource, verbose_name = _('Resource'))
54 - 54 +
55 class Meta: 55 class Meta:
56 verbose_name = "Action_Resource" 56 verbose_name = "Action_Resource"
57 verbose_name_plural = "Action_Resources" 57 verbose_name_plural = "Action_Resources"
58 58
59 def __str__(self): 59 def __str__(self):
60 return ''.join([self.action.name, " / ", self.resource.name]) 60 return ''.join([self.action.name, " / ", self.resource.name])
61 - 61 +
62 62
63 class Notification(models.Model): 63 class Notification(models.Model):
64 """ 64 """
@@ -77,7 +77,7 @@ class Notification(models.Model): @@ -77,7 +77,7 @@ class Notification(models.Model):
77 datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True) 77 datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True)
78 action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource')) 78 action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource'))
79 actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True) 79 actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True)
80 - 80 +
81 class Meta: 81 class Meta:
82 verbose_name = _("Notification") 82 verbose_name = _("Notification")
83 verbose_name_plural = _("Notifications") 83 verbose_name_plural = _("Notifications")
forum/models.py
@@ -41,6 +41,15 @@ class Post(models.Model): @@ -41,6 +41,15 @@ class Post(models.Model):
41 def __str__(self): 41 def __str__(self):
42 return ''.join([self.user.name, " / ", str(self.post_date)]) 42 return ''.join([self.user.name, " / ", str(self.post_date)])
43 43
  44 + def is_modified(self):
  45 + create = self.post_date.strftime("%Y-%m-%d %H:%M:%S")
  46 + edit = self.modification_date.strftime("%Y-%m-%d %H:%M:%S")
  47 +
  48 + if create != edit:
  49 + return True
  50 +
  51 + return False
  52 +
44 """ 53 """
45 It represents an answer to a forum's post 54 It represents an answer to a forum's post
46 """ 55 """
forum/static/js/forum.js
@@ -50,6 +50,54 @@ function showForum(url, forum_id) { @@ -50,6 +50,54 @@ function showForum(url, forum_id) {
50 $('#forumModal').modal(); 50 $('#forumModal').modal();
51 } 51 }
52 52
  53 +/*
  54 +*
  55 +* Function to load form to edit post
  56 +*
  57 +*/
  58 +function edit_post(url, post_id) {
  59 + $.ajax({
  60 + url: url,
  61 + success: function(data) {
  62 + $("#post_"+post_id).find(".post_content").hide();
  63 + $("#post_"+post_id).find(".post_content").after(data);
  64 +
  65 + var frm = $("#post_"+post_id).find(".edit_post_form");
  66 + frm.submit(function () {
  67 + $.ajax({
  68 + type: frm.attr('method'),
  69 + url: frm.attr('action'),
  70 + data: frm.serialize(),
  71 + success: function (data) {
  72 + $("#post_"+post_id).parent().after(data);
  73 + frm.parent().parent().remove();
  74 + },
  75 + error: function(data) {
  76 + console.log(frm.serialize());
  77 + console.log('Error');
  78 + }
  79 + });
  80 + return false;
  81 + });
  82 + }
  83 + });
  84 +}
  85 +
  86 +/*
  87 +*
  88 +* Function to cancel post edition
  89 +*
  90 +*/
  91 +function cancelEditPost(post_id) {
  92 + $("#post_"+post_id).find(".post_content").show();
  93 + $("#post_"+post_id).find(".edit_post_form").remove();
  94 +}
  95 +
  96 +/*
  97 +*
  98 +* Function to delete a post
  99 +*
  100 +*/
53 function delete_post(url, post) { 101 function delete_post(url, post) {
54 var csrftoken = getCookie('csrftoken'); 102 var csrftoken = getCookie('csrftoken');
55 103
@@ -74,36 +122,4 @@ function answer(id, url) { @@ -74,36 +122,4 @@ function answer(id, url) {
74 }); 122 });
75 123
76 $("#post_"+id).find(".answer_post").show(); 124 $("#post_"+id).find(".answer_post").show();
77 -}  
78 -  
79 -function showPosts(url, forum) {  
80 - if ($("#collapse" + forum).hasClass('in')) {  
81 - $("#collapse" + forum).collapse('hide');  
82 - } else {  
83 - $.ajax({  
84 - url: url,  
85 - data: {'forum': forum},  
86 - success: function(data) {  
87 - $("#collapse" + forum).find(".well").html(data);  
88 - }  
89 - });  
90 -  
91 - $("#collapse" + forum).collapse('show');  
92 - }  
93 -}  
94 -  
95 -function showPostsAnswers(url, post) {  
96 - if ($("#collapse" + post).hasClass('in')) {  
97 - $("#collapse" + post).collapse('hide');  
98 - } else {  
99 - $.ajax({  
100 - url: url,  
101 - data: {'post': post},  
102 - success: function(data) {  
103 - $("#collapse" + post).find(".well").html(data);  
104 - }  
105 - });  
106 -  
107 - $("#collapse" + post).collapse('show');  
108 - }  
109 } 125 }
110 \ No newline at end of file 126 \ No newline at end of file
forum/templates/post/post_list.html
@@ -17,17 +17,24 @@ @@ -17,17 +17,24 @@
17 <i class="material-icons">more_horiz</i> 17 <i class="material-icons">more_horiz</i>
18 </a> 18 </a>
19 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> 19 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
20 - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> 20 + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li>
21 <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> 21 <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li>
22 </ul> 22 </ul>
23 </div> 23 </div>
24 {% endif %} 24 {% endif %}
25 </div> 25 </div>
26 </h3> 26 </h3>
27 - <div class="card-data">  
28 - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> 27 + <div class="post_content">
  28 + <div class="card-data">
  29 + <p class="comment-date">
  30 + <i class="fa fa-clock-o"></i> {{ post.post_date }}
  31 + {% if post.is_modified %}
  32 + <em> - {% trans 'Edited' %}</em>
  33 + {% endif %}
  34 + </p>
  35 + </div>
  36 + <p class="comment-text">{{ post.message|linebreaks }}</p>
29 </div> 37 </div>
30 - <p class="comment-text">{{ post.message|linebreaks }}</p>  
31 <div class="answer_post"></div> 38 <div class="answer_post"></div>
32 </div> 39 </div>
33 </div> 40 </div>
forum/templates/post/post_render.html
@@ -15,17 +15,24 @@ @@ -15,17 +15,24 @@
15 <i class="material-icons">more_horiz</i> 15 <i class="material-icons">more_horiz</i>
16 </a> 16 </a>
17 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> 17 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
18 - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> 18 + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"></li>
19 <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> 19 <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li>
20 </ul> 20 </ul>
21 </div> 21 </div>
22 {% endif %} 22 {% endif %}
23 </div> 23 </div>
24 </h3> 24 </h3>
25 - <div class="card-data">  
26 - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> 25 + <div class="post_content">
  26 + <div class="card-data">
  27 + <p class="comment-date">
  28 + <i class="fa fa-clock-o"></i> {{ post.post_date }}
  29 + {% if post.post_date != post.modifiction_date %}
  30 + <em> - {% trans 'Edited' %}</em>
  31 + {% endif %}
  32 + </p>
  33 + </div>
  34 + <p class="comment-text">{{ post.message|linebreaks }}</p>
27 </div> 35 </div>
28 - <p class="comment-text">{{ post.message|linebreaks }}</p>  
29 <div class="answer_post"></div> 36 <div class="answer_post"></div>
30 </div> 37 </div>
31 </div> 38 </div>
32 \ No newline at end of file 39 \ No newline at end of file
forum/templates/post/post_update_form.html 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +{% load i18n permission_tags list_post %}
  2 +{% load widget_tweaks %}
  3 +
  4 +<form class="edit_post_form" method="post" action="{% url 'forum:update_post' post.id %}" enctype="multipart/form-data">
  5 + {% csrf_token %}
  6 + {% for field in form %}
  7 + {% if field.field.widget.input_type == 'hidden' %}
  8 + {% render_field field class='form-control' %}
  9 + {% else %}
  10 + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput">
  11 + <div class="input-group">
  12 + {% render_field field class='form-control' placeholder="Post a message" %}
  13 + <span class="help-block">{{ field.help_text }}</span>
  14 + {% if field.errors %}
  15 + <div class="row">
  16 + <br />
  17 + <div class="alert alert-danger alert-dismissible" role="alert">
  18 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  19 + <span aria-hidden="true">&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 \ No newline at end of file 38 \ No newline at end of file
forum/templatetags/list_post.py
@@ -14,6 +14,6 @@ def list_posts(request, forum): @@ -14,6 +14,6 @@ def list_posts(request, forum):
14 'request': request, 14 'request': request,
15 } 15 }
16 16
17 - context['posts'] = Post.objects.filter(forum = forum) 17 + context['posts'] = Post.objects.filter(forum = forum).order_by('post_date')
18 18
19 return context 19 return context
20 \ No newline at end of file 20 \ No newline at end of file
@@ -6,10 +6,10 @@ from . import views @@ -6,10 +6,10 @@ from . import views
6 urlpatterns = [ 6 urlpatterns = [
7 url(r'^$', views.ForumIndex.as_view(), name='index'), 7 url(r'^$', views.ForumIndex.as_view(), name='index'),
8 url(r'^create$', views.CreateForumView.as_view(), name='create'), 8 url(r'^create$', views.CreateForumView.as_view(), name='create'),
9 - url(r'^posts$', views.PostIndex.as_view(), name='posts'),  
10 url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'), 9 url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'),
11 - url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'), 10 + url(r'^update_post/(?P<pk>[\w_-]+)/$', views.PostUpdateView.as_view(), name='update_post'),
12 url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'), 11 url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'),
  12 + url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'),
13 url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), 13 url(r'^post_deleted/$', views.post_deleted, name='deleted_post'),
14 url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'), 14 url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'),
15 url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'), 15 url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'),
forum/views.py
@@ -36,20 +36,6 @@ class CreateForumView(LoginRequiredMixin, generic.edit.CreateView): @@ -36,20 +36,6 @@ class CreateForumView(LoginRequiredMixin, generic.edit.CreateView):
36 form_class = ForumForm 36 form_class = ForumForm
37 success_url = reverse_lazy('forum:index') 37 success_url = reverse_lazy('forum:index')
38 38
39 -class PostIndex(LoginRequiredMixin, generic.ListView):  
40 - login_url = reverse_lazy("core:home")  
41 - redirect_field_name = 'next'  
42 -  
43 - template_name = "post/post_list.html"  
44 - context_object_name = 'posts'  
45 -  
46 - def get_queryset(self):  
47 - forum = get_object_or_404(Forum, slug = self.request.GET.get('forum', ''))  
48 -  
49 - context = Post.objects.filter(forum = forum)  
50 -  
51 - return context  
52 -  
53 class CreatePostView(LoginRequiredMixin, generic.edit.CreateView): 39 class CreatePostView(LoginRequiredMixin, generic.edit.CreateView):
54 login_url = reverse_lazy("core:home") 40 login_url = reverse_lazy("core:home")
55 redirect_field_name = 'next' 41 redirect_field_name = 'next'
@@ -77,6 +63,19 @@ def render_post(request, post): @@ -77,6 +63,19 @@ def render_post(request, post):
77 63
78 return render(request, "post/post_render.html", context) 64 return render(request, "post/post_render.html", context)
79 65
  66 +class PostUpdateView(LoginRequiredMixin, generic.UpdateView):
  67 + login_url = reverse_lazy("core:home")
  68 + redirect_field_name = 'next'
  69 +
  70 + form_class = PostForm
  71 + model = Post
  72 + template_name = "post/post_update_form.html"
  73 +
  74 + def get_success_url(self):
  75 + self.success_url = reverse('forum:render_post', args = (self.object.id, ))
  76 +
  77 + return self.success_url
  78 +
80 class PostDeleteView(LoginRequiredMixin, generic.DeleteView): 79 class PostDeleteView(LoginRequiredMixin, generic.DeleteView):
81 login_url = reverse_lazy("core:home") 80 login_url = reverse_lazy("core:home")
82 redirect_field_name = 'next' 81 redirect_field_name = 'next'
1 from django.contrib import admin 1 from django.contrib import admin
2 2
3 -# Register your models here. 3 +from .models import Poll, Answer
  4 +
  5 +class PollAdmin(admin.ModelAdmin):
  6 + list_display = ['name', 'slug','limit_date']
  7 + search_fields = ['name','slug']
  8 +
  9 +class AnswerAdmin(admin.ModelAdmin):
  10 + list_display = ['answer','order']
  11 + search_fields = ['answer']
  12 +
  13 +admin.site.register(Poll, PollAdmin)
  14 +admin.site.register(Answer, AnswerAdmin)
poll/forms.py 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  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 + # password = forms.CharField(label=_('Password'), widget=forms.PasswordInput)
  9 + # password2 = forms.CharField(label = _('Password confirmation'), widget = forms.PasswordInput)
  10 + # birth_date = forms.DateField(widget=forms.SelectDateWidget())
  11 + # MIN_LENGTH = 8
  12 +
  13 + class Meta:
  14 + model = Poll
  15 + # exclude = ['is_staff', 'is_active']
  16 + fields = ['name','limit_date']
  17 +
  18 + widgets = {
  19 + 'name': forms.TextInput(attrs={'placeholder': 'Question?'}),
  20 + 'description': forms.DateTimeInput(
  21 + attrs={'placeholder': 'Maximum date permited to resolve the poll'}),
  22 + }
poll/models.py
@@ -14,7 +14,7 @@ class Poll(Activity): @@ -14,7 +14,7 @@ class Poll(Activity):
14 verbose_name_plural = _('Polls') 14 verbose_name_plural = _('Polls')
15 15
16 def __str__(self): 16 def __str__(self):
17 - return str(self.question) + str("/") + str(self.topic) 17 + return str(self.name) + str("/") + str(self.topic)
18 18
19 class Answer(models.Model): 19 class Answer(models.Model):
20 answer = models.CharField(_("Answer"), max_length = 200) 20 answer = models.CharField(_("Answer"), max_length = 200)
@@ -27,4 +27,4 @@ class Answer(models.Model): @@ -27,4 +27,4 @@ class Answer(models.Model):
27 verbose_name_plural = _('Answers') 27 verbose_name_plural = _('Answers')
28 28
29 def __str__(self): 29 def __str__(self):
30 - return str(self.question) + str("/") + str(self.topic) 30 + return str(self.answer) + str("/") + str(self.poll)
poll/permissions.py 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +from rolepermissions.permissions import register_object_checker
  2 +from amadeus.roles import SystemAdmin
  3 +
  4 +@register_object_checker()
  5 +def edit_poll(role, user, poll):
  6 + if (role == SystemAdmin):
  7 + return True
  8 +
  9 + if (user in poll.topic.subject.professors.all()):
  10 + return True
  11 +
  12 + return False
poll/templates/poll/create_update.html 0 → 100644
@@ -0,0 +1,173 @@ @@ -0,0 +1,173 @@
  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 + <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 %}>
  97 + {% if form.limit_date.errors %}
  98 + <div class="not_submited">
  99 + </br>
  100 + <div class="alert alert-danger alert-dismissible" role="alert">
  101 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  102 + <span aria-hidden="true">&times;</span>
  103 + </button>
  104 + <ul>
  105 + {% for error in form.limit_date.errors %}
  106 + <li>{{ error }}</li>
  107 + {% endfor %}
  108 + </ul>
  109 + </div>
  110 + </div>
  111 + {% endif %}
  112 + </div>
  113 +
  114 + </div>
  115 +
  116 + <!-- Modal Footer -->
  117 + <div class="modal-footer">
  118 +
  119 + <!-- Don't remove that!!! -->
  120 + <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button>
  121 +
  122 + <!-- Put curtom buttons here!!! -->
  123 + <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button>
  124 + </div>
  125 +
  126 + </div>
  127 + </div>
  128 +</div>
  129 +<script type="text/javascript">
  130 +// Este js tem que ficar aqui se não a tag "trans" não vai funcionar
  131 +$(window).ready(function() { // utilizado para abrir o modal quando tiver tido algum erro no preenchimento do formulario
  132 + if($('.not_submited').length){
  133 + $('#poll').modal('show');
  134 + }
  135 +});
  136 +$( "#form" ).sortable({ // utilizado para fazer a re-organização das respostas
  137 + delay: 100,
  138 + distance: 5,
  139 + update: function( event, ui ) {
  140 + var cont = 1;
  141 + $("#form div div div input").each(function(){
  142 + $(this).attr('name',cont++);
  143 + });
  144 + },
  145 +});
  146 +name = 2;
  147 +$("#add").click(function() { // utilizado para adicionar um novo campo de resposta
  148 + //Obs: não funcionar se estiver importado no head, só funciona se estiver no final do arquivo
  149 + $("#form").append('\
  150 + <div class="row form-group">\
  151 + <div class="col-md-1">\
  152 + </br>\
  153 + <label><span class="glyphicon glyphicon-move"></span></label>\
  154 + </div>\
  155 + <div class="col-md-10">\
  156 + <div class="has-success is-empty">\
  157 + <input type="text" name="1" class="form-control" placeholder="{% trans "Answer" %}">\
  158 + <span class="help-block">{% trans "Possible answer for the question" %}</span>\
  159 + </div>\
  160 + </div>\
  161 + <div class="col-md-1">\
  162 + </br>\
  163 + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>\
  164 + </div>\
  165 + </div>');
  166 + var cont = 1;
  167 + $("#form div div div input").each(function(){
  168 + $(this).attr('name',cont++);
  169 + });
  170 +});
  171 +</script>
  172 +<a href="" data-toggle="modal" data-target="#poll">modal</a>
  173 +{% endblock content %}
poll/templates/poll/poll.html
@@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
1 -{% extends "topic/index.html" %}  
2 -  
3 -{% load i18n %}  
4 -  
5 -{% block style %}  
6 - <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>  
7 -{% endblock %}  
8 -  
9 -{% block content %}  
10 -<div class="col-md-8 col-md-offset-2">  
11 - <div class="panel panel-primary">  
12 - <div class="panel-heading">  
13 - <h3 class="panel-title">  
14 - <span class="glyphicon glyphicon-hand-right"></span> Question?</h3>  
15 - </div>  
16 - <div class="container-fluid">  
17 - <form id="form" class="" action="" method="post">  
18 - <div class="row form-group">  
19 - <div class="col-md-1">  
20 - </br>  
21 - <label><a href=""><span class="glyphicon glyphicon-move"></span></a></label>  
22 - </div>  
23 - <div class="col-md-10">  
24 - <div class="form-control-md has-success is-empty">  
25 - <input type="text" class="form-control" placeholder="Email address default size">  
26 - <span class="help-block">{% trans "Possible Answer" %}</span>  
27 - </div>  
28 - </div>  
29 - <div class="col-md-1">  
30 - </br>  
31 - <label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label>  
32 - </div>  
33 - </div>  
34 - </form>  
35 - </br>  
36 - </div>  
37 -  
38 - </div>  
39 - <div class="panel-footer text-center">  
40 - <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button>  
41 - </div>  
42 - </div>  
43 -<script type="text/javascript">  
44 -$( "#form" ).sortable({  
45 - delay: 100,  
46 - distance: 5,  
47 -});  
48 -$("#add").click(function() {  
49 - $("#form").append('<div class="row form-group"><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-move"></span></a></label></div><div class="col-md-10"><div class="form-group-md has-success is-empty"><input type="text" class="form-control" placeholder="Email address default size"><span class="help-block">Please enter a valid email address</span></div></div><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label></div></br></div>');  
50 -});  
51 -</script>  
52 -{% endblock content %}  
poll/templatetags/__init__.py 0 → 100644
poll/templatetags/dict_access.py 0 → 100644
@@ -0,0 +1,13 @@ @@ -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]
@@ -3,6 +3,7 @@ from django.conf.urls import url @@ -3,6 +3,7 @@ from django.conf.urls import url
3 from . import views 3 from . import views
4 4
5 urlpatterns = [ 5 urlpatterns = [
6 - url(r'^to/poll/to/$', views.Poll.as_view(), name='poll'), 6 + url(r'^create/$', views.CreatePoll.as_view(), name='create_poll'),
  7 + url(r'^update/(?P<slug>[\w\-_]+)/$', views.UpdatePoll.as_view(), name='update_poll'),
7 8
8 ] 9 ]
@@ -8,31 +8,122 @@ from django.core.urlresolvers import reverse_lazy @@ -8,31 +8,122 @@ from django.core.urlresolvers import reverse_lazy
8 from django.utils.translation import ugettext_lazy as _ 8 from django.utils.translation import ugettext_lazy as _
9 from rolepermissions.verifications import has_role 9 from rolepermissions.verifications import has_role
10 from rolepermissions.verifications import has_object_permission 10 from rolepermissions.verifications import has_object_permission
  11 +# from django.views.generic.edit import FormMixin
11 12
12 -# from .forms import CourseForm, UpdateCourseForm, CategoryForm, SubjectForm,TopicForm  
13 -# from .models import Course, Subject, Category,Topic, SubjectCategory 13 +from .forms import PollForm
  14 +from .models import Poll, Answer
14 from core.mixins import NotificationMixin 15 from core.mixins import NotificationMixin
15 from users.models import User 16 from users.models import User
16 -from courses.models import Course 17 +from courses.models import Course, Topic
17 18
18 -class Poll(generic.TemplateView): 19 +class CreatePoll(LoginRequiredMixin,generic.CreateView):
19 20
20 # login_url = reverse_lazy("core:home") 21 # login_url = reverse_lazy("core:home")
21 # redirect_field_name = 'next' 22 # redirect_field_name = 'next'
22 - # model = Course  
23 - # context_object_name = 'course'  
24 - template_name = 'poll/poll.html' 23 + model = Poll
  24 + form_class = PollForm
  25 + context_object_name = 'poll'
  26 + template_name = 'poll/create_update.html'
25 # queryset = Course.objects.all() 27 # queryset = Course.objects.all()
26 - 28 + success_url = reverse_lazy('core:home')
27 # def get_queryset(self): 29 # def get_queryset(self):
28 # return Course.objects.all()[0] 30 # return Course.objects.all()[0]
29 31
  32 + def form_invalid(self, form,**kwargs):
  33 + context = super(CreatePoll, self).form_invalid(form)
  34 + answers = {}
  35 + for key in self.request.POST:
  36 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date'):
  37 + answers[key] = self.request.POST[key]
  38 +
  39 + keys = sorted(answers)
  40 + context.context_data['answers'] = answers
  41 + context.context_data['keys'] = keys
  42 + return context
  43 +
  44 + def form_valid(self, form):
  45 + self.object = form.save(commit = False)
  46 + topic = Topic.objects.all()[0]
  47 + self.object.student = self.request.user
  48 + self.object.question = "question"
  49 + self.object.topic = topic
  50 + self.object.save()
  51 +
  52 + for key in self.request.POST:
  53 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date'):
  54 + answer = Answer(answer=self.request.POST[key],order=key,poll=self.object)
  55 + answer.save()
  56 +
  57 + return super(CreatePoll, self).form_valid(form)
  58 +
30 def get_context_data(self, **kwargs): 59 def get_context_data(self, **kwargs):
31 - context = super(Poll, self).get_context_data(**kwargs) 60 + context = super(CreatePoll, self).get_context_data(**kwargs)
32 course = Course.objects.all()[0] 61 course = Course.objects.all()[0]
  62 + # print (self.object)
33 context['course'] = course 63 context['course'] = course
34 context['subject'] = course.subjects.all()[0] 64 context['subject'] = course.subjects.all()[0]
35 context['subjects'] = course.subjects.all() 65 context['subjects'] = course.subjects.all()
36 - # if (has_role(self.request.user,'system_admin')):  
37 - # context['subjects'] = self.object.course.subjects.all() 66 + return context
  67 +
  68 +class UpdatePoll(LoginRequiredMixin,generic.UpdateView):
  69 +
  70 + login_url = reverse_lazy("core:home")
  71 + redirect_field_name = 'next'
  72 + model = Poll
  73 + form_class = PollForm
  74 + context_object_name = 'poll'
  75 + template_name = 'poll/create_update.html'
  76 + success_url = reverse_lazy('core:home')
  77 +
  78 + def dispatch(self, *args, **kwargs):
  79 + poll = get_object_or_404(Poll, slug = self.kwargs.get('slug'))
  80 + if(not has_object_permission('edit_poll', self.request.user, poll)):
  81 + return self.handle_no_permission()
  82 + return super(UpdatePoll, self).dispatch(*args, **kwargs)
  83 +
  84 + def get_object(self, queryset=None):
  85 + return get_object_or_404(Poll, slug = self.kwargs.get('slug'))
  86 +
  87 + def form_invalid(self, form,**kwargs):
  88 + context = super(UpdatePoll, self).form_invalid(form)
  89 + answers = {}
  90 + for key in self.request.POST:
  91 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date'):
  92 + answers[key] = self.request.POST[key]
  93 +
  94 + keys = sorted(answers)
  95 + context.context_data['answers'] = answers
  96 + context.context_data['keys'] = keys
  97 + return context
  98 +
  99 + def form_valid(self, form):
  100 + poll = self.object
  101 + poll = form.save(commit = False)
  102 + poll.answers.all().delete()
  103 + poll.save()
  104 +
  105 +
  106 + for key in self.request.POST:
  107 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date'):
  108 + answer = Answer(answer=self.request.POST[key],order=key,poll=poll)
  109 + answer.save()
  110 +
  111 + return super(UpdatePoll, self).form_valid(form)
  112 +
  113 + def get_context_data(self, **kwargs):
  114 + context = super(UpdatePoll, self).get_context_data(**kwargs)
  115 + poll = self.object
  116 + context['course'] = poll.topic.subject.course
  117 + context['subject'] = poll.topic.subject
  118 + context['subjects'] = poll.topic.subject.course.subjects.all()
  119 +
  120 + answers = {}
  121 + for answer in poll.answers.all():
  122 + # print (key.answer)
  123 + answers[answer.order] = answer.answer
  124 +
  125 + keys = sorted(answers)
  126 + context['answers'] = answers
  127 + context['keys'] = keys
  128 +
38 return context 129 return context