From c26732d1019c227775ee9198ef0a7a7e368564dd Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Fri, 5 Oct 2018 22:53:27 +0300 Subject: [PATCH] Implemented folder management. --- .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/watcherTasks.xml | 51 ++ .idea/workspace.xml | 463 ++++++++++++++++++ .idea/youtube-channel-manager.iml | 31 ++ YtManager/urls.py | 2 + YtManagerApp/management.py | 44 ++ .../migrations/0004_auto_20181005_1626.py | 18 + .../static/YtManagerApp/css/style.css | 32 +- .../static/YtManagerApp/css/style.css.map | 7 + .../static/YtManagerApp/css/style.scss | 38 ++ .../YtManagerApp/import/jstree/jstree.js | 2 +- .../static/YtManagerApp/js/subtree.js | 50 -- .../controls/folder_edit_dialog.html | 42 +- .../controls/subscription_edit_dialog.html | 39 ++ .../templates/YtManagerApp/index.html | 23 +- .../YtManagerApp/js/subscription_tree.js | 174 +++++++ YtManagerApp/views.py | 47 +- 19 files changed, 994 insertions(+), 90 deletions(-) create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/watcherTasks.xml create mode 100644 .idea/workspace.xml create mode 100644 .idea/youtube-channel-manager.iml create mode 100644 YtManagerApp/management.py create mode 100644 YtManagerApp/migrations/0004_auto_20181005_1626.py create mode 100644 YtManagerApp/static/YtManagerApp/css/style.css.map create mode 100644 YtManagerApp/static/YtManagerApp/css/style.scss delete mode 100644 YtManagerApp/static/YtManagerApp/js/subtree.js create mode 100644 YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html create mode 100644 YtManagerApp/templates/YtManagerApp/js/subscription_tree.js diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3999087 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b4d776c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 0000000..1f0a12e --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..48da65d --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lds + get_selected + url + delete_fo + folder + + + loading + + subscription + subscription + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- -
- + +
+
-
- - +
+ +
+ +
- - -
- +
+ + - \ No newline at end of file + \ No newline at end of file diff --git a/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html b/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html new file mode 100644 index 0000000..acd1b54 --- /dev/null +++ b/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/YtManagerApp/templates/YtManagerApp/index.html b/YtManagerApp/templates/YtManagerApp/index.html index 3c85c9e..029287a 100644 --- a/YtManagerApp/templates/YtManagerApp/index.html +++ b/YtManagerApp/templates/YtManagerApp/index.html @@ -2,25 +2,38 @@ {% load static %} {% block stylesheets %} - + {% endblock %} {% block scripts %} - - + + + + {% include 'YtManagerApp/controls/folder_edit_dialog.html' %} + {% endblock %} {% block master %}
diff --git a/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js b/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js new file mode 100644 index 0000000..33ba124 --- /dev/null +++ b/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js @@ -0,0 +1,174 @@ +function folderEditDialog_Show(isNew, editNode) +{ + let dialog = $("#folder_edit_dialog"); + dialog.find('#folder_edit_dialog_title').text(isNew ? "New folder" : "Edit folder"); + dialog.find("#folder_edit_dialog_loading").show(); + dialog.find("#folder_edit_dialog_error").hide(); + dialog.find("#folder_edit_dialog_form").hide(); + dialog.modal(); + + $.get("{% url 'ajax_get_folders' %}") + .done(function(folders) + { + // Populate list of folders + let selParent = dialog.find("#folder_edit_dialog_parent"); + selParent.empty(); + selParent.append(new Option('(None)', '#')); + + let parentId = null; + if (!isNew) { + parentId = editNode.parent.replace('folder', ''); + } + + for (let folder of folders) + { + let o = new Option(folder.text, folder.id); + if (!isNew && folder.id.toString() === parentId.toString()) + o.selected = true; + + selParent.append(o); + } + + // Show form + dialog.find("#folder_edit_dialog_loading").hide(); + dialog.find("#folder_edit_dialog_form").show(); + dialog.find("#folder_edit_dialog_submit").text(isNew ? "Create" : "Save"); + + if (isNew) + { + dialog.find("#folder_edit_dialog_id").val('#'); + dialog.find("#folder_edit_dialog_name").val(''); + } + if (!isNew) + { + idTrimmed = editNode.id.replace('folder', ''); + dialog.find("#folder_edit_dialog_id").val(idTrimmed); + dialog.find("#folder_edit_dialog_name").val(editNode.text); + } + }) + .fail(function() { + let msgError = dialog.find("#folder_edit_dialog_error"); + msgError.show(); + msgError.text("An error occurred!"); + }); +} + +function folderEditDialog_ShowNew() +{ + folderEditDialog_Show(true, null); +} + +function folderEditDialog_Close() +{ + $("#folder_edit_dialog").modal('hide'); +} + +function folderEditDialog_Submit(e) +{ + let form = $(this); + let url = form.attr('action'); + + $.post(url, form.serialize()) + .done(tree_Refresh); + + folderEditDialog_Close(); + e.preventDefault(); +} + +function treeNode_Edit() +{ + let selectedNodes = $("#tree-wrapper").jstree('get_selected', true); + if (selectedNodes.length === 1) + { + let node = selectedNodes[0]; + if (node.type === 'folder') { + folderEditDialog_Show(false, node); + } + else { + // TODO... + } + } +} + +function treeNode_Delete() +{ + let selectedNodes = $("#tree-wrapper").jstree('get_selected', true); + if (selectedNodes.length === 1) + { + let node = selectedNodes[0]; + if (node.type === 'folder') { + let folderId = node.id.toString().replace('folder', ''); + if (confirm('Are you sure you want to delete folder "' + node.text + '" and all its descendants?\nNote: the subscriptions won\'t be deleted, they will only be moved outside.')) + { + $.post("{% url 'ajax_delete_folder' 99999 %}".replace('99999', folderId), { + csrfmiddlewaretoken: '{{ csrf_token }}' + }).done(tree_Refresh); + } + } + else { + // TODO... + } + } +} + +function tree_Initialize() +{ + let treeWrapper = $("#tree-wrapper"); + treeWrapper.jstree({ + core : { + data : { + url : "{% url 'ajax_get_children' %}" + }, + check_callback : tree_ValidateChange, + themes : { + dots : false + }, + }, + types : { + folder : { + icon : "material-icons material-folder" + }, + sub : { + icon : "material-icons material-person", + max_depth : 0 + } + }, + plugins : [ "types", "wholerow", "dnd" ] + }); + treeWrapper.on("changed.jstree", tree_OnSelectionChanged); +} + +function tree_Refresh() +{ + $("#tree-wrapper").jstree("refresh"); +} + +function tree_ValidateChange(operation, node, parent, position, more) +{ + if (more.dnd) + { + // create_node, rename_node, delete_node, move_node and copy_node + if (operation === "copy_node" || operation === "move_node") + { + if (more.ref.type === "sub") + return false; + } + } + + return true; +} + +function tree_OnSelectionChanged(e, data) +{ + node = data.instance.get_selected(true)[0]; +} + +$(document).ready(function () +{ + tree_Initialize(); + $("#btn_create_folder").on("click", folderEditDialog_ShowNew); + $("#btn_edit_node").on("click", treeNode_Edit); + $("#btn_delete_node").on("click", treeNode_Delete); + + $("#folder_edit_dialog_form").submit(folderEditDialog_Submit); +}); diff --git a/YtManagerApp/views.py b/YtManagerApp/views.py index 5e097c5..8b7ee03 100644 --- a/YtManagerApp/views.py +++ b/YtManagerApp/views.py @@ -1,36 +1,42 @@ from django.shortcuts import render from django.http import HttpResponse, HttpRequest, JsonResponse from .models import SubscriptionFolder, Subscription +from .management import FolderManager + def get_children_recurse(parent_id): children = [] for folder in SubscriptionFolder.objects.filter(parent_id=parent_id).order_by('name'): children.append({ - "id" : "folder" + str(folder.id), - "text" : folder.name, - "type" : "folder", - "children" : get_children_recurse(folder.id) + "id": "folder" + str(folder.id), + "text": folder.name, + "type": "folder", + "state": {"opened": True}, + "children": get_children_recurse(folder.id) }) for sub in Subscription.objects.filter(parent_folder_id=parent_id).order_by('name'): children.append({ - "id" : "sub" + str(sub.id), - "type" : "sub", - "text" : sub.name + "id": "sub" + str(sub.id), + "type": "sub", + "text": sub.name }) return children -def get_folders(parent_id, path = ""): +def get_folders(parent_id, path=""): folders = [] + prefix = path + "/" + if len(path) == 0: + prefix = "" for folder in SubscriptionFolder.objects.filter(parent_id=parent_id).order_by('name'): - folder_path = path + "/" + folder.name + folder_path = prefix + folder.name folders.append({ - "id" : "folder" + str(folder.id), - "text" : folder_path + "id": folder.id, + "text": folder_path }) folders.extend(get_folders(folder.id, folder_path)) @@ -40,9 +46,26 @@ def get_folders(parent_id, path = ""): def ajax_get_children(request: HttpRequest): return JsonResponse(get_children_recurse(None), safe=False) + def ajax_get_folders(request: HttpRequest): return JsonResponse(get_folders(None), safe=False) + +def ajax_edit_folder(request: HttpRequest): + if request.method == 'POST': + fid = request.POST['id'] + name = request.POST['name'] + parent_id = request.POST['parent'] + FolderManager.create_or_edit(fid, name, parent_id) + + return HttpResponse() + + +def ajax_delete_folder(request: HttpRequest, fid): + FolderManager.delete(fid) + return HttpResponse() + + def index(request: HttpRequest): context = {} - return render(request, 'YtManagerApp/index.html', context) \ No newline at end of file + return render(request, 'YtManagerApp/index.html', context)