Skip to content

Commit 32dc0e6

Browse files
authored
Prohibited name flow tweaks (#12549)
* List releases by date in prohibited name flow * Add click-to-copy in prohibited names flow
1 parent 3ebae1f commit 32dc0e6

File tree

4 files changed

+93
-8
lines changed

4 files changed

+93
-8
lines changed

tests/unit/admin/views/test_prohibited_project_names.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import uuid
1414

15+
from collections import defaultdict
16+
1517
import pretend
1618
import pytest
1719

@@ -101,17 +103,36 @@ def test_nothing_to_delete(self, db_request):
101103

102104
assert result == {
103105
"prohibited_project_names": {"project": "foo", "comment": ""},
104-
"existing": {"project": None, "releases": [], "files": [], "roles": []},
106+
"existing": {
107+
"project": None,
108+
"releases": [],
109+
"files": [],
110+
"roles": [],
111+
"releases_by_date": defaultdict(list),
112+
},
105113
}
106114

107115
def test_stuff_to_delete(self, db_request):
116+
db_request.user = UserFactory.create()
108117
project = ProjectFactory.create()
118+
release = ReleaseFactory.create(project=project)
119+
file_ = FileFactory.create(release=release, filename="who cares")
120+
role = RoleFactory.create(project=project, user=db_request.user)
121+
109122
db_request.GET["project"] = project.name
110123
result = views.confirm_prohibited_project_names(db_request)
111124

112125
assert result == {
113126
"prohibited_project_names": {"project": project.name, "comment": ""},
114-
"existing": {"project": project, "releases": [], "files": [], "roles": []},
127+
"existing": {
128+
"project": project,
129+
"releases": [release],
130+
"files": [file_],
131+
"roles": [role],
132+
"releases_by_date": defaultdict(
133+
list, {release.created.strftime("%Y-%m-%d"): [release]}
134+
),
135+
},
115136
}
116137

117138

warehouse/admin/static/js/warehouse.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
* limitations under the License.
1212
*/
1313

14+
// We'll use docReady as a modern replacement for $(document).ready() which
15+
// does not require all of jQuery to use. This will let us use it without
16+
// having to load all of jQuery, which will make things faster.
17+
import docReady from "warehouse/utils/doc-ready";
18+
19+
import Clipboard from "clipboard";
20+
1421
document.querySelectorAll("a[data-form-submit]").forEach(function (element) {
1522
element.addEventListener("click", function(event) {
1623
// We're turning this element into a form submission, so instead of the
@@ -74,3 +81,38 @@ document.querySelectorAll(".btn-group[data-input][data-state]").forEach(function
7481
});
7582
});
7683
});
84+
85+
// Copy handler for copy tooltips, e.g.
86+
// - the pip command on package detail page
87+
// - the copy hash on package detail page
88+
// - the copy hash on release maintainers page
89+
//
90+
// Copied from https://github.com/pypi/warehouse/blob/3ebae1ffe8f9f258f80eb8bf048f0e1fcc2df2b4/warehouse/static/js/warehouse/index.js#LL76-L107C4
91+
docReady(() => {
92+
let setCopiedTooltip = (e) => {
93+
e.trigger.setAttribute("data-tooltip-label", "Copied");
94+
e.trigger.setAttribute("role", "alert");
95+
e.clearSelection();
96+
};
97+
98+
new Clipboard(".copy-tooltip").on("success", setCopiedTooltip);
99+
100+
let setOriginalLabel = (element) => {
101+
element.setAttribute("data-tooltip-label", "Copy to clipboard");
102+
element.removeAttribute("role");
103+
element.blur();
104+
};
105+
106+
let tooltippedElems = Array.from(document.querySelectorAll(".copy-tooltip"));
107+
108+
tooltippedElems.forEach((element) => {
109+
element.addEventListener("focusout",
110+
setOriginalLabel.bind(undefined, element),
111+
false
112+
);
113+
element.addEventListener("mouseout",
114+
setOriginalLabel.bind(undefined, element),
115+
false
116+
);
117+
});
118+
});

warehouse/admin/templates/admin/prohibited_project_names/confirm.html

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,22 @@
3030
<p>
3131
Prohibiting {{ prohibited_project_names.project }} will irreversibly delete
3232
the <a href="{{ request.route_path('admin.project.detail', project_name=existing.project.normalized_name) }}">{{ existing.project.name }}</a>
33-
project along with
34-
<a href="{{ request.route_path('admin.project.releases', project_name=existing.project.normalized_name) }}">{{ existing.releases|length() }} releases</a>
35-
and {{ existing.files|length() }} files. This project has
36-
{{ existing.users|length() }} users able to access it, listed
37-
below:
33+
project along with the following
34+
<a href="{{ request.route_path('admin.project.releases', project_name=existing.project.normalized_name) }}">{{ existing.releases|length() }} releases</a>:
35+
36+
<ul>
37+
{% for date, releases in existing.releases_by_date|dictsort(reverse=True) %}
38+
<li>
39+
{{ date }} -
40+
{% for release in releases %}
41+
<a href="{{ request.route_path('admin.project.release', project_name=release.project.normalized_name, version=release.version) }}">{{ release.version }}</a>{% if not loop.last %}, {% endif %}
42+
{% endfor %}
43+
</li>
44+
{% endfor %}
45+
</ul>
46+
47+
This project has {{ existing.roles|length }} users able to
48+
access it, listed below:
3849

3950
<ul>
4051
{% for role in existing.roles %}
@@ -51,7 +62,11 @@
5162

5263
<p>
5364
Are you sure you want to prohibit
54-
<strong>{{ prohibited_project_names.project }}</strong>?
65+
<code id="project-name">{{ prohibited_project_names.project }}</code>
66+
<button type="button" class="copy-tooltip copy-tooltip-s" data-clipboard-target="#project-name" data-tooltip-label="Copy to clipboard">
67+
<i class="fa fa-copy" aria-hidden="true"></i>
68+
</button>
69+
?
5570
</p>
5671

5772
<form action="{{ request.current_route_path() }}" method="POST">

warehouse/admin/views/prohibited_project_names.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import shlex
1414

15+
from collections import defaultdict
16+
1517
from packaging.utils import canonicalize_name
1618
from paginate_sqlalchemy import SqlalchemyOrmPage as SQLAlchemyORMPage
1719
from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPSeeOther
@@ -125,11 +127,16 @@ def confirm_prohibited_project_names(request):
125127
files = []
126128
roles = []
127129

130+
releases_by_date = defaultdict(list)
131+
for release in releases:
132+
releases_by_date[release.created.strftime("%Y-%m-%d")].append(release)
133+
128134
return {
129135
"prohibited_project_names": {"project": project_name, "comment": comment},
130136
"existing": {
131137
"project": project,
132138
"releases": releases,
139+
"releases_by_date": releases_by_date,
133140
"files": files,
134141
"roles": roles,
135142
},

0 commit comments

Comments
 (0)