Commit d4e29336cddf18369319311b0268ee15d2604b3b
Exists in
master
and in
5 other branches
Merge
Showing
11 changed files
with
192 additions
and
20 deletions
Show diff stats
core/tests.py
@@ -89,7 +89,7 @@ class RegisterUserTestCase(TestCase): | @@ -89,7 +89,7 @@ class RegisterUserTestCase(TestCase): | ||
89 | } | 89 | } |
90 | 90 | ||
91 | response = self.client.post(self.url, data) | 91 | response = self.client.post(self.url, data) |
92 | - self.assertFormError(response, 'form', 'email', 'Insira um endereço de email válido.') | 92 | + self.assertFormError(response, 'form', 'email', 'Enter a valid email address.') |
93 | 93 | ||
94 | data = { | 94 | data = { |
95 | 'username': '', | 95 | 'username': '', |
@@ -102,7 +102,7 @@ class RegisterUserTestCase(TestCase): | @@ -102,7 +102,7 @@ class RegisterUserTestCase(TestCase): | ||
102 | 'gender': 'F', | 102 | 'gender': 'F', |
103 | } | 103 | } |
104 | response = self.client.post(self.url, data) | 104 | response = self.client.post(self.url, data) |
105 | - self.assertFormError(response, 'form', 'username', 'Este campo é obrigatório.') | 105 | + self.assertFormError(response, 'form', 'username', 'This field is required.') |
106 | 106 | ||
107 | class RememberPasswordTestCase(TestCase): | 107 | class RememberPasswordTestCase(TestCase): |
108 | 108 | ||
@@ -194,7 +194,7 @@ class UpdateUserTestCase(TestCase): | @@ -194,7 +194,7 @@ class UpdateUserTestCase(TestCase): | ||
194 | 'gender': 'F', | 194 | 'gender': 'F', |
195 | } | 195 | } |
196 | response = self.client.post(self.url, data) | 196 | response = self.client.post(self.url, data) |
197 | - self.assertFormError(response, 'form', 'username', 'Este campo é obrigatório.') | 197 | + self.assertFormError(response, 'form', 'username', 'This field is required.') |
198 | 198 | ||
199 | 199 | ||
200 | class DeleteUserTestCase(TestCase): | 200 | class DeleteUserTestCase(TestCase): |
courses/templates/subject/form_view_teacher.html
@@ -32,16 +32,6 @@ | @@ -32,16 +32,6 @@ | ||
32 | <div class="modal-body"> | 32 | <div class="modal-body"> |
33 | <section> | 33 | <section> |
34 | <div class="forum_topics"></div> | 34 | <div class="forum_topics"></div> |
35 | - <div class="form-group"> | ||
36 | - <div class="input-group"> | ||
37 | - <textarea type="text" id="addon3a" class="form-control" placeholder="{% trans 'Post a comment...' %}"></textarea> | ||
38 | - <span class="input-group-btn"> | ||
39 | - <button type="button" class="btn btn-fab btn-fab-mini"> | ||
40 | - <i class="material-icons">send</i> | ||
41 | - </button> | ||
42 | - </span> | ||
43 | - </div> | ||
44 | - </div> | ||
45 | </section> | 35 | </section> |
46 | </div> | 36 | </div> |
47 | <div class="modal-footer"> | 37 | <div class="modal-footer"> |
forum/forms.py
1 | from django import forms | 1 | from django import forms |
2 | from django.utils.translation import ugettext_lazy as _ | 2 | from django.utils.translation import ugettext_lazy as _ |
3 | -from .models import Forum, PostAnswer | 3 | +from .models import Forum, Post, PostAnswer |
4 | 4 | ||
5 | class ForumForm(forms.ModelForm): | 5 | class ForumForm(forms.ModelForm): |
6 | 6 | ||
@@ -19,6 +19,19 @@ class ForumForm(forms.ModelForm): | @@ -19,6 +19,19 @@ class ForumForm(forms.ModelForm): | ||
19 | 'description': forms.Textarea(attrs={'cols': 80, 'rows': 5}), | 19 | 'description': forms.Textarea(attrs={'cols': 80, 'rows': 5}), |
20 | } | 20 | } |
21 | 21 | ||
22 | +class PostForm(forms.ModelForm): | ||
23 | + | ||
24 | + class Meta: | ||
25 | + model = Post | ||
26 | + fields = ('message', 'forum', ) | ||
27 | + labels = { | ||
28 | + 'message': _('Message') | ||
29 | + } | ||
30 | + widgets = { | ||
31 | + 'message': forms.Textarea(attrs={'cols': 80, 'rows': 3}), | ||
32 | + 'forum': forms.HiddenInput(), | ||
33 | + } | ||
34 | + | ||
22 | class PostAnswerForm(forms.ModelForm): | 35 | class PostAnswerForm(forms.ModelForm): |
23 | 36 | ||
24 | class Meta: | 37 | class Meta: |
forum/static/js/forum.js
1 | +function getCookie(name) { | ||
2 | + var cookieValue = null; | ||
3 | + if (document.cookie && document.cookie !== '') { | ||
4 | + var cookies = document.cookie.split(';'); | ||
5 | + for (var i = 0; i < cookies.length; i++) { | ||
6 | + var cookie = jQuery.trim(cookies[i]); | ||
7 | + // Does this cookie string begin with the name we want? | ||
8 | + if (cookie.substring(0, name.length + 1) === (name + '=')) { | ||
9 | + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); | ||
10 | + break; | ||
11 | + } | ||
12 | + } | ||
13 | + } | ||
14 | + return cookieValue; | ||
15 | +} | ||
16 | + | ||
17 | + | ||
1 | /* | 18 | /* |
2 | * | 19 | * |
3 | * Function to load forum to modal | 20 | * Function to load forum to modal |
@@ -9,12 +26,45 @@ function showForum(url, forum_id) { | @@ -9,12 +26,45 @@ function showForum(url, forum_id) { | ||
9 | data: {'forum_id': forum_id}, | 26 | data: {'forum_id': forum_id}, |
10 | success: function(data) { | 27 | success: function(data) { |
11 | $(".forum_topics").html(data); | 28 | $(".forum_topics").html(data); |
29 | + | ||
30 | + var frm = $('#form_post'); | ||
31 | + frm.submit(function () { | ||
32 | + $.ajax({ | ||
33 | + type: frm.attr('method'), | ||
34 | + url: frm.attr('action'), | ||
35 | + data: frm.serialize(), | ||
36 | + success: function (data) { | ||
37 | + $("#posts_list").append(data); | ||
38 | + frm[0].reset(); | ||
39 | + }, | ||
40 | + error: function(data) { | ||
41 | + console.log(frm.serialize()); | ||
42 | + console.log('Error'); | ||
43 | + } | ||
44 | + }); | ||
45 | + return false; | ||
46 | + }); | ||
12 | } | 47 | } |
13 | }); | 48 | }); |
14 | 49 | ||
15 | $('#forumModal').modal(); | 50 | $('#forumModal').modal(); |
16 | } | 51 | } |
17 | 52 | ||
53 | +function delete_post(url, post) { | ||
54 | + var csrftoken = getCookie('csrftoken'); | ||
55 | + | ||
56 | + $.ajax({ | ||
57 | + method: 'post', | ||
58 | + beforeSend: function (request) { | ||
59 | + request.setRequestHeader('X-CSRFToken', csrftoken); | ||
60 | + }, | ||
61 | + url: url, | ||
62 | + success: function(data) { | ||
63 | + $("#post_"+post).remove(); | ||
64 | + } | ||
65 | + }); | ||
66 | +} | ||
67 | + | ||
18 | function answer(id, url) { | 68 | function answer(id, url) { |
19 | $.ajax({ | 69 | $.ajax({ |
20 | url: url, | 70 | url: url, |
forum/templates/forum/forum_list.html
1 | {% load i18n permission_tags list_post %} | 1 | {% load i18n permission_tags list_post %} |
2 | +{% load widget_tweaks %} | ||
2 | 3 | ||
3 | <div class="comments-list"> | 4 | <div class="comments-list"> |
4 | <div class="section-heading"> | 5 | <div class="section-heading"> |
@@ -8,7 +9,45 @@ | @@ -8,7 +9,45 @@ | ||
8 | </div> | 9 | </div> |
9 | </div> | 10 | </div> |
10 | 11 | ||
11 | -{% list_posts request forum %} | 12 | +<div id="posts_list"> |
13 | + {% list_posts request forum %} | ||
14 | +</div> | ||
15 | + | ||
16 | +<form id="form_post" method="post" action="{% url 'forum:create_post' %}" enctype="multipart/form-data"> | ||
17 | + {% csrf_token %} | ||
18 | + {% for field in form %} | ||
19 | + {% if field.field.widget.input_type == 'hidden' %} | ||
20 | + {% render_field field class='form-control' value=forum.id %} | ||
21 | + {% else %} | ||
22 | + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
23 | + <div class="input-group"> | ||
24 | + {% render_field field class='form-control' placeholder="Post a message" %} | ||
25 | + <span class="help-block">{{ field.help_text }}</span> | ||
26 | + {% if field.errors %} | ||
27 | + <div class="row"> | ||
28 | + <br /> | ||
29 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
30 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
31 | + <span aria-hidden="true">×</span> | ||
32 | + </button> | ||
33 | + <ul> | ||
34 | + {% for error in field.errors %} | ||
35 | + <li>{{ error }}</li> | ||
36 | + {% endfor %} | ||
37 | + </ul> | ||
38 | + </div> | ||
39 | + </div> | ||
40 | + {% endif %} | ||
41 | + <span class="input-group-btn"> | ||
42 | + <button type="submit" class="btn btn-fab btn-fab-mini"> | ||
43 | + <i class="material-icons">send</i> | ||
44 | + </button> | ||
45 | + </span> | ||
46 | + </div> | ||
47 | + </div> | ||
48 | + {% endif %} | ||
49 | + {% endfor %} | ||
50 | +</form> | ||
12 | 51 | ||
13 | <!--{% if foruns|length > 0 %} | 52 | <!--{% if foruns|length > 0 %} |
14 | {% for forum in foruns %} | 53 | {% for forum in foruns %} |
forum/templates/post/post_list.html
@@ -11,13 +11,14 @@ | @@ -11,13 +11,14 @@ | ||
11 | <i class="material-icons">reply</i> | 11 | <i class="material-icons">reply</i> |
12 | </a> | 12 | </a> |
13 | {% if request.user|has_role:'system_admin' or request.user|has_role:'professor' and request.user == post.user %} | 13 | {% if request.user|has_role:'system_admin' or request.user|has_role:'professor' and request.user == post.user %} |
14 | + {% csrf_token %} | ||
14 | <div class="btn-group icon-more-horiz"> | 15 | <div class="btn-group icon-more-horiz"> |
15 | <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | 16 | <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
16 | <i class="material-icons">more_horiz</i> | 17 | <i class="material-icons">more_horiz</i> |
17 | </a> | 18 | </a> |
18 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | 19 | <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> |
19 | <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | 20 | <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> |
20 | - <li><a href="javascript:void(0)"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | 21 | + <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> |
21 | </ul> | 22 | </ul> |
22 | </div> | 23 | </div> |
23 | {% endif %} | 24 | {% endif %} |
@@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
1 | +{% load i18n permission_tags %} | ||
2 | + | ||
3 | +<div class="row"> | ||
4 | + <div id="post_{{ post.id }}" class="col-sm-12 col-xs-12"> | ||
5 | + <h3 class="user-name"> | ||
6 | + {{ post.user }} | ||
7 | + <div class="pull-right"> | ||
8 | + <a href="javascript:answer('{{ post.id }}', '{% url 'forum:reply_post' %}');"> | ||
9 | + <i class="material-icons">reply</i> | ||
10 | + </a> | ||
11 | + {% if request.user|has_role:'system_admin' or request.user|has_role:'professor' and request.user == post.user %} | ||
12 | + {% csrf_token %} | ||
13 | + <div class="btn-group icon-more-horiz"> | ||
14 | + <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
15 | + <i class="material-icons">more_horiz</i> | ||
16 | + </a> | ||
17 | + <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | ||
18 | + <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | ||
19 | + <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | ||
20 | + </ul> | ||
21 | + </div> | ||
22 | + {% endif %} | ||
23 | + </div> | ||
24 | + </h3> | ||
25 | + <div class="card-data"> | ||
26 | + <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p> | ||
27 | + </div> | ||
28 | + <p class="comment-text">{{ post.message|linebreaks }}</p> | ||
29 | + <div class="answer_post"></div> | ||
30 | + </div> | ||
31 | +</div> | ||
0 | \ No newline at end of file | 32 | \ No newline at end of file |
forum/templates/post_answers/post_answer_form.html
@@ -25,7 +25,7 @@ | @@ -25,7 +25,7 @@ | ||
25 | </div> | 25 | </div> |
26 | {% endif %} | 26 | {% endif %} |
27 | <span class="input-group-btn"> | 27 | <span class="input-group-btn"> |
28 | - <button type="button" class="btn btn-fab btn-fab-mini"> | 28 | + <button type="submit" class="btn btn-fab btn-fab-mini"> |
29 | <i class="material-icons">send</i> | 29 | <i class="material-icons">send</i> |
30 | </button> | 30 | </button> |
31 | </span> | 31 | </span> |
forum/urls.py
@@ -7,6 +7,10 @@ urlpatterns = [ | @@ -7,6 +7,10 @@ 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'), | 9 | url(r'^posts$', views.PostIndex.as_view(), name='posts'), |
10 | + url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'), | ||
11 | + url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'), | ||
12 | + url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'), | ||
13 | + url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), | ||
10 | url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'), | 14 | url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'), |
11 | url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'), | 15 | url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'), |
12 | ] | 16 | ] |
forum/views.py
1 | +from django.http import HttpResponse | ||
1 | from django.shortcuts import render, get_object_or_404 | 2 | from django.shortcuts import render, get_object_or_404 |
2 | -from django.core.urlresolvers import reverse_lazy | 3 | +from django.core.urlresolvers import reverse, reverse_lazy |
3 | from django.utils.translation import ugettext_lazy as _ | 4 | from django.utils.translation import ugettext_lazy as _ |
4 | from django.views import generic | 5 | from django.views import generic |
5 | from django.contrib.auth.mixins import LoginRequiredMixin | 6 | from django.contrib.auth.mixins import LoginRequiredMixin |
@@ -7,7 +8,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin | @@ -7,7 +8,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin | ||
7 | from .models import Forum, Post, PostAnswer | 8 | from .models import Forum, Post, PostAnswer |
8 | from courses.models import Topic | 9 | from courses.models import Topic |
9 | 10 | ||
10 | -from .forms import ForumForm, PostAnswerForm | 11 | +from .forms import ForumForm, PostForm, PostAnswerForm |
11 | 12 | ||
12 | class ForumIndex(LoginRequiredMixin, generic.ListView): | 13 | class ForumIndex(LoginRequiredMixin, generic.ListView): |
13 | login_url = reverse_lazy("core:home") | 14 | login_url = reverse_lazy("core:home") |
@@ -20,6 +21,12 @@ class ForumIndex(LoginRequiredMixin, generic.ListView): | @@ -20,6 +21,12 @@ class ForumIndex(LoginRequiredMixin, generic.ListView): | ||
20 | forum_id = self.request.GET.get('forum_id', 0) | 21 | forum_id = self.request.GET.get('forum_id', 0) |
21 | 22 | ||
22 | context = Forum.objects.get(id = forum_id) | 23 | context = Forum.objects.get(id = forum_id) |
24 | + | ||
25 | + return context | ||
26 | + | ||
27 | + def get_context_data(self, **kwargs): | ||
28 | + context = super(ForumIndex, self).get_context_data(**kwargs) | ||
29 | + context['form'] = PostForm() | ||
23 | 30 | ||
24 | return context | 31 | return context |
25 | 32 | ||
@@ -43,6 +50,44 @@ class PostIndex(LoginRequiredMixin, generic.ListView): | @@ -43,6 +50,44 @@ class PostIndex(LoginRequiredMixin, generic.ListView): | ||
43 | 50 | ||
44 | return context | 51 | return context |
45 | 52 | ||
53 | +class CreatePostView(LoginRequiredMixin, generic.edit.CreateView): | ||
54 | + login_url = reverse_lazy("core:home") | ||
55 | + redirect_field_name = 'next' | ||
56 | + | ||
57 | + form_class = PostForm | ||
58 | + | ||
59 | + def form_valid(self, form): | ||
60 | + self.object = form.save(commit = False) | ||
61 | + self.object.user = self.request.user | ||
62 | + | ||
63 | + self.object.save() | ||
64 | + | ||
65 | + return super(CreatePostView, self).form_valid(form) | ||
66 | + | ||
67 | + def get_success_url(self): | ||
68 | + self.success_url = reverse('forum:render_post', args = (self.object.id, )) | ||
69 | + | ||
70 | + return self.success_url | ||
71 | + | ||
72 | +def render_post(request, post): | ||
73 | + last_post = get_object_or_404(Post, id = post) | ||
74 | + | ||
75 | + context = {} | ||
76 | + context['post'] = last_post | ||
77 | + | ||
78 | + return render(request, "post/post_render.html", context) | ||
79 | + | ||
80 | +class PostDeleteView(LoginRequiredMixin, generic.DeleteView): | ||
81 | + login_url = reverse_lazy("core:home") | ||
82 | + redirect_field_name = 'next' | ||
83 | + | ||
84 | + model = Post | ||
85 | + pk_url_kwarg = 'pk' | ||
86 | + success_url = reverse_lazy('forum:deleted_post') | ||
87 | + | ||
88 | +def post_deleted(request): | ||
89 | + return HttpResponse(_("Post deleted successfully.")) | ||
90 | + | ||
46 | class PostAnswerIndex(LoginRequiredMixin, generic.ListView): | 91 | class PostAnswerIndex(LoginRequiredMixin, generic.ListView): |
47 | login_url = reverse_lazy("core:home") | 92 | login_url = reverse_lazy("core:home") |
48 | redirect_field_name = 'next' | 93 | redirect_field_name = 'next' |
requirements.txt
@@ -10,7 +10,6 @@ itsdangerous==0.24 | @@ -10,7 +10,6 @@ itsdangerous==0.24 | ||
10 | Jinja2==2.8 | 10 | Jinja2==2.8 |
11 | MarkupSafe==0.23 | 11 | MarkupSafe==0.23 |
12 | Pillow==3.3.1 | 12 | Pillow==3.3.1 |
13 | -pkg-resources==0.0.0 | ||
14 | pycpfcnpj==1.0.2 | 13 | pycpfcnpj==1.0.2 |
15 | six==1.10.0 | 14 | six==1.10.0 |
16 | slugify==0.0.1 | 15 | slugify==0.0.1 |