FossilRepo

Gate chat behind FEATURE_CHAT constance flag (default off)

ragelink 2026-04-14 04:43 UTC trunk
Commit a53acf34c1c6f6f05c635bcc2d5af21b6cea597ede0701f8bf857360b68189a5
--- config/settings.py
+++ config/settings.py
@@ -254,18 +254,21 @@
254254
"GITLAB_OAUTH_CLIENT_SECRET": ("", "GitLab OAuth App Client Secret"),
255255
# Cloudflare Turnstile (optional bot protection on login)
256256
"TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"),
257257
"TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"),
258258
"TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"),
259
+ # Feature flags
260
+ "FEATURE_CHAT": (False, "Enable project chat rooms"),
259261
}
260262
CONSTANCE_CONFIG_FIELDSETS = {
261263
"General": ("SITE_NAME",),
262264
"Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"),
263265
"Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"),
264266
"GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"),
265267
"GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"),
266268
"Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"),
269
+ "Features": ("FEATURE_CHAT",),
267270
}
268271
269272
# --- Sentry ---
270273
271274
SENTRY_DSN = env_str("SENTRY_DSN")
272275
--- config/settings.py
+++ config/settings.py
@@ -254,18 +254,21 @@
254 "GITLAB_OAUTH_CLIENT_SECRET": ("", "GitLab OAuth App Client Secret"),
255 # Cloudflare Turnstile (optional bot protection on login)
256 "TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"),
257 "TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"),
258 "TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"),
 
 
259 }
260 CONSTANCE_CONFIG_FIELDSETS = {
261 "General": ("SITE_NAME",),
262 "Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"),
263 "Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"),
264 "GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"),
265 "GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"),
266 "Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"),
 
267 }
268
269 # --- Sentry ---
270
271 SENTRY_DSN = env_str("SENTRY_DSN")
272
--- config/settings.py
+++ config/settings.py
@@ -254,18 +254,21 @@
254 "GITLAB_OAUTH_CLIENT_SECRET": ("", "GitLab OAuth App Client Secret"),
255 # Cloudflare Turnstile (optional bot protection on login)
256 "TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"),
257 "TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"),
258 "TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"),
259 # Feature flags
260 "FEATURE_CHAT": (False, "Enable project chat rooms"),
261 }
262 CONSTANCE_CONFIG_FIELDSETS = {
263 "General": ("SITE_NAME",),
264 "Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"),
265 "Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"),
266 "GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"),
267 "GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"),
268 "Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"),
269 "Features": ("FEATURE_CHAT",),
270 }
271
272 # --- Sentry ---
273
274 SENTRY_DSN = env_str("SENTRY_DSN")
275
--- core/context_processors.py
+++ core/context_processors.py
@@ -1,5 +1,7 @@
1
+from constance import config
2
+
13
from pages.models import Page
24
from projects.models import Project, ProjectGroup
35
46
57
def sidebar(request):
@@ -38,6 +40,7 @@
3840
"sidebar_grouped": grouped_projects,
3941
"sidebar_ungrouped": ungrouped_projects,
4042
"sidebar_pages": pages, # Keep for backwards compat
4143
"sidebar_product_docs": product_docs,
4244
"sidebar_kb_pages": kb_pages,
45
+ "feature_chat": config.FEATURE_CHAT,
4346
}
4447
--- core/context_processors.py
+++ core/context_processors.py
@@ -1,5 +1,7 @@
 
 
1 from pages.models import Page
2 from projects.models import Project, ProjectGroup
3
4
5 def sidebar(request):
@@ -38,6 +40,7 @@
38 "sidebar_grouped": grouped_projects,
39 "sidebar_ungrouped": ungrouped_projects,
40 "sidebar_pages": pages, # Keep for backwards compat
41 "sidebar_product_docs": product_docs,
42 "sidebar_kb_pages": kb_pages,
 
43 }
44
--- core/context_processors.py
+++ core/context_processors.py
@@ -1,5 +1,7 @@
1 from constance import config
2
3 from pages.models import Page
4 from projects.models import Project, ProjectGroup
5
6
7 def sidebar(request):
@@ -38,6 +40,7 @@
40 "sidebar_grouped": grouped_projects,
41 "sidebar_ungrouped": ungrouped_projects,
42 "sidebar_pages": pages, # Keep for backwards compat
43 "sidebar_product_docs": product_docs,
44 "sidebar_kb_pages": kb_pages,
45 "feature_chat": config.FEATURE_CHAT,
46 }
47
--- fossil/views.py
+++ fossil/views.py
@@ -4482,10 +4482,14 @@
44824482
# --- Chat ---
44834483
44844484
44854485
@login_required
44864486
def chat_room(request, slug):
4487
+ from constance import config
4488
+
4489
+ if not config.FEATURE_CHAT:
4490
+ raise Http404
44874491
project, fossil_repo, reader = _get_repo_and_reader(slug, request)
44884492
from fossil.chat import ChatMessage
44894493
44904494
messages = ChatMessage.objects.filter(repository=fossil_repo).select_related("user").order_by("-created_at")[:50]
44914495
messages = list(reversed(messages))
@@ -4501,10 +4505,14 @@
45014505
)
45024506
45034507
45044508
@login_required
45054509
def chat_send(request, slug):
4510
+ from constance import config
4511
+
4512
+ if not config.FEATURE_CHAT:
4513
+ raise Http404
45064514
from fossil.chat import ChatMessage
45074515
45084516
if request.method == "POST":
45094517
project, fossil_repo, reader = _get_repo_and_reader(slug, request, "write")
45104518
body = request.POST.get("body", "").strip()
45114519
--- fossil/views.py
+++ fossil/views.py
@@ -4482,10 +4482,14 @@
4482 # --- Chat ---
4483
4484
4485 @login_required
4486 def chat_room(request, slug):
 
 
 
 
4487 project, fossil_repo, reader = _get_repo_and_reader(slug, request)
4488 from fossil.chat import ChatMessage
4489
4490 messages = ChatMessage.objects.filter(repository=fossil_repo).select_related("user").order_by("-created_at")[:50]
4491 messages = list(reversed(messages))
@@ -4501,10 +4505,14 @@
4501 )
4502
4503
4504 @login_required
4505 def chat_send(request, slug):
 
 
 
 
4506 from fossil.chat import ChatMessage
4507
4508 if request.method == "POST":
4509 project, fossil_repo, reader = _get_repo_and_reader(slug, request, "write")
4510 body = request.POST.get("body", "").strip()
4511
--- fossil/views.py
+++ fossil/views.py
@@ -4482,10 +4482,14 @@
4482 # --- Chat ---
4483
4484
4485 @login_required
4486 def chat_room(request, slug):
4487 from constance import config
4488
4489 if not config.FEATURE_CHAT:
4490 raise Http404
4491 project, fossil_repo, reader = _get_repo_and_reader(slug, request)
4492 from fossil.chat import ChatMessage
4493
4494 messages = ChatMessage.objects.filter(repository=fossil_repo).select_related("user").order_by("-created_at")[:50]
4495 messages = list(reversed(messages))
@@ -4501,10 +4505,14 @@
4505 )
4506
4507
4508 @login_required
4509 def chat_send(request, slug):
4510 from constance import config
4511
4512 if not config.FEATURE_CHAT:
4513 raise Http404
4514 from fossil.chat import ChatMessage
4515
4516 if request.method == "POST":
4517 project, fossil_repo, reader = _get_repo_and_reader(slug, request, "write")
4518 body = request.POST.get("body", "").strip()
4519
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -25,10 +25,16 @@
2525
</a>
2626
<a href="{% url 'fossil:forum' slug=project.slug %}"
2727
class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'forum' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
2828
Forum
2929
</a>
30
+ {% if feature_chat %}
31
+ <a href="{% url 'fossil:chat' slug=project.slug %}"
32
+ class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'chat' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
33
+ Chat
34
+ </a>
35
+ {% endif %}
3036
<a href="{% url 'fossil:releases' slug=project.slug %}"
3137
class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'releases' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
3238
Releases
3339
</a>
3440
<a href="{% url 'fossil:unversioned' slug=project.slug %}"
3541
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -25,10 +25,16 @@
25 </a>
26 <a href="{% url 'fossil:forum' slug=project.slug %}"
27 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'forum' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
28 Forum
29 </a>
 
 
 
 
 
 
30 <a href="{% url 'fossil:releases' slug=project.slug %}"
31 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'releases' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
32 Releases
33 </a>
34 <a href="{% url 'fossil:unversioned' slug=project.slug %}"
35
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -25,10 +25,16 @@
25 </a>
26 <a href="{% url 'fossil:forum' slug=project.slug %}"
27 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'forum' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
28 Forum
29 </a>
30 {% if feature_chat %}
31 <a href="{% url 'fossil:chat' slug=project.slug %}"
32 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'chat' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
33 Chat
34 </a>
35 {% endif %}
36 <a href="{% url 'fossil:releases' slug=project.slug %}"
37 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'releases' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
38 Releases
39 </a>
40 <a href="{% url 'fossil:unversioned' slug=project.slug %}"
41

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button