@@ -193,15 +193,23 @@
193 193 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return f'href="{base}/wiki/page/{m.group(1)}"'
194 194 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
# /tktview/HASH or /tktview?name=HASH -> ticket detail
195 195 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m = re.match(r"/tktview[?/](?:name=)?([0-9a-f]+)", url)
196 196 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if m:
197 197 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return f'href="{base}/tickets/{m.group(1)}/"'
198 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # /vdiff?from=X&to=Y -> compare view
199 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m = re.match(r"/vdiff\?from=([0-9a-f]+)&to=([0-9a-f]+)", url)
200 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if m:
201 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return f'href="{base}/compare/?from={m.group(1)}&to={m.group(2)}"'
198 202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
# /timeline -> timeline
199 203 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if url.startswith("/timeline"):
200 204 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return f'href="{base}/timeline/"'
201 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- # /forum -> forum
202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if url.startswith("/forumpost") or url.startswith("/forum"):
205 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # /forumpost/HASH -> forum thread
206 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m = re.match(r"/forumpost/([0-9a-f]+)", url)
207 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if m:
208 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return f'href="{base}/forum/{m.group(1)}/"'
209 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # /forum -> forum list
210 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if url.startswith("/forum"):
203 211 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return f'href="{base}/forum/"'
204 212 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
# /www/file.wiki or /www/subdir/file -> doc page viewer
205 213 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m = re.match(r"/(www/.+)", url)
206 214 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if m:
207 215 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return f'href="{base}/docs/{m.group(1)}"'
@@ -616,24 +624,35 @@
616 624 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
P.PROJECT_VIEW.check(request.user)
617 625 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
project, fossil_repo, reader = _get_repo_and_reader(slug)
618 626 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
619 627 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
with reader:
620 628 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
ticket = reader.get_ticket_detail(ticket_uuid)
629 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ comments = reader.get_ticket_comments(ticket_uuid) if ticket else []
621 630 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
622 631 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if not ticket:
623 632 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
raise Http404("Ticket not found")
624 633 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
625 634 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
body_html = mark_safe(_render_fossil_content(ticket.body, project_slug=slug)) if ticket.body else ""
635 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ rendered_comments = []
636 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for c in comments:
637 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ rendered_comments.append(
638 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {
639 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "user": c["user"],
640 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "timestamp": c["timestamp"],
641 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "html": mark_safe(_render_fossil_content(c["comment"], project_slug=slug)),
642 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
643 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
626 644 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
627 645 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return render(
628 646 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
request,
629 647 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"fossil/ticket_detail.html",
630 648 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
{
631 649 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"project": project,
632 650 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"fossil_repo": fossil_repo,
633 651 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"ticket": ticket,
634 652 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"body_html": body_html,
653 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "comments": rendered_comments,
635 654 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"active_tab": "tickets",
636 655 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
},
637 656 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
)
638 657 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
639 658 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
@@ -993,10 +1012,76 @@
993 1012 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"results": results,
994 1013 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
"active_tab": "code",
995 1014 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
},
996 1015 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
)
997 1016 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
1017 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1018 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # --- RSS Feed ---
1019 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1020 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1021 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @login_required
1022 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def timeline_rss(request, slug):
1023 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """RSS feed of recent timeline entries."""
1024 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ P.PROJECT_VIEW.check(request.user)
1025 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ project, fossil_repo, reader = _get_repo_and_reader(slug)
1026 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1027 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with reader:
1028 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ entries = reader.get_timeline(limit=30, event_type="ci")
1029 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1030 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from django.http import HttpResponse as DjHttpResponse
1031 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from django.utils.html import escape
1032 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1033 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ items = []
1034 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for e in entries:
1035 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ link = request.build_absolute_uri(f"/projects/{slug}/fossil/checkin/{e.uuid}/")
1036 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ items.append(
1037 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<item><title>{escape(e.comment)}</title><link>{link}</link>"
1038 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<author>{escape(e.user)}</author>"
1039 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<pubDate>{e.timestamp.strftime('%a, %d %b %Y %H:%M:%S +0000')}</pubDate>"
1040 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<guid>{e.uuid}</guid></item>"
1041 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
1042 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1043 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ tl_link = request.build_absolute_uri(f"/projects/{slug}/fossil/timeline/")
1044 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ rss = (
1045 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ '<?xml version="1.0" encoding="UTF-8"?>'
1046 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ '<rss version="2.0"><channel>'
1047 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<title>{escape(project.name)} — Timeline</title>"
1048 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<link>{tl_link}</link>"
1049 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"<description>Recent checkins for {escape(project.name)}</description>"
1050 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ f"{''.join(items)}"
1051 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "</channel></rss>"
1052 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
1053 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return DjHttpResponse(rss, content_type="application/rss+xml")
1054 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1055 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1056 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # --- CSV Export ---
1057 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1058 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1059 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @login_required
1060 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def tickets_csv(request, slug):
1061 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """Export all tickets as CSV."""
1062 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ P.PROJECT_VIEW.check(request.user)
1063 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ project, fossil_repo, reader = _get_repo_and_reader(slug)
1064 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1065 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with reader:
1066 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ tickets = reader.get_tickets(limit=5000)
1067 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1068 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import csv
1069 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import io
1070 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1071 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from django.http import HttpResponse as DjHttpResponse
1072 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1073 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ output = io.StringIO()
1074 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ writer = csv.writer(output)
1075 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ writer.writerow(["UUID", "Title", "Status", "Type", "Priority", "Severity", "Created"])
1076 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for t in tickets:
1077 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ writer.writerow([t.uuid, t.title, t.status, t.type, t.priority, t.severity, t.created.isoformat() if t.created else ""])
1078 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
1079 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ response = DjHttpResponse(output.getvalue(), content_type="text/csv")
1080 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ response["Content-Disposition"] = f'attachment; filename="{slug}-tickets.csv"'
1081 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return response
1082 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
998 1083 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
999 1084 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
# --- File History ---
1000 1085 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
1001 1086 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
1002 1087 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
@login_required
1003 1088 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!