This is an automated email from the ASF dual-hosted git repository.
tn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 244640e add slugify filter, create a common init method, add a modal
dialog without confirm
244640e is described below
commit 244640edbf43efa7234afb73dc639fb64d2c2d1f
Author: Thomas Neidhart <[email protected]>
AuthorDate: Tue Apr 1 09:46:35 2025 +0200
add slugify filter, create a common init method, add a modal dialog without
confirm
---
atr/filters.py | 26 +++++++++++++++++++++
atr/server.py | 3 +++
atr/static/js/atr.js | 40 +++++++++++++++++++++++++++++++--
atr/templates/draft-add.html | 38 +++----------------------------
atr/templates/draft-directory.html | 38 +++----------------------------
atr/templates/draft-review.html | 21 +++++++++++++----
atr/templates/macros/dialog.html | 46 ++++++++++++++++++++++++++++++++------
7 files changed, 129 insertions(+), 83 deletions(-)
diff --git a/atr/filters.py b/atr/filters.py
new file mode 100644
index 0000000..e1dab48
--- /dev/null
+++ b/atr/filters.py
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from typing import Any
+
+import asfquart.base as base
+
+
+def register_filters(app: base.QuartApp) -> None:
+ @app.template_filter("slugify")
+ def slugify(value: Any) -> str:
+ return str(value).lower().replace(" ", "-").replace("_",
"-").replace("/", "-").replace(".", "-")
diff --git a/atr/server.py b/atr/server.py
index cac3c7f..e62d89b 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -40,6 +40,7 @@ import atr.preload as preload
import atr.ssh as ssh
import atr.user as user
import atr.util as util
+from atr.filters import register_filters
# TODO: Technically this is a global variable
# We should probably find a cleaner way to do this
@@ -200,6 +201,8 @@ def create_app(app_config: type[config.AppConfig]) ->
base.QuartApp:
register_routes(app)
blueprints.register(app)
+ register_filters(app)
+
config_mode = config.get_mode()
app_setup_context(app)
diff --git a/atr/static/js/atr.js b/atr/static/js/atr.js
index 5582be2..b194273 100644
--- a/atr/static/js/atr.js
+++ b/atr/static/js/atr.js
@@ -1,4 +1,40 @@
+function init() {
+ document.addEventListener("DOMContentLoaded", function () {
+ const copyButtons = document.querySelectorAll(".atr-copy-btn");
+
+ copyButtons.forEach(button => {
+ button.addEventListener("click", function () {
+ const targetId = this.getAttribute("data-clipboard-target");
+ const targetElement = document.querySelector(targetId);
+
+ if (targetElement) {
+ const textToCopy = targetElement.textContent;
+
+ navigator.clipboard.writeText(textToCopy)
+ .then(() => {
+ const originalText = this.innerHTML;
+ this.innerHTML = '<i class="bi bi-check2"></i>
Copied!';
+
+ // Reset the button text after 2000ms
+ setTimeout(() => {
+ this.innerHTML = originalText;
+ }, 2000);
+ })
+ .catch(err => {
+ console.error("Failed to copy: ", err);
+ this.innerHTML = '<i class="bi
bi-exclamation-triangle"></i> Failed!';
+
+ setTimeout(() => {
+ this.innerHTML = '<i class="bi
bi-clipboard"></i> Copy';
+ }, 2000);
+ });
+ }
+ });
+ });
+ });
+}
+
function updateDeleteButton(inputElement, buttonId) {
- let button = document.getElementById(buttonId);
- button.disabled = inputElement.value !== "DELETE";
+ let button = document.getElementById(buttonId);
+ button.disabled = inputElement.value !== "DELETE";
}
diff --git a/atr/templates/draft-add.html b/atr/templates/draft-add.html
index 259c9dd..ca91333 100644
--- a/atr/templates/draft-add.html
+++ b/atr/templates/draft-add.html
@@ -50,10 +50,10 @@
</div>
<div class="card-footer bg-light border-1 pt-4 pb-4
position-relative">
<button class="btn btn-sm btn-outline-secondary atr-copy-btn
fs-6 position-absolute top-0 end-0 m-2"
- data-clipboard-target="#cmd-newdraft-{{
project.name|replace('.', '-') }}">
+ data-clipboard-target="#cmd-newdraft-{{
project.name|slugify }}">
<i class="bi bi-clipboard"></i> Copy
</button>
- <pre class="small mb-0" id="cmd-newdraft-{{
project.name|replace('.', '-') }}">rsync -av -e 'ssh -p 2222' your/files/ \
+ <pre class="small mb-0" id="cmd-newdraft-{{ project.name|slugify
}}">rsync -av -e 'ssh -p 2222' your/files/ \
{{ asf_id }}@{{ server_domain }}:/{{ project.name }}/VERSION/</pre>
</div>
</div>
@@ -125,38 +125,6 @@
{% block javascripts %}
{{ super() }}
<script>
- document.addEventListener("DOMContentLoaded", function() {
- const copyButtons = document.querySelectorAll(".atr-copy-btn");
-
- copyButtons.forEach(button => {
- button.addEventListener("click", function() {
- const targetId =
this.getAttribute("data-clipboard-target");
- const targetElement = document.querySelector(targetId);
-
- if (targetElement) {
- const textToCopy = targetElement.textContent;
-
- navigator.clipboard.writeText(textToCopy)
- .then(() => {
- const originalText = this.innerHTML;
- this.innerHTML = '<i class="bi
bi-check2"></i> Copied!';
-
- // Reset the button text after 2000ms
- setTimeout(() => {
- this.innerHTML = originalText;
- }, 2000);
- })
- .catch(err => {
- console.error("Failed to copy: ", err);
- this.innerHTML = '<i class="bi
bi-exclamation-triangle"></i> Failed!';
-
- setTimeout(() => {
- this.innerHTML = '<i class="bi
bi-clipboard"></i> Copy';
- }, 2000);
- });
- }
- });
- });
- });
+ init();
</script>
{% endblock javascripts %}
diff --git a/atr/templates/draft-directory.html
b/atr/templates/draft-directory.html
index 6a32c75..a744d48 100644
--- a/atr/templates/draft-directory.html
+++ b/atr/templates/draft-directory.html
@@ -56,10 +56,10 @@
</div>
<div class="card-footer bg-light border-1 pt-4 pb-4
position-relative">
<button class="btn btn-sm btn-outline-secondary atr-copy-btn fs-6
position-absolute top-0 end-0 m-2"
- data-clipboard-target="#cmd-{{ release.name|replace('.',
'-') }}">
+ data-clipboard-target="#cmd-{{ release.name|slugify }}">
<i class="bi bi-clipboard"></i> Copy
</button>
- <pre class="small mb-0" id="cmd-{{ release.name|replace('.', '-')
}}">rsync -av -e 'ssh -p 2222' your/files/ \
+ <pre class="small mb-0" id="cmd-{{ release.name|slugify }}">rsync
-av -e 'ssh -p 2222' your/files/ \
{{ asf_id }}@{{ server_domain }}:/{{ release.project.name }}/{{
release.version }}/</pre>
</div>
</div>
@@ -78,38 +78,6 @@
{% block javascripts %}
{{ super() }}
<script>
- document.addEventListener("DOMContentLoaded", function() {
- const copyButtons = document.querySelectorAll(".atr-copy-btn");
-
- copyButtons.forEach(button => {
- button.addEventListener("click", function() {
- const targetId = this.getAttribute("data-clipboard-target");
- const targetElement = document.querySelector(targetId);
-
- if (targetElement) {
- const textToCopy = targetElement.textContent;
-
- navigator.clipboard.writeText(textToCopy)
- .then(() => {
- const originalText = this.innerHTML;
- this.innerHTML = '<i class="bi bi-check2"></i>
Copied!';
-
- // Reset the button text after 2000ms
- setTimeout(() => {
- this.innerHTML = originalText;
- }, 2000);
- })
- .catch(err => {
- console.error("Failed to copy: ", err);
- this.innerHTML = '<i class="bi
bi-exclamation-triangle"></i> Failed!';
-
- setTimeout(() => {
- this.innerHTML = '<i class="bi
bi-clipboard"></i> Copy';
- }, 2000);
- });
- }
- });
- });
- });
+ init();
</script>
{% endblock javascripts %}
diff --git a/atr/templates/draft-review.html b/atr/templates/draft-review.html
index 85181be..23f7bee 100644
--- a/atr/templates/draft-review.html
+++ b/atr/templates/draft-review.html
@@ -100,11 +100,11 @@
class="btn btn-sm btn-outline-primary fs-6 small
ms-2">Review file</a>
<button class="btn btn-sm btn-outline-danger"
data-bs-toggle="modal"
- data-bs-target="#delete-{{ path }}">Delete
file</button>
+ data-bs-target="#delete-{{ path|slugify }}">Delete
file</button>
</td>
</tr>
{% set file_id = path|string %}
- {{ dialog.delete_modal_with_confirm(file_id, "Delete file",
"file", as_url(routes.draft.delete_file, project_name=project_name,
version_name=version_name) , delete_file_form, "file_path") }}
+ {{ dialog.delete_modal(file_id, "Delete file", "file",
as_url(routes.draft.delete_file, project_name=project_name,
version_name=version_name) , delete_file_form, "file_path") }}
{% endfor %}
</tbody>
</table>
@@ -158,9 +158,22 @@
<a href="{{ as_url(routes.draft.add_file,
project_name=release.project.name, version_name=release.version) }}">Upload a
file in the browser</a>, or use the command below to add or modify files in
this release using rsync:
</p>
</div>
- <pre class="card-footer bg-light border-1 pt-4 small">
+ <div class="card-footer bg-light border-1 pt-4 pb-4 position-relative">
+ <button class="btn btn-sm btn-outline-secondary atr-copy-btn fs-6
position-absolute top-0 end-0 m-2"
+ data-clipboard-target="#cmd-newdraft-{{
release.project.name|slugify }}">
+ <i class="bi bi-clipboard"></i> Copy
+ </button>
+ <pre class="small mb-0" id="cmd-newdraft-{{ release.project.name|slugify
}}">
rsync -av -e 'ssh -p 2222' your/files/ \
{{ asf_id }}@{{ server_domain }}:/{{ project_name }}/{{ version_name }}/
- </pre>
+ </pre>
+ </div>
</div>
{% endblock content %}
+
+{% block javascripts %}
+ {{ super() }}
+ <script>
+ init();
+ </script>
+{% endblock javascripts %}
diff --git a/atr/templates/macros/dialog.html b/atr/templates/macros/dialog.html
index 07b9b92..22189dd 100644
--- a/atr/templates/macros/dialog.html
+++ b/atr/templates/macros/dialog.html
@@ -1,15 +1,16 @@
{% macro delete_modal_with_confirm(id, title, item, action, form, field_name)
%}
+ {% set element_id = id|slugify %}
<div class="modal modal-lg fade"
- id="delete-{{ id }}"
+ id="delete-{{ element_id }}"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
- aria-labelledby="delete-{{ id }}-label"
+ aria-labelledby="delete-{{ element_id }}-label"
aria-hidden="true">
<div class="modal-dialog border-primary">
<div class="modal-content">
<div class="modal-header bg-danger bg-opacity-10 text-danger">
- <h1 class="modal-title fs-5" id="delete-{{ id }}-label">{{ title
}}</h1>
+ <h1 class="modal-title fs-5" id="delete-{{ element_id }}-label">{{
title }}</h1>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
@@ -21,19 +22,50 @@
{{ form.hidden_tag() }}
{{ form[field_name](value_=id, hidden=True) }}
<div class="mb-3">
- <label for="confirm_delete_{{ id }}" class="form-label">
+ <label for="confirm_delete_{{ element_id }}" class="form-label">
Type <strong>DELETE</strong> to confirm:
</label>
<input class="form-control mt-2"
- id="confirm_delete_{{ id }}"
+ id="confirm_delete_{{ element_id }}"
name="confirm_delete"
placeholder="DELETE"
required=""
type="text"
value=""
- onkeyup="updateDeleteButton(this, 'delete-button-{{ id
}}')" />
+ onkeyup="updateDeleteButton(this, 'delete-button-{{
element_id }}')" />
</div>
- {{ form.submit(class_="btn btn-danger", id_="delete-button-" + id,
disabled=True) }}
+ {{ form.submit(class_="btn btn-danger", id_="delete-button-" +
element_id, disabled=True) }}
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+{% endmacro %}
+
+{% macro delete_modal(id, title, item, action, form, field_name) %}
+ {% set element_id = id|slugify %}
+ <div class="modal modal-lg fade"
+ id="delete-{{ element_id }}"
+ data-bs-backdrop="static"
+ data-bs-keyboard="false"
+ tabindex="-1"
+ aria-labelledby="delete-{{ element_id }}-label"
+ aria-hidden="true">
+ <div class="modal-dialog border-primary">
+ <div class="modal-content">
+ <div class="modal-header bg-danger bg-opacity-10 text-danger">
+ <h1 class="modal-title fs-5" id="delete-{{ element_id }}-label">{{
title }}</h1>
+ <button type="button"
+ class="btn-close"
+ data-bs-dismiss="modal"
+ aria-label="Close"></button>
+ </div>
+ <div class="modal-body">
+ <p class="text-muted mb-3">Warning: This action will permanently
delete this {{ item }} and cannot be undone.</p>
+ <form method="post" action="{{ action }}">
+ {{ form.hidden_tag() }}
+ {{ form[field_name](value_=id, hidden=True) }}
+ {{ form.submit(class_="btn btn-danger", id_="delete-button-" +
element_id) }}
</form>
</div>
</div>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]