Commit a98874b2c97343982adbda4092a6e1b4af638490
1 parent
614033fd
Exists in
master
and in
3 other branches
Adding resource list ordering
Showing
12 changed files
with
210 additions
and
16 deletions
Show diff stats
amadeus/static/css/base/amadeus.css
@@ -767,6 +767,8 @@ a.add-row { | @@ -767,6 +767,8 @@ a.add-row { | ||
767 | padding: 10px 15px; | 767 | padding: 10px 15px; |
768 | margin-bottom: -1px; | 768 | margin-bottom: -1px; |
769 | border-width: 1px !important; | 769 | border-width: 1px !important; |
770 | + overflow: inherit; | ||
771 | + cursor: pointer; | ||
770 | } | 772 | } |
771 | 773 | ||
772 | .resource_list > .list-group-item:last-child { | 774 | .resource_list > .list-group-item:last-child { |
@@ -777,4 +779,13 @@ a.add-row { | @@ -777,4 +779,13 @@ a.add-row { | ||
777 | 779 | ||
778 | .resource_list .list-group-item .list-group-item-heading { | 780 | .resource_list .list-group-item .list-group-item-heading { |
779 | font-weight: 500; | 781 | font-weight: 500; |
782 | +} | ||
783 | + | ||
784 | +.resource_list .btn-group { | ||
785 | + display: inline; | ||
786 | + margin: 0px; | ||
787 | +} | ||
788 | + | ||
789 | +.resource_list a:hover, .resource_list a:focus { | ||
790 | + text-decoration: none; | ||
780 | } | 791 | } |
781 | \ No newline at end of file | 792 | \ No newline at end of file |
amadeus/static/css/themes/green.css
@@ -361,10 +361,22 @@ a.add-row { | @@ -361,10 +361,22 @@ a.add-row { | ||
361 | border: 1px solid #ddd; | 361 | border: 1px solid #ddd; |
362 | } | 362 | } |
363 | 363 | ||
364 | +.resource_list > .list-group-item:hover { | ||
365 | + background: #F5F5F5; | ||
366 | +} | ||
367 | + | ||
364 | .resource_list > .list-group-item:last-child { | 368 | .resource_list > .list-group-item:last-child { |
365 | border-bottom: 1px solid #ddd !important; | 369 | border-bottom: 1px solid #ddd !important; |
366 | } | 370 | } |
367 | 371 | ||
372 | +.resource_list .category-card-items i { | ||
373 | + color: #CCCCCC; | ||
374 | +} | ||
375 | + | ||
376 | +.resource_list a { | ||
377 | + color: inherit; | ||
378 | +} | ||
379 | + | ||
368 | @media(max-width: 768px) { | 380 | @media(max-width: 768px) { |
369 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { | 381 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { |
370 | color: #333333 !important; | 382 | color: #333333 !important; |
amadeus/static/js/topics.js
@@ -31,7 +31,7 @@ $('.collapse').on('hide.bs.collapse', function (e) { | @@ -31,7 +31,7 @@ $('.collapse').on('hide.bs.collapse', function (e) { | ||
31 | $("#topics-accordion").sortable({ | 31 | $("#topics-accordion").sortable({ |
32 | delay: 100, | 32 | delay: 100, |
33 | distance: 5, | 33 | distance: 5, |
34 | - handle: 'i.fa-arrows', | 34 | + handle: 'i.move_topic', |
35 | update: function( event, ui ) { | 35 | update: function( event, ui ) { |
36 | var cont = 1; | 36 | var cont = 1; |
37 | var data = []; | 37 | var data = []; |
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +def always_as_child(fn): | ||
2 | + """ | ||
3 | + Tries to run child model method if relevant | ||
4 | + should be applied on KnowsChild child class | ||
5 | + """ | ||
6 | + def f(self, *args, **kwargs): | ||
7 | + child_self = self.as_child() | ||
8 | + f_parent = getattr(self.__class__, fn.__name__) | ||
9 | + f_child = getattr(child_self.__class__, fn.__name__) | ||
10 | + | ||
11 | + if f_parent != f_child: | ||
12 | + return f_child(child_self, *args, **kwargs) | ||
13 | + else: | ||
14 | + return fn(self, *args, **kwargs) | ||
15 | + | ||
16 | + return f | ||
0 | \ No newline at end of file | 17 | \ No newline at end of file |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2017-01-23 21:18 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations, models | ||
6 | + | ||
7 | + | ||
8 | +class Migration(migrations.Migration): | ||
9 | + | ||
10 | + dependencies = [ | ||
11 | + ('topics', '0005_resource'), | ||
12 | + ] | ||
13 | + | ||
14 | + operations = [ | ||
15 | + migrations.AddField( | ||
16 | + model_name='resource', | ||
17 | + name='_my_subclass', | ||
18 | + field=models.CharField(default='webpage', max_length=200), | ||
19 | + preserve_default=False, | ||
20 | + ), | ||
21 | + ] |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2017-01-23 22:11 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations | ||
6 | + | ||
7 | + | ||
8 | +class Migration(migrations.Migration): | ||
9 | + | ||
10 | + dependencies = [ | ||
11 | + ('topics', '0006_resource__my_subclass'), | ||
12 | + ] | ||
13 | + | ||
14 | + operations = [ | ||
15 | + migrations.AlterModelOptions( | ||
16 | + name='resource', | ||
17 | + options={'ordering': ['order'], 'verbose_name': 'Resource', 'verbose_name_plural': 'Resources'}, | ||
18 | + ), | ||
19 | + ] |
topics/models.py
@@ -6,6 +6,8 @@ from subjects.models import Subject, Tag | @@ -6,6 +6,8 @@ from subjects.models import Subject, Tag | ||
6 | from students_group.models import StudentsGroup | 6 | from students_group.models import StudentsGroup |
7 | from users.models import User | 7 | from users.models import User |
8 | 8 | ||
9 | +from .decorators import always_as_child | ||
10 | + | ||
9 | class Topic(models.Model): | 11 | class Topic(models.Model): |
10 | name = models.CharField(_('Name'), max_length = 200) | 12 | name = models.CharField(_('Name'), max_length = 200) |
11 | slug = AutoSlugField(_("Slug"), populate_from = 'name', unique = True) | 13 | slug = AutoSlugField(_("Slug"), populate_from = 'name', unique = True) |
@@ -25,7 +27,27 @@ class Topic(models.Model): | @@ -25,7 +27,27 @@ class Topic(models.Model): | ||
25 | def __str__(self): | 27 | def __str__(self): |
26 | return self.name | 28 | return self.name |
27 | 29 | ||
28 | -class Resource(models.Model): | 30 | +""" |
31 | + Abstract model to make easier to know which kind of Resource we are dealing with | ||
32 | +""" | ||
33 | +class KnowsChild(models.Model): | ||
34 | + # Make a place to store the class name of the child | ||
35 | + _my_subclass = models.CharField(max_length=200) | ||
36 | + | ||
37 | + class Meta: | ||
38 | + abstract = True | ||
39 | + | ||
40 | + def as_child(self): | ||
41 | + return getattr(self, self._my_subclass) | ||
42 | + | ||
43 | + def save(self, *args, **kwargs): | ||
44 | + # save what kind we are. | ||
45 | + if not self._my_subclass: | ||
46 | + self._my_subclass = self.__class__.__name__.lower() | ||
47 | + | ||
48 | + super(KnowsChild, self).save(*args, **kwargs) | ||
49 | + | ||
50 | +class Resource(KnowsChild): | ||
29 | name = models.CharField(_('Name'), max_length = 200) | 51 | name = models.CharField(_('Name'), max_length = 200) |
30 | slug = AutoSlugField(_("Slug"), populate_from = 'name', unique = True) | 52 | slug = AutoSlugField(_("Slug"), populate_from = 'name', unique = True) |
31 | brief_description = models.TextField(_('Brief Description'), blank = True) | 53 | brief_description = models.TextField(_('Brief Description'), blank = True) |
@@ -43,6 +65,15 @@ class Resource(models.Model): | @@ -43,6 +65,15 @@ class Resource(models.Model): | ||
43 | class Meta: | 65 | class Meta: |
44 | verbose_name = _('Resource') | 66 | verbose_name = _('Resource') |
45 | verbose_name_plural = _('Resources') | 67 | verbose_name_plural = _('Resources') |
68 | + ordering = ['order'] | ||
46 | 69 | ||
47 | def __str__(self): | 70 | def __str__(self): |
48 | return self.name | 71 | return self.name |
72 | + | ||
73 | + """ | ||
74 | + Method to get the appropriated view link | ||
75 | + Must override in the child models | ||
76 | + """ | ||
77 | + @always_as_child | ||
78 | + def access_link(self): | ||
79 | + pass |
topics/templates/topics/list.html
@@ -28,7 +28,7 @@ | @@ -28,7 +28,7 @@ | ||
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> | 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> | 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> |
30 | </ul> | 30 | </ul> |
31 | - <a href="" ><i class="fa fa-arrows" aria-hidden="true"></i></a> | 31 | + <a href="" ><i class="fa fa-arrows move_topic" aria-hidden="true"></i></a> |
32 | </div> | 32 | </div> |
33 | {% endif %} | 33 | {% endif %} |
34 | </div> | 34 | </div> |
topics/urls.py
@@ -8,4 +8,5 @@ urlpatterns = [ | @@ -8,4 +8,5 @@ urlpatterns = [ | ||
8 | url(r'^update/(?P<sub_slug>[\w_-]+)/(?P<slug>[\w_-]+)/$', views.UpdateView.as_view(), name = 'update'), | 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'), | 9 | url(r'^delete/(?P<slug>[\w_-]+)/$', views.DeleteView.as_view(), name = 'delete'), |
10 | url(r'^update_order/$', views.update_order, name = 'update_order'), | 10 | url(r'^update_order/$', views.update_order, name = 'update_order'), |
11 | + url(r'^update_resource_order/$', views.update_resource_order, name = 'update_resource_order'), | ||
11 | ] | 12 | ] |
topics/views.py
@@ -12,7 +12,7 @@ from amadeus.permissions import has_subject_permissions | @@ -12,7 +12,7 @@ from amadeus.permissions import has_subject_permissions | ||
12 | 12 | ||
13 | from subjects.models import Subject | 13 | from subjects.models import Subject |
14 | 14 | ||
15 | -from .models import Topic | 15 | +from .models import Topic, Resource |
16 | from .forms import TopicForm | 16 | from .forms import TopicForm |
17 | 17 | ||
18 | class CreateView(LoginRequiredMixin, generic.edit.CreateView): | 18 | class CreateView(LoginRequiredMixin, generic.edit.CreateView): |
@@ -149,4 +149,19 @@ def update_order(request): | @@ -149,4 +149,19 @@ def update_order(request): | ||
149 | 149 | ||
150 | return JsonResponse({'message': 'ok'}) | 150 | return JsonResponse({'message': 'ok'}) |
151 | 151 | ||
152 | + return JsonResponse({'message': 'No data received'}) | ||
153 | + | ||
154 | +def update_resource_order(request): | ||
155 | + data = request.GET.get('data', None) | ||
156 | + | ||
157 | + if not data is None: | ||
158 | + data = json.loads(data) | ||
159 | + | ||
160 | + for t_data in data: | ||
161 | + resource = get_object_or_404(Resource, id = t_data['resource_id']) | ||
162 | + resource.order = t_data['resource_order'] | ||
163 | + resource.save() | ||
164 | + | ||
165 | + return JsonResponse({'message': 'ok'}) | ||
166 | + | ||
152 | return JsonResponse({'message': 'No data received'}) | 167 | return JsonResponse({'message': 'No data received'}) |
153 | \ No newline at end of file | 168 | \ No newline at end of file |
webpage/models.py
@@ -12,4 +12,10 @@ class Webpage(Resource): | @@ -12,4 +12,10 @@ class Webpage(Resource): | ||
12 | verbose_name_plural = _('WebPages') | 12 | verbose_name_plural = _('WebPages') |
13 | 13 | ||
14 | def __str__(self): | 14 | def __str__(self): |
15 | - return self.name | ||
16 | \ No newline at end of file | 15 | \ No newline at end of file |
16 | + return self.name | ||
17 | + | ||
18 | + def access_link(self): | ||
19 | + if self.show_window: | ||
20 | + return 'webpages:window_view' | ||
21 | + | ||
22 | + return 'webpages:view' |
webpage/templates/webpages/list.html
1 | {% load static i18n pagination permissions_tags %} | 1 | {% load static i18n pagination permissions_tags %} |
2 | {% load django_bootstrap_breadcrumbs %} | 2 | {% load django_bootstrap_breadcrumbs %} |
3 | 3 | ||
4 | -<div class="list-group resource_list"> | 4 | +<div id="resource_{{ topic.slug }}" class="list-group resource_list"> |
5 | {% for resource in topic.resource_topic.all %} | 5 | {% for resource in topic.resource_topic.all %} |
6 | <div class="list-group-item"> | 6 | <div class="list-group-item"> |
7 | + <input type="hidden" class="id_inp" name="id" value="{{ resource.id }}" /> | ||
8 | + <input type="hidden" class="order_inp" name="order" value="{{ resource.order }}" /> | ||
9 | + <input type="hidden" class="url_order" value="{% url 'topics:update_resource_order' %}" /> | ||
10 | + | ||
7 | <h4 class="pull-left list-group-item-heading"> | 11 | <h4 class="pull-left list-group-item-heading"> |
8 | - {{ resource }} | 12 | + <a href="{% url resource.access_link resource.slug %}" class="resource_link" {% if resource.show_window %}target="_blank"{% endif %}> |
13 | + {{ resource }} | ||
14 | + </a> | ||
9 | </h4> | 15 | </h4> |
10 | <div class="pull-right category-card-items"> | 16 | <div class="pull-right category-card-items"> |
11 | - <a href=""><i class="fa fa-arrows" aria-hidden="true"></i></a> | ||
12 | - <a href="" id="moreResources" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
13 | - <i class="fa fa-ellipsis-v" aria-hidden="true"></i> | ||
14 | - </a> | ||
15 | - <ul class="dropdown-menu pull-right" aria-labelledby="moreResources"> | ||
16 | - <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> | ||
17 | - <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> | ||
18 | - </ul> | 17 | + <a><i class="fa fa-arrows" aria-hidden="true"></i></a> |
18 | + <span class="btn-group"> | ||
19 | + <a href="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
20 | + <i class="fa fa-ellipsis-v" aria-hidden="true"></i> | ||
21 | + </a> | ||
22 | + <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="moreResources"> | ||
23 | + <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> | ||
24 | + <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> | ||
25 | + </ul> | ||
26 | + </span> | ||
19 | </div> | 27 | </div> |
20 | <br clear="all" /> | 28 | <br clear="all" /> |
21 | {% autoescape off %} | 29 | {% autoescape off %} |
@@ -23,4 +31,58 @@ | @@ -23,4 +31,58 @@ | ||
23 | {% endautoescape %} | 31 | {% endautoescape %} |
24 | </div> | 32 | </div> |
25 | {% endfor %} | 33 | {% endfor %} |
26 | -</div> | ||
27 | \ No newline at end of file | 34 | \ No newline at end of file |
35 | +</div> | ||
36 | +<script type="text/javascript"> | ||
37 | + $(".list-group-item").unbind().on('click', function (e) { | ||
38 | + var arrow = $(this).find('i.fa-arrows').is(e.target), | ||
39 | + menu = $(this).find('i.fa-ellipsis-v').is(e.target); | ||
40 | + | ||
41 | + if (!arrow && !menu) { | ||
42 | + var link = $(this).find('.resource_link').attr('href'), | ||
43 | + target = $(this).find('.resource_link').attr('target'); | ||
44 | + | ||
45 | + if (typeof(target) != 'undefined') { | ||
46 | + window.open(link, target); | ||
47 | + } else { | ||
48 | + window.location = link; | ||
49 | + } | ||
50 | + } | ||
51 | + }); | ||
52 | + | ||
53 | + $("#resource_{{ topic.slug }}").sortable({ | ||
54 | + delay: 100, | ||
55 | + distance: 5, | ||
56 | + handle: 'i.fa-arrows', | ||
57 | + update: function( event, ui ) { | ||
58 | + var cont = 1; | ||
59 | + var data = []; | ||
60 | + | ||
61 | + $("#resource_{{ topic.slug }}").find('.order_inp').each(function () { | ||
62 | + $(this).val(cont++); | ||
63 | + | ||
64 | + data.push({ | ||
65 | + 'resource_id': $(this).parent().find('.id_inp').val(), | ||
66 | + 'resource_order': $(this).val() | ||
67 | + }); | ||
68 | + }); | ||
69 | + | ||
70 | + data = JSON.stringify(data); | ||
71 | + | ||
72 | + sendUpdateResource(data); | ||
73 | + }, | ||
74 | + }); | ||
75 | + | ||
76 | + function sendUpdateResource(data) { | ||
77 | + $.ajax({ | ||
78 | + url: $("#resource_{{ topic.slug }}").find('.url_order').val(), | ||
79 | + dataType: 'json', | ||
80 | + data: {'data': data}, | ||
81 | + success: function(response) { | ||
82 | + console.log(response); | ||
83 | + }, | ||
84 | + error: function(response) { | ||
85 | + console.log(response); | ||
86 | + } | ||
87 | + }); | ||
88 | + } | ||
89 | +</script> | ||
28 | \ No newline at end of file | 90 | \ No newline at end of file |