@@ -5,9 +5,11 @@ import io.ktor.server.application.Application
5
5
import io.ktor.server.application.call
6
6
import io.ktor.server.html.respondHtml
7
7
import io.ktor.server.html.respondHtmlTemplate
8
+ import io.ktor.server.request.receive
8
9
import io.ktor.server.response.respondRedirect
9
10
import io.ktor.server.response.respondText
10
11
import io.ktor.server.routing.get
12
+ import io.ktor.server.routing.post
11
13
import io.ktor.server.routing.routing
12
14
import kotlinx.html.BODY
13
15
import kotlinx.html.FlowContent
@@ -24,6 +26,7 @@ import kotlinx.html.li
24
26
import kotlinx.html.link
25
27
import kotlinx.html.script
26
28
import kotlinx.html.small
29
+ import kotlinx.html.stream.appendHTML
27
30
import kotlinx.html.style
28
31
import kotlinx.html.table
29
32
import kotlinx.html.td
@@ -67,19 +70,45 @@ class ContentExplorer(private val client: IModelClient, private val repoManager:
67
70
get(" /content/{versionHash}/" ) {
68
71
val versionHash = call.parameters[" versionHash" ]
69
72
if (versionHash.isNullOrEmpty()) {
70
- call.respondText(" version not found" , status = HttpStatusCode .InternalServerError )
73
+ call.respondText(" version not found" , status = HttpStatusCode .BadRequest )
71
74
return @get
72
75
}
76
+
73
77
val tree = CLVersion .loadFromHash(versionHash, client.storeCache).getTree()
74
78
val rootNode = PNodeAdapter (ITree .ROOT_ID , TreePointer (tree))
79
+
75
80
call.respondHtmlTemplate(PageWithMenuBar (" repos/" , " ../.." )) {
76
81
headContent {
77
82
title(" Content Explorer" )
78
83
link(" ../../public/content-explorer.css" , rel = " stylesheet" )
79
84
script(" text/javascript" , src = " ../../public/content-explorer.js" ) {}
80
85
}
81
- bodyContent { contentPageBody(rootNode, versionHash) }
86
+ bodyContent { contentPageBody(rootNode, versionHash, emptySet()) }
87
+ }
88
+ }
89
+ post(" /content/{versionHash}/" ) {
90
+ val versionHash = call.parameters[" versionHash" ]
91
+ if (versionHash.isNullOrEmpty()) {
92
+ call.respondText(" version not found" , status = HttpStatusCode .BadRequest )
93
+ return @post
94
+ }
95
+ val expandedNodes = call.receive<ContentExplorerExpandedNodes >()
96
+
97
+ val tree = CLVersion .loadFromHash(versionHash, client.storeCache).getTree()
98
+ val rootNode = PNodeAdapter (ITree .ROOT_ID , TreePointer (tree))
99
+
100
+ var expandedNodeIds = expandedNodes.expandedNodeIds
101
+ if (expandedNodes.expandAll) {
102
+ expandedNodeIds = expandedNodeIds + collectExpandableChildNodes(rootNode, expandedNodes.expandedNodeIds)
82
103
}
104
+
105
+ call.respondText(
106
+ buildString {
107
+ appendHTML().ul(" treeRoot" ) {
108
+ nodeItem(rootNode, expandedNodeIds)
109
+ }
110
+ },
111
+ )
83
112
}
84
113
get(" /content/{versionHash}/{nodeId}/" ) {
85
114
val id = call.parameters[" nodeId" ]!! .toLong()
@@ -100,7 +129,25 @@ class ContentExplorer(private val client: IModelClient, private val repoManager:
100
129
}
101
130
}
102
131
103
- private fun FlowContent.contentPageBody (rootNode : PNodeAdapter , versionHash : String ) {
132
+ // The method traverses the expanded tree based on the alreadyExpandedNodeIds and
133
+ // collects the expandable (not empty) nodes which are not expanded yet
134
+ private fun collectExpandableChildNodes (under : PNodeAdapter , alreadyExpandedNodeIds : Set <String >): Set <String > {
135
+ if (alreadyExpandedNodeIds.contains(under.nodeId.toString())) {
136
+ val expandableIds = mutableSetOf<String >()
137
+ for (child in under.allChildren) {
138
+ expandableIds.addAll(collectExpandableChildNodes(child as PNodeAdapter , alreadyExpandedNodeIds))
139
+ }
140
+ return expandableIds
141
+ }
142
+
143
+ if (under.allChildren.toList().isNotEmpty()) {
144
+ // Node is collected if it is expandable
145
+ return setOf (under.nodeId.toString())
146
+ }
147
+ return emptySet()
148
+ }
149
+
150
+ private fun FlowContent.contentPageBody (rootNode : PNodeAdapter , versionHash : String , expandedNodeIds : Set <String >) {
104
151
h1 { + " Model Server Content" }
105
152
small {
106
153
style = " color: #888; text-align: center; margin-bottom: 15px;"
@@ -120,18 +167,19 @@ class ContentExplorer(private val client: IModelClient, private val repoManager:
120
167
div {
121
168
id = " treeWrapper"
122
169
ul(" treeRoot" ) {
123
- nodeItem(rootNode)
170
+ nodeItem(rootNode, expandedNodeIds )
124
171
}
125
172
}
126
173
div {
127
174
id = " nodeInspector"
128
175
}
129
176
}
130
177
131
- private fun UL.nodeItem (node : PNodeAdapter ) {
178
+ private fun UL.nodeItem (node : PNodeAdapter , expandedNodeIds : Set < String > ) {
132
179
li(" nodeItem" ) {
180
+ val expanded = expandedNodeIds.contains(node.nodeId.toString())
133
181
if (node.allChildren.toList().isNotEmpty()) {
134
- div(" expander" ) { unsafe { + " ▶" } }
182
+ div(if (expanded) " expander expander-expanded " else " expander" ) { unsafe { + " ▶" } }
135
183
}
136
184
div(" nameField" ) {
137
185
attributes[" data-nodeid" ] = node.nodeId.toString()
@@ -157,10 +205,12 @@ class ContentExplorer(private val client: IModelClient, private val repoManager:
157
205
}
158
206
}
159
207
}
160
- div(" nested" ) {
161
- ul(" nodeTree" ) {
162
- for (child in node.allChildren) {
163
- nodeItem(child as PNodeAdapter )
208
+ div(if (expanded) " nested active" else " nested" ) {
209
+ if (expanded) {
210
+ ul(" nodeTree" ) {
211
+ for (child in node.allChildren) {
212
+ nodeItem(child as PNodeAdapter , expandedNodeIds)
213
+ }
164
214
}
165
215
}
166
216
}
0 commit comments