# This file is part of NIT ( http://www.nitlanguage.org ). # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Web server to server generated files and modify the wiki from a web form module wiki_edit import nitcorn import markdown import md5 intrude import wiki_html # Page for editing markdown source class WikiEditForm super WikiArticle # Part of the title before the name of the page var title_prefix: String # Markdown content, for previews redef var md # Custom HTML code, for forms and links var html: String init do content = (md or else "").md_to_html.to_s + html redef fun dir_href do return "edit" / href redef fun tpl_article do var s = super s.title = title_prefix + title return s end # Fill and return a new `HttpResponse` with this page content fun to_http_response: HttpResponse do var resp = new HttpResponse(200) resp.body = tpl_page.write_to_string return resp end end # Action to serve edit forms, show previews and apply changes class EditAction super Action # Full public URL for the root of this wiki var root_url: String # Path to the wiki config var config_file_path: String # Configuration of the Wiki, loaded once var wiki_config = new WikiConfig(config_file_path) is lazy # Path to the root of the wiki private var wiki_root: String = config_file_path.dirname is lazy # Path to the source files private var source_dir: String = (wiki_root / wiki_config.source_dir).simplify_path + "/" is lazy # List of acceptable password to apply modifications # # If `null`, no password checks are applied and all modifications are accepted. var passwords: nullable Collection[String] # Reload the wiki instance with the latest changes fun wiki: Nitiwiki do var wiki = new Nitiwiki(wiki_config) wiki.parse return wiki end redef fun answer(http_request, turi) do var action = http_request.string_arg("action") var markdown = http_request.post_args.get_or_default("content", "") var file_path = turi.strip_leading_slash file_path = wiki_root / file_path var abs_file_path = file_path.to_absolute_path var abs_source_dir = source_dir.to_absolute_path if not abs_file_path.has_prefix(abs_source_dir) then # Attempting to access a file outside the source directory var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Access denied: ", "", "

Target outside of the source directory

") return entity.to_http_response end if action == "Submit" then var passwords = passwords var password = http_request.post_args.get_or_null("password") if passwords != null and (password == null or not passwords.has(password.md5)) then # Deny modification var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Changes rejected: ", "", "

Password invalid

") return entity.to_http_response end # Save markdown source markdown = markdown.replace('\r', "") markdown.write_to_file file_path # Update HTML files var wiki = wiki wiki.render var link if turi.has_prefix("/pages/") then link = root_url / turi.substring_from(7) else link = root_url / turi link = link.strip_extension(".md") + ".html" # Show confirmation var body = """

Your edits were recorded and the file is updated: {{{link}}}

""" var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Changes saved: ", "", body) return entity.to_http_response else # Show edit form, and preview when requested # When not in a preview, use the local content of the file if action != "Preview" then markdown = file_path.to_path.read_all var form = """
You may edit the file. When you are done, click on "Submit".

""" if passwords != null then form += """ Password:
""" form += """
""" # Show processed markdown only on preview if action != "Preview" then markdown = "" var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Edit source: ", markdown, form) return entity.to_http_response end end end redef class String private fun strip_leading_slash: String do if has_prefix("/") then return substring_from(1) return self end private fun to_absolute_path: String do return (getcwd / self).simplify_path end end var config_file_path = "config.ini" var iface = "localhost:8080" var password_file_path = "passwords" # Load passwords for file var passwords = if password_file_path.file_exists then password_file_path.to_path.read_lines else null var vh = new VirtualHost(iface) # Serve Markdown editing form var action = new EditAction("http://" + iface, config_file_path, passwords) vh.routes.add new Route("/edit", action) # Serve the static (and generated) content var path_to_public_files = config_file_path.dirname / action.wiki_config.out_dir vh.routes.add new Route(null, new FileServer(path_to_public_files)) var factory = new HttpFactory.and_libevent factory.config.virtual_hosts.add vh factory.run