- site/colorlock.py: both XWikis force black and white with !important (a HIGH CONTRAST DARK THEME block in the skin's cssVariablesInit.css). On our pages only, the design's colours win: each inline colour made !important, and a stylesheet scoped to the page root (.ssos) for the rest. Used by site/com/build.py and site/org/build.py; the .com reinstalled with it. - site/org/inquiries (Captain Bob, 2026-11-07): the .com's inquiries go to an admin-only master/detail on the .org -- Inquiries.WebHome (Live Data table), one page per inquiry under Inquiries.Entries with a sheet and a status, PublicInquiry (public) filing them with its author's rights and echoing nothing a visitor sends; a hidden trap field for robots. The .com form posts there. Installed and tested: a guest's inquiry filed and deleted; one whose name and message were script macros was stored as text, nothing ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
116 lines
5.9 KiB
Bash
Executable File
116 lines
5.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# install.sh -- inquiries on the .org XWiki (Captain Bob, 2026-11-07: the .com's inquiries go to an
|
|
# admin-only page on the .org, master/detail):
|
|
#
|
|
# Inquiries.WebPreferences view, edit, comment, delete: $ADMIN_USER alone -- made first
|
|
# Inquiries.Code.InquiryClass the fields (made by Inquiries.Code.ClassSetup, viewed once as the
|
|
# administrator: the class has no REST API), bound to the sheet
|
|
# Inquiries.Code.InquirySheet the detail: one inquiry, its status set by editing the page
|
|
# Inquiries.WebHome the master: every inquiry, newest first (a Live Data table)
|
|
# Inquiries.Entries.I<time> one page per inquiry, made by PublicInquiry
|
|
# PublicInquiry.WebHome public: the .com's form posts here; it files the inquiry with its
|
|
# author's rights and thanks the visitor, echoing nothing they sent
|
|
#
|
|
# XWIKI_USER=rajames XWIKI_PASSWORD=... site/org/inquiries/install.sh [https://starshipos-f18.org]
|
|
#
|
|
# It ends by checking a guest's view, then sends one test inquiry as a guest, finds it as the
|
|
# administrator, and deletes it.
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
base=${1:-https://starshipos-f18.org}
|
|
rest="$base/rest/wikis/xwiki"
|
|
user=${XWIKI_USER:?set XWIKI_USER}
|
|
pass=${XWIKI_PASSWORD:?set XWIKI_PASSWORD}
|
|
admin=${ADMIN_USER:-XWiki.$user}
|
|
auth=(-u "$user:$pass")
|
|
|
|
xml_page() { # xml_page <title> <file>
|
|
python3 -c '
|
|
import sys
|
|
from xml.sax.saxutils import escape
|
|
print("<page xmlns=\"http://www.xwiki.org\"><title>%s</title><syntax>xwiki/2.1</syntax>"
|
|
"<content>%s</content></page>" % (escape(sys.argv[1]), escape(open(sys.argv[2]).read())))' "$1" "$2"
|
|
}
|
|
page() { # page <space path, a/b> <name> <title> <file>
|
|
local spaces=${1//\//\/spaces\/}
|
|
curl -fsS -o /dev/null -w " page ${1//\//.}.$2: %{http_code}\n" "${auth[@]}" -X PUT \
|
|
-H 'Content-Type: application/xml' --data-binary "$(xml_page "$3" "$4")" "$rest/spaces/$spaces/pages/$2"
|
|
}
|
|
objects() { # objects <space path> <page> <class>: how many
|
|
local spaces=${1//\//\/spaces\/}
|
|
curl -fsS "${auth[@]}" -H 'Accept: application/json' "$rest/spaces/$spaces/pages/$2/objects/$3" |
|
|
python3 -c 'import json,sys; print(len(json.load(sys.stdin).get("objectSummaries") or []))'
|
|
}
|
|
object() { # object <space path> <page> <class> <property=value>...: add one, as XML
|
|
local spaces=${1//\//\/spaces\/} s=$1 p=$2 c=$3; shift 3
|
|
local body
|
|
body=$(python3 -c '
|
|
import sys
|
|
from xml.sax.saxutils import escape
|
|
props = "".join("<property name=\"%s\"><value>%s</value></property>" % (escape(k), escape(v))
|
|
for k, v in (a.split("=", 1) for a in sys.argv[2:]))
|
|
print("<object xmlns=\"http://www.xwiki.org\"><className>%s</className>%s</object>" % (escape(sys.argv[1]), props))' "$c" "$@")
|
|
curl -fsS -o /dev/null -w " ${s//\//.}.$p: $c added (%{http_code})\n" "${auth[@]}" -X POST \
|
|
-H 'Content-Type: application/xml' --data-binary "$body" "$rest/spaces/$spaces/pages/$p/objects"
|
|
}
|
|
|
|
echo "=== $base, as $user"
|
|
echo "=== rights first: Inquiries is the administrator's alone"
|
|
page Inquiries WebPreferences "Inquiries rights" /dev/null
|
|
if [ "$(objects Inquiries WebPreferences XWiki.XWikiGlobalRights)" = 0 ]; then
|
|
object Inquiries WebPreferences XWiki.XWikiGlobalRights \
|
|
"levels=view,edit,comment,delete" "users=$admin" "groups=" "allow=1"
|
|
else
|
|
echo " rights there already, left as they are"
|
|
fi
|
|
|
|
echo "=== the class, its sheet"
|
|
page Inquiries/Code InquiryClass "Inquiry class" /dev/null
|
|
page Inquiries/Code ClassSetup "Inquiry class setup" ClassSetup.xwiki
|
|
out=$(curl -fsS "${auth[@]}" "$base/bin/view/Inquiries/Code/ClassSetup?basicauth=1")
|
|
case "$out" in
|
|
*CLASS-SAVED*) echo " the class's fields: made";;
|
|
*CLASS-UNCHANGED*) echo " the class's fields: there already";;
|
|
*) echo "install: the class setup did not run (programming right?)" >&2; exit 1;;
|
|
esac
|
|
page Inquiries/Code InquirySheet "Inquiry sheet" InquirySheet.xwiki
|
|
if [ "$(objects Inquiries/Code InquiryClass XWiki.ClassSheetBinding)" = 0 ]; then
|
|
object Inquiries/Code InquiryClass XWiki.ClassSheetBinding "sheet=Inquiries.Code.InquirySheet"
|
|
else
|
|
echo " sheet bound already"
|
|
fi
|
|
|
|
echo "=== the master, and the public intake"
|
|
page Inquiries WebHome "Inquiries" Inquiries.WebHome.xwiki
|
|
page PublicInquiry WebHome "Inquiry" PublicInquiry.WebHome.xwiki
|
|
|
|
echo "=== as a guest"
|
|
bad=
|
|
for p in Inquiries:401 PublicInquiry:200; do
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: application/json' "$rest/spaces/${p%%:*}/pages/WebHome")
|
|
if [ "$code" = "${p##*:}" ]; then echo " ${p%%:*}.WebHome: $code -- as it should be"
|
|
else echo " ${p%%:*}.WebHome: $code -- NOT as it should be"; bad=1; fi
|
|
done
|
|
[ -z "$bad" ] || { echo "install: a guest's view is wrong; see above" >&2; exit 1; }
|
|
|
|
echo "=== a test inquiry, sent as a guest"
|
|
reply=$(curl -fsS -X POST "$base/bin/view/PublicInquiry/" \
|
|
--data-urlencode "name=install.sh test" --data-urlencode "email=test@example.org" \
|
|
--data-urlencode "topic=General question" --data-urlencode "message=A test from site/org/inquiries/install.sh; it deletes this.")
|
|
case "$reply" in *"has been received"*) echo " thanked";; *) echo "install: no thanks for the test" >&2; exit 1;; esac
|
|
found=$(curl -fsS "${auth[@]}" -H 'Accept: application/json' "$rest/spaces/Inquiries/spaces/Entries/pages" |
|
|
python3 -c '
|
|
import json,sys
|
|
print(" ".join(p["name"] for p in json.load(sys.stdin)["pageSummaries"]))')
|
|
test_page=
|
|
for n in $found; do
|
|
if curl -fsS "${auth[@]}" -H 'Accept: application/json' \
|
|
"$rest/spaces/Inquiries/spaces/Entries/pages/$n/objects/Inquiries.Code.InquiryClass/0/properties/name" |
|
|
grep -q 'install.sh test'; then test_page=$n; fi
|
|
done
|
|
[ -n "$test_page" ] || { echo "install: the test inquiry was not filed" >&2; exit 1; }
|
|
echo " filed as Inquiries.Entries.$test_page"
|
|
curl -fsS -o /dev/null -w " deleted: %{http_code}\n" "${auth[@]}" -X DELETE \
|
|
"$rest/spaces/Inquiries/spaces/Entries/pages/$test_page"
|
|
echo "=== done: $base/bin/view/Inquiries/"
|