From: Alexis Laferrière Date: Fri, 4 Dec 2015 17:45:31 +0000 (-0500) Subject: contrib/nitiwiki: complete the nitcorn server to modify a nitiwiki X-Git-Tag: v0.8~50^2~5 X-Git-Url: http://nitlanguage.org contrib/nitiwiki: complete the nitcorn server to modify a nitiwiki Signed-off-by: Alexis Laferrière --- diff --git a/contrib/nitiwiki/Makefile b/contrib/nitiwiki/Makefile index 57d2cda..f040751 100644 --- a/contrib/nitiwiki/Makefile +++ b/contrib/nitiwiki/Makefile @@ -1,9 +1,12 @@ -all: nitiwiki +all: nitiwiki bin/nitiwiki_server nitiwiki: mkdir -p bin ../../bin/nitc src/nitiwiki.nit -o bin/nitiwiki +bin/nitiwiki_server: $(shell ../../bin/nitls -M src/wiki_edit.nit) + ../../bin/nitc -o $@ src/wiki_edit.nit + check: nitiwiki cd tests; make diff --git a/contrib/nitiwiki/src/wiki_edit.nit b/contrib/nitiwiki/src/wiki_edit.nit index 725f788..22faa25 100644 --- a/contrib/nitiwiki/src/wiki_edit.nit +++ b/contrib/nitiwiki/src/wiki_edit.nit @@ -1,75 +1,191 @@ +# 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 -fun html_document(body: String): String do - return """ - - - - Nitiwiki Edit - - - """ + body + """ - - """ + 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 -class EditMarkdownAction - super Action - - redef fun answer(http_request, turi) - do - var response = new HttpResponse(200) - var file_path = turi.substring(1, turi.length) - var md_file = new FileReader.open(file_path) - response.body = html_document(""" -
- You may edit the file. When you are done, click on "Submit".
-
- - - -
""") - md_file.close - return response - 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 + + if not file_path.simplify_path.has_prefix(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 -class PreviewMarkdown2HTMLAction - super Action - - redef fun answer(http_request, turi) - do - var response = new HttpResponse(200) - var content = http_request.post_args["content"] - var action = http_request.post_args["action"] - var file_path = http_request.post_args["filepath"] - if action == "Preview" then - response.body = html_document(""" -

- """ + content.md_to_html.to_s + """ -

- """) - else if action == "Submit" then - var md_file = new FileWriter.open(file_path) - md_file.write(content) - response.body = html_document(""" -

Updated file!

- """) - md_file.close - end - return response - end +redef class String + private fun strip_leading_slash: String + do + if has_prefix("/") then return substring_from(1) + return self + end end -var vh = new VirtualHost("localhost:8080") +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 -vh.routes.add new Route("/edit", new EditMarkdownAction) -vh.routes.add new Route("/preview", new PreviewMarkdown2HTMLAction) +# Serve Markdown editing form +var action = new EditAction("http://" + iface, config_file_path, passwords) +vh.routes.add new Route("/edit", action) -# Serve everything else with a standard `FileServer` -vh.routes.add new Route(null, new FileServer("/var/www/")) +# 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