Commit c42f7077a3b25f47d982ecebaf1917a891b12731
Exists in
master
and in
3 other branches
Merge branch 'refactoring' of https://github.com/amadeusproject/amadeuslms into refactoring
Showing
11 changed files
with
214 additions
and
49 deletions
Show diff stats
amadeus/permissions.py
@@ -10,10 +10,10 @@ def has_subject_permissions(user, subject): | @@ -10,10 +10,10 @@ def has_subject_permissions(user, subject): | ||
10 | if user.is_staff: | 10 | if user.is_staff: |
11 | return True | 11 | return True |
12 | 12 | ||
13 | - if user in subject.professor.all(): | 13 | + if subject.professor.filter(id = user.id).exists(): |
14 | return True | 14 | return True |
15 | 15 | ||
16 | - if user in subject.category.coordinators.all(): | 16 | + if subject.category.coordinators.filter(id = user.id).exists(): |
17 | return True | 17 | return True |
18 | 18 | ||
19 | return False | 19 | return False |
amadeus/static/css/themes/green.css
@@ -63,6 +63,10 @@ a, a:focus, a:hover { | @@ -63,6 +63,10 @@ a, a:focus, a:hover { | ||
63 | background-color: #7BA5B9 !important; | 63 | background-color: #7BA5B9 !important; |
64 | } | 64 | } |
65 | 65 | ||
66 | +.topic-panel > .category-panel-content, .topic-panel-invisible > .category-panel-content { | ||
67 | + background: #FFFFFF; | ||
68 | +} | ||
69 | + | ||
66 | .topic-panel-invisible > .panel-heading { | 70 | .topic-panel-invisible > .panel-heading { |
67 | background-color: #BDBDBD !important; | 71 | background-color: #BDBDBD !important; |
68 | color: #F5F5F5; | 72 | color: #F5F5F5; |
@@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
1 | +// Update breadcrumb with topic's name | ||
2 | +$('.collapse').on('shown.bs.collapse', function (e) { | ||
3 | + if($(this).is(e.target)){ | ||
4 | + var li = $(".breadcrumb").find('li:last-child'); | ||
5 | + var li_text = $(li).html(); | ||
6 | + var url = $(".subs_url").val(); | ||
7 | + var new_li = $(li).clone(); | ||
8 | + | ||
9 | + new_li.html($(this).parent().find('.panel-title').text()); | ||
10 | + | ||
11 | + $(li).html("<a href='" + url + "'>" + li_text + "</a>"); | ||
12 | + $(li).append("<span class='divider'>/</span>"); | ||
13 | + | ||
14 | + new_li.appendTo('.breadcrumb'); | ||
15 | + } | ||
16 | +}); | ||
17 | + | ||
18 | +// Reset breadcrumb to it's normal state | ||
19 | +$('.collapse').on('hide.bs.collapse', function (e) { | ||
20 | + if($(this).is(e.target)){ | ||
21 | + $(".breadcrumb").find('li:last-child').remove(); | ||
22 | + | ||
23 | + var li = $(".breadcrumb").find('li:last-child'); | ||
24 | + var text = $(li).find('a').text(); | ||
25 | + | ||
26 | + $(li).html(text); | ||
27 | + } | ||
28 | +}); | ||
29 | + | ||
30 | +// utilizado para fazer a re-organização dos tópicos | ||
31 | +$("#topics-accordion").sortable({ | ||
32 | + delay: 100, | ||
33 | + distance: 5, | ||
34 | + handle: 'i.fa-arrows', | ||
35 | + update: function( event, ui ) { | ||
36 | + var cont = 1; | ||
37 | + var data = []; | ||
38 | + | ||
39 | + $("#topics-accordion").find('.order_inp').each(function () { | ||
40 | + $(this).val(cont++); | ||
41 | + | ||
42 | + data.push({ | ||
43 | + 'topic_id': $(this).parent().find('.id_inp').val(), | ||
44 | + 'topic_order': $(this).val() | ||
45 | + }); | ||
46 | + }); | ||
47 | + | ||
48 | + data = JSON.stringify(data); | ||
49 | + | ||
50 | + sendUpdate(data); | ||
51 | + }, | ||
52 | +}); | ||
53 | + | ||
54 | +function sendUpdate(data) { | ||
55 | + $.ajax({ | ||
56 | + url: $('.url_order').val(), | ||
57 | + dataType: 'json', | ||
58 | + data: {'data': data}, | ||
59 | + success: function(response) { | ||
60 | + console.log(response); | ||
61 | + }, | ||
62 | + error: function(response) { | ||
63 | + console.log(response); | ||
64 | + } | ||
65 | + }); | ||
66 | +} | ||
67 | + | ||
68 | +function delete_topic(url) { | ||
69 | + $('.modal').remove(); | ||
70 | + | ||
71 | + $.get(url, function (modal) { | ||
72 | + $("#topics-accordion").after(modal); | ||
73 | + | ||
74 | + $('.modal').modal('show'); | ||
75 | + }); | ||
76 | +} | ||
0 | \ No newline at end of file | 77 | \ No newline at end of file |
subjects/utils.py
@@ -6,14 +6,14 @@ from django.db.models import Q | @@ -6,14 +6,14 @@ from django.db.models import Q | ||
6 | 6 | ||
7 | def has_student_profile(user, category): | 7 | def has_student_profile(user, category): |
8 | for subject in category.subject_category.all(): | 8 | for subject in category.subject_category.all(): |
9 | - if user in subject.students.all() and subject.visible: | 9 | + if subject.students.filter(id = user.id).exists() and subject.visible: |
10 | return True | 10 | return True |
11 | 11 | ||
12 | return False | 12 | return False |
13 | 13 | ||
14 | def has_professor_profile(user, category): | 14 | def has_professor_profile(user, category): |
15 | for subject in category.subject_category.all(): | 15 | for subject in category.subject_category.all(): |
16 | - if user in subject.professor.all() and subject.visible: | 16 | + if subject.professor.filter(id = user.id).exists() and subject.visible: |
17 | return True | 17 | return True |
18 | 18 | ||
19 | return False | 19 | return False |
@@ -30,7 +30,8 @@ def count_subjects( user, all_subs = True): | @@ -30,7 +30,8 @@ def count_subjects( user, all_subs = True): | ||
30 | else: | 30 | else: |
31 | total += category.subject_category.count()""" | 31 | total += category.subject_category.count()""" |
32 | if all_subs: | 32 | if all_subs: |
33 | - total += Category.objects.filter(Q(coordinators__pk = pk) | Q(visible=True) ).distinct().count() | 33 | + #total += Category.objects.filter(Q(coordinators__pk = pk) | Q(visible=True) ).distinct().count() |
34 | + total = Subject.objects.filter(Q(students__pk=pk) | Q(professor__pk=pk) | Q(category__coordinators__pk=pk) | Q(visible = True)).distinct().count() | ||
34 | else: | 35 | else: |
35 | 36 | ||
36 | total = Subject.objects.filter(Q(students__pk=pk) | Q(professor__pk=pk) | Q(category__coordinators__pk=pk)).distinct().count() | 37 | total = Subject.objects.filter(Q(students__pk=pk) | Q(professor__pk=pk) | Q(category__coordinators__pk=pk)).distinct().count() |
subjects/views.py
@@ -74,7 +74,7 @@ class IndexView(LoginRequiredMixin, ListView): | @@ -74,7 +74,7 @@ class IndexView(LoginRequiredMixin, ListView): | ||
74 | else: | 74 | else: |
75 | pk = self.request.user.pk | 75 | pk = self.request.user.pk |
76 | 76 | ||
77 | - categories = Category.objects.filter(Q(coordinators__pk = pk) | Q(visible=True) ).order_by('name') | 77 | + categories = Category.objects.filter(Q(coordinators__pk = pk) | Q(visible=True) ).distinct().order_by('name') |
78 | 78 | ||
79 | self.totals['all_subjects'] = count_subjects(self.request.user) | 79 | self.totals['all_subjects'] = count_subjects(self.request.user) |
80 | 80 |
topics/forms.py
@@ -18,7 +18,10 @@ class TopicForm(forms.ModelForm): | @@ -18,7 +18,10 @@ class TopicForm(forms.ModelForm): | ||
18 | name = self.cleaned_data.get('name', '') | 18 | name = self.cleaned_data.get('name', '') |
19 | repo = self.cleaned_data.get('repository', False) | 19 | repo = self.cleaned_data.get('repository', False) |
20 | 20 | ||
21 | - same_name = len(self.subject.topic_subject.filter(name__unaccent__iexact = name)) | 21 | + if self.instance.id: |
22 | + same_name = len(self.subject.topic_subject.filter(name__unaccent__iexact = name).exclude(id = self.instance.id)) | ||
23 | + else: | ||
24 | + same_name = len(self.subject.topic_subject.filter(name__unaccent__iexact = name)) | ||
22 | 25 | ||
23 | if same_name > 0: | 26 | if same_name > 0: |
24 | if repo: | 27 | if repo: |
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +{% load i18n %} | ||
2 | + | ||
3 | +<div class="modal fade" id="topic" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> | ||
4 | + <div class="modal-dialog" role="document"> | ||
5 | + <div class="modal-content"> | ||
6 | + <div class="modal-body"> | ||
7 | + <form id="delete_form" action="{% url 'topics:delete' topic.slug %}" method="post"> | ||
8 | + {% csrf_token %} | ||
9 | + <h4>{% trans 'Are you sure you want delete the topic' %}: {{ topic }}?</h4> | ||
10 | + <p>{% trans 'All data will be lost and havent how recover it.' %}</p> | ||
11 | + </form> | ||
12 | + </div> | ||
13 | + <div class="modal-footer"> | ||
14 | + <div class="pull-right"> | ||
15 | + <button type="button" class="btn btn-default btn-raised" data-dismiss="modal">{% trans "Close" %}</button> | ||
16 | + </div> | ||
17 | + <div class="pull-left"> | ||
18 | + <button type="submit" form="delete_form" class="btn btn-success btn-raised">{% trans "Delete" %}</button> | ||
19 | + </div> | ||
20 | + </div> | ||
21 | + </div> | ||
22 | + </div> | ||
23 | +</div> | ||
0 | \ No newline at end of file | 24 | \ No newline at end of file |
topics/templates/topics/list.html
@@ -4,9 +4,12 @@ | @@ -4,9 +4,12 @@ | ||
4 | {% subject_permissions request.user subject as has_subject_permissions %} | 4 | {% subject_permissions request.user subject as has_subject_permissions %} |
5 | 5 | ||
6 | <div class="panel-group subject-group" id="topics-accordion" role="tablist" aria-multiselectable="true"> | 6 | <div class="panel-group subject-group" id="topics-accordion" role="tablist" aria-multiselectable="true"> |
7 | + <input type="hidden" class="url_order" value="{% url 'topics:update_order' %}" /> | ||
8 | + <input type="hidden" class="subs_url" value="{% url 'subjects:view' subject.slug %}" /> | ||
9 | + | ||
7 | {% for topic in subject.topic_subject.all %} | 10 | {% for topic in subject.topic_subject.all %} |
8 | {% if not topic.repository and topic.visible or has_subject_permissions %} | 11 | {% if not topic.repository and topic.visible or has_subject_permissions %} |
9 | - <div class="panel panel-info {% if not topic.visible or topic.repository %} topic-panel-invisible {% else %} topic-panel {% endif %} topic-panel"> | 12 | + <div class="panel panel-info {% if not topic.visible or topic.repository %} topic-panel-invisible {% else %} topic-panel {% endif %}"> |
10 | <div class="panel-heading"> | 13 | <div class="panel-heading"> |
11 | <div class="row"> | 14 | <div class="row"> |
12 | <div class="col-md-12 category-header"> | 15 | <div class="col-md-12 category-header"> |
@@ -22,9 +25,8 @@ | @@ -22,9 +25,8 @@ | ||
22 | <i class="fa fa-ellipsis-v" aria-hidden="true"></i> | 25 | <i class="fa fa-ellipsis-v" aria-hidden="true"></i> |
23 | </a> | 26 | </a> |
24 | <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> | 27 | <ul class="dropdown-menu pull-right" aria-labelledby="moreActions"> |
25 | - <li><a href="{% url 'subjects:replicate' subject.slug %}"><i class="fa fa-files-o fa-fw" aria-hidden="true"></i>{% trans 'Replicate' %}</a></li> | ||
26 | - <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li> | ||
27 | - <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> | 28 | + <li><a href="{% url 'topics:update' subject.slug topic.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li> |
29 | + <li><a href="javascript:delete_topic('{% url 'topics:delete' topic.slug %}')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i> {% trans 'Remove' %}</a></li> | ||
28 | </ul> | 30 | </ul> |
29 | <a href="" ><i class="fa fa-arrows" aria-hidden="true"></i></a> | 31 | <a href="" ><i class="fa fa-arrows" aria-hidden="true"></i></a> |
30 | </div> | 32 | </div> |
@@ -35,47 +37,15 @@ | @@ -35,47 +37,15 @@ | ||
35 | <div id="{{topic.slug}}" class="panel-collapse collapse category-panel-content"> | 37 | <div id="{{topic.slug}}" class="panel-collapse collapse category-panel-content"> |
36 | <input type="hidden" class="id_inp" name="id" value="{{ topic.id }}" /> | 38 | <input type="hidden" class="id_inp" name="id" value="{{ topic.id }}" /> |
37 | <input type="hidden" class="order_inp" name="order" value="{{ topic.order }}" /> | 39 | <input type="hidden" class="order_inp" name="order" value="{{ topic.order }}" /> |
40 | + | ||
41 | + {% autoescape off %} | ||
42 | + {{ topic.description }} | ||
43 | + {% endautoescape %} | ||
38 | </div> | 44 | </div> |
39 | </div> | 45 | </div> |
40 | {% endif %} | 46 | {% endif %} |
41 | {% endfor %} | 47 | {% endfor %} |
42 | </div> | 48 | </div> |
43 | 49 | ||
44 | -<script type="text/javascript"> | ||
45 | - $("#topics-accordion").sortable({ // utilizado para fazer a re-organização dos tópicos | ||
46 | - delay: 100, | ||
47 | - distance: 5, | ||
48 | - handler: 'i.fa-arrows', | ||
49 | - update: function( event, ui ) { | ||
50 | - var cont = 1; | ||
51 | - var data = []; | ||
52 | - | ||
53 | - $("#topics-accordion").find('.order_inp').each(function () { | ||
54 | - $(this).val(cont++); | ||
55 | - | ||
56 | - data.push({ | ||
57 | - 'topic_id': $(this).parent().find('.id_inp').val(), | ||
58 | - 'topic_order': $(this).val() | ||
59 | - }); | ||
60 | - }); | ||
61 | - | ||
62 | - data = JSON.stringify(data); | ||
63 | - | ||
64 | - sendUpdate(data); | ||
65 | - }, | ||
66 | - }); | ||
67 | - | ||
68 | - function sendUpdate(data) { | ||
69 | - $.ajax({ | ||
70 | - url: '{% url "topics:update_order" %}', | ||
71 | - dataType: 'json', | ||
72 | - data: {'data': data}, | ||
73 | - success: function(response) { | ||
74 | - console.log(response); | ||
75 | - }, | ||
76 | - error: function(response) { | ||
77 | - console.log(response); | ||
78 | - } | ||
79 | - }); | ||
80 | - } | ||
81 | -</script> | ||
82 | \ No newline at end of file | 50 | \ No newline at end of file |
51 | +<script type="text/javascript" src="{% static 'js/topics.js' %}"></script> | ||
52 | +<script type="text/javascript" src="{% static 'js/course.js' %}"></script> | ||
83 | \ No newline at end of file | 53 | \ No newline at end of file |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +{% extends 'subjects/view.html' %} | ||
2 | + | ||
3 | +{% load django_bootstrap_breadcrumbs %} | ||
4 | + | ||
5 | +{% block breadcrumbs %} | ||
6 | + {{ block.super }} | ||
7 | + {% breadcrumb 'Update Topic' 'topics:update' subject.slug topic.slug %} | ||
8 | +{% endblock %} | ||
9 | + | ||
10 | +{% block content %} | ||
11 | + <div class="card"> | ||
12 | + <div class="card-content"> | ||
13 | + <div class="card-body"> | ||
14 | + {% include 'topics/_form.html' %} | ||
15 | + </div> | ||
16 | + </div> | ||
17 | + </div> | ||
18 | + <br clear="all" /> | ||
19 | + <br clear="all" /> | ||
20 | +{% endblock %} |
topics/urls.py
@@ -5,5 +5,7 @@ from . import views | @@ -5,5 +5,7 @@ from . import views | ||
5 | 5 | ||
6 | urlpatterns = [ | 6 | urlpatterns = [ |
7 | url(r'^create/(?P<slug>[\w_-]+)/$', views.CreateView.as_view(), name = 'create'), | 7 | url(r'^create/(?P<slug>[\w_-]+)/$', views.CreateView.as_view(), name = 'create'), |
8 | + url(r'^update/(?P<sub_slug>[\w_-]+)/(?P<slug>[\w_-]+)/$', views.UpdateView.as_view(), name = 'update'), | ||
9 | + url(r'^delete/(?P<slug>[\w_-]+)/$', views.DeleteView.as_view(), name = 'delete'), | ||
8 | url(r'^update_order/$', views.update_order, name = 'update_order'), | 10 | url(r'^update_order/$', views.update_order, name = 'update_order'), |
9 | ] | 11 | ] |
topics/views.py
@@ -70,6 +70,72 @@ class CreateView(LoginRequiredMixin, generic.edit.CreateView): | @@ -70,6 +70,72 @@ class CreateView(LoginRequiredMixin, generic.edit.CreateView): | ||
70 | 70 | ||
71 | return reverse_lazy('subjects:view', kwargs = {'slug': self.object.subject.slug}) | 71 | return reverse_lazy('subjects:view', kwargs = {'slug': self.object.subject.slug}) |
72 | 72 | ||
73 | +class UpdateView(LoginRequiredMixin, generic.UpdateView): | ||
74 | + login_url = reverse_lazy("users:login") | ||
75 | + redirect_field_name = 'next' | ||
76 | + | ||
77 | + template_name = 'topics/update.html' | ||
78 | + form_class = TopicForm | ||
79 | + model = Topic | ||
80 | + context_object_name = 'topic' | ||
81 | + | ||
82 | + def dispatch(self, request, *args, **kwargs): | ||
83 | + slug = self.kwargs.get('sub_slug', '') | ||
84 | + subject = get_object_or_404(Subject, slug = slug) | ||
85 | + | ||
86 | + if not has_subject_permissions(request.user, subject): | ||
87 | + return redirect(reverse_lazy('subjects:home')) | ||
88 | + | ||
89 | + return super(UpdateView, self).dispatch(request, *args, **kwargs) | ||
90 | + | ||
91 | + def get_initial(self): | ||
92 | + initial = super(UpdateView, self).get_initial() | ||
93 | + | ||
94 | + slug = self.kwargs.get('sub_slug', '') | ||
95 | + | ||
96 | + initial['subject'] = get_object_or_404(Subject, slug = slug) | ||
97 | + | ||
98 | + return initial | ||
99 | + | ||
100 | + def get_context_data(self, **kwargs): | ||
101 | + context = super(UpdateView, self).get_context_data(**kwargs) | ||
102 | + | ||
103 | + context['title'] = _('Update Topic') | ||
104 | + | ||
105 | + slug = self.kwargs.get('sub_slug', '') | ||
106 | + subject = get_object_or_404(Subject, slug = slug) | ||
107 | + | ||
108 | + context['subject'] = subject | ||
109 | + | ||
110 | + return context | ||
111 | + | ||
112 | + def get_success_url(self): | ||
113 | + messages.success(self.request, _('Topic "%s" was updated on virtual enviroment "%s" successfully!')%(self.object.name, self.object.subject.name)) | ||
114 | + | ||
115 | + return reverse_lazy('subjects:view', kwargs = {'slug': self.object.subject.slug}) | ||
116 | + | ||
117 | +class DeleteView(LoginRequiredMixin, generic.DeleteView): | ||
118 | + login_url = reverse_lazy("users:login") | ||
119 | + redirect_field_name = 'next' | ||
120 | + | ||
121 | + template_name = 'topics/delete.html' | ||
122 | + model = Topic | ||
123 | + context_object_name = 'topic' | ||
124 | + | ||
125 | + def dispatch(self, request, *args, **kwargs): | ||
126 | + slug = self.kwargs.get('slug', '') | ||
127 | + topic = get_object_or_404(Topic, slug = slug) | ||
128 | + | ||
129 | + if not has_subject_permissions(request.user, topic.subject): | ||
130 | + return redirect(reverse_lazy('subjects:home')) | ||
131 | + | ||
132 | + return super(DeleteView, self).dispatch(request, *args, **kwargs) | ||
133 | + | ||
134 | + def get_success_url(self): | ||
135 | + messages.success(self.request, _('Topic "%s" was removed from virtual enviroment "%s" successfully!')%(self.object.name, self.object.subject.name)) | ||
136 | + | ||
137 | + return reverse_lazy('subjects:view', kwargs = {'slug': self.object.subject.slug}) | ||
138 | + | ||
73 | def update_order(request): | 139 | def update_order(request): |
74 | data = request.GET.get('data', None) | 140 | data = request.GET.get('data', None) |
75 | 141 |