Commit 4d667d767733936f7883d6a1790af5e56f479a98
Exists in
master
and in
5 other branches
Merge branch 'dev' of https://github.com/amadeusproject/amadeuslms into dev
Showing
36 changed files
with
399 additions
and
143 deletions
Show diff stats
amadeus/local_settings.py.example
@@ -1,16 +0,0 @@ | @@ -1,16 +0,0 @@ | ||
1 | -import os | ||
2 | - | ||
3 | -DEBUG = True | ||
4 | - | ||
5 | -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
6 | - | ||
7 | -DATABASES = { | ||
8 | - 'default': { | ||
9 | - 'ENGINE': 'django.db.backends.postgresql', | ||
10 | - 'NAME': 'amadeus', | ||
11 | - 'USER': 'amadeus_admin', | ||
12 | - 'PASSWORD': 'amadeus', | ||
13 | - 'HOST': '127.0.0.1', | ||
14 | - 'PORT': '5432', | ||
15 | - } | ||
16 | -} | ||
17 | \ No newline at end of file | 0 | \ No newline at end of file |
amadeus/settings.py
avaliacao/__init__.py
avaliacao/admin.py
avaliacao/apps.py
avaliacao/migrations/__init__.py
avaliacao/models.py
@@ -1,20 +0,0 @@ | @@ -1,20 +0,0 @@ | ||
1 | -from django.utils.translation import ugettext_lazy as _ | ||
2 | -from django.db import models | ||
3 | -from autoslug.fields import AutoSlugField | ||
4 | -from users.models import User | ||
5 | -from core.models import Resource | ||
6 | -from courses.models import Activity | ||
7 | - | ||
8 | -class Avaliacao(Activity): | ||
9 | - | ||
10 | - name_avalicao = models.CharField(_('Name'), max_length = 100) | ||
11 | - init_date = models.DateField(_('Begin of Avaliacao Date')) | ||
12 | - end_date = models.DateField(_('End of Avaliacao Date')) | ||
13 | - | ||
14 | - class Meta: | ||
15 | - #ordering = ('create_date','name') | ||
16 | - verbose_name = _('Avaliacao') | ||
17 | - verbose_name_plural = _('Avaliacoes') | ||
18 | - | ||
19 | -def __str__(self): | ||
20 | - return str(self.name) + str("/") + str(self.topic) |
avaliacao/tests.py
avaliacao/urls.py
avaliacao/views.py
courses/urls.py
@@ -26,5 +26,4 @@ urlpatterns = [ | @@ -26,5 +26,4 @@ urlpatterns = [ | ||
26 | 26 | ||
27 | url(r'^forum/', include('forum.urls', namespace = 'forum')), | 27 | url(r'^forum/', include('forum.urls', namespace = 'forum')), |
28 | url(r'^poll/', include('poll.urls', namespace = 'poll')), | 28 | url(r'^poll/', include('poll.urls', namespace = 'poll')), |
29 | - url(r'^avaliacao/', include('avaliacao.urls', namespace = 'avaliacao')) | ||
30 | ] | 29 | ] |
courses/views.py
@@ -292,7 +292,7 @@ class TopicsView(LoginRequiredMixin, generic.ListView): | @@ -292,7 +292,7 @@ class TopicsView(LoginRequiredMixin, generic.ListView): | ||
292 | topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | 292 | topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) |
293 | context = super(TopicsView, self).get_context_data(**kwargs) | 293 | context = super(TopicsView, self).get_context_data(**kwargs) |
294 | activitys = Activity.objects.filter(topic__name = topic.name) | 294 | activitys = Activity.objects.filter(topic__name = topic.name) |
295 | - students_activit = User.objects.filter(activities = Activity.objects.all()) | 295 | + students_activit = User.objects.filter(activities__in = Activity.objects.all()) |
296 | # page_user = User.objects.get(id= self.kwargs['user_id']) | 296 | # page_user = User.objects.get(id= self.kwargs['user_id']) |
297 | context['topic'] = topic | 297 | context['topic'] = topic |
298 | context['subject'] = topic.subject | 298 | context['subject'] = topic.subject |
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | +from django import forms | ||
2 | +from .models import Exam | ||
3 | + | ||
4 | +class ExamForm(forms.ModelForm): | ||
5 | + def clean_end_date(self): | ||
6 | + beginDate = self.data['beginDate'] | ||
7 | + endDate = self.data['endDate'] | ||
8 | + | ||
9 | + if beginDate and endDate and endDate < beginDate: | ||
10 | + raise forms.ValidationError(_('The end date may not be before the start date.')) | ||
11 | + return endDate | ||
12 | + | ||
13 | + def clean_begin_date(self): | ||
14 | + endDate = self.data['endDate'] | ||
15 | + beginDate = self.data['beginDate'] | ||
16 | + | ||
17 | + if enDate and benginDate and beginDate <= endDate: | ||
18 | + raise forms.ValidationError(_('The exam start date must be after the end of registration.')) | ||
19 | + return beginDate | ||
20 | + | ||
21 | + def clean_end_date(self): | ||
22 | + beginDate = self.data['beginDate'] | ||
23 | + endDate = self.data['endDate'] | ||
24 | + | ||
25 | + if beginDate and endDate and endDate < beginDate: | ||
26 | + raise forms.ValidationError(_('The finish date may not be before the start date.')) | ||
27 | + return end_date | ||
28 | + | ||
29 | + | ||
30 | + | ||
31 | + class Meta: | ||
32 | + model = Exam | ||
33 | + fields = ['name','beginDate','endDate'] | ||
34 | + | ||
35 | + widgets = { | ||
36 | + 'name': forms.TextInput(attrs={'placeholder': 'Exam?'}), | ||
37 | + 'beginDate': forms.DateTimeInput(attrs={'placeholder': 'Start date to resolve the exam'}), | ||
38 | + 'endDate': forms.DateTimeInput(attrs={'placeholder': 'Finish date permited to resolve the exam'}), | ||
39 | + } |
@@ -0,0 +1,48 @@ | @@ -0,0 +1,48 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2016-10-06 19:57 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations, models | ||
6 | +import django.db.models.deletion | ||
7 | + | ||
8 | + | ||
9 | +class Migration(migrations.Migration): | ||
10 | + | ||
11 | + initial = True | ||
12 | + | ||
13 | + dependencies = [ | ||
14 | + ] | ||
15 | + | ||
16 | + operations = [ | ||
17 | + migrations.CreateModel( | ||
18 | + name='Answer', | ||
19 | + fields=[ | ||
20 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
21 | + ('answer', models.CharField(max_length=200, verbose_name='Answer')), | ||
22 | + ('order', models.PositiveSmallIntegerField(verbose_name='Order')), | ||
23 | + ], | ||
24 | + options={ | ||
25 | + 'verbose_name_plural': 'Answers', | ||
26 | + 'ordering': ('order',), | ||
27 | + 'verbose_name': 'Answer', | ||
28 | + }, | ||
29 | + ), | ||
30 | + migrations.CreateModel( | ||
31 | + name='Exam', | ||
32 | + fields=[ | ||
33 | + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
34 | + ('name', models.CharField(max_length=100, verbose_name='Name')), | ||
35 | + ('beginDate', models.DateTimeField(auto_now_add=True, verbose_name='Start Date')), | ||
36 | + ('endDate', models.DateTimeField(auto_now=True, verbose_name='Date of last update')), | ||
37 | + ], | ||
38 | + options={ | ||
39 | + 'verbose_name_plural': 'Exams', | ||
40 | + 'verbose_name': 'Exam', | ||
41 | + }, | ||
42 | + ), | ||
43 | + migrations.AddField( | ||
44 | + model_name='answer', | ||
45 | + name='exam', | ||
46 | + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='exam.Exam', verbose_name='Answers'), | ||
47 | + ), | ||
48 | + ] |
@@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
1 | +from django.utils.translation import ugettext_lazy as _ | ||
2 | +from django.db import models | ||
3 | +from autoslug.fields import AutoSlugField | ||
4 | +from users.models import User | ||
5 | +from core.models import Resource | ||
6 | +from courses.models import Activity | ||
7 | + | ||
8 | + | ||
9 | + | ||
10 | +class Exam(models.Model): | ||
11 | + name = models.CharField(_('Name'), max_length = 100) | ||
12 | + beginDate = models.DateTimeField(_('Start Date'), auto_now_add = True) | ||
13 | + endDate = models.DateTimeField(_('Date of last update'), auto_now=True) | ||
14 | + | ||
15 | + class Meta: | ||
16 | + #ordering = ('create_date','name') | ||
17 | + verbose_name = _('Exam') | ||
18 | + verbose_name_plural = _('Exams') | ||
19 | + | ||
20 | + def __str__(self): | ||
21 | + return str(self.name) + str("/") + str(self.topic) | ||
22 | + | ||
23 | +class Answer(models.Model): | ||
24 | + answer = models.CharField(_("Answer"), max_length = 200) | ||
25 | + order = models.PositiveSmallIntegerField(_("Order")) | ||
26 | + exam = models.ForeignKey(Exam, verbose_name = _('Answers'), related_name='answers') | ||
27 | + | ||
28 | + class Meta: | ||
29 | + ordering = ('order',) | ||
30 | + verbose_name = _('Answer') | ||
31 | + verbose_name_plural = _('Answers') | ||
32 | + | ||
33 | + def __str__(self): | ||
34 | + return str(self.answer) + str("/") + str(self.poll) |
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +from rolepermissions.permissions import register_object_checker | ||
2 | +from amadeus.roles import SystemAdmin | ||
3 | + | ||
4 | +@register_object_checker() | ||
5 | +def edit_exam(role, user, exam): | ||
6 | + if (role == SystemAdmin): | ||
7 | + return True | ||
8 | + | ||
9 | + if (user in exam.topic.subject.professors.all()): | ||
10 | + return True | ||
11 | + | ||
12 | + return False |
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | +from django.conf.urls import url | ||
2 | + | ||
3 | +from . import views | ||
4 | + | ||
5 | +urlpatterns = [ | ||
6 | + url(r'^create/(?P<slug>[\w\-_]+)/$', views.CreateExam.as_view(), name='create_poll'), | ||
7 | + url(r'^update/(?P<slug>[\w\-_]+)/$', views.UpdateExam.as_view(), name='update_poll'), | ||
8 | + | ||
9 | +] |
@@ -0,0 +1,97 @@ | @@ -0,0 +1,97 @@ | ||
1 | +from django.shortcuts import render, get_object_or_404, redirect | ||
2 | +from django.views import generic | ||
3 | +from django.contrib.auth.decorators import login_required | ||
4 | +from django.core.paginator import Paginator, EmptyPage | ||
5 | +from django.contrib.auth.mixins import LoginRequiredMixin | ||
6 | +from rolepermissions.mixins import HasRoleMixin | ||
7 | +from django.core.urlresolvers import reverse_lazy | ||
8 | +from django.utils.translation import ugettext_lazy as _ | ||
9 | +from rolepermissions.verifications import has_role | ||
10 | +from rolepermissions.verifications import has_object_permission | ||
11 | +# from django.views.generic.edit import FormMixin | ||
12 | + | ||
13 | +from .forms import ExamForm | ||
14 | +from .models import Exam, Answer | ||
15 | +from core.mixins import NotificationMixin | ||
16 | +from users.models import User | ||
17 | +from courses.models import Course, Topic | ||
18 | + | ||
19 | +class CreateExam(LoginRequiredMixin,generic.CreateView): | ||
20 | + | ||
21 | + login_url = reverse_lazy("core:home") | ||
22 | + redirect_field_name = 'next' | ||
23 | + model = Exam | ||
24 | + form_class = PollForm | ||
25 | + context_object_name = 'exam' | ||
26 | + template_name = 'exam/form_exam.html' | ||
27 | + success_url = reverse_lazy('core:home') | ||
28 | + | ||
29 | + def form_valid(self, form): | ||
30 | + self.object = form.save(commit = False) | ||
31 | + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | ||
32 | + self.object.topic = topic | ||
33 | + self.object.save() | ||
34 | + | ||
35 | + for key in self.request.POST: | ||
36 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'beginDate' and key != 'endDate'): | ||
37 | + answer = Answer(answer=self.request.POST[key],order=key,poll=self.object) | ||
38 | + answer.save() | ||
39 | + | ||
40 | + return super(CreateExam, self).form_valid(form) | ||
41 | + | ||
42 | + def form_invalid(self, form,**kwargs): | ||
43 | + context = super(CreateExam, self).form_invalid(form) | ||
44 | + answers = {} | ||
45 | + for key in self.request.POST: | ||
46 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'beginDate' and key != 'endDate'): | ||
47 | + answers[key] = self.request.POST[key] | ||
48 | + | ||
49 | + keys = sorted(answers) | ||
50 | + context.context_data['answers'] = answers | ||
51 | + context.context_data['keys'] = keys | ||
52 | + return context | ||
53 | + | ||
54 | +class UpdateExam(LoginRequiredMixin,generic.UpdateView): | ||
55 | + | ||
56 | + login_url = reverse_lazy("core:home") | ||
57 | + redirect_field_name = 'next' | ||
58 | + model = Exam | ||
59 | + form_class = ExamForm | ||
60 | + context_object_name = 'exam' | ||
61 | + template_name = 'poll/form_exam.html' | ||
62 | + success_url = reverse_lazy('core:home') | ||
63 | + | ||
64 | + def dispatch(self, *args, **kwargs): | ||
65 | + poll = get_object_or_404(Poll, slug = self.kwargs.get('slug')) | ||
66 | + | ||
67 | + if(not has_object_permission('edit_exam', self.request.user, exam)): | ||
68 | + return self.handle_no_permission() | ||
69 | + return super(UpdateExam, self).dispatch(*args, **kwargs) | ||
70 | + | ||
71 | + def get_object(self, queryset=None): | ||
72 | + return get_object_or_404(Poll, slug = self.kwargs.get('slug')) | ||
73 | + | ||
74 | + def form_valid(self, form): | ||
75 | + poll = self.object | ||
76 | + poll = form.save(commit = False) | ||
77 | + poll.answers.all().delete() | ||
78 | + poll.save() | ||
79 | + | ||
80 | + for key in self.request.POST: | ||
81 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'beginDate' and key != 'endDate'): | ||
82 | + answer = Answer(answer=self.request.POST[key],order=key,exam=exam) | ||
83 | + answer.save() | ||
84 | + | ||
85 | + return super(UpdateExam, self).form_valid(form) | ||
86 | + | ||
87 | + def form_invalid(self, form,**kwargs): | ||
88 | + context = super(UpdateExam, self).form_invalid(form) | ||
89 | + answers = {} | ||
90 | + for key in self.request.POST: | ||
91 | + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'beginDate' and key != 'endDate'): | ||
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 |
forum/static/js/forum.js
@@ -255,6 +255,49 @@ function answer(id, url) { | @@ -255,6 +255,49 @@ function answer(id, url) { | ||
255 | 255 | ||
256 | /* | 256 | /* |
257 | * | 257 | * |
258 | +* Function to load form to edit post answer | ||
259 | +* | ||
260 | +*/ | ||
261 | +function edit_post_answer(url, answer_id) { | ||
262 | + $.ajax({ | ||
263 | + url: url, | ||
264 | + success: function(data) { | ||
265 | + $("#answer_"+answer_id).find(".post_answer_content").hide(); | ||
266 | + $("#answer_"+answer_id).find(".post_answer_content").after(data); | ||
267 | + | ||
268 | + var frm = $("#answer_"+answer_id).find(".answer_post_form"); | ||
269 | + frm.submit(function () { | ||
270 | + $.ajax({ | ||
271 | + type: frm.attr('method'), | ||
272 | + url: frm.attr('action'), | ||
273 | + data: frm.serialize(), | ||
274 | + success: function (data) { | ||
275 | + $("#answer_"+answer_id).parent().after(data); | ||
276 | + frm.parent().parent().remove(); | ||
277 | + }, | ||
278 | + error: function(data) { | ||
279 | + console.log(frm.serialize()); | ||
280 | + console.log('Error'); | ||
281 | + } | ||
282 | + }); | ||
283 | + return false; | ||
284 | + }); | ||
285 | + } | ||
286 | + }); | ||
287 | +} | ||
288 | + | ||
289 | +/* | ||
290 | +* | ||
291 | +* Function to cancel post answer edition | ||
292 | +* | ||
293 | +*/ | ||
294 | +function cancelEditPostAnswer(answer_id) { | ||
295 | + $("#answer_"+answer_id).find(".post_answer_content").show(); | ||
296 | + $("#answer_"+answer_id).find(".answer_post_form").remove(); | ||
297 | +} | ||
298 | + | ||
299 | +/* | ||
300 | +* | ||
258 | * Function to delete an answer | 301 | * Function to delete an answer |
259 | * | 302 | * |
260 | */ | 303 | */ |
forum/templates/post/post_update_form.html
1 | {% load i18n permission_tags list_post %} | 1 | {% load i18n permission_tags list_post %} |
2 | {% load widget_tweaks %} | 2 | {% load widget_tweaks %} |
3 | 3 | ||
4 | -<form class="edit_post_form" method="post" action="{% url 'forum:update_post' post.id %}" enctype="multipart/form-data"> | 4 | +<form class="edit_post_form" method="post" action="{% url 'course:forum:update_post' post.id %}" enctype="multipart/form-data"> |
5 | {% csrf_token %} | 5 | {% csrf_token %} |
6 | {% for field in form %} | 6 | {% for field in form %} |
7 | {% if field.field.widget.input_type == 'hidden' %} | 7 | {% if field.field.widget.input_type == 'hidden' %} |
forum/templates/post_answers/post_answer_form.html
1 | {% load static i18n %} | 1 | {% load static i18n %} |
2 | {% load widget_tweaks %} | 2 | {% load widget_tweaks %} |
3 | 3 | ||
4 | -<form class="answer_post_form" method="post" action="{% if answer %}{% else %}{% url 'course:forum:reply_post' %}{% endif %}" enctype="multipart/form-data"> | 4 | +<form class="answer_post_form" method="post" action="{% if answer %}{% url 'course:forum:update_post_answer' answer.id %}{% else %}{% url 'course:forum:reply_post' %}{% endif %}" enctype="multipart/form-data"> |
5 | {% csrf_token %} | 5 | {% csrf_token %} |
6 | {% for field in form %} | 6 | {% for field in form %} |
7 | {% if field.field.widget.input_type == 'hidden' %} | 7 | {% if field.field.widget.input_type == 'hidden' %} |
@@ -27,11 +27,18 @@ | @@ -27,11 +27,18 @@ | ||
27 | </div> | 27 | </div> |
28 | </div> | 28 | </div> |
29 | {% endif %} | 29 | {% endif %} |
30 | - <span class="input-group-btn"> | ||
31 | - <button type="submit" class="btn btn-fab btn-fab-mini"> | ||
32 | - <i class="material-icons">send</i> | ||
33 | - </button> | ||
34 | - </span> | 30 | + {% if answer %} |
31 | + <div class="pull-right"> | ||
32 | + <button type="button" onclick="cancelEditPostAnswer('{{ answer.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 | + {% else %} | ||
36 | + <span class="input-group-btn"> | ||
37 | + <button type="submit" class="btn btn-fab btn-fab-mini"> | ||
38 | + <i class="material-icons">send</i> | ||
39 | + </button> | ||
40 | + </span> | ||
41 | + {% endif %} | ||
35 | </div> | 42 | </div> |
36 | </div> | 43 | </div> |
37 | {% endif %} | 44 | {% endif %} |
forum/templates/post_answers/post_answer_list.html
@@ -2,28 +2,36 @@ | @@ -2,28 +2,36 @@ | ||
2 | 2 | ||
3 | {% if answers|length > 0 %} | 3 | {% if answers|length > 0 %} |
4 | {% for answer in answers %} | 4 | {% for answer in answers %} |
5 | - <div id="answer_{{ answer.id }}" class="row" style="background-color: #e0e0e0"> | ||
6 | - <div class="col-sm-12 col-xs-12"> | ||
7 | - <h3 class="user-name"> | ||
8 | - {{ answer.user }} | ||
9 | - {% if request.user|has_role:'system_admin' or request.user == answer.user %} | ||
10 | - <div class="pull-right"> | ||
11 | - <div class="btn-group icon-more-horiz"> | ||
12 | - <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
13 | - <i class="material-icons">more_horiz</i> | ||
14 | - </a> | ||
15 | - <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | ||
16 | - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | ||
17 | - <li><a href="javascript:delete_answer('{% url 'course:forum:delete_answer' answer.id %}', '{{ answer.id }}', '{% trans "Are you sure you want to delete this answer?" %}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | ||
18 | - </ul> | 5 | + <div> |
6 | + <div id="answer_{{ answer.id }}" class="row" style="background-color: #e0e0e0"> | ||
7 | + <div class="col-sm-12 col-xs-12"> | ||
8 | + <h3 class="user-name"> | ||
9 | + {{ answer.user }} | ||
10 | + {% if request.user|has_role:'system_admin' or request.user == answer.user %} | ||
11 | + <div class="pull-right"> | ||
12 | + <div class="btn-group icon-more-horiz"> | ||
13 | + <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
14 | + <i class="material-icons">more_horiz</i> | ||
15 | + </a> | ||
16 | + <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | ||
17 | + <li><a href="javascript:edit_post_answer('{% url 'course:forum:update_post_answer' answer.id %}', '{{ answer.id }}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | ||
18 | + <li><a href="javascript:delete_answer('{% url 'course:forum:delete_answer' answer.id %}', '{{ answer.id }}', '{% trans "Are you sure you want to delete this answer?" %}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | ||
19 | + </ul> | ||
20 | + </div> | ||
19 | </div> | 21 | </div> |
22 | + {% endif %} | ||
23 | + </h3> | ||
24 | + <div class="post_answer_content"> | ||
25 | + <div class="card-data"> | ||
26 | + <p class="comment-date"><i class="fa fa-clock-o"></i> {{ answer.answer_date|timesince }} {% trans 'ago' %} | ||
27 | + {% if answer.answer_date != answer.modifiction_date %} | ||
28 | + <em> - {% trans 'Edited' %}</em> | ||
29 | + {% endif %} | ||
30 | + </p> | ||
20 | </div> | 31 | </div> |
21 | - {% endif %} | ||
22 | - </h3> | ||
23 | - <div class="card-data"> | ||
24 | - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ answer.answer_date|timesince }} {% trans 'ago' %}</p> | 32 | + <p class="comment-text">{{ answer.message|linebreaks }}</p> |
33 | + </div> | ||
25 | </div> | 34 | </div> |
26 | - <p class="comment-text">{{ answer.message|linebreaks }}</p> | ||
27 | </div> | 35 | </div> |
28 | </div> | 36 | </div> |
29 | {% endfor %} | 37 | {% endfor %} |
forum/templates/post_answers/post_answer_render.html
1 | {% load i18n permission_tags %} | 1 | {% load i18n permission_tags %} |
2 | 2 | ||
3 | -<div id="answer_{{ answer.id }}" class="row" style="background-color: #e0e0e0"> | ||
4 | - <div class="col-sm-12 col-xs-12"> | ||
5 | - <h3 class="user-name"> | ||
6 | - {{ answer.user }} | ||
7 | - {% if request.user|has_role:'system_admin' or request.user == answer.user %} | ||
8 | - <div class="pull-right"> | ||
9 | - <div class="btn-group icon-more-horiz"> | ||
10 | - <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
11 | - <i class="material-icons">more_horiz</i> | ||
12 | - </a> | ||
13 | - <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | ||
14 | - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | ||
15 | - <li><a href="javascript:delete_answer('{% url 'course:forum:delete_answer' answer.id %}', '{{ answer.id }}', '{% trans "Are you sure you want to delete this answer?" %}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | ||
16 | - </ul> | 3 | +<div> |
4 | + <div id="answer_{{ answer.id }}" class="row" style="background-color: #e0e0e0"> | ||
5 | + <div class="col-sm-12 col-xs-12"> | ||
6 | + <h3 class="user-name"> | ||
7 | + {{ answer.user }} | ||
8 | + {% if request.user|has_role:'system_admin' or request.user == answer.user %} | ||
9 | + <div class="pull-right"> | ||
10 | + <div class="btn-group icon-more-horiz"> | ||
11 | + <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
12 | + <i class="material-icons">more_horiz</i> | ||
13 | + </a> | ||
14 | + <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> | ||
15 | + <li><a href="javascript:edit_post_answer('{% url 'course:forum:update_post_answer' answer.id %}', '{{ answer.id }}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | ||
16 | + <li><a href="javascript:delete_answer('{% url 'course:forum:delete_answer' answer.id %}', '{{ answer.id }}', '{% trans "Are you sure you want to delete this answer?" %}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | ||
17 | + </ul> | ||
18 | + </div> | ||
17 | </div> | 19 | </div> |
20 | + {% endif %} | ||
21 | + </h3> | ||
22 | + <div class="post_answer_content"> | ||
23 | + <div class="card-data"> | ||
24 | + <p class="comment-date"><i class="fa fa-clock-o"></i> {{ answer.answer_date|timesince }} {% trans 'ago' %} | ||
25 | + {% if answer.answer_date != answer.modifiction_date %} | ||
26 | + <em> - {% trans 'Edited' %}</em> | ||
27 | + {% endif %} | ||
28 | + </p> | ||
18 | </div> | 29 | </div> |
19 | - {% endif %} | ||
20 | - </h3> | ||
21 | - <div class="card-data"> | ||
22 | - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ answer.answer_date|timesince }} {% trans 'ago' %}</p> | 30 | + <p class="comment-text">{{ answer.message|linebreaks }}</p> |
31 | + </div> | ||
23 | </div> | 32 | </div> |
24 | - <p class="comment-text">{{ answer.message|linebreaks }}</p> | ||
25 | </div> | 33 | </div> |
26 | </div> | 34 | </div> |
27 | \ No newline at end of file | 35 | \ No newline at end of file |
forum/urls.py
@@ -18,6 +18,7 @@ urlpatterns = [ | @@ -18,6 +18,7 @@ urlpatterns = [ | ||
18 | url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), | 18 | url(r'^post_deleted/$', views.post_deleted, name='deleted_post'), |
19 | url(r'^post_answers/$', views.PostAnswerIndex.as_view(), name='post_answers'), | 19 | url(r'^post_answers/$', views.PostAnswerIndex.as_view(), name='post_answers'), |
20 | url(r'^reply_post/$', views.CreatePostAnswerView.as_view(), name='reply_post'), | 20 | url(r'^reply_post/$', views.CreatePostAnswerView.as_view(), name='reply_post'), |
21 | + url(r'^update_post_answer/(?P<pk>[\w_-]+)/$', views.PostAnswerUpdateView.as_view(), name='update_post_answer'), | ||
21 | url(r'^render_post_answer/([\w_-]+)/$', views.render_post_answer, name='render_post_answer'), | 22 | url(r'^render_post_answer/([\w_-]+)/$', views.render_post_answer, name='render_post_answer'), |
22 | url(r'^delete_post_answer/(?P<pk>[\w_-]+)/$', views.PostAnswerDeleteView.as_view(), name='delete_answer'), | 23 | url(r'^delete_post_answer/(?P<pk>[\w_-]+)/$', views.PostAnswerDeleteView.as_view(), name='delete_answer'), |
23 | url(r'^post_answer_deleted/$', views.answer_deleted, name='deleted_answer'), | 24 | url(r'^post_answer_deleted/$', views.answer_deleted, name='deleted_answer'), |
forum/views.py
@@ -210,6 +210,20 @@ def render_post_answer(request, answer): | @@ -210,6 +210,20 @@ def render_post_answer(request, answer): | ||
210 | 210 | ||
211 | return render(request, "post_answers/post_answer_render.html", context) | 211 | return render(request, "post_answers/post_answer_render.html", context) |
212 | 212 | ||
213 | +class PostAnswerUpdateView(LoginRequiredMixin, generic.UpdateView): | ||
214 | + login_url = reverse_lazy("core:home") | ||
215 | + redirect_field_name = 'next' | ||
216 | + | ||
217 | + form_class = PostAnswerForm | ||
218 | + model = PostAnswer | ||
219 | + template_name = "post_answers/post_answer_form.html" | ||
220 | + context_object_name = 'answer' | ||
221 | + | ||
222 | + def get_success_url(self): | ||
223 | + self.success_url = reverse('course:forum:render_post_answer', args = (self.object.id, )) | ||
224 | + | ||
225 | + return self.success_url | ||
226 | + | ||
213 | class PostAnswerDeleteView(LoginRequiredMixin, generic.DeleteView): | 227 | class PostAnswerDeleteView(LoginRequiredMixin, generic.DeleteView): |
214 | login_url = reverse_lazy("core:home") | 228 | login_url = reverse_lazy("core:home") |
215 | redirect_field_name = 'next' | 229 | redirect_field_name = 'next' |
links/models.py
1 | + | ||
1 | from django.db import models | 2 | from django.db import models |
2 | from courses.models import Material | 3 | from courses.models import Material |
3 | # Create your models here. | 4 | # Create your models here. |
4 | -class Link(Material,models.Model): | ||
5 | - name = models.CharField(max_lenght = 100) | ||
6 | - link = models.UrlField() | ||
7 | - description = models.CharField(max_lenght = 200) | 5 | +class Link(models.Model): |
6 | + name = models.CharField(max_length=100) | ||
7 | + link = models.URLField() | ||
8 | + description = models.CharField(max_length=200) | ||
8 | class Meta: | 9 | class Meta: |
9 | verbose_name = 'Link' | 10 | verbose_name = 'Link' |
10 | verbose_name_plural = "Links" | 11 | verbose_name_plural = "Links" |
11 | def __str__(self): | 12 | def __str__(self): |
12 | return str(self.name) | 13 | return str(self.name) |
13 | - | ||
14 | - | ||
15 | - |
users/templates/users/profile.html
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <ul class="nav nav-pills nav-stacked"> | 20 | <ul class="nav nav-pills nav-stacked"> |
21 | <li><a href="{% url 'app:index' %}">{% trans 'Home' %}</a></li> | 21 | <li><a href="{% url 'app:index' %}">{% trans 'Home' %}</a></li> |
22 | <li><a href="{% url 'users:profile' %}">{% trans 'View Profile' %}</a></li> | 22 | <li><a href="{% url 'users:profile' %}">{% trans 'View Profile' %}</a></li> |
23 | - <li><a href="{% url 'users:edit_profile' user.id %}">{% trans 'Edit Profile' %}</a></li> | 23 | + <li><a href="{% url 'users:update_profile' %}">{% trans 'Edit Profile' %}</a></li> |
24 | </ul> | 24 | </ul> |
25 | </div> | 25 | </div> |
26 | </div> | 26 | </div> |
users/urls.py
@@ -9,7 +9,6 @@ urlpatterns = [ | @@ -9,7 +9,6 @@ urlpatterns = [ | ||
9 | url(r'^view/(?P<username>[\w_-]+)/$', views.View.as_view(), name='view'), | 9 | url(r'^view/(?P<username>[\w_-]+)/$', views.View.as_view(), name='view'), |
10 | url(r'^delete/(?P<username>[\w_-]+)/$', views.delete, name='delete'), | 10 | url(r'^delete/(?P<username>[\w_-]+)/$', views.delete, name='delete'), |
11 | url(r'^profile/$', views.Profile.as_view(), name='profile'), | 11 | url(r'^profile/$', views.Profile.as_view(), name='profile'), |
12 | - url(r'^profile/editar/(?P<username>[\w_-]+)/$', views.EditProfile.as_view(), name='edit_profile'), | ||
13 | # | 12 | # |
14 | url(r'^profile/update/$', views.UpdateProfile.as_view(), name='update_profile'), | 13 | url(r'^profile/update/$', views.UpdateProfile.as_view(), name='update_profile'), |
15 | url(r'^profile/delete/$', views.DeleteUser.as_view(), name='delete_profile'), | 14 | url(r'^profile/delete/$', views.DeleteUser.as_view(), name='delete_profile'), |
users/views.py
@@ -142,32 +142,4 @@ class Profile(LoginRequiredMixin, generic.DetailView): | @@ -142,32 +142,4 @@ class Profile(LoginRequiredMixin, generic.DetailView): | ||
142 | 142 | ||
143 | def get_object(self): | 143 | def get_object(self): |
144 | user = get_object_or_404(User, username = self.request.user.username) | 144 | user = get_object_or_404(User, username = self.request.user.username) |
145 | - return user | ||
146 | - | ||
147 | -class EditProfile(LoginRequiredMixin, generic.UpdateView): | ||
148 | - | ||
149 | - login_url = reverse_lazy('core:home') | ||
150 | - redirect_field_name = 'next' | ||
151 | - template_name = 'users/edit_profile.html' | ||
152 | - form_class = UserForm | ||
153 | - success_url = reverse_lazy('app:users:edit_profile') | ||
154 | - | ||
155 | - def get_object(self): | ||
156 | - user = get_object_or_404(User, username = self.request.user.username) | ||
157 | - return user | ||
158 | - | ||
159 | - def form_valid(self, form): | ||
160 | - self.object = form.save(commit = False) | ||
161 | - | ||
162 | - if self.object.type_profile == 2: | ||
163 | - assign_role(self.object, 'student') | ||
164 | - elif self.object.type_profile == 1: | ||
165 | - assign_role(self.object, 'professor') | ||
166 | - elif self.object.is_staff: | ||
167 | - assign_role(self.object, 'system_admin') | ||
168 | - | ||
169 | - self.object.save() | ||
170 | - | ||
171 | - messages.success(self.request, _('Profile edited successfully!')) | ||
172 | - | ||
173 | - return super(EditProfile, self).form_valid(form) | ||
174 | \ No newline at end of file | 145 | \ No newline at end of file |
146 | + return user | ||
175 | \ No newline at end of file | 147 | \ No newline at end of file |