FossilRepo

Split sidebar: FossilRepo Docs (read-only) + Knowledge Base (org wiki) FossilRepo Docs: product documentation (setup, API, architecture, etc.) with no + New button — managed by superusers only. Knowledge Base: org-wide wiki for user-created content (runbooks, guides, onboarding). Has + New for users with page permissions. Seeded with a home page explaining the purpose. Context processor splits pages by known product doc slugs vs user-created.

lmata 2026-04-07 19:39 trunk
Commit 303df36bb6324ca187540e6c526b09f122d92a280b837f3d4c7f61a9f67cfc00
--- core/context_processors.py
+++ core/context_processors.py
@@ -22,11 +22,21 @@
2222
grouped_projects.append({"group": group, "projects": group_projects})
2323
grouped_ids.update(p.id for p in group_projects)
2424
2525
ungrouped_projects = [p for p in projects if p.id not in grouped_ids]
2626
27
+ # Split pages: product docs (known slugs) vs org knowledge base (user-created)
28
+ PRODUCT_DOC_SLUGS = {
29
+ "agentic-development", "api-reference", "architecture",
30
+ "administration", "setup-guide",
31
+ }
32
+ product_docs = [p for p in pages if p.slug in PRODUCT_DOC_SLUGS]
33
+ kb_pages = [p for p in pages if p.slug not in PRODUCT_DOC_SLUGS]
34
+
2735
return {
2836
"sidebar_projects": projects,
2937
"sidebar_grouped": grouped_projects,
3038
"sidebar_ungrouped": ungrouped_projects,
31
- "sidebar_pages": pages,
39
+ "sidebar_pages": pages, # Keep for backwards compat
40
+ "sidebar_product_docs": product_docs,
41
+ "sidebar_kb_pages": kb_pages,
3242
}
3343
--- core/context_processors.py
+++ core/context_processors.py
@@ -22,11 +22,21 @@
22 grouped_projects.append({"group": group, "projects": group_projects})
23 grouped_ids.update(p.id for p in group_projects)
24
25 ungrouped_projects = [p for p in projects if p.id not in grouped_ids]
26
 
 
 
 
 
 
 
 
27 return {
28 "sidebar_projects": projects,
29 "sidebar_grouped": grouped_projects,
30 "sidebar_ungrouped": ungrouped_projects,
31 "sidebar_pages": pages,
 
 
32 }
33
--- core/context_processors.py
+++ core/context_processors.py
@@ -22,11 +22,21 @@
22 grouped_projects.append({"group": group, "projects": group_projects})
23 grouped_ids.update(p.id for p in group_projects)
24
25 ungrouped_projects = [p for p in projects if p.id not in grouped_ids]
26
27 # Split pages: product docs (known slugs) vs org knowledge base (user-created)
28 PRODUCT_DOC_SLUGS = {
29 "agentic-development", "api-reference", "architecture",
30 "administration", "setup-guide",
31 }
32 product_docs = [p for p in pages if p.slug in PRODUCT_DOC_SLUGS]
33 kb_pages = [p for p in pages if p.slug not in PRODUCT_DOC_SLUGS]
34
35 return {
36 "sidebar_projects": projects,
37 "sidebar_grouped": grouped_projects,
38 "sidebar_ungrouped": ungrouped_projects,
39 "sidebar_pages": pages, # Keep for backwards compat
40 "sidebar_product_docs": product_docs,
41 "sidebar_kb_pages": kb_pages,
42 }
43
--- templates/includes/sidebar.html
+++ templates/includes/sidebar.html
@@ -85,13 +85,13 @@
8585
{% endif %}
8686
</div>
8787
</div>
8888
{% endif %}
8989
90
- <!-- FossilRepo Docs section -->
91
- {% if perms.pages.view_page %}
92
- <div>
90
+ <!-- FossilRepo Docs (product docs — read-only) -->
91
+ {% if sidebar_product_docs %}
92
+ <div x-data="{ docsOpen: false }">
9393
<button @click="collapsed ? (collapsed = false, docsOpen = true) : (docsOpen = !docsOpen)"
9494
class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium {% if '/kb/' in request.path %}text-white{% else %}text-gray-400 hover:bg-gray-800 hover:text-white{% endif %}"
9595
:title="collapsed ? 'FossilRepo Docs' : ''">
9696
<span class="flex items-center gap-2">
9797
<svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
@@ -102,17 +102,47 @@
102102
<svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="docsOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
103103
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
104104
</svg>
105105
</button>
106106
<div x-show="docsOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
107
- {% for p in sidebar_pages %}
107
+ {% for p in sidebar_product_docs %}
108
+ <a href="{% url 'pages:detail' slug=p.slug %}"
109
+ class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110
+ {{ p.name }}
111
+ </a>
112
+ {% endfor %}
113
+ </div>
114
+ </div>
115
+ {% endif %}
116
+
117
+ <!-- Knowledge Base (org wiki — user-editable) -->
118
+ {% if perms.pages.view_page %}
119
+ <div x-data="{ kbOpen: false }">
120
+ <button @click="collapsed ? (collapsed = false, kbOpen = true) : (kbOpen = !kbOpen)"
121
+ class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium text-gray-400 hover:bg-gray-800 hover:text-white"
122
+ :title="collapsed ? 'Knowledge Base' : ''">
123
+ <span class="flex items-center gap-2">
124
+ <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
125
+ <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" />
126
+ </svg>
127
+ <span x-show="!collapsed" class="truncate">Knowledge Base</span>
128
+ </span>
129
+ <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="kbOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
130
+ <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
131
+ </svg>
132
+ </button>
133
+ <div x-show="kbOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
134
+ {% for p in sidebar_kb_pages %}
108135
<a href="{% url 'pages:detail' slug=p.slug %}"
109136
class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110137
{{ p.name }}
111138
</a>
112139
{% endfor %}
113
- {% if user.is_superuser %}
140
+ {% if sidebar_kb_pages|length == 0 %}
141
+ <p class="px-3 py-1.5 text-xs text-gray-600">No articles yet.</p>
142
+ {% endif %}
143
+ {% if perms.pages.add_page %}
114144
<a href="{% url 'pages:create' %}"
115145
class="block rounded-md px-3 py-1.5 text-sm text-gray-600 hover:text-brand-light">
116146
+ New
117147
</a>
118148
{% endif %}
119149
--- templates/includes/sidebar.html
+++ templates/includes/sidebar.html
@@ -85,13 +85,13 @@
85 {% endif %}
86 </div>
87 </div>
88 {% endif %}
89
90 <!-- FossilRepo Docs section -->
91 {% if perms.pages.view_page %}
92 <div>
93 <button @click="collapsed ? (collapsed = false, docsOpen = true) : (docsOpen = !docsOpen)"
94 class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium {% if '/kb/' in request.path %}text-white{% else %}text-gray-400 hover:bg-gray-800 hover:text-white{% endif %}"
95 :title="collapsed ? 'FossilRepo Docs' : ''">
96 <span class="flex items-center gap-2">
97 <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
@@ -102,17 +102,47 @@
102 <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="docsOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
103 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
104 </svg>
105 </button>
106 <div x-show="docsOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
107 {% for p in sidebar_pages %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108 <a href="{% url 'pages:detail' slug=p.slug %}"
109 class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110 {{ p.name }}
111 </a>
112 {% endfor %}
113 {% if user.is_superuser %}
 
 
 
114 <a href="{% url 'pages:create' %}"
115 class="block rounded-md px-3 py-1.5 text-sm text-gray-600 hover:text-brand-light">
116 + New
117 </a>
118 {% endif %}
119
--- templates/includes/sidebar.html
+++ templates/includes/sidebar.html
@@ -85,13 +85,13 @@
85 {% endif %}
86 </div>
87 </div>
88 {% endif %}
89
90 <!-- FossilRepo Docs (product docs — read-only) -->
91 {% if sidebar_product_docs %}
92 <div x-data="{ docsOpen: false }">
93 <button @click="collapsed ? (collapsed = false, docsOpen = true) : (docsOpen = !docsOpen)"
94 class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium {% if '/kb/' in request.path %}text-white{% else %}text-gray-400 hover:bg-gray-800 hover:text-white{% endif %}"
95 :title="collapsed ? 'FossilRepo Docs' : ''">
96 <span class="flex items-center gap-2">
97 <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
@@ -102,17 +102,47 @@
102 <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="docsOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
103 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
104 </svg>
105 </button>
106 <div x-show="docsOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
107 {% for p in sidebar_product_docs %}
108 <a href="{% url 'pages:detail' slug=p.slug %}"
109 class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110 {{ p.name }}
111 </a>
112 {% endfor %}
113 </div>
114 </div>
115 {% endif %}
116
117 <!-- Knowledge Base (org wiki — user-editable) -->
118 {% if perms.pages.view_page %}
119 <div x-data="{ kbOpen: false }">
120 <button @click="collapsed ? (collapsed = false, kbOpen = true) : (kbOpen = !kbOpen)"
121 class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium text-gray-400 hover:bg-gray-800 hover:text-white"
122 :title="collapsed ? 'Knowledge Base' : ''">
123 <span class="flex items-center gap-2">
124 <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
125 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" />
126 </svg>
127 <span x-show="!collapsed" class="truncate">Knowledge Base</span>
128 </span>
129 <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="kbOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
130 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
131 </svg>
132 </button>
133 <div x-show="kbOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
134 {% for p in sidebar_kb_pages %}
135 <a href="{% url 'pages:detail' slug=p.slug %}"
136 class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
137 {{ p.name }}
138 </a>
139 {% endfor %}
140 {% if sidebar_kb_pages|length == 0 %}
141 <p class="px-3 py-1.5 text-xs text-gray-600">No articles yet.</p>
142 {% endif %}
143 {% if perms.pages.add_page %}
144 <a href="{% url 'pages:create' %}"
145 class="block rounded-md px-3 py-1.5 text-sm text-gray-600 hover:text-brand-light">
146 + New
147 </a>
148 {% endif %}
149

Keyboard Shortcuts

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