Skip to content

Commit 6cc5aa5

Browse files
Merge branch 'main' into task-scheduler
2 parents 0061119 + cf4cc3b commit 6cc5aa5

File tree

7 files changed

+287
-65
lines changed

7 files changed

+287
-65
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<p align="center"><a href="https://github.com/OWASP/BLT/actions" rel="noopener noreferrer" target="__blank"><img alt="Build" src="https://github.com/OWASP/BLT/actions/workflows/auto-merge.yml/badge.svg"></a> <a href="https://github.com/OWASP/BLT/blob/main/LICENSE.md" rel="noopener noreferrer"><img src="https://img.shields.io/badge/license-AGPL--3.0-blue"></a>
55
<a href="https://github.com/OWASP/BLT" rel="noopener noreferrer" target="__blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/OWASP/BLT?style=social"></a></p>
66

7-
<img alt="Views" src="https://blt.owasp.org/projects/blt/badge"></a>
7+
<img alt="Views" src="https://blt.owasp.org/repos/blt/badge/">
88

99
Everything is on our <a href="https://blt.owasp.org">homepage</a>
1010

blt/urls.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,13 @@
174174
view_hunt,
175175
weekly_report,
176176
)
177-
from website.views.project import ( # ProjectBadgeView,
177+
from website.views.project import (
178+
ProjectBadgeView,
178179
ProjectDetailView,
179180
ProjectListView,
180181
ProjectsDetailView,
181182
ProjectView,
183+
RepoBadgeView,
182184
RepoDetailView,
183185
blt_tomato,
184186
create_project,
@@ -604,9 +606,8 @@
604606
re_path(r"^api/v1/count/$", issue_count, name="api_count"),
605607
re_path(r"^api/v1/contributors/$", contributors, name="api_contributor"),
606608
path("project/<slug:slug>/", ProjectDetailView.as_view(), name="project_view"),
607-
# path(
608-
# "projects/<slug:slug>/badge/", ProjectBadgeView.as_view(), name="project-badge"
609-
# ),
609+
path("projects/<slug:slug>/badge/", ProjectBadgeView.as_view(), name="project-badge"),
610+
path("repos/<slug:slug>/badge/", RepoBadgeView.as_view(), name="repo-badge"),
610611
path("repository/<slug:slug>/", RepoDetailView.as_view(), name="repo_detail"),
611612
re_path(r"^report-ip/$", ReportIpView.as_view(), name="report_ip"),
612613
re_path(r"^reported-ips/$", ReportedIpListView.as_view(), name="reported_ips_list"),
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 5.1.3 on 2025-01-10 20:22
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("website", "0179_contributorstats"),
9+
]
10+
11+
operations = [
12+
migrations.RenameField(
13+
model_name="repo",
14+
old_name="project_visit_count",
15+
new_name="repo_visit_count",
16+
),
17+
]

website/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,7 @@ class Repo(models.Model):
12601260
last_updated = models.DateTimeField(null=True, blank=True)
12611261
total_issues = models.IntegerField(default=0)
12621262
# rename this to repo_visit_count and make sure the github badge works with this
1263-
project_visit_count = models.IntegerField(default=0)
1263+
repo_visit_count = models.IntegerField(default=0)
12641264
watchers = models.IntegerField(default=0)
12651265
open_pull_requests = models.IntegerField(default=0)
12661266
primary_language = models.CharField(max_length=50, null=True, blank=True)

website/templates/projects/project_detail.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,27 @@ <h1 class="text-3xl font-bold text-white mb-2">{{ project.name }}</h1>
158158
</div>
159159
</div>
160160
</div>
161+
<!-- Badge URL Section -->
162+
<div class="p-6 bg-gray-50 border-t border-gray-200">
163+
<h3 class="text-lg font-semibold text-gray-900 mb-4">Project View Count Badge</h3>
164+
<div class="flex flex-col gap-4">
165+
<!-- HTML Format -->
166+
<div class="space-y-2">
167+
<div class="text-gray-700 font-medium">HTML:</div>
168+
<div class="relative">
169+
<input type="text"
170+
readonly
171+
value='&lt;img alt="Views" src="{{ request.scheme }}://{{ request.get_host }}{% url 'project-badge' project.slug %}" &gt;'
172+
class="w-full px-4 py-2 bg-white border border-gray-300 rounded-lg pr-24 font-mono text-sm focus:ring-2 focus:ring-red-500 focus:border-transparent"
173+
id="badge-url-html">
174+
<button onclick="copyToClipboard('badge-url-html')"
175+
class="absolute right-2 top-1/2 -translate-y-1/2 px-3 py-1 bg-red-500 hover:bg-red-600 text-white text-sm rounded-md transition-colors">
176+
Copy
177+
</button>
178+
</div>
179+
</div>
180+
</div>
181+
</div>
161182
</div>
162183
<!-- Associated Repositories -->
163184
<div class="grid gap-6">
@@ -287,3 +308,37 @@ <h2 class="text-xl font-bold text-gray-900 mb-4">Project Timeline</h2>
287308
</div>
288309
</div>
289310
{% endblock content %}
311+
{% block after_js %}
312+
<script>
313+
function copyToClipboard(elementId) {
314+
const element = document.getElementById(elementId);
315+
316+
// Select the text
317+
element.select();
318+
element.setSelectionRange(0, 99999); // For mobile devices
319+
320+
// Copy the text
321+
try {
322+
navigator.clipboard.writeText(element.value).then(() => {
323+
// Get the button
324+
const button = element.nextElementSibling;
325+
const originalText = button.textContent;
326+
327+
// Change button style to show success
328+
button.textContent = 'Copied!';
329+
button.classList.remove('bg-red-500', 'hover:bg-red-600');
330+
button.classList.add('bg-green-500', 'hover:bg-green-600');
331+
332+
// Reset button after 2 seconds
333+
setTimeout(() => {
334+
button.textContent = originalText;
335+
button.classList.remove('bg-green-500', 'hover:bg-green-600');
336+
button.classList.add('bg-red-500', 'hover:bg-red-600');
337+
}, 2000);
338+
});
339+
} catch (err) {
340+
console.error('Failed to copy text: ', err);
341+
}
342+
}
343+
</script>
344+
{% endblock %}

website/templates/projects/repo_detail.html

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ <h1 class="text-3xl font-bold text-gray-900">{{ repo.name }}</h1>
7272
</a>
7373
</div>
7474
</div>
75+
<!-- Badge URL Section -->
76+
<div class="mt-6 p-4 bg-gray-50 rounded-lg border border-gray-200">
77+
<h3 class="text-lg font-semibold text-gray-900 mb-4">Repository View Count Badge</h3>
78+
<div class="flex flex-col gap-4">
79+
<!-- HTML Format -->
80+
<div class="space-y-2">
81+
<div class="text-gray-700 font-medium">HTML:</div>
82+
<div class="relative">
83+
<input type="text"
84+
readonly
85+
value='&lt;img alt="Views" src="{{ request.scheme }}://{{ request.get_host }}{% url 'repo-badge' repo.slug %}" &gt;'
86+
class="w-full px-4 py-2 bg-white border border-gray-300 rounded-lg pr-24 font-mono text-sm focus:ring-2 focus:ring-red-500 focus:border-transparent"
87+
id="badge-url-html">
88+
<button onclick="copyToClipboard('badge-url-html')"
89+
class="absolute right-2 top-1/2 -translate-y-1/2 px-3 py-1 bg-red-500 hover:bg-red-600 text-white text-sm rounded-md transition-colors">
90+
Copy
91+
</button>
92+
</div>
93+
</div>
94+
</div>
95+
</div>
7596
<!-- Repository Type & Metadata -->
7697
<div class="mt-6 flex items-center justify-between border-t border-gray-200 pt-6">
7798
<div class="flex items-center gap-6">
@@ -740,7 +761,38 @@ <h3 class="font-medium text-gray-900 group-hover:text-red-600 transition-colors"
740761
{% endblock content %}
741762
{% block after_js %}
742763
<script>
743-
const refreshSection = async (button, section) => {
764+
function copyToClipboard(elementId) {
765+
const element = document.getElementById(elementId);
766+
767+
// Select the text
768+
element.select();
769+
element.setSelectionRange(0, 99999); // For mobile devices
770+
771+
// Copy the text
772+
try {
773+
navigator.clipboard.writeText(element.value).then(() => {
774+
// Get the button
775+
const button = element.nextElementSibling;
776+
const originalText = button.textContent;
777+
778+
// Change button style to show success
779+
button.textContent = 'Copied!';
780+
button.classList.remove('bg-red-500', 'hover:bg-red-600');
781+
button.classList.add('bg-green-500', 'hover:bg-green-600');
782+
783+
// Reset button after 2 seconds
784+
setTimeout(() => {
785+
button.textContent = originalText;
786+
button.classList.remove('bg-green-500', 'hover:bg-green-600');
787+
button.classList.add('bg-red-500', 'hover:bg-red-600');
788+
}, 2000);
789+
});
790+
} catch (err) {
791+
console.error('Failed to copy text: ', err);
792+
}
793+
}
794+
795+
const refreshSection = async (button, section) => {
744796
if (button.classList.contains('refresh_spinner')) {
745797
return;
746798
}

0 commit comments

Comments
 (0)