Commit a98874b2c97343982adbda4092a6e1b4af638490

Authored by Zambom
1 parent 614033fd

Adding resource list ordering

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 = [];
topics/decorators.py 0 → 100644
@@ -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
topics/migrations/0006_resource__my_subclass.py 0 → 100644
@@ -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 + ]
topics/migrations/0007_auto_20170123_1911.py 0 → 100644
@@ -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>&nbsp;{% 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>&nbsp;{% 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>&nbsp;{% 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