From a0feb25389615e9d2ab2b036a491a1aeeb059a52 Mon Sep 17 00:00:00 2001 From: Maciej Tronowski Date: Wed, 15 Apr 2015 18:04:05 +0200 Subject: [PATCH] add saving favorite locations --- filex/admin.py | 11 +- filex/forms.py | 9 ++ filex/migrations/0001_initial.py | 35 ++++++ filex/migrations/0002_auto_20150415_1744.py | 20 +++ filex/models.py | 19 ++- filex/static/filex/filex.js | 181 ++++++++++++++++++++++----- filex/templates/filex/source.html | 36 +++++- filex/templates/filex/source.js.html | 13 +- filex/templatetags/filex.py | 37 ++++++ filex/urls.py | 3 + filex/views.py | 32 ++++- qcg/static/qcg/main.css | 25 +++- qcg/templates/qcg/gridftp.html | 4 +- 13 files changed, 372 insertions(+), 53 deletions(-) create mode 100644 filex/migrations/0001_initial.py create mode 100644 filex/migrations/0002_auto_20150415_1744.py create mode 100644 filex/templatetags/__init__.py create mode 100644 filex/templatetags/filex.py diff --git a/filex/admin.py b/filex/admin.py index 8c38f3f..9c68c68 100644 --- a/filex/admin.py +++ b/filex/admin.py @@ -1,3 +1,12 @@ from django.contrib import admin -# Register your models here. +from filex.models import Favorite + + +class FavoriteAdmin(admin.ModelAdmin): + list_display = ('owner', 'host', 'path') + list_filter = ('owner',) + date_hierarchy = 'created' + + +admin.site.register(Favorite, FavoriteAdmin) diff --git a/filex/forms.py b/filex/forms.py index 1c9ffe9..db6587b 100644 --- a/filex/forms.py +++ b/filex/forms.py @@ -1,6 +1,15 @@ # coding=utf-8 from django import forms +from filex.models import Favorite + + +class FavoriteForm(forms.ModelForm): + class Meta: + model = Favorite + fields = ('owner', 'host', 'path') + widgets = {'owner': forms.HiddenInput()} + class NewDirForm(forms.Form): host = forms.CharField(label=u'Host', max_length=256, widget=forms.HiddenInput()) diff --git a/filex/migrations/0001_initial.py b/filex/migrations/0001_initial.py new file mode 100644 index 0000000..96acc89 --- /dev/null +++ b/filex/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Favorite', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('host', models.CharField(max_length=256, verbose_name='Host')), + ('path', models.CharField(max_length=1024, verbose_name='\u015acie\u017cka')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='Utworzono')), + ('updated', models.DateTimeField(auto_now=True, verbose_name='Uaktualniono')), + ('owner', models.ForeignKey(related_name='favorites', verbose_name='W\u0142a\u015bciciel', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Ulubiona lokalizacja', + 'verbose_name_plural': 'Ulubione lokalizacje', + }, + bases=(models.Model,), + ), + migrations.AlterUniqueTogether( + name='favorite', + unique_together=set([('owner', 'host', 'path')]), + ), + ] diff --git a/filex/migrations/0002_auto_20150415_1744.py b/filex/migrations/0002_auto_20150415_1744.py new file mode 100644 index 0000000..be6c972 --- /dev/null +++ b/filex/migrations/0002_auto_20150415_1744.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('filex', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='favorite', + name='path', + field=models.CharField(default=b'/', max_length=1024, verbose_name='\u015acie\u017cka'), + preserve_default=True, + ), + ] diff --git a/filex/models.py b/filex/models.py index 71a8362..e7e8247 100644 --- a/filex/models.py +++ b/filex/models.py @@ -1,3 +1,20 @@ +# coding=utf-8 +from django.conf import settings from django.db import models -# Create your models here. + +class Favorite(models.Model): + owner = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"Właściciel", related_name='favorites') + host = models.CharField(u"Host", max_length=256) + path = models.CharField(u"Ścieżka", max_length=1024, default='/') + + created = models.DateTimeField(u"Utworzono", auto_now_add=True) + updated = models.DateTimeField(u"Uaktualniono", auto_now=True) + + class Meta: + verbose_name = u"Ulubiona lokalizacja" + verbose_name_plural = u"Ulubione lokalizacje" + unique_together = ('owner', 'host', 'path') + + def __unicode__(self): + return u'{}{} ({})'.format(self.host, self.path, self.owner) diff --git a/filex/static/filex/filex.js b/filex/static/filex/filex.js index 5132ec7..29589c3 100644 --- a/filex/static/filex/filex.js +++ b/filex/static/filex/filex.js @@ -95,8 +95,7 @@ $(function(){ model: Filex.PathBit, initialize: function() { - this.listenTo(this, 'reset', this.setActive); - this.listenTo(this, 'add', this.setActive); + this.listenTo(this, 'reset add', this.setActive); }, setActive: function() { @@ -210,7 +209,9 @@ $(function(){ events: { 'change #show-hidden': 'toggleHidden', 'click #select-all': 'selectAll', - 'click #btn-refresh': 'reloadFiles' + 'click #btn-refresh': 'reloadFiles', + 'click #btn-favorites': 'toggleFavorites', + 'click #btn-host': 'editHost' }, initialize: function(options) { @@ -218,14 +219,15 @@ $(function(){ this.$error = $('#error'); this.$showHidden = $('#show-hidden'); this.$selectAll = $('#select-all'); + this.$favorites = $('#btn-favorites'); + this.$host = $('#btn-host'); - this.host = options.host; this.path = new Filex.Path(); this.files = new Filex.FileList(); this.listenTo(this.path, 'reset', this.resetPath); - this.listenTo(this.path, 'add', this.reloadFiles); this.listenTo(this.path, 'add', this.addPath); + this.listenTo(this.path, 'add reset', this.changedPath); this.listenTo(this.path, 'selected', this.selectedPath); this.listenTo(this.files, 'reset', this.resetFiles); this.listenTo(this.files, 'selected:dir', this.selectedDir); @@ -236,58 +238,102 @@ $(function(){ optionTemplate = _.template('
<%= host %>
<%= path %>
'); this.$('#host-selector').selectize({ - valueField: 'host', + optgroupField: 'group', labelField: 'host', searchField: ['host', 'path'], sortField: [ {field: 'host', direction: 'asc'}, {field: 'path', direction: 'asc'} ], - items: [this.host], - options: options.hostOptions, + options: options.locations, + optgroups: [ + {value: 'sys', label: 'Podstawowe'}, + {value: 'usr', label: 'Użytkownika'} + ], + lockOptgroupOrder: true, + create: function(input, callback) { + var $form = $('#favorite-form'), + parts = input.split('/', 1), + callback_called = false; + + $form.find('#id_host').val(parts[0]); + + if (parts.length > 1) + $form.find('#id_path').val(parts[1]); + + $form.on('submit', function(e) { + var $this = $(this), + $btn = $this.find('[type="submit"]'); + + e.preventDefault(); + $btn.button('loading'); + + $.post($this.attr('action'), $this.serialize(), function(data) { + callback(data); + callback_called = true; + + $this.modal('hide'); + $btn.button('reset'); + }, 'json').fail(function() { + console.error(arguments); + $btn.button('error'); + }); + }); + + $form.one('hide.bs.modal', function() { + if (!callback_called) + callback(); + $form.off(); + }); + + $form.modal(); + }, render: { option: function(item) { return optionTemplate(item); + }, + option_create : function(data, escape) { + return '
Dodaj ' + escape(data.input) + '
'; } }, - onDropdownClose: function() { + onItemRemove: function(value) { + this.oldValue = value; + }, + onItemAdd: function(value) { + view.load(value); this.blur(); }, onBlur: function() { - var value = this.getValue(); - if (!value) { - this.addItem(view.host, true); - return; - } - - if (value != view.host) { - var location = this.options[value]; + if (!this.getValue() && this.oldValue) + this.addItem(this.oldValue); - view.host = location.host; - view.navigate(location.path); - } + $('#host').removeClass('edit'); + this.clear(); } }); this.hostSelectize = this.$('#host-selector')[0].selectize; this.render(); - this.navigate(this.hostSelectize.options[this.host].path); }, render: function() { this.updateSelectAll(); + this.updateFavorites(); this.$noItems.toggle(!Boolean(this.visibleFiles().length)); this.$error.hide(); }, - navigate: function(path) { - var pathBits = [new Filex.PathBit({'text': '/', 'path': ''})]; - - pathBits = pathBits.concat(_.map(path.replace(/(^\/+|\/+$)/g, '').split('/'), function(name) { - return new Filex.PathBit({'text': name, 'path': name}); - })); + load: function(location) { + var path = location.replace(/(^\/+|\/+$)/g, '').split('/'), + host = path.shift(), + pathBits = [new Filex.PathBit({'text': '/', 'path': ''})].concat(_.map(path, function(name) { + return new Filex.PathBit({'text': name, 'path': name}); + })); + this.host = host; this.path.reset(pathBits); + + this.$host.text(this.host); }, reloadFiles: function() { @@ -321,8 +367,6 @@ $(function(){ }, resetPath: function(models, options) { - this.reloadFiles(); - _.each(options.previousModels, function(model) { model.trigger('remove'); }); @@ -351,9 +395,7 @@ $(function(){ }, selectedPath: function(bit) { - var newPath = this.path.slice(0, this.path.indexOf(bit) + 1); - this.path.set(newPath); - this.reloadFiles(); + this.path.reset(this.path.slice(0, this.path.indexOf(bit) + 1)); }, showHidden: function() { @@ -401,8 +443,81 @@ $(function(){ _.each(this.visibleFiles(), function(item) { item.set('checked', false); }); + }, - this.updateSelectAll(); + toggleFavorites: function() { + var $btn = this.$favorites, + locations = this.hostSelectize, + is_active = $btn.hasClass('active'), + url = is_active ? '/filex/fav/delete/' : '/filex/fav/add/', + data = { + host: this.host, + path: this.path.full() + }; + + $btn.button('loading'); + + $.post(url, data, 'json').done(function () { + $btn.button('reset'); + + if (is_active) { + locations.removeOption(data.host + data.path); + } + else { + locations.addOption({ + group: 'usr', + host: data.host, + path: data.path, + value: data.host + data.path + }); + } + }).fail(function() { + $btn.button('reset'); + $btn.button('toggle'); + + console.error(arguments); + }); + }, + + initialLoad: function() { + if (!this.host) { + var opts = this.hostSelectize.options; + + this.load(opts[Object.keys(opts)[0]].value); + } + }, + + changedPath: function () { + this.reloadFiles(); + this.updateFavorites(); + }, + + updateFavorites: function() { + var loc = this.host + this.path.full(), + favorites = this.hostSelectize.options; + + if (favorites.hasOwnProperty(loc)) { + if (favorites[loc].group == 'sys') { + this.$favorites.addClass('disabled').prop('disabled', true); + if (this.$favorites.hasClass('active')) + this.$favorites.button('toggle'); + } + else { + this.$favorites.removeClass('disabled').prop('disabled', false); + if (!this.$favorites.hasClass('active')) + this.$favorites.button('toggle'); + } + } + else { + this.$favorites.removeClass('disabled').prop('disabled', false); + if (this.$favorites.hasClass('active')) + this.$favorites.button('toggle'); + } + }, + + editHost: function() { + $('#host').addClass('edit'); + this.hostSelectize.focus(); } }); }); diff --git a/filex/templates/filex/source.html b/filex/templates/filex/source.html index 4a3b21e..ba88414 100644 --- a/filex/templates/filex/source.html +++ b/filex/templates/filex/source.html @@ -1,15 +1,24 @@ +{% load filex %} +