From 8a44f9176e55519198c26c67f86f18de1a6e3228 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sat, 12 Nov 2022 22:04:01 +1300 Subject: [PATCH 01/41] Trying out HTTP 103 Early Hints --- src/dispatcher-tree.rkt | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/dispatcher-tree.rkt b/src/dispatcher-tree.rkt index 9e072bc..06709a6 100644 --- a/src/dispatcher-tree.rkt +++ b/src/dispatcher-tree.rkt @@ -8,7 +8,9 @@ (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer) (prefix-in lift: web-server/dispatchers/dispatch-lift) (prefix-in filter: web-server/dispatchers/dispatch-filter) + web-server/private/connection-manager "config.rkt" + "static-data.rkt" "url-utils.rkt") (provide @@ -31,6 +33,24 @@ ; don't forget that I'm returning *code* - return a call to the function (datum->syntax stx `(make-dispatcher-tree ,ds))) +(define (write-bytes* out . bs) + (for ([b bs]) (write-bytes b out))) + +;; HTTP 103 Early Hints https://datatracker.ietf.org/doc/html/rfc8297 +;; a dispatcher services a request or calls next-dispatcher +(define ((early-hints:make original-dispatcher) conn req) + ;; normally this would call output-response/method to write output based on the method + ;; e.g. if it's HEAD then only output the headers + (define o (connection-o-port conn)) + (write-bytes* o + #"HTTP/1.1 103 Early Hints\r\n" + #"Link: " (string->bytes/latin-1 link-header) #"\r\n" + #"\r\n") + (flush-output o) + ;; after doing early hints, I need to run the original dispatcher to generate the real response. + ;; dispatchers do not return anything. + (original-dispatcher conn req)) + (define (make-dispatcher-tree ds) (host:make (λ (host-sym) @@ -47,7 +67,7 @@ (pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works)) (filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category))) (filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file))) - (filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki))) + (filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (early-hints:make (lift:make (hash-ref ds 'page-wiki)))) (filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (hash-ref ds 'page-search))) (filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (hash-ref ds 'redirect-wiki-home))) (hash-ref ds 'static-dispatcher) From 8c7a04583077f32a8732b1327544c8aba5eed16a Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 13 Nov 2022 22:52:29 +1300 Subject: [PATCH 02/41] NIWA notice: say brought to you by breezewiki --- src/application-globals.rkt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/application-globals.rkt b/src/application-globals.rkt index a0ff8af..661f1a1 100644 --- a/src/application-globals.rkt +++ b/src/application-globals.rkt @@ -91,7 +91,7 @@ (p ,(fifth ind)) (div (@ (class "niwa__divider"))) (p "Why are you seeing this message? Fandom refuses to delete or archive their copy of this wiki, so that means their pages will appear high up in search results. Fandom hopes to get clicks from readers who don't know any better.") - (p (@ (class "niwa__feedback")) (a (@ (href "https://www.kotaku.com.au/2022/10/massive-zelda-wiki-reclaims-independence-six-months-before-tears-of-the-kingdom/")) "More info") " / " (a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "Feedback on this notice?"))) + (p (@ (class "niwa__feedback")) "This notice brought to you by BreezeWiki / " (a (@ (href "https://www.kotaku.com.au/2022/10/massive-zelda-wiki-reclaims-independence-six-months-before-tears-of-the-kingdom/")) "Info & Context") " / " (a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "Feedback?"))) (div (@ (class "niwa__right")) (img (@ (class "niwa__logo") (src ,(format "https://www.niwanetwork.org~a" (fourth ind))))))))) "")) From ef12faf72dd7c1a1297168b4a7c37b04c32d0c3b Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 15 Nov 2022 20:45:44 +1300 Subject: [PATCH 03/41] Uncollapse more wiki sections --- src/page-wiki.rkt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/page-wiki.rkt b/src/page-wiki.rkt index a0a2944..b2028d6 100644 --- a/src/page-wiki.rkt +++ b/src/page-wiki.rkt @@ -205,6 +205,16 @@ attributes) ;; children ((compose1 + ; more uncollapsing - sample: bandori/wiki/BanG_Dream!_Wikia + (curry u + (λ (v) (has-class? "mw-collapsible-content" attributes)) + (λ (v) (for/list ([element v]) + (u (λ (element) (pair? element)) + (λ (element) + `(,(car element) + (@ ,@(attribute-maybe-update 'style (λ (a) (regexp-replace #rx"display: *none" a "display:inline")) (bits->attributes element))) + ,@(filter element-is-content? (cdr element)))) + element)))) ; wrap blinking animated images in a slot so they can be animated with CSS (curry u (λ (v) (and (has-class? "animated" attributes) From bf6efde97925cbcf2156254ac8b2e4f1fe57900d Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 15 Nov 2022 20:46:56 +1300 Subject: [PATCH 04/41] Add countdown timer for gacha enthusiasts --- src/application-globals.rkt | 1 + static/countdown.js | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 static/countdown.js diff --git a/src/application-globals.rkt b/src/application-globals.rkt index 661f1a1..b139192 100644 --- a/src/application-globals.rkt +++ b/src/application-globals.rkt @@ -136,6 +136,7 @@ ,(if (config-true? 'feature_search_suggestions) `(script (@ (type "module") (src ,(get-static-url "search-suggestions.js")))) "") + (script (@ (type "module") (src ,(get-static-url "countdown.js")))) (link (@ (rel "icon") (href ,(u (λ (v) (config-true? 'strict_proxy)) (λ (v) (u-proxy-url v)) (head-data^-icon-url head-data)))))) diff --git a/static/countdown.js b/static/countdown.js new file mode 100644 index 0000000..9ae6b46 --- /dev/null +++ b/static/countdown.js @@ -0,0 +1,53 @@ +// countdown timer for gacha enthusiasts +// sample: bandori/wiki/BanG_Dream!_Wikia +// sample: ensemble-stars/wiki/The_English_Ensemble_Stars_Wiki + +import {h, htm, render, useState, useEffect, createContext, useContext, signal, computed, effect} from "./preact.js" +const html = htm.bind(h) + +const now = signal(Date.now()) +setInterval(() => now.value = Date.now(), 1000) + +const units = [ + ["w", 7*24*60*60*1000], + ["d", 24*60*60*1000], + ["h", 60*60*1000], + ["m", 60*1000], + ["s", 1000] +] + +function getDisplayTime(datetime, now, or) { + let difference = datetime - now + let foundSignificantField = false + if (difference > 0) { + return units.map(([letter, duration], index) => { + const multiplier = Math.floor(difference / duration) + difference -= multiplier * duration + if (multiplier > 0 || foundSignificantField) { + foundSignificantField = true + return multiplier + letter + } + }).filter(s => s).join(" ") + } else if (or) { + return or + } else { + return `[timer ended on ${new Date(datetime).toLocaleString()}]` + } +} + +function Countdown(props) { + return html`${props.display}` +} + +document.querySelectorAll(".countdown").forEach(eCountdown => { + // grab information and make variables + const eDate = eCountdown.querySelector(".countdowndate") + const eOr = eCountdown.nextElementSibling + const or = eOr?.textContent + const datetime = new Date(eDate.textContent).getTime() + // the mapped signal + const display = computed(() => getDisplayTime(datetime, now.value, or)) + // clear content and render + while (eDate.childNodes[0] !== undefined) eDate.childNodes[0].remove() + render(html`<${Countdown} display=${display} />`, eDate); +}) From 324e34eb720413d4f50aba63b339aeb8daaa52de Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 15 Nov 2022 20:49:25 +1300 Subject: [PATCH 05/41] Remove unused imports --- static/countdown.js | 2 +- static/search-suggestions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/countdown.js b/static/countdown.js index 9ae6b46..ad8d0be 100644 --- a/static/countdown.js +++ b/static/countdown.js @@ -2,7 +2,7 @@ // sample: bandori/wiki/BanG_Dream!_Wikia // sample: ensemble-stars/wiki/The_English_Ensemble_Stars_Wiki -import {h, htm, render, useState, useEffect, createContext, useContext, signal, computed, effect} from "./preact.js" +import {h, htm, render, signal, computed, effect} from "./preact.js" const html = htm.bind(h) const now = signal(Date.now()) diff --git a/static/search-suggestions.js b/static/search-suggestions.js index 1a9a3d3..f4586be 100644 --- a/static/search-suggestions.js +++ b/static/search-suggestions.js @@ -1,4 +1,4 @@ -import {h, htm, render, useState, useEffect, createContext, useContext, signal, computed, effect} from "./preact.js" +import {h, htm, render, signal, computed, effect} from "./preact.js" const html = htm.bind(h) const classNames = classArr => classArr.filter(el => el).join(" ") From 1c83c0b4d3ce543b05befa981eccc4bfe1f440e5 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 15 Nov 2022 22:21:12 +1300 Subject: [PATCH 06/41] Support X-Canonical-Origin for instance admins --- src/dispatcher-tree.rkt | 35 +++++++++++++---------------- src/page-subdomain.rkt | 49 ++++++++++++++++++++++++++++++++++++++--- src/syntax.rkt | 2 +- 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/src/dispatcher-tree.rkt b/src/dispatcher-tree.rkt index 9e072bc..63c053d 100644 --- a/src/dispatcher-tree.rkt +++ b/src/dispatcher-tree.rkt @@ -3,6 +3,7 @@ (for-syntax racket/base) racket/string net/url + web-server/http (prefix-in host: web-server/dispatchers/dispatch-host) (prefix-in pathprocedure: web-server/dispatchers/dispatch-pathprocedure) (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer) @@ -32,23 +33,17 @@ (datum->syntax stx `(make-dispatcher-tree ,ds))) (define (make-dispatcher-tree ds) - (host:make - (λ (host-sym) - (if/out (config-true? 'canonical_origin) - (let* ([host-header (symbol->string host-sym)] - [splitter (string-append "." (url-host (string->url (config-get 'canonical_origin))))] - [s (string-split host-header splitter #:trim? #f)]) - (if/in (and (eq? 2 (length s)) (equal? "" (cadr s))) - ((hash-ref ds 'subdomain-dispatcher) (car s)))) - (sequencer:make - (pathprocedure:make "/" (hash-ref ds 'page-home)) - (pathprocedure:make "/proxy" (hash-ref ds 'page-proxy)) - (pathprocedure:make "/search" (hash-ref ds 'page-global-search)) - (pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works)) - (filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category))) - (filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file))) - (filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki))) - (filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (hash-ref ds 'page-search))) - (filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (hash-ref ds 'redirect-wiki-home))) - (hash-ref ds 'static-dispatcher) - (lift:make (hash-ref ds 'page-not-found))))))) + (define subdomain-dispatcher (hash-ref ds 'subdomain-dispatcher)) + (sequencer:make + subdomain-dispatcher + (pathprocedure:make "/" (hash-ref ds 'page-home)) + (pathprocedure:make "/proxy" (hash-ref ds 'page-proxy)) + (pathprocedure:make "/search" (hash-ref ds 'page-global-search)) + (pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works)) + (filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category))) + (filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file))) + (filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki))) + (filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (hash-ref ds 'page-search))) + (filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (hash-ref ds 'redirect-wiki-home))) + (hash-ref ds 'static-dispatcher) + (lift:make (hash-ref ds 'page-not-found)))) diff --git a/src/page-subdomain.rkt b/src/page-subdomain.rkt index dededfb..c254236 100644 --- a/src/page-subdomain.rkt +++ b/src/page-subdomain.rkt @@ -1,22 +1,65 @@ #lang racket/base -(require racket/path +(require racket/match + racket/path racket/string net/url web-server/http + web-server/dispatchers/dispatch + (only-in racket/promise delay) (prefix-in lift: web-server/dispatchers/dispatch-lift) "application-globals.rkt" "config.rkt" + "syntax.rkt" "xexpr-utils.rkt") (provide subdomain-dispatcher) -(define (subdomain-dispatcher subdomain) +(module+ test + (require rackunit)) + +(define (do-redirect:make subdomain canonical-origin) (lift:make (λ (req) (response-handler (define uri (request-uri req)) (define path (url-path uri)) (define path-string (string-join (map (λ (p) (path/param-path p)) path) "/")) - (define dest (format "~a/~a/~a" (config-get 'canonical_origin) subdomain path-string)) + (define dest (format "~a/~a/~a" canonical-origin subdomain path-string)) (generate-redirect dest))))) + +(define (router req) + (define host (bytes->string/utf-8 (header-value (headers-assq* #"host" (request-headers/raw req))))) + (define x-canonical-origin (headers-assq* #"x-canonical-origin" (request-headers/raw req))) + (define canonical-origin + (cond + [x-canonical-origin (bytes->string/utf-8 (header-value x-canonical-origin))] + [(config-true? 'canonical_origin) (config-get 'canonical_origin)] + [#t #f])) + (if/out canonical-origin + (let* ([canonical-origin-host (url-host (string->url canonical-origin))]) + (if/in canonical-origin-host + (let* ([splitter (string-append "." (url-host (string->url canonical-origin)))] + [s (string-split host splitter #:trim? #f)]) + (if/in (and (eq? 2 (length s)) (equal? "" (cadr s))) + (list 'redirect (car s) canonical-origin))))) + 'next-dispatcher)) +(module+ test + (define (qr url headers) + (request #"GET" (string->url url) (map (λ (h) (header (car h) (cadr h))) headers) (delay '()) #f "127.0.0.1" 10416 "127.0.0.1")) + (parameterize ([(config-parameter 'canonical_origin) "https://breezewiki.com"]) + (check-equal? (router (qr "/" '((#"Host" #"breezewiki.com")))) + 'next-dispatcher) + (check-equal? (router (qr "/wiki/Spell" '((#"Host" #"magic.breezewiki.com")))) + '(redirect "magic" "https://breezewiki.com")) + (check-equal? (router (qr "/" '((#"Host" #"magic.bw.breezewiki.com") + (#"X-Canonical-Origin" #"https://bw.breezewiki.com")))) + '(redirect "magic" "https://bw.breezewiki.com")) + (check-equal? (router (qr "/" '((#"Host" #"magic.bwxxxxx.onion") + (#"X-Canonical-Origin" #"http://bwxxxxx.onion")))) + '(redirect "magic" "http://bwxxxxx.onion")))) + +(define (subdomain-dispatcher conn req) + (match (router req) + [(list 'redirect subdomain canonical-origin) ((do-redirect:make subdomain canonical-origin) conn req)] + [_ (next-dispatcher)])) diff --git a/src/syntax.rkt b/src/syntax.rkt index 8205326..f8fda0d 100644 --- a/src/syntax.rkt +++ b/src/syntax.rkt @@ -37,7 +37,7 @@ [(and (pair? node) (eq? 'if/out (car node))) node] ; -- replace if/in [(and (pair? node) (eq? 'if/in (car node))) - (append '(if) (cdr node) else)] + (append '(if) (walk (cdr node)) else)] ; recurse down pair head and tail [(pair? node) (cons (walk (car node)) (walk (cdr node)))] ; something else that can't be recursed into, so pass it through From 92591a5eab3653ad3c742288cb040d320199f599 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 17 Nov 2022 23:25:06 +1300 Subject: [PATCH 07/41] Fix some special characters in titles like ? and ; --- src/page-category.rkt | 2 +- src/page-home.rkt | 8 ++++---- src/page-search.rkt | 2 +- src/url-utils.rkt | 28 +++++++++++++++++++--------- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/page-category.rkt b/src/page-category.rkt index 89bc45a..69d9f42 100644 --- a/src/page-category.rkt +++ b/src/page-category.rkt @@ -52,7 +52,7 @@ ,@(map (λ (result) (define title (jp "/title" result)) - (define page-path (regexp-replace* #rx" " title "_")) + (define page-path (page-title->path title)) `(li (a (@ (href ,(format "/~a/wiki/~a" wikiname page-path))) ,title))) diff --git a/src/page-home.rkt b/src/page-home.rkt index 6037d9a..23ede8b 100644 --- a/src/page-home.rkt +++ b/src/page-home.rkt @@ -18,9 +18,9 @@ (define examples '(("minecraft" "Bricks") - ("crosscode" "CrossCode_Wiki") - ("undertale" "Hot_Dog...%3F") - ("tardis" "Eleanor_Blake") + ("crosscode" "CrossCode Wiki") + ("undertale" "Hot Dog...?") + ("tardis" "Eleanor Blake") ("zelda" "Boomerang"))) (define content @@ -45,7 +45,7 @@ (h2 "Example pages") (ul ,@(map (λ (x) - `(li (a (@ (href ,(apply format "/~a/wiki/~a" x))) + `(li (a (@ (href ,(format "/~a/wiki/~a" (car x) (page-title->path (cadr x))))) ,(apply format "~a: ~a" x)))) examples)) (h2 "Testimonials") diff --git a/src/page-search.rkt b/src/page-search.rkt index f4d1ce3..0404233 100644 --- a/src/page-search.rkt +++ b/src/page-search.rkt @@ -38,7 +38,7 @@ (ul ,@(map (λ (result) (let* ([title (jp "/title" result)] - [page-path (regexp-replace* #rx" " title "_")] + [page-path (page-title->path title)] [timestamp (jp "/timestamp" result)] [wordcount (jp "/wordcount" result)] [size (jp "/size" result)]) diff --git a/src/url-utils.rkt b/src/url-utils.rkt index b70b245..1df933c 100644 --- a/src/url-utils.rkt +++ b/src/url-utils.rkt @@ -17,7 +17,9 @@ ; prints "out: " log-outgoing ; pass in a header, headers, or something useless. they'll all combine into a list - build-headers) + build-headers + ; try to follow wikimedia's format for which characters should be encoded/replaced in page titles for the url + page-title->path) (module+ test (require "typed-rackunit.rkt")) @@ -26,14 +28,18 @@ ;; https://url.spec.whatwg.org/#urlencoded-serializing -(define urlencoded-set '(#\! #\' #\( #\) #\~ ; urlencoded set - #\$ #\% #\& #\+ #\, ; component set - #\/ #\: #\; #\= #\@ #\[ #\\ #\] #\^ #\| ; userinfo set - #\? #\` #\{ #\} ; path set - #\ #\" #\# #\< #\> ; query set - ; c0 controls included elsewhere - ; higher ranges included elsewhere - )) +(define path-set '(#\; ; semicolon is part of the userinfo set in the URL standard, but I'm putting it here + #\? #\` #\{ #\} ; path set + #\ #\" #\# #\< #\> ; query set + ; c0 controls included elsewhere + ; higher ranges included elsewhere + )) +(define urlencoded-set (append + '(#\! #\' #\( #\) #\~ ; urlencoded set + #\$ #\% #\& #\+ #\, ; component set + #\/ #\: #\= #\@ #\[ #\\ #\] #\^ #\| ; userinfo set + ) + path-set)) (: percent-encode (String (Listof Char) Boolean -> Bytes)) (define (percent-encode value set space-as-plus) @@ -98,3 +104,7 @@ [(header? f) (list f)] [(pair? f) f])) fs))) + +(: page-title->path (String -> Bytes)) +(define (page-title->path title) + (percent-encode (regexp-replace* " " title "_") path-set #f)) From 9afccbb9cd88c851b9c29545793060c56371fa49 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 30 Nov 2022 00:03:54 +1300 Subject: [PATCH 08/41] Support light/dark themes as per Fandom's styles --- breezewiki.rkt | 2 ++ dist.rkt | 2 ++ info.rkt | 2 +- src/application-globals.rkt | 39 ++++++++++++++++++++++++------ src/data.rkt | 34 +++++++++++++++++++++++++- src/dispatcher-tree.rkt | 1 + src/page-category.rkt | 3 +++ src/page-file.rkt | 7 ++++-- src/page-search.rkt | 5 ++-- src/page-set-user-settings.rkt | 18 ++++++++++++++ src/page-wiki.rkt | 12 ++++++---- static/icon-theme-dark.svg | 2 ++ static/icon-theme-default.svg | 2 ++ static/icon-theme-light.svg | 2 ++ static/main.css | 44 +++++++++++++++++++++++++++++++++- 15 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 src/page-set-user-settings.rkt create mode 100644 static/icon-theme-dark.svg create mode 100644 static/icon-theme-default.svg create mode 100644 static/icon-theme-light.svg diff --git a/breezewiki.rkt b/breezewiki.rkt index a8b8c28..44d6771 100644 --- a/breezewiki.rkt +++ b/breezewiki.rkt @@ -17,6 +17,7 @@ (require-reloadable "src/page-proxy.rkt" page-proxy) (require-reloadable "src/page-redirect-wiki-home.rkt" redirect-wiki-home) (require-reloadable "src/page-search.rkt" page-search) +(require-reloadable "src/page-set-user-settings.rkt" page-set-user-settings) (require-reloadable "src/page-static.rkt" static-dispatcher) (require-reloadable "src/page-subdomain.rkt" subdomain-dispatcher) (require-reloadable "src/page-wiki.rkt" page-wiki) @@ -40,6 +41,7 @@ page-not-found page-proxy page-search + page-set-user-settings page-wiki page-file redirect-wiki-home diff --git a/dist.rkt b/dist.rkt index 777e81a..35e1824 100644 --- a/dist.rkt +++ b/dist.rkt @@ -11,6 +11,7 @@ (require (only-in "src/page-proxy.rkt" page-proxy)) (require (only-in "src/page-redirect-wiki-home.rkt" redirect-wiki-home)) (require (only-in "src/page-search.rkt" page-search)) +(require (only-in "src/page-set-user-settings.rkt" page-set-user-settings)) (require (only-in "src/page-static.rkt" static-dispatcher)) (require (only-in "src/page-subdomain.rkt" subdomain-dispatcher)) (require (only-in "src/page-wiki.rkt" page-wiki)) @@ -29,6 +30,7 @@ page-not-found page-proxy page-search + page-set-user-settings page-wiki page-file redirect-wiki-home diff --git a/info.rkt b/info.rkt index 46512df..dd34d49 100644 --- a/info.rkt +++ b/info.rkt @@ -1,3 +1,3 @@ #lang info -(define build-deps '("rackunit-lib" "web-server-lib" "http-easy-lib" "html-parsing" "html-writing" "json-pointer" "ini-lib" "memo")) +(define build-deps '("rackunit-lib" "web-server-lib" "http-easy-lib" "html-parsing" "html-writing" "json-pointer" "ini-lib" "memo" "net-cookies-lib")) diff --git a/src/application-globals.rkt b/src/application-globals.rkt index b139192..0fe26dc 100644 --- a/src/application-globals.rkt +++ b/src/application-globals.rkt @@ -1,8 +1,10 @@ #lang racket/base -(require racket/list +(require racket/file + racket/list racket/string json (prefix-in easy: net/http-easy) + html-parsing html-writing web-server/http "config.rkt" @@ -34,6 +36,11 @@ (header #"Link" (string->bytes/latin-1 link-header)))) (define timeouts (easy:make-timeout-config #:lease 5 #:connect 5)) +(define theme-icons + (for/hasheq ([theme '(default light dark)]) + (values theme + (html->xexp (file->string (format "static/icon-theme-~a.svg" theme) #:mode 'binary))))) + (define (application-footer source-url #:license [license-in #f]) (define license (or license-in license-default)) `(footer (@ (class "custom-footer")) @@ -98,24 +105,27 @@ (define (generate-wiki-page content + #:req req #:source-url source-url #:wikiname wikiname #:title title #:head-data [head-data-in #f] - #:siteinfo [siteinfo-in #f]) + #:siteinfo [siteinfo-in #f] + #:user-cookies [user-cookies-in #f]) (define siteinfo (or siteinfo-in siteinfo-default)) (define head-data (or head-data-in ((head-data-getter wikiname)))) + (define user-cookies (or user-cookies-in (user-cookies-getter req))) (define (required-styles origin) (map (λ (dest-path) (define url (format dest-path origin)) (if (config-true? 'strict_proxy) (u-proxy-url url) url)) - '(#;"~a/load.php?lang=en&modules=skin.fandomdesktop.styles&only=styles&skin=fandomdesktop" + `(#;"~a/load.php?lang=en&modules=skin.fandomdesktop.styles&only=styles&skin=fandomdesktop" #;"~a/load.php?lang=en&modules=ext.gadget.dungeonsWiki%2CearthWiki%2Csite-styles%2Csound-styles&only=styles&skin=fandomdesktop" #;"~a/load.php?lang=en&modules=site.styles&only=styles&skin=fandomdesktop" ; combine the above entries into a single request for potentially extra speed - fandom.com doesn't even do this! - "~a/wikia.php?controller=ThemeApi&method=themeVariables" + ,(format "~~a/wikia.php?controller=ThemeApi&method=themeVariables&variant=~a" (user-cookies^-theme user-cookies)) "~a/load.php?lang=en&modules=skin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.gadget.site-styles%2Csound-styles%7Csite.styles&only=styles&skin=fandomdesktop"))) `(*TOP* (*DECL* DOCTYPE html) @@ -155,7 +165,22 @@ (label (@ (for "bw-search-input")) "Search ") (div (@ (id "bw-pr-search-input")) (input (@ (type "text") (name "q") (id "bw-search-input") (autocomplete "off")))) - (div (@ (class "bw-ss__container") (id "bw-pr-search-suggestions")))))) + (div (@ (class "bw-ss__container") (id "bw-pr-search-suggestions")))) + (div (@ (class "bw-theme__select")) + (span (@ (class "bw-theme__main-label")) "Page theme") + (div (@ (class "bw-theme__items")) + ,@(for/list ([theme '(default light dark)]) + (define class + (if (equal? theme (user-cookies^-theme user-cookies)) + "bw-theme__item bw-theme__item--selected" + "bw-theme__item")) + `(a (@ (href ,(user-cookies-setter-url + req + (struct-copy user-cookies^ user-cookies + [theme theme]))) (class ,class)) + (div (@ (class "bw-theme__icon-container")) + ,(hash-ref theme-icons theme)) + ,(format "~a" theme))))))) (div (@ (id "content") #;(class "page-content")) (div (@ (id "mw-content-text")) ,content)) @@ -179,11 +204,11 @@ page)))) "/proxy?dest=https%3A%2F%2Ftest.fandom.com"))) -(define (generate-redirect dest) +(define (generate-redirect dest #:headers [headers-in '()]) (define dest-bytes (string->bytes/utf-8 dest)) (response/output #:code 302 - #:headers (list (header #"Location" dest-bytes)) + #:headers (append (list (header #"Location" dest-bytes)) headers-in) (λ (out) (write-html `(html diff --git a/src/data.rkt b/src/data.rkt index f856710..e8a9e69 100644 --- a/src/data.rkt +++ b/src/data.rkt @@ -1,6 +1,9 @@ #lang racket/base (require racket/list racket/match + web-server/http/request-structs + net/url-string + (only-in net/cookies/server cookie-header->alist cookie->set-cookie-header make-cookie) (prefix-in easy: net/http-easy) memo "static-data.rkt" @@ -11,11 +14,16 @@ (struct-out siteinfo^) (struct-out license^) (struct-out head-data^) + (struct-out user-cookies^) siteinfo-fetch siteinfo-default license-default head-data-getter - head-data-default) + head-data-default + user-cookies-getter + user-cookies-default + user-cookies-setter + user-cookies-setter-url) (struct siteinfo^ (sitename basepage license) #:transparent) (struct license^ (text url) #:transparent) @@ -61,3 +69,27 @@ (set! this-data data)) ;; then no matter what, return the best information we have so far this-data)) + +(struct user-cookies^ (theme) #:prefab) +(define user-cookies-default (user-cookies^ 'default)) +(define (user-cookies-getter req) + (define cookie-header (headers-assq* #"cookie" (request-headers/raw req))) + (define cookies-alist (if cookie-header (cookie-header->alist (header-value cookie-header) bytes->string/utf-8) null)) + (define cookies-hash + (for/hasheq ([pair cookies-alist]) + (match pair + [(cons "theme" (and theme (or "light" "dark" "default"))) + (values 'theme (string->symbol theme))] + [_ (values #f #f)]))) + (user-cookies^ + (hash-ref cookies-hash 'theme (user-cookies^-theme user-cookies-default)))) + +(define (user-cookies-setter user-cookies) + (map (λ (c) (header #"Set-Cookie" (cookie->set-cookie-header c))) + (list (make-cookie "theme" (symbol->string (user-cookies^-theme user-cookies)) + #:path "/" + #:max-age (* 60 60 24 365 10))))) + +(define (user-cookies-setter-url req new-settings) + (format "/set-user-settings?~a" (params->query `(("ref" . ,(url->string (request-uri req))) + ("new_settings" . ,(format "~a" new-settings)))))) diff --git a/src/dispatcher-tree.rkt b/src/dispatcher-tree.rkt index 63c053d..251aa43 100644 --- a/src/dispatcher-tree.rkt +++ b/src/dispatcher-tree.rkt @@ -39,6 +39,7 @@ (pathprocedure:make "/" (hash-ref ds 'page-home)) (pathprocedure:make "/proxy" (hash-ref ds 'page-proxy)) (pathprocedure:make "/search" (hash-ref ds 'page-global-search)) + (pathprocedure:make "/set-user-settings" (hash-ref ds 'page-set-user-settings)) (pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works)) (filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category))) (filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file))) diff --git a/src/page-category.rkt b/src/page-category.rkt index 69d9f42..5933f3e 100644 --- a/src/page-category.rkt +++ b/src/page-category.rkt @@ -29,6 +29,7 @@ '#hasheq((batchcomplete . #t) (continue . #hasheq((cmcontinue . "page|4150504c45|41473") (continue . "-||"))) (query . #hasheq((categorymembers . (#hasheq((ns . 0) (pageid . 25049) (title . "Item (entity)")) #hasheq((ns . 0) (pageid . 128911) (title . "3D")) #hasheq((ns . 0) (pageid . 124018) (title . "A Very Fine Item")) #hasheq((ns . 0) (pageid . 142208) (title . "Amethyst Shard")) #hasheq((ns . 0) (pageid . 121612) (title . "Ankle Monitor"))))))))) (define (generate-results-page + #:req req #:source-url source-url #:wikiname wikiname #:title title @@ -38,6 +39,7 @@ #:siteinfo [siteinfo #f]) (define members (jp "/query/categorymembers" members-data)) (generate-wiki-page + #:req req #:source-url source-url #:wikiname wikiname #:title title @@ -96,6 +98,7 @@ (define page (html->xexp page-html)) (define head-data ((head-data-getter wikiname) page-data)) (define body (generate-results-page + #:req req #:source-url source-url #:wikiname wikiname #:title title diff --git a/src/page-file.rkt b/src/page-file.rkt index 1802568..d9dfd91 100644 --- a/src/page-file.rkt +++ b/src/page-file.rkt @@ -51,7 +51,8 @@ [(regexp-match? #rx"(?i:^video/)" content-type) `(video (@ (src ,maybe-proxied-url) (controls)))] [else `""])) -(define (generate-results-page #:source-url source-url +(define (generate-results-page #:req req + #:source-url source-url #:wikiname wikiname #:title title #:media-detail media-detail @@ -68,6 +69,7 @@ (define maybe-proxied-raw-image-url (if (config-true? 'strict_proxy) (u-proxy-url raw-image-url) raw-image-url)) (generate-wiki-page + #:req req #:source-url source-url #:wikiname wikiname #:title title @@ -125,7 +127,8 @@ #f (url-content-type (jp "/imageUrl" media-detail)))) (define body - (generate-results-page #:source-url source-url + (generate-results-page #:req req + #:source-url source-url #:wikiname wikiname #:title title #:media-detail media-detail diff --git a/src/page-search.rkt b/src/page-search.rkt index 0404233..214a3a2 100644 --- a/src/page-search.rkt +++ b/src/page-search.rkt @@ -25,9 +25,10 @@ (define search-json-data '#hasheq((batchcomplete . #t) (query . #hasheq((search . (#hasheq((ns . 0) (pageid . 219) (size . 1482) (snippet . "") (timestamp . "2022-08-21T08:54:23Z") (title . "Gacha Capsule") (wordcount . 214)) #hasheq((ns . 0) (pageid . 201) (size . 1198) (snippet . "") (timestamp . "2022-07-11T17:52:47Z") (title . "Badges") (wordcount . 181))))))))) -(define (generate-results-page dest-url wikiname query data #:siteinfo [siteinfo #f]) +(define (generate-results-page req dest-url wikiname query data #:siteinfo [siteinfo #f]) (define search-results (jp "/query/search" data)) (generate-wiki-page + #:req req #:source-url dest-url #:wikiname wikiname #:title query @@ -74,7 +75,7 @@ (define data (easy:response-json dest-res)) - (define body (generate-results-page dest-url wikiname query data #:siteinfo siteinfo)) + (define body (generate-results-page req dest-url wikiname query data #:siteinfo siteinfo)) (when (config-true? 'debug) ; used for its side effects ; convert to string with error checking, error will be raised if xexp is invalid diff --git a/src/page-set-user-settings.rkt b/src/page-set-user-settings.rkt new file mode 100644 index 0000000..3f37692 --- /dev/null +++ b/src/page-set-user-settings.rkt @@ -0,0 +1,18 @@ +#lang racket/base +(require racket/dict + net/url + web-server/http + "application-globals.rkt" + "data.rkt" + "url-utils.rkt" + "xexpr-utils.rkt") + +(provide + page-set-user-settings) + +(define (page-set-user-settings req) + (response-handler + (define ref (dict-ref (url-query (request-uri req)) 'ref)) + (define new-settings (read (open-input-string (dict-ref (url-query (request-uri req)) 'new_settings)))) + (define headers (user-cookies-setter new-settings)) + (generate-redirect ref #:headers headers))) diff --git a/src/page-wiki.rkt b/src/page-wiki.rkt index b2028d6..2cd14dc 100644 --- a/src/page-wiki.rkt +++ b/src/page-wiki.rkt @@ -276,6 +276,7 @@ (define (page-wiki req) (define wikiname (path/param-path (first (url-path (request-uri req))))) + (define user-cookies (user-cookies-getter req)) (define origin (format "https://~a.fandom.com" wikiname)) (define path (string-join (map path/param-path (cddr (url-path (request-uri req)))) "/")) (define source-url (format "https://~a.fandom.com/wiki/~a" wikiname path)) @@ -290,7 +291,9 @@ ("formatversion" . "2") ("format" . "json"))))) (log-outgoing dest-url) - (easy:get dest-url #:timeouts timeouts)] + (easy:get dest-url + #:timeouts timeouts + #:headers `#hasheq((cookie . ,(format "theme=~a" (user-cookies^-theme user-cookies)))))] [siteinfo (siteinfo-fetch wikiname)]) (cond @@ -307,6 +310,7 @@ (define body (generate-wiki-page (update-tree-wiki page wikiname) + #:req req #:source-url source-url #:wikiname wikiname #:title title @@ -317,9 +321,9 @@ (build-headers always-headers (when redirect-msg - (let* ([dest (get-attribute 'href (bits->attributes ((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))))] - [value (bytes-append #"0;url=" (string->bytes/utf-8 dest))]) - (header #"Refresh" value))))) + (let* ([dest (get-attribute 'href (bits->attributes ((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))))] + [value (bytes-append #"0;url=" (string->bytes/utf-8 dest))]) + (header #"Refresh" value))))) (when (config-true? 'debug) ; used for its side effects ; convert to string with error checking, error will be raised if xexp is invalid diff --git a/static/icon-theme-dark.svg b/static/icon-theme-dark.svg new file mode 100644 index 0000000..bc36ede --- /dev/null +++ b/static/icon-theme-dark.svg @@ -0,0 +1,2 @@ + + diff --git a/static/icon-theme-default.svg b/static/icon-theme-default.svg new file mode 100644 index 0000000..4f5655c --- /dev/null +++ b/static/icon-theme-default.svg @@ -0,0 +1,2 @@ + + diff --git a/static/icon-theme-light.svg b/static/icon-theme-light.svg new file mode 100644 index 0000000..cc19c3a --- /dev/null +++ b/static/icon-theme-light.svg @@ -0,0 +1,2 @@ + + diff --git a/static/main.css b/static/main.css index 0976e72..c6b2791 100644 --- a/static/main.css +++ b/static/main.css @@ -51,7 +51,7 @@ p { .custom-top { display: flex; flex-wrap: wrap; - align-items: baseline; + align-items: center; justify-content: space-between; } .page-title { @@ -227,6 +227,7 @@ figcaption, .lightbox-caption, .thumbcaption { display: grid; grid-template-columns: auto 1fr; grid-gap: 0px 5px; + align-items: baseline; } .bw-ss__container { grid-column: 2; @@ -282,6 +283,47 @@ figcaption, .lightbox-caption, .thumbcaption { text-align: left; } +/* (breezewiki) theme selector */ +.bw-theme__select { + display: grid; + grid-template-columns: auto auto; + grid-gap: 0px 5px; + justify-content: right; + align-items: baseline; + margin-top: 4px; +} +.bw-theme__items { + display: flex; +} +.bw-theme__item { + display: flex; + align-items: baseline; + padding: 2px; + border: 1px solid var(--theme-border-color); + border-right-width: 0px; + background-color: var(--custom-table-background); + color: var(--theme-page-text-color); + transition: none; +} +.bw-theme__item:hover, .bw-theme__item:focus { + /* background-color: var(--theme-page-background-color); */ + color: var(--theme-accent-color); +} +.bw-theme__item:first-child { + border-radius: 4px 0px 0px 4px; +} +.bw-theme__item:last-child { + border-radius: 0px 4px 4px 0px; + border-right-width: 1px; +} +.bw-theme__item--selected, .bw-theme__item--selected:hover, .bw-theme__item--selected:focus { + background-color: var(--theme-accent-color); + color: var(--theme-accent-label-color); +} +.bw-theme__icon-container svg { + vertical-align: middle; +} + /* nintendo independent wiki alliance notice */ .niwa__notice { background: #fdedd8; From 50a3bc819a1cf085b0cf588d60df837786990025 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 30 Nov 2022 00:36:53 +1300 Subject: [PATCH 09/41] Fix tests by providing a fake req --- src/application-globals.rkt | 8 ++++++-- src/page-category.rkt | 4 +++- src/page-file.rkt | 6 ++++-- src/page-search.rkt | 5 +++-- src/test-utils.rkt | 8 ++++++++ 5 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 src/test-utils.rkt diff --git a/src/application-globals.rkt b/src/application-globals.rkt index 0fe26dc..e6feaaf 100644 --- a/src/application-globals.rkt +++ b/src/application-globals.rkt @@ -1,6 +1,7 @@ #lang racket/base (require racket/file racket/list + racket/runtime-path racket/string json (prefix-in easy: net/http-easy) @@ -29,17 +30,19 @@ (module+ test (require rackunit - html-writing)) + html-writing + "test-utils.rkt")) (define always-headers (list (header #"Referrer-Policy" #"same-origin") ; header to not send referers to fandom (header #"Link" (string->bytes/latin-1 link-header)))) (define timeouts (easy:make-timeout-config #:lease 5 #:connect 5)) +(define-runtime-path path-static "../static") (define theme-icons (for/hasheq ([theme '(default light dark)]) (values theme - (html->xexp (file->string (format "static/icon-theme-~a.svg" theme) #:mode 'binary))))) + (html->xexp (file->string (build-path path-static (format "icon-theme-~a.svg" theme)) #:mode 'binary))))) (define (application-footer source-url #:license [license-in #f]) (define license (or license-in license-default)) @@ -190,6 +193,7 @@ (parameterize ([(config-parameter 'strict_proxy) "true"]) (generate-wiki-page '(template) + #:req test-req #:source-url "" #:title "test" #:wikiname "test"))) diff --git a/src/page-category.rkt b/src/page-category.rkt index 5933f3e..62b7f38 100644 --- a/src/page-category.rkt +++ b/src/page-category.rkt @@ -24,7 +24,8 @@ page-category) (module+ test - (require rackunit) + (require rackunit + "test-utils.rkt") (define category-json-data '#hasheq((batchcomplete . #t) (continue . #hasheq((cmcontinue . "page|4150504c45|41473") (continue . "-||"))) (query . #hasheq((categorymembers . (#hasheq((ns . 0) (pageid . 25049) (title . "Item (entity)")) #hasheq((ns . 0) (pageid . 128911) (title . "3D")) #hasheq((ns . 0) (pageid . 124018) (title . "A Very Fine Item")) #hasheq((ns . 0) (pageid . 142208) (title . "Amethyst Shard")) #hasheq((ns . 0) (pageid . 121612) (title . "Ankle Monitor"))))))))) @@ -119,6 +120,7 @@ (module+ test (check-not-false ((query-selector (attribute-selector 'href "/test/wiki/Ankle_Monitor") (generate-results-page + #:req test-req #:source-url "" #:wikiname "test" #:title "Category:Items" diff --git a/src/page-file.rkt b/src/page-file.rkt index d9dfd91..d999ba4 100644 --- a/src/page-file.rkt +++ b/src/page-file.rkt @@ -23,7 +23,8 @@ (provide page-file) (module+ test - (require rackunit) + (require rackunit + "test-utils.rkt") (define test-media-detail '#hasheq((fileTitle . "Example file") (videoEmbedCode . "") @@ -162,7 +163,8 @@ (check-not-false ((query-selector (attribute-selector 'src "/proxy?dest=https%3A%2F%2Fstatic.wikia.nocookie.net%2Fexamplefile") - (generate-results-page #:source-url "" + (generate-results-page #:req test-req + #:source-url "" #:wikiname "test" #:title "File:Example file" #:media-detail test-media-detail diff --git a/src/page-search.rkt b/src/page-search.rkt index 214a3a2..10ff66c 100644 --- a/src/page-search.rkt +++ b/src/page-search.rkt @@ -21,7 +21,8 @@ page-search) (module+ test - (require rackunit) + (require rackunit + "test-utils.rkt") (define search-json-data '#hasheq((batchcomplete . #t) (query . #hasheq((search . (#hasheq((ns . 0) (pageid . 219) (size . 1482) (snippet . "") (timestamp . "2022-08-21T08:54:23Z") (title . "Gacha Capsule") (wordcount . 214)) #hasheq((ns . 0) (pageid . 201) (size . 1198) (snippet . "") (timestamp . "2022-07-11T17:52:47Z") (title . "Badges") (wordcount . 181))))))))) @@ -87,4 +88,4 @@ (write-html body out)))))) (module+ test (check-not-false ((query-selector (attribute-selector 'href "/test/wiki/Gacha_Capsule") - (generate-results-page "" "test" "Gacha" search-json-data))))) + (generate-results-page test-req "" "test" "Gacha" search-json-data))))) diff --git a/src/test-utils.rkt b/src/test-utils.rkt new file mode 100644 index 0000000..1c9860d --- /dev/null +++ b/src/test-utils.rkt @@ -0,0 +1,8 @@ +#lang racket/base +(require web-server/http/request-structs + net/url-structs + (only-in racket/promise delay)) +(provide + test-req) + +(define test-req (request #"GET" (url "https" #f "breezewiki.com" #f #t (list (path/param "" '())) '() #f) '() (delay '()) #f "127.0.0.1" 0 "127.0.0.1")) From a661ddb313b3c5a50a7413b22b3f9a176c1a5145 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 30 Nov 2022 00:37:07 +1300 Subject: [PATCH 10/41] Make the search box magically fit the space --- static/main.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/main.css b/static/main.css index c6b2791..b697350 100644 --- a/static/main.css +++ b/static/main.css @@ -256,6 +256,9 @@ figcaption, .lightbox-caption, .thumbcaption { .bw-ss__list--loading { background: #c0c0c0; } +.bw-ss__input { + width: 100%; /* magically makes it fit the available space */ +} .bw-ss__input--accepted { background: #fffbc0; } From ba3b39242cef005f60ddf9e50a63ba0c190ce9a2 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 4 Dec 2022 23:44:35 +1300 Subject: [PATCH 11/41] Rename ref parameter to next_location --- src/data.rkt | 2 +- src/page-set-user-settings.rkt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data.rkt b/src/data.rkt index e8a9e69..cdd8c39 100644 --- a/src/data.rkt +++ b/src/data.rkt @@ -91,5 +91,5 @@ #:max-age (* 60 60 24 365 10))))) (define (user-cookies-setter-url req new-settings) - (format "/set-user-settings?~a" (params->query `(("ref" . ,(url->string (request-uri req))) + (format "/set-user-settings?~a" (params->query `(("next_location" . ,(url->string (request-uri req))) ("new_settings" . ,(format "~a" new-settings)))))) diff --git a/src/page-set-user-settings.rkt b/src/page-set-user-settings.rkt index 3f37692..2fbc573 100644 --- a/src/page-set-user-settings.rkt +++ b/src/page-set-user-settings.rkt @@ -12,7 +12,7 @@ (define (page-set-user-settings req) (response-handler - (define ref (dict-ref (url-query (request-uri req)) 'ref)) + (define next-location (dict-ref (url-query (request-uri req)) 'next_location)) (define new-settings (read (open-input-string (dict-ref (url-query (request-uri req)) 'new_settings)))) (define headers (user-cookies-setter new-settings)) - (generate-redirect ref #:headers headers))) + (generate-redirect next-location #:headers headers))) From 9fd2b4699d9697b87fabcf982184ce6c381634f0 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 4 Dec 2022 23:46:24 +1300 Subject: [PATCH 12/41] Optimise update-tree-wiki somewhat --- src/page-wiki.rkt | 233 +------------------------------------ src/tree-updater.rkt | 267 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+), 228 deletions(-) create mode 100644 src/tree-updater.rkt diff --git a/src/page-wiki.rkt b/src/page-wiki.rkt index 2cd14dc..6f22ad8 100644 --- a/src/page-wiki.rkt +++ b/src/page-wiki.rkt @@ -19,6 +19,7 @@ "data.rkt" "pure-utils.rkt" "syntax.rkt" + "tree-updater.rkt" "xexpr-utils.rkt" "url-utils.rkt") @@ -30,51 +31,19 @@ preprocess-html-wiki) (module+ test - (require rackunit) - (define wiki-document - '(*TOP* - (div (@ (class "mw-parser-output")) - (aside (@ (role "region") (class "portable-infobox pi-theme-wikia pi-layout-default")) - (h2 (@ (class "pi-item pi-title") (data-source "title")) - "Infobox Title") - (figure (@ (class "pi-item pi-image") (data-source "image")) - (a (@ (href "https://static.wikia.nocookie.net/nice-image.png") (class "image image-thumbnail") (title "")) - (img (@ (src "https://static.wikia.nocookie.net/nice-image-thumbnail.png") (class "pi-image-thumbnail"))))) - (div (@ (class "pi-item pi-data") (data-source "description")) - (h3 (@ (class "pi-data-label")) - "Description") - (div (@ (class "pi-data-value")) - "Mystery infobox!"))) - (div (@ (data-test-collapsesection) (class "collapsible collapsetoggle-inline collapsed")) - (i (b "This section is hidden for dramatic effect.")) - (div (@ (class "collapsible-content")) - (p "Another page link: " - (a (@ (data-test-wikilink) (href "https://test.fandom.com/wiki/Another_Page") (title "Another Page")) - "Another Page")))) - (figure (@ (class "thumb tnone")) - (a (@ (href "https://static.wikia.nocookie.net/nice-image.png") (class "image") (data-test-figure-a)) - (img (@ (src "data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw%3D%3D") - (data-src "https://static.wikia.nocookie.net/nice-image-thumbnail.png") - (class "thumbimage lazyload")))) - (noscript - (a (@ (href "https://static.wikia.nocookie.net/nice-image.png") (class "image")) - (img (@ (src "https://static.wikia.nocookie.net/nice-image-thumbnail.png") - (data-src "https://static.wikia.nocookie.net/nice-image-thumbnail.png") - (class "thumbimage"))))) - (figcaption "Test figure!")) - (iframe (@ (src "https://example.com/iframe-src"))))))) + (require rackunit)) (define (preprocess-html-wiki html) - (define (rr* find replace contents) + (define ((rr* find replace) contents) (regexp-replace* find contents replace)) ((compose1 ; fix navbox list nesting ; navbox on right of page has incorrect html "
  • " and the xexpr parser puts the
  • much further up the tree ; add a