Add start of bulk tag editor

This commit is contained in:
rubenwardy 2020-07-14 03:49:30 +01:00
parent aa6892da82
commit 04db19d1ac
7 changed files with 197 additions and 70 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);
}
});
}
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.");
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.");
}
})
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) {

View File

@ -1,7 +1,7 @@
.ui-autocomplete, ui-front {
position:absolute;
cursor:default;
z-index:1001 !important
z-index:1100 !important
}
.ui-autocomplete {

View File

@ -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)

View File

@ -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">

View File

@ -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">&times;</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 %}