Permalink
Browse files

[feature] opages: Remove from git-hub

  • Loading branch information...
1 parent b9da4b1 commit 33dfdd67b96dc71b2d75c2f2345338fe72aa2f32 @BourgerieQuentin BourgerieQuentin committed Apr 19, 2011
View
17 OPAges/Makefile
@@ -0,0 +1,17 @@
+########################################
+# USER VARIABLES
+EXE = main.exe
+PACKNAME = main.opx
+SRC = src/main.opa src/page.opa src/users.opa
+PCK = opace.opx
+PCKDIR = ./
+PLUGIN =
+PLUGINDIR =
+
+#Compiler variables
+OPACOMPILER = opa.exe
+FLAG =
+OPADOC = opadoc-gen.exe
+PORT = 9000
+
+include Makefile.common
View
70 OPAges/Makefile.common
@@ -0,0 +1,70 @@
+########################################
+# MAKEFILE VARIABLES
+OPA = $(OPACOMPILER) $(FLAG) $(OPAOPT)
+PWD ?= $(shell pwd)
+BUILDDIR ?= $(PWD)/_build/
+export BUILDDIR
+BUILDDOCDIR ?=$(PWD)/doc/
+export BUILDDOCDIR
+PACKDOC = $(BUILDDOCDIR)/$(PACKNAME:%.opx=%.doc)/
+BUILDDOC = $(PACKDOC)
+OPACOMPILER ?= opa.exe
+OPADOC ?= opadoc-gen.exe
+DEPENDS = $(SRC) $(PCK:%=$(BUILDDIR)/%) $(PLUGIN:%=$(BUILDDIR)/%)
+
+########################################
+# MAIN RULE
+exe : $(EXE)
+pack : $(PACKNAME)
+doc : $(PACKDOC) doc.sub
+
+########################################
+# MAIN PACKAGE BUILDING
+$(PACKNAME) : $(BUILDDIR)/$(PACKNAME)
+
+$(BUILDDIR)/$(PACKNAME) : $(DEPENDS)
+ @echo "### Building package $(PACKNAME)"
+ @$(OPA) --autocompile $? --build-dir $(BUILDDIR)
+ @mv $(PACKNAME) $(BUILDDIR)/
+
+########################################
+# SUBS PACKAGE/PLUGIN BUILDING
+$(BUILDDIR)/%.opx :
+ make $(@:$(BUILDDIR)/%.opx=-C $(PCKDIR)%) pack
+
+$(BUILDDIR)/%.opp :
+ make $(@:$(BUILDDIR)/%.opp=-C $(PLUGINDIR)/%)
+
+########################################
+# EXECUTABLE BUILDING
+$(EXE) : $(PCK:%.opx=$(BUILDDIR)/%.opx) $(SRC)
+ @echo "### Building executable $(EXE)"
+ $(OPA) $(SRC) -o $@ -I $(BUILDDIR) --build-dir $(BUILDDIR)/$(EXE)
+
+$(EXE:%.exe=%.run) : $(EXE)
+ ./$(EXE) -p $(PORT)
+
+########################################
+# DOCUMENTATION BUILDING - Dirty...
+$(PACKDOC) : $(DEPENDS)
+ @echo "### Building documentation $(PACKNAME:%.opx=%.doc)"
+ @mkdir -p $(BUILDDOC)
+ @$(OPACOMPILER) $(SRC) $(PLUGIN:%=$(BUILDDIR)/%) --generate-interface -I $(BUILDDIR)
+ @mv $(SRC:%=%.api) $(BUILDDOC)
+ @mv $(SRC:%=%.api-txt) $(BUILDDOC)
+ @ ls $(BUILDDOC)
+ @$(OPADOC) $(BUILDDOC)
+ @mkdir -p $(PACKDOC)
+ @mv doc/*.html doc/*.css $(PACKDOC)
+
+doc.sub :
+ @if [ -n "$(PCK)" ]; then make $(PCK:%.opx=-C $(PCKDIR)/%) doc; fi
+
+########################################
+# CLEANING
+clean :
+ make $(PCK:%.opx=-C $(PCKDIR)/%) $(PLUGIN:%.opp=-C $(PLUGINDIR)/%) clean
+ @if [ -n "$(PACKNAME)" ]; then echo "### Cleaning $(PACKNAME)"; rm -rf $(BUILDDIR)/$(PACKNAME)*; fi
+ @if [ -n "$(EXE)" ]; then echo "### Cleaning $(EXE)"; rm -rf $(BUILDDIR)/$(EXE) $(EXE); fi
+ @if [ -n "$(PACKDOC)" ]; then echo "### Cleaning $(PACKNAME:%.opx=%.doc)"; rm -rf $(PACKDOC); fi
+
View
0 OPAges/README
No changes.
View
17 OPAges/opace/Makefile
@@ -0,0 +1,17 @@
+########################################
+# USER VARIABLES
+EXE =
+PACKNAME = opace.opx
+SRC = ace.opa
+PCK =
+PCKDIR =
+PLUGIN = ace.opp
+PLUGINDIR = plugin
+
+#Compiler variables
+OPACOMPILER = opa.exe
+FLAG =
+
+PORT = 8080
+
+include Makefile.common
View
1 OPAges/opace/Makefile.common
View
77 OPAges/opace/ace.opa
@@ -0,0 +1,77 @@
+package opace
+
+/**
+ * {1 About this module}
+ * Ace editor is a standalone code editor written in JavaScript. This
+ * module provides a binding to this client code editor.
+ *
+ * {1 Where should I start?}
+ * You should start by define an editable zone like that :
+ * [<div id="myzone">This is an editable zone</div>]
+ * Then you can use [Ace.edit("myzone")] this function replace the dom
+ * node with id "myzone" by an Ace editor.
+ * Finally you can access to Ace properties ('Set & Get Ace editor
+ * properties') and do some action on your Ace editor ('Action on Ace
+ * editor').
+ */
+
+/**
+ * {1 Types defined in this module}.
+ */
+/**
+ * Type of an Ace editor.
+ */
+type Ace.t = external
+
+Ace = {{
+
+ /**
+ * {1 Initialization}
+ */
+ /**
+ * [edit(id)] Create an ACE editor which edit element with the given
+ * [id].
+ */
+ edit(id) = (%%plugin_ace.edit%%)(id)
+
+ /**
+ * {1 Set & Get Ace editor properties}
+ */
+ /**
+ * [set_mode(ace, mode)] Load [mode] for the given [ace]
+ * editor. List of mode are defined on ace plugin. If the mode
+ * doesn't exists returns [false].
+ */
+ set_mode(ace:Ace.t, mode:string) =
+ (%%plugin_ace.set_mode%%)(ace, mode)
+
+ /**
+ * Get content of ACE editor.
+ */
+ get_content = %%plugin_ace.get_content%%
+
+ /**
+ * Set content of ACE editor.
+ */
+ set_content = %%plugin_ace.set_content%%
+
+ /**
+ * Add an event handler.
+ */
+ add_event_listener(ace : Ace.t, kind : Dom.event.kind, action : -> void) =
+ (%%plugin_ace.add_event_listener%%)(ace, Dom.Event.get_name(kind), action)
+
+ /**
+ * {1 Action on Ace editor}
+ */
+ /**
+ * Undo the last action.
+ */
+ undo = %%plugin_ace.undo%%
+
+ /**
+ * Redo the last action.
+ */
+ redo = %%plugin_ace.redo%%
+
+}}
View
26 OPAges/opace/plugin/ace/Makefile
@@ -0,0 +1,26 @@
+########################################
+# USER VARIABLES
+MKLIB = bslregister
+PLUGNAME = ace.opp
+SRC = plugin_ACE.js external_ACE.js
+CONF = myconf.jsconf
+
+########################################
+# MAKEFILE VARIABLES
+ifeq ($(origin BUILDDIR), undefined)
+ PWD := $(shell pwd)
+ BUILDDIR := $(PWD)/_build/
+ export BUILDDIR
+endif
+
+########################################
+# PLUGIN BUILDING
+$(PLUGNAME) : $(SRC)
+ @echo "### Building plugin $(PLUGNAME)"
+ @$(MKLIB) --lib $(@:%.opp=%) $? $(CONF) --build-dir $(BUILDDIR)
+
+########################################
+# CLEANING
+clean :
+ @echo "### Cleaning plugin $(PLUGNAME)"
+ @rm -rf $(BUILDDIR)/$(PLUGNAME)
View
28 OPAges/opace/plugin/ace/external_ACE.js
28 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
2 OPAges/opace/plugin/ace/myconf.jsconf
@@ -0,0 +1,2 @@
+[external_ACE.js.*]
+verbatim: true
View
56 OPAges/opace/plugin/ace/plugin_ACE.js
@@ -0,0 +1,56 @@
+/**
+ * For documentation please see [ace.opa]
+ */
+##extern-type [opaname] Ace.t = object
+
+##register edit : string -> Ace.t
+##args(id)
+{
+ var r = ace.edit(id);
+ return r;
+}
+
+##register set_mode : Ace.t, string -> bool
+##args(editor, mode)
+{
+ var M = require("ace/mode/"+mode);
+ if(M == undefined) return false;
+ M = M.Mode;
+ if(M == undefined) return false;
+ editor.getSession().setMode(new M());
+ return true;
+}
+
+##register get_content : Ace.t -> string
+##args(editor)
+{
+ return editor.getSession().getValue();
+}
+
+##register set_content : Ace.t, string -> void
+##args(editor, content)
+{
+ editor.getSession().setValue(content);
+ return js_void;
+}
+
+##register add_event_listener : Ace.t, string, ( -> void) -> void
+##args(editor, kind, handler)
+{
+ editor.getSession().addEventListener(kind, function(){handler()});
+ return js_void;
+}
+
+##register redo : Ace.t -> void
+##args(editor)
+{
+ editor.getSession().getUndoManager().redo();
+ return js_void;
+}
+
+##register undo : Ace.t -> void
+##args(editor)
+{
+ editor.getSession().getUndoManager().undo();
+ return js_void;
+}
View
133 OPAges/src/main.opa
@@ -0,0 +1,133 @@
+import components.applicationframe
+
+/**
+ * {1 Main}
+ * This main file define a command line parser, the services to run and
+ * provides css.
+
+ * - command line parser accepts a uniq option without arguments
+ * "--init-admin" used for initialize "admin" password. Then password
+ * is printed on stderr.
+ *
+ * - The first service on HTTP just serves page in database.
+ *
+
+ * - The second service on HTTPS is same of HTTP service but provide
+ * additionaly an access to the administration pages.
+ */
+
+/** Init admin is does not exists or if needed on command line. */
+admin_init =
+ cmdline:CommandLine.family(bool) = {
+ init = false
+ parsers = [{ CommandLine.default_parser with
+ names = ["--init-admin"]
+ description = "Reinitialize administrator password and print it."
+ on_encounter(_) = {no_params = true}
+ }]
+ }
+ if not(User.has_admin()) || CommandLine.filter("OPAges", cmdline) then
+ User.init_admin()
+
+/** Non secure server - just serve page in database */
+server =
+ dispatcher = parser
+ | url=(.*) ->
+ match Page.resource(Text.to_string(url))
+ | ~{embedded} -> Resource.full_page("Opalang",embedded.body, embedded.header,
+ {success}, [])
+ | ~{resource} -> resource
+ { Server.simple_server(dispatcher) with server_name = "http" }
+
+/** Secure server - page in database + administration page access */
+server =
+ build_html(url, embedded) =
+ /* Application frame configuration. */
+ app_frame_config = { CApplicationFrame.default_config with
+ authenticate(name, pass) =
+ if User.is_admin(name, pass) then none
+ else some("Invalid password")
+ private_page(_title, user) = Page.admin(url, user)
+ public_page(_title) = embedded.body
+ public_head(_title) = embedded.header
+ }
+ /* Application frame initialization. */
+ init = CApplicationFrame.init(app_frame_config)
+ CApplicationFrame.make(init, app_frame_config, "OPAges")
+ /* OPAges url dispatcher it's just a coating of Page.resource */
+ url_dispatcher = parser
+ | url=(.*) ->
+ url = Text.to_string(url)
+ match Page.resource(url) with
+ | ~{embedded} ->
+ /* Build application frame resource that embedded the returned
+ embedded html.*/
+ build_html(url, embedded)
+ | ~{resource} ->
+ /* Page return directly a resource. */
+ Resource.secure(resource)
+ /* Secured service */
+ secure = Server.secure(Server.ssl_default_params, url_dispatcher)
+ { secure with
+ /* Record extension will give us a record, i.e. a closed sum so, open it
+ to make it compatible with type [Server.encryption]. */
+ encryption = {Server.ssl_default_params with
+ certificate="./main.crt"
+ private_key="./main.key"} <: Server.encryption
+ server_name = "https"
+ }
+
+/** CSS used for administration pages for moment all services share
+ css. */
+css = css
+ #admin_files {
+ position:absolute;
+ top:10%;
+ left:0%;
+ height:80%;
+ width:19%;
+ overflow: scroll;
+ }
+ #admin_editor_container {
+ position:absolute;
+ top:10%;
+ left:20%;
+ height:80%;
+ width:80%;
+ float:left;
+ }
+ .admin_editor {
+ position:absolute;
+ height:100%;
+ width:100%;
+ float:left;
+ }
+ #admin_buttons {
+ position:absolute;
+ top:90%;
+ width:100%;
+ height:10%;
+ }
+ .ace_editor {
+ position: relative;
+ width:99%;
+ float:left;
+ }
+ #admin_editor_container .off {
+ display: none;
+ }
+ #admin_files_table .on {
+ font-weight:bold;
+ }
+ .admin_editor_html_header {
+ height:10%;
+ }
+ .admin_editor_html_body {
+ height:80%;
+ }
+ .admin_editor_css {
+ height:90%;
+ }
+ .admin_editor_source {
+ height:90%;
+ }
View
418 OPAges/src/page.opa
@@ -0,0 +1,418 @@
+import opace
+
+/**
+ * {1 About this module}
+ * This module provides a function for build an xhtml administration
+ * interface, with this interface OPAges administrator can add, edit,
+ * remove... OPAges resources. [Page] module provides also a resources
+ * function that be able to returns resources added by administrator
+ * or it build default error resource.
+ */
+
+/**
+ * {1 Types defined in this module}
+ */
+/**
+ * Html page that should be embedded by the applications.
+ */
+/* @private */type Page.embedded = {
+ header : string
+ body : string
+}
+
+/**
+ * Database embeddable resource
+ */
+/* @private */type Page.resource =
+ {image : image} /
+ {css : string} /
+ {source : string; mime : string}
+
+/**
+ * Define what is a page.
+ * - embedded means that the page should be embedded in the applications.
+ * - resource is a subset of [resource] that be able to store in database.
+ */
+/* @private */type Page.t =
+ {embedded : Page.embedded} /
+ {resource : Page.resource}
+
+/**
+ * Define different class of file.
+ */
+/* @private */type Page.class =
+ {embedded} / {image} / {octet} / {source}
+
+/**
+ * A record used for give some primitive to the admin GUI unless
+ * publish critical primitives.
+ */
+/* @private */type Page.access = {
+ get_class : (string -> Page.class)
+ get : (string -> Page.t)
+ save : (string, Page.t -> void)
+ remove : (string -> void)
+}
+
+/*
+ * A database stringmap that contains binding beetween url and html
+ * page.
+ */
+db /pages/public : stringmap(Page.t)
+
+/*
+ * A database path which contains the 404 page. The default value is
+ * [Pages.default_404]
+ */
+db /pages/not_found : Page.embedded = {
+ header = "<title>OPAges Not found</title>"
+ body = "<h1>404 - Not found</h1>"
+}
+
+/* Some default needed default values. */
+db /pages/public[_] = { embedded = /pages/not_found }
+db /pages/public[_]/resource/image = {png=binary_of_string("")}
+db /pages/public[_]/resource = {css = ""}
+
+Page = {{
+
+ /**
+ * {2 Privates utils}
+ */
+ /**
+ * Transform an "string" embedded to "xhtml" embedded
+ */
+ @private embedded_to_xembedded(embedded) = {embedded = {
+ header = Xhtml.of_string_unsafe(embedded.header)
+ body = Xhtml.of_string_unsafe(embedded.body)
+ }
+ }
+
+ /**
+ * Get mode corresponding to the [file].
+ */
+ @private get_suffix(file) =
+ match file with
+ | "/" -> some("html")
+ | _ -> x = Parser.try_parse(Rule.has_suffix(Rule.alphanum_string), file)
+ Option.map((x -> x.f2), x)
+
+
+ /**
+ * Return default value corresponding to a file mode.
+ */
+ @private default =
+ | {some = "html"} ->
+ { embedded = { header = "<!-- Enter your headers here -->"
+ body="<!-- Enter your body here -->" } }
+ | {some = "css"} ->
+ { resource = { css = "/* Enter your css here */"} }
+ | _ ->
+ { resource = { source = "Default ressource" mime="text/plain" } }
+
+
+ /**
+ * Save a public [page] at [url].
+ */
+ @private save(url, page) = /pages/public[url] <- page
+
+ /**
+ * Delete page at [url].
+ */
+ @private remove(url) = /pages/public <- StringMap.remove(url, /pages/public)
+
+ /**
+ * Get a page at [url].
+ */
+ @private get(url) = match ?/pages/public[url]
+ | ~{some} -> some
+ | {none} -> default(get_suffix(url))
+
+ @private get_class(url) = match ?/pages/public[url]
+ | {some = {embedded = _}} -> {embedded}
+ | {some = {resource = {image = _}}} -> {image}
+ | {some = {resource = {mime = "application/octet-stream" ...}}} -> {octet}
+ | _ -> {source}
+
+ /**
+ * {2 Graphical user interface (admin)}
+ */
+ /**
+ * This modules provides a build function for administration
+ * interface.
+ */
+ AdminGui = {{
+
+ /** Just a simple string transformation for xhtml insert. */
+ @private file_for_xhtml =
+ | "/" -> "*index*"
+ | x -> x
+
+ /** A translation that allows to use file on dom identifiers. */
+ @private file_id = md5
+
+ /** Create a file line for table insert. */
+ @private file_line(access:Page.access, opened, file) =
+ class = if opened then "on" else "off"
+ <tr class="{class}" id="admin_files_table_{file_id(file)}">
+ <td onclick={Action.open_file(access, file)}>
+ {file_for_xhtml(file)}
+ </td>
+ </tr>
+
+ /** Insert if necessary a file line into files table. */
+ @private file_line_insert(access, opened, file) =
+ line = Dom.select_id("admin_files_table_{file_id(file)}")
+ if Dom.is_empty(line) then
+ /* Insert a line on files table. */
+ table = Dom.select_id("admin_files_table")
+ _ = Dom.put_at_end(table, Dom.of_xhtml(file_line(access, true, file)))
+ void
+ else Dom.add_class(line, if opened then "on" else "off")
+
+ /**
+ * Build a buffer and insert it on dom node with identifier
+ * [id]. The buffer can be specialized according to file
+ * extension.
+ *
+ * Note : Client side function because need dom access.
+ */
+ @client build_buffer(access, file, id) =
+ make_buttons(get_page) =
+ buttons =
+ remove = <button type="button"
+ onclick={Action.remove_file(access, file)}>Remove</button>
+ match get_page with
+ | {some = get_page} ->
+ <><button type="button"
+ onclick={_ -> access.save(file, get_page())}>Save</button>
+ {remove}</>
+ | {none} -> remove
+ _ = Dom.put_at_end(Dom.select_id(id), Dom.of_xhtml(buttons))
+ void
+ match access.get_class(file) with
+ | {image} ->
+ dom = Dom.of_xhtml(<img src="{file}"/>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ make_buttons(none)
+ | {octet} ->
+ dom = Dom.of_xhtml(<span>Uneditable binary file</span>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ make_buttons(none)
+ | _ ->
+ match access.get(file) with
+ | {embedded = ~{body header}} ->
+ /* Html embedded editor is divided in two ace editor, one for
+ headers another for body */
+ head_id = "{id}_headers" body_id = "{id}_body"
+ dom = Dom.of_xhtml(<div id="{head_id}" class="admin_editor_html_header"/>
+ <div id="{body_id}" class="admin_editor_html_body"/>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ /* Head ace */
+ ace_head = Ace.edit(head_id)
+ do Ace.set_content(ace_head, header)
+ _ = Ace.set_mode(ace_head, "html")
+ /* Body ace */
+ ace_body = Ace.edit(body_id)
+ do Ace.set_content(ace_body, body)
+ _ = Ace.set_mode(ace_body, "html")
+ /* Buffer buttons */
+ make_buttons(some( -> {embedded = {header=Ace.get_content(ace_head)
+ body=Ace.get_content(ace_body)}}))
+
+
+ | {resource = ~{css}} ->
+ /* Css editor is just a simple ace editor */
+ id_css = "{id}_css"
+ dom = Dom.of_xhtml(<div id="{id_css}" class="admin_editor_css"/>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ ace_css = Ace.edit(id_css)
+ _ = Ace.set_content(ace_css, css)
+ _ = Ace.set_mode(ace_css, "css")
+ make_buttons(some( -> {resource = {css=Ace.get_content(ace_css)}}))
+
+ | {resource = ~{source mime}} ->
+ /* A raw resource editor is an ace editor + an input for mime */
+ id_src = "{id}_src"
+ dom = Dom.of_xhtml(<div id="{id_src}" class="admin_editor_source"/>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ ace_src = Ace.edit(id_src)
+ _ = Ace.set_content(ace_src, source)
+ /* Buffer buttons */
+ get_mime() = Dom.get_value(Dom.select_id("admin_editor_mime"))
+ do make_buttons(some( -> {resource = {source=Ace.get_content(ace_src)
+ mime=get_mime()}}))
+ dom = Dom.of_xhtml(<input type="text" value="{mime}" id="admin_editor_mime"/>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ void
+ | _ ->
+ dom = Dom.of_xhtml(<span>Unexpected kind of file</span>)
+ _ = Dom.put_at_end(Dom.select_id(id), dom)
+ void
+
+
+ /**
+ * Create a buffer for the given [file].
+ */
+ @private file_buffer(access:Page.access, opened, file) =
+ id = "admin_buffer_{file_id(file)}"
+ class = if opened then "on" else "off"
+ <div class="{class} admin_editor" id="{id}"
+ onready={_ -> _ = build_buffer(access, file, id) void}>
+ </div>
+
+ /**
+ * Create and insert a buffer for given [file] on dom.
+ */
+ @client insert_buffer(access:Page.access, opened, file) =
+ edito = Dom.select_id("admin_editor_container")
+ buffer = Dom.of_xhtml(file_buffer(access, opened, file))
+ _ = Dom.put_at_end(edito, buffer)
+ void
+
+ /**
+ * This module provides fun action for admin interface.
+ */
+ @client @private Action = {{
+
+ /**
+ * Switch all "on" classes to "off" classes
+ */
+ @private all_off() =
+ dom_on = Dom.select_class("on")
+ do Dom.add_class(dom_on, "off")
+ do Dom.remove_class(dom_on, "on")
+ void
+
+ /**
+ * Open file
+ */
+ open_file(access:Page.access, file) : FunAction.t = _event ->
+ do all_off()
+ do file_line_insert(access, true, file)
+ buf = Dom.select_id("admin_buffer_{file_id(file)}")
+ if Dom.is_empty(buf) then
+ insert_buffer(access, true, file)
+ else
+ do Dom.add_class(buf, "on")
+ do Dom.remove_class(buf, "off")
+ void
+
+ /**
+ * Create a new file.
+ */
+ new_file(access:Page.access) : FunAction.t = event ->
+ /* Select file to open */
+ file = Dom.get_value(Dom.select_id("admin_new_file"))
+ do file_line_insert(access, true, file)
+ open_file(access, file)(event)
+
+ remove_file(access:Page.access, file) : FunAction.t = _event ->
+ do access.remove(file)
+ fl = Dom.select_id("admin_files_table_{file_id(file)}")
+ buf = Dom.select_id("admin_buffer_{file_id(file)}")
+ do Dom.remove(fl)
+ do Dom.remove(buf)
+ void
+
+ save_file(access:Page.access, get, file) : FunAction.t = _event ->
+ // TODO - Something
+ access.save(file, get())
+
+
+ }}
+
+ /**
+ * Build the xhtml administration interface.
+ */
+ build(access:Page.access, url) =
+ /* Add default page if url does not already exists on page map */
+ page_map =
+ map = /pages/public
+ if StringMap.mem(url, map) then map
+ else StringMap.add(url, default(get_suffix(url)), map)
+
+ /* Perform an uploaded file */
+ perform_file(file) =
+ /* Save page resource for the uploaded file using file
+ suffix. */
+ save(filename, content) =
+ content = match get_suffix(filename) with
+ | {some = "html"} | {some = "xhtml"} ->
+ {embedded = {header="" body=content}}
+ | {some = "png"} -> {resource= {image={png=content}}}
+ | {some = "jpg"} -> {resource= {image={jpg=content}}}
+ | {some = "ico"} -> {resource= {image={ico=content}}}
+ | {some = "gif"} -> {resource= {image={gif=content}}}
+ | _ -> {resource = {source = content mime="application/octet-stream"}}
+ save(filename, content)
+ /* Waiting full file content */
+ rec aux() =
+ match file.content() with
+ | {partial = _} ->
+ Scheduler.sleep(1000, aux)
+ | ~{content} ->
+ filename = "/{file.filename}"
+ do save(filename, content)
+ do Log.notice("Uploader", "Uploading of {file.filename} was done")
+ do Action.open_file(access, filename)(Dom.Event.default_event)
+ void
+ aux()
+ /* Build admin xhtml body page */
+ <div id="admin_files">
+ <table id="admin_files_table">
+ <caption> Published files </caption>
+ {
+ /* Insert xhtml list of <tr><td> */
+ StringMap.rev_fold(
+ (file, _, lxhtml -> file_line(access, file == url, file) +> lxhtml),
+ page_map, []
+ )
+ }
+ </table>
+ </div>
+ <div id="admin_editor_container">
+ {file_buffer(access, true, url)}
+ </div>
+ <div id="admin_buttons">
+ <input id="admin_new_file" type="text"/>
+ <button type="button" onclick={Action.new_file(access)}>New file</button>
+ {Upload.make({Upload.default_config with ~perform_file})}
+ </div>
+
+ }}
+
+ /**
+ * Provides an interface which allows to create and modify pages.
+ * This iterface is composed :
+ * 1 - A file viewer
+ * 2 - A code editor
+ * 3 - An action zone
+ * This admin interface open by default an editor for [url] file.
+ */
+ admin(url, _user) =
+ AdminGui.build(~{get_class get save remove}, url)
+
+ /**
+ * Return a record corresponding to the resource at [url].
+ * - [{embedded = ...}] Means that page should be embedded into
+ * applications, that provides a header and body field.
+ * - [{resource = ...}] Is a raw resource that be able to return
+ * directly to the client.
+ */
+ resource(url) =
+ match ?/pages/public[url] with
+ | {some = ~{embedded}} -> embedded_to_xembedded(embedded)
+ | {some = {resource = ~{image}}} -> {resource = Resource.image(image)}
+ | {some = {resource = ~{css}}} -> {resource = Resource.build_css(css)}
+ | {some = {resource = ~{source mime}}} -> {resource = Resource.source(source, mime)}
+ | {none} -> match get_suffix(url) with
+ | {some="html"} -> embedded_to_xembedded(/pages/not_found)
+ | _ -> {resource = Resource.full_page("",
+ Xhtml.of_string_unsafe(/pages/not_found/body),
+ Xhtml.of_string_unsafe(/pages/not_found/header),
+ {wrong_address}, []
+ )}
+
+
+}}
View
37 OPAges/src/users.opa
@@ -0,0 +1,37 @@
+/**
+ * {1 About this module}
+ * Manage OPAges users. For now OPAges have only one administrator
+ * user, that name is "admin".
+ */
+
+/**
+ * A database stringmap that contains binding beetween administrators
+ * name and password.
+ */
+db /users/admin/pass : stringmap(string)
+
+User = {{
+ /**
+ * (Re)Initialize admin password and print it on server output.
+ */
+ init_admin() =
+ pwd = Random.string(30)
+ do /users/admin/pass["admin"] <- pwd
+ do jlog("Admin password is \"{pwd}\"")
+ void
+
+ /**
+ * [is_admin(name, password)] Returns true if [name] is an
+ * administrator with a valid [password]
+ */
+ is_admin(name, password) =
+ match ?/users/admin/pass[name] with
+ | {none} -> false
+ | ~{some} -> some == password
+
+ /**
+ * Returns [true] if an admin already exists.
+ */
+ has_admin() = Option.is_some(?/users/admin/pass["admin"])
+
+}}

0 comments on commit 33dfdd6

Please sign in to comment.