1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Web server to server generated files and modify the wiki from a web form
22 intrude import wiki_html
24 # Page for editing markdown source
28 # Part of the title before the name of the page
29 var title_prefix
: String
31 # Markdown content, for previews
34 # Custom HTML code, for forms and links
37 init do content
= (md
or else "").md_to_html
.to_s
+ html
39 redef fun dir_href
do return "edit" / href
44 s
.title
= title_prefix
+ title
48 # Fill and return a new `HttpResponse` with this page content
49 fun to_http_response
: HttpResponse
51 var resp
= new HttpResponse(200)
52 resp
.body
= tpl_page
.write_to_string
57 # Action to serve edit forms, show previews and apply changes
61 # Full public URL for the root of this wiki
64 # Path to the wiki config
65 var config_file_path
: String
67 # Configuration of the Wiki, loaded once
68 var wiki_config
= new WikiConfig(config_file_path
) is lazy
70 # Path to the root of the wiki
71 private var wiki_root
: String = config_file_path
.dirname
is lazy
73 # Path to the source files
74 private var source_dir
: String = (wiki_root
/ wiki_config
.source_dir
).simplify_path
+ "/" is lazy
76 # List of acceptable password to apply modifications
78 # If `null`, no password checks are applied and all modifications are accepted.
79 var passwords
: nullable Collection[String]
81 # Reload the wiki instance with the latest changes
84 var wiki
= new Nitiwiki(wiki_config
)
89 redef fun answer
(http_request
, turi
)
91 var action
= http_request
.string_arg
("action")
92 var markdown
= http_request
.post_args
.get_or_default
("content", "")
94 var file_path
= turi
.strip_leading_slash
95 file_path
= wiki_root
/ file_path
97 var abs_file_path
= file_path
.to_absolute_path
98 var abs_source_dir
= source_dir
.to_absolute_path
100 if not abs_file_path
.has_prefix
(abs_source_dir
) then
101 # Attempting to access a file outside the source directory
102 var entity
= new WikiEditForm(wiki
, turi
.strip_leading_slash
,
103 "Access denied: ", "", "<p>Target outside of the source directory</p>")
104 return entity
.to_http_response
107 if action
== "Submit" then
108 var passwords
= passwords
109 var password
= http_request
.post_args
.get_or_null
("password")
110 if passwords
!= null and (password
== null or not passwords
.has
(password
.md5
)) then
112 var entity
= new WikiEditForm(wiki
, turi
.strip_leading_slash
,
113 "Changes rejected: ", "", "<p>Password invalid</p>")
114 return entity
.to_http_response
117 # Save markdown source
118 markdown
= markdown
.replace
('\r', "")
119 markdown
.write_to_file file_path
126 if turi
.has_prefix
("/pages/") then
127 link
= root_url
/ turi
.substring_from
(7)
128 else link
= root_url
/ turi
129 link
= link
.strip_extension
(".md") + ".html"
133 <p>Your edits were recorded and the file is updated: <a href="{{{link}}}">{{{link}}}</a></p>
135 var entity
= new WikiEditForm(wiki
, turi
.strip_leading_slash
, "Changes saved: ", "", body
)
136 return entity
.to_http_response
138 # Show edit form, and preview when requested
140 # When not in a preview, use the local content of the file
141 if action
!= "Preview" then markdown
= file_path
.to_path
.read_all
144 <form method="POST" action="/edit{{{turi}}}">
145 You may edit the file. When you are done, click on "Submit".<br/>
146 <textarea name="content" rows="30" cols="80">{{{markdown.html_escape}}}</textarea><br/>
148 if passwords
!= null then form
+= """
149 Password: <input type="password" name="password"><br/>
152 <input type="submit" name="action" value="Preview">
153 <input type="submit" name="action" value="Submit">
157 # Show processed markdown only on preview
158 if action
!= "Preview" then markdown
= ""
160 var entity
= new WikiEditForm(wiki
, turi
.strip_leading_slash
, "Edit source: ", markdown
, form
)
161 return entity
.to_http_response
167 private fun strip_leading_slash
: String
169 if has_prefix
("/") then return substring_from
(1)
173 private fun to_absolute_path
: String
175 return (getcwd
/ self).simplify_path
179 var config_file_path
= "config.ini"
180 var iface
= "localhost:8080"
181 var password_file_path
= "passwords"
183 # Load passwords for file
184 var passwords
= if password_file_path
.file_exists
then
185 password_file_path
.to_path
.read_lines
188 var vh
= new VirtualHost(iface
)
190 # Serve Markdown editing form
191 var action
= new EditAction("http://" + iface
, config_file_path
, passwords
)
192 vh
.routes
.add
new Route("/edit", action
)
194 # Serve the static (and generated) content
195 var path_to_public_files
= config_file_path
.dirname
/ action
.wiki_config
.out_dir
196 vh
.routes
.add
new Route(null, new FileServer(path_to_public_files
))
198 var factory
= new HttpFactory.and_libevent
199 factory
.config
.virtual_hosts
.add vh