diff options
author | rubenwardy <rw@rubenwardy.com> | 2020-07-14 03:49:30 +0100 |
---|---|---|
committer | rubenwardy <rw@rubenwardy.com> | 2020-07-14 03:49:30 +0100 |
commit | 04db19d1ac7b74b31e3dddeb873ef236c9477c5a (patch) | |
tree | b0409b0cbd651a08a6acc51a5c7be3e952b025fa | |
parent | aa6892da82c709670a447434496f16392617aa69 (diff) | |
download | cheatdb-tageditor.tar.xz |
Add start of bulk tag editortageditor
-rw-r--r-- | app/blueprints/packages/releases.py | 5 | ||||
-rw-r--r-- | app/blueprints/todo/__init__.py | 9 | ||||
-rw-r--r-- | app/public/static/tagselector.js | 132 | ||||
-rw-r--r-- | app/scss/components.scss | 2 | ||||
-rw-r--r-- | app/tasks/importtasks.py | 2 | ||||
-rw-r--r-- | app/templates/base.html | 2 | ||||
-rw-r--r-- | app/templates/todo/tags.html | 113 |
7 files changed, 196 insertions, 69 deletions
diff --git a/app/blueprints/packages/releases.py b/app/blueprints/packages/releases.py index cc209aa..1d11477 100644 --- a/app/blueprints/packages/releases.py +++ b/app/blueprints/packages/releases.py @@ -22,7 +22,7 @@ from . import bp from app.rediscache import has_key, set_key, make_download_key from app.models import * -from app.tasks.importtasks import makeVCSRelease, checkZipRelease +from app.tasks.importtasks import makeVCSRelease, checkZipRelease, updateMetaFromRelease from app.utils import * from celery import uuid @@ -111,6 +111,7 @@ def create_release(package): db.session.commit() checkZipRelease.apply_async((rel.id, uploadedPath), task_id=rel.task_id) + updateMetaFromRelease.delay(rel.id, uploadedPath) msg = "Release {} created".format(rel.title) addNotification(package.maintainers, current_user, msg, rel.getEditURL(), package) @@ -120,6 +121,7 @@ def create_release(package): return render_template("packages/release_new.html", package=package, form=form) + @bp.route("/packages/<author>/<name>/releases/<id>/download/") @is_package_page def download_release(package, id): @@ -149,6 +151,7 @@ def download_release(package, id): return redirect(release.url, code=300) + @bp.route("/packages/<author>/<name>/releases/<id>/", methods=["GET", "POST"]) @login_required @is_package_page diff --git a/app/blueprints/todo/__init__.py b/app/blueprints/todo/__init__.py index 12b0622..89e24b8 100644 --- a/app/blueprints/todo/__init__.py +++ b/app/blueprints/todo/__init__.py @@ -100,3 +100,12 @@ def topics(): topic_count=topic_count, query=qb.search, show_discarded=qb.show_discarded, \ next_url=next_url, prev_url=prev_url, page=page, page_max=query.pages, \ n=num, sort_by=qb.order_by) + + +@bp.route("/todo/tags/") +@login_required +def tags(): + packages = Package.query.filter_by(approved=True, soft_deleted=False).all() + tags = Tag.query.order_by(db.asc(Tag.title)).all() + + return render_template("todo/tags.html", packages=packages, tags=tags) diff --git a/app/public/static/tagselector.js b/app/public/static/tagselector.js index 9bbb764..f099415 100644 --- a/app/public/static/tagselector.js +++ b/app/public/static/tagselector.js @@ -19,80 +19,80 @@ $.fn.selectSelector = function(source, select) { return this.each(function() { - var selector = $(this), - input = $('input[type=text]', this); + var selector = $(this), + input = $('input[type=text]', this); - selector.click(function() { input.focus(); }) - .delegate('.badge a', 'click', function() { - var id = $(this).parent().data("id"); - select.find("option[value=" + id + "]").attr("selected", false) - recreate(); - }); + selector.click(function() { input.focus(); }) + .delegate('.badge a', 'click', function() { + var id = $(this).parent().data("id"); + select.find("option[value=" + id + "]").attr("selected", false) + recreate(); + }); - function addTag(id, text) { - $('<span class="badge badge-pill badge-primary"/>') - .text(text + ' ') - .data("id", id) - .append('<a>x</a>') - .insertBefore(input); - input.attr("placeholder", null); - select.find("option[value='" + id + "']").attr("selected", "selected") - hide_error(input); - } + function addTag(id, text) { + $('<span class="badge badge-pill badge-primary"/>') + .text(text + ' ') + .data("id", id) + .append('<a>x</a>') + .insertBefore(input); + input.attr("placeholder", null); + select.find("option[value='" + id + "']").attr("selected", "selected") + hide_error(input); + } - function recreate() { - selector.find("span").remove(); - select.find("option").each(function() { - if (this.hasAttribute("selected")) { - addTag(this.getAttribute("value"), this.innerText); - } - }); + function recreate() { + selector.find("span").remove(); + select.find("option").each(function() { + if (this.hasAttribute("selected")) { + addTag(this.getAttribute("value"), this.innerText); + } + }); + } + recreate(); + + input.focusout(function(e) { + var value = input.val().trim() + if (value != "") { + show_error(input, "Please select an existing tag, it;s not possible to add custom ones."); } - recreate(); + }) - input.focusout(function(e) { - var value = input.val().trim() - if (value != "") { - show_error(input, "Please select an existing tag, it;s not possible to add custom ones."); - } + input.keydown(function(e) { + if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active) + e.preventDefault(); }) + .autocomplete({ + minLength: 0, + source: source, + select: function(event, ui) { + addTag(ui.item.id, ui.item.toString()); + input.val(""); + return false; + } + }).focus(function() { + // The following works only once. + // $(this).trigger('keydown.autocomplete'); + // As suggested by digitalPBK, works multiple times + // $(this).data("autocomplete").search($(this).val()); + // As noted by Jonny in his answer, with newer versions use uiAutocomplete + $(this).data("ui-autocomplete").search($(this).val()); + }); - input.keydown(function(e) { - if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active) - e.preventDefault(); - }) - .autocomplete({ - minLength: 0, - source: source, - select: function(event, ui) { - addTag(ui.item.id, ui.item.toString()); - input.val(""); - return false; - } - }).focus(function() { - // The following works only once. - // $(this).trigger('keydown.autocomplete'); - // As suggested by digitalPBK, works multiple times - // $(this).data("autocomplete").search($(this).val()); - // As noted by Jonny in his answer, with newer versions use uiAutocomplete - $(this).data("ui-autocomplete").search($(this).val()); - }); - - input.data('ui-autocomplete')._renderItem = function(ul, item) { - return $('<li/>') - .data('item.autocomplete', item) - .append($('<a/>').text(item.toString())) - .appendTo(ul); - }; + input.data('ui-autocomplete')._renderItem = function(ul, item) { + return $('<li/>') + .data('item.autocomplete', item) + .append($('<a/>').text(item.toString())) + .appendTo(ul); + }; - input.data('ui-autocomplete')._resizeMenu = function(ul, item) { - var ul = this.menu.element; - ul.outerWidth(Math.max( - ul.width('').outerWidth(), - selector.outerWidth() - )); - }; - }); + input.data('ui-autocomplete')._resizeMenu = function(ul, item) { + var ul = this.menu.element; + ul.outerWidth(Math.max( + ul.width('').outerWidth(), + selector.outerWidth() + )); + }; + }); } $.fn.csvSelector = function(source, name, result, allowSlash) { diff --git a/app/scss/components.scss b/app/scss/components.scss index 3f10ed4..a9ce978 100644 --- a/app/scss/components.scss +++ b/app/scss/components.scss @@ -1,7 +1,7 @@ .ui-autocomplete, ui-front { position:absolute; cursor:default; - z-index:1001 !important + z-index:1100 !important } .ui-autocomplete { diff --git a/app/tasks/importtasks.py b/app/tasks/importtasks.py index e0b2308..ff085cd 100644 --- a/app/tasks/importtasks.py +++ b/app/tasks/importtasks.py @@ -299,6 +299,8 @@ def makeVCSRelease(id, branch): release.approve(release.package.author) db.session.commit() + updateMetaFromRelease.delay(release.id, destPath) + return release.url finally: shutil.rmtree(gitDir) diff --git a/app/templates/base.html b/app/templates/base.html index 534538b..a6b7b69 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -7,7 +7,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title> <link rel="stylesheet" type="text/css" href="/static/bootstrap.css"> - <link rel="stylesheet" type="text/css" href="/static/custom.css?v=12"> + <link rel="stylesheet" type="text/css" href="/static/custom.css?v=13"> <link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" /> <link rel="shortcut icon" href="/favicon-16.png" sizes="16x16"> <link rel="icon" href="/favicon-128.png" sizes="128x128"> diff --git a/app/templates/todo/tags.html b/app/templates/todo/tags.html new file mode 100644 index 0000000..e7675f8 --- /dev/null +++ b/app/templates/todo/tags.html @@ -0,0 +1,113 @@ +{% extends "base.html" %} + +{% block title %} + Tags +{% endblock %} + +{% block content %} + +<style> + table { + width:auto; + } + td { + white-space:nowrap; + } + table td:last-child { + width: 100%; + } +</style> + +<table class="table"> + <tr> + <th>Package</th> + <th>Tags</th> + </tr> + {% for package in packages %} + <tr> + <td> + <a href="{{ package.getDetailsURL() }}"> + {{ package.title }} + </a> + + by {{ package.author.display_name }} + </td> + <td class="tags"> + {% for tag in package.tags %} + <span class="badge badge-primary mr-1">{{ tag.title }}</span> + {% endfor %} + <a class="badge badge-secondary add-btn px-2" href="#"> + <i class="fas fa-plus"></i> + </a> + </td> + </tr> + {% endfor %} +</table> + +<div class="modal"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title">{{ _("Edit tags") }}</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <select name="tags" multiple> + {% for tag in tags %} + <option value="{{ tag.name }}">{{ tag.title }}</option> + {% endfor %} + </select> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-primary">Update</button> + </div> + </div> + </div> +</div> +{% endblock %} + +{% from "macros/forms.html" import form_scripts %} + +{% block scriptextra %} +{{ form_scripts() }} + +<script> + $(".add-btn").click(function() { + const row = $(this).parent().parent() + + $(".modal select option").removeAttr("selected"); + $(".multichoice_selector").remove(); + + $(".modal .modal-body").prepend(` + <div class="multichoice_selector bulletselector form-control"> + <input type="text" placeholder="Start typing to see suggestions"> + <div class="clearboth"></div> + </div> + `); + + $(".modal").modal("show"); + $(".modal input").focus(); + $(".multichoice_selector").each(function() { + var ele = $(this); + var sel = ele.parent().find("select"); + sel.hide(); + + var options = []; + sel.find("option").each(function() { + var text = $(this).text(); + options.push({ + id: $(this).attr("value"), + value: text, + toString: function() { return text; }, + }); + }); + + ele.selectSelector(options, sel); + }); + }); + +</script> +{% endblock %} |