summary refs log tree commit diff homepage
path: root/src/xhtml.cr
diff options
context:
space:
mode:
Diffstat (limited to 'src/xhtml.cr')
-rw-r--r--src/xhtml.cr190
1 files changed, 190 insertions, 0 deletions
diff --git a/src/xhtml.cr b/src/xhtml.cr
new file mode 100644
index 0000000..ea4f87e
--- /dev/null
+++ b/src/xhtml.cr
@@ -0,0 +1,190 @@
+# XHTML generation
+# Copyright (C) 2023  Nguyễn Gia Phong
+#
+# This file if part of hybring.
+#
+# Hybring is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Hybring is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with hybring.  If not, see <https://www.gnu.org/licenses/>.
+
+require "uri"
+require "xml"
+
+require "./sqlite"
+
+CSS = "
+      html {
+          margin: auto;
+          max-width: 72ch;
+      }
+      body { margin-bottom: 2rem }
+      h1, h2, h3, h4, h5, h6 { margin: 1ex 0 }
+      form {
+          display: grid;
+          grid-template-columns: max-content 1fr 0;
+      }
+      form label { margin-right: 1ch }
+      form input { margin-bottom: 1ex }
+      .error { color: red }
+    "
+
+class Page
+  def initialize(static_dir : String, static_url : String, api_url : String,
+                 db : Database)
+    Dir.mkdir_p static_dir
+    @static_file = Path[static_dir] / "index.xhtml"
+    @static_url = static_url
+    @static_host = URI.parse(static_url).host
+    @api_url = api_url
+    @db = db
+  end
+
+  def criteria(xml)
+    xml.element "h2" do xml.text "criteria" end
+    xml.element "p" do
+      xml.text "We accept pretty much any site"
+      xml.text " served under an OpenNIC domain"
+      xml.text " meeting the following requirements."
+    end
+    xml.element "ul" do
+      xml.element "li" do xml.text "Family-friendly content" end
+      xml.element "li" do xml.text "No violence incitation or doxing" end
+      xml.element "li" do xml.text "No user tracking" end
+      xml.element "li" do
+        xml.text "ICANN counterpart accessible via HTTPS"
+      end
+      xml.element "li" do
+        xml.text "Both linking to neighboring members"
+      end
+    end
+    xml.element "p" do
+      xml.text "Note that the two sites needn't be the same, as long as"
+      xml.text " they represent the same entity, e.g. person or organization."
+      xml.text "  The clear net site is for serving visitors"
+      xml.text " without OpenNIC access or preferring secure connections."
+    end
+  end
+
+  def input(xml, name, label, hint, error, value)
+    if error
+      xml.element "span"
+      xml.element "span", class: "error" do xml.text error end
+      xml.element "br"
+    end
+    xml.element "label", for: name do xml.text label end
+    xml.element "input", name: name, placeholder: hint, value: value,
+                required: "required"
+    xml.element "br"
+  end
+
+  def form(xml, errors = {} of String => String,
+           params = {} of String => String)
+    xml.element "p" do xml.text "Then, please fill out the form below." end
+    nick = params.fetch("nick", nil)
+    xml.element "form", action: @api_url, method: "POST" do
+      input xml, "nick", "Nickname", "digits or lowercase letters",
+            errors.fetch("nick", nil), nick || ""
+      input xml, "opennic", "OpenNIC URL", "e.g. http://example.null",
+            errors.fetch("opennic", nil), params.fetch("opennic", "")
+      input xml, "icann", "ICANN URL", "e.g. https://example.net",
+            errors.fetch("icann", nil), params.fetch("icann", "")
+      xml.element "span" do
+        xml.element "input", type: "hidden", name: "host", value: @static_host
+      end
+      xml.element "input", type: "submit", value: "Let me in!"
+      xml.element "br"
+    end
+
+    xml.element "p" do
+      xml.text "Your application is pending for approval."
+      xml.text "  Please subscribe to this "
+      xml.element "a", href: "#{@static_url}/#{nick}.atom" do
+          xml.text "web feed"
+      end
+      xml.text " for further instructions."
+    end if nick && errors.empty?
+  end
+
+  def member(xml, nick, opennic, icann)
+    xml.element "h3" do xml.text nick end
+    xml.text "OpenNIC: "
+    xml.element "a", href: opennic do xml.text opennic end
+    xml.element "br"
+    xml.text "ICANN: "
+    xml.element "a", href: icann do xml.text icann end
+    xml.element "br"
+  end
+
+  def build(errors = {} of String => String,
+            params = {} of String => String) : String
+    XML.build encoding: "UTF-8", indent: "  " do |xml|
+      xml.element "html", xmlns: "http://www.w3.org/1999/xhtml", lang: "en" do
+        xml.element "head" do
+          xml.element "meta", name: "viewport",
+                      content: "width=device-width,initial-scale=1.0"
+          xml.element "link", rel: "icon", href: "data:,"
+          xml.element "style" do xml.text CSS end
+          xml.element "title" do xml.text "le cercle libre" end
+        end
+        xml.element "body" do
+          xml.element "h1" do xml.text "le cercle libre" end
+          xml.element "p" do
+            xml.text "The Free Circle is a web ring for sites"
+            xml.text " with an OpenNIC domain."
+          end
+          criteria xml
+
+          xml.element "h2" do xml.text "members" end
+          @db.members.each_value do |nick, opennic, icann|
+            member xml, nick, opennic, icann
+          end
+
+          xml.element "h2" do xml.text "joining" end
+          xml.element "p" do
+            xml.text "First, add "
+            xml.element "a",
+                        href: "https://html.spec.whatwg.org/#the-a-element" do
+                xml.text "anchors"
+            end
+            xml.text " refering to the following neighbors to your sites"
+            xml.text " on both OpenNIC and ICANN domains."
+            xml.text "  Your neighbors will change as people come and go,"
+            xml.text " so you will be given an Atom feed with instructions"
+            xml.text " to keep the hyperlinks up to date."
+          end
+          xml.element "ul" do
+            xml.element "li" do
+              xml.text "Left: "
+              _, left, _ = @db.members.last_value
+              xml.element "a", href: left do xml.text left end
+            end
+            xml.element "li" do
+              xml.text "Right: "
+              _, right, _ = @db.members.first_value
+              xml.element "a", href: right do xml.text right end
+            end
+          end
+          form xml, errors, params
+
+          xml.element "h2" do xml.text "applicants" end
+          @db.applicants.each_value do |nick, opennic, icann|
+            member xml, nick, opennic, icann
+          end
+        end
+      end
+    end
+  end
+
+  def write
+    File.write @static_file, self.build
+  end
+end