Compare commits
28 Commits
70632e8e56
...
d97fcec00d
Author | SHA1 | Date |
---|---|---|
Renovate | d97fcec00d | |
Renovate | 3179adba31 | |
Renovate | ee0649ec3c | |
Renovate | f2e1f81900 | |
Renovate | 4e7e0f4b6d | |
Renovate | b414c73804 | |
Jake Howard | a1808f4bb4 | |
Renovate | f1934ecdaa | |
Jake Howard | 17969086a4 | |
Jake Howard | f83c38c2f1 | |
Jake Howard | 90b18c7d72 | |
Jake Howard | 506e554230 | |
Jake Howard | 9b27baf1ba | |
Jake Howard | 3a8e6182ad | |
Jake Howard | fe43b9c683 | |
Jake Howard | 6cbac34f2d | |
Jake Howard | e5de558958 | |
Jake Howard | 1a9d981c7d | |
Jake Howard | b0f1191d8f | |
Jake Howard | bd4c1a193a | |
Jake Howard | 1934b36ec1 | |
Jake Howard | 23ce49ca8f | |
Jake Howard | 926e62518c | |
Jake Howard | ec609ae562 | |
Jake Howard | a19964199f | |
Jake Howard | c69d8d8329 | |
Jake Howard | 0424c2dba2 | |
Jake Howard | 4c600651b6 |
|
@ -1,3 +1,6 @@
|
|||
{
|
||||
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"]
|
||||
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"],
|
||||
"rules": {
|
||||
"scss/at-extend-no-missing-placeholder": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ FROM python:3.12-slim as production
|
|||
|
||||
ENV VIRTUAL_ENV=/venv
|
||||
|
||||
# renovate: datasource=github-tags depName=gchq/cyberchef
|
||||
# renovate: datasource=github-tags depName=just-containers/s6-overlay
|
||||
ENV S6_OVERLAY_VERSION=3.1.6.2
|
||||
|
||||
RUN useradd website --create-home -u 1000 && mkdir /app $VIRTUAL_ENV && chown -R website /app $VIRTUAL_ENV
|
||||
|
|
4
justfile
4
justfile
|
@ -52,5 +52,9 @@ lint_python:
|
|||
docker-compose -f {{ DEV_COMPOSE }} up -d
|
||||
docker-compose -f {{ DEV_COMPOSE }} exec web bash
|
||||
|
||||
@sh-root:
|
||||
docker-compose -f {{ DEV_COMPOSE }} up -d
|
||||
docker-compose -f {{ DEV_COMPOSE }} exec --user=root web bash
|
||||
|
||||
@down:
|
||||
docker-compose -f {{ DEV_COMPOSE }} down
|
||||
|
|
|
@ -9,19 +9,19 @@
|
|||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@fontsource/fira-code": "5.0.2",
|
||||
"@fortawesome/fontawesome-free": "6.4.0",
|
||||
"@fortawesome/fontawesome-free": "6.5.2",
|
||||
"@ledge/is-ie-11": "7.0.0",
|
||||
"bulma": "0.9.4",
|
||||
"elevator.js": "1.0.1",
|
||||
"esbuild": "0.19.2",
|
||||
"glightbox": "3.2.0",
|
||||
"esbuild": "0.20.2",
|
||||
"glightbox": "3.3.0",
|
||||
"htmx.org": "1.9.2",
|
||||
"lite-youtube-embed": "0.3.0",
|
||||
"lodash.clamp": "4.0.3",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"lodash.throttle": "4.1.1",
|
||||
"npm-run-all": "4.1.5",
|
||||
"sass": "1.70.0",
|
||||
"sass": "1.75.0",
|
||||
"shareon": "2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -94,10 +94,25 @@
|
|||
"postcss-selector-parser": "^6.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz",
|
||||
"integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
@ -110,9 +125,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -125,9 +140,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -140,9 +155,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -155,9 +170,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -170,9 +185,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -185,9 +200,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -200,9 +215,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz",
|
||||
"integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
@ -215,9 +230,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -230,9 +245,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz",
|
||||
"integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
@ -245,9 +260,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz",
|
||||
"integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
|
||||
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
|
@ -260,9 +275,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz",
|
||||
"integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
|
||||
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
|
@ -275,9 +290,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz",
|
||||
"integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
@ -290,9 +305,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz",
|
||||
"integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
|
||||
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
|
@ -305,9 +320,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz",
|
||||
"integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
|
||||
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
|
@ -320,9 +335,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -335,9 +350,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -350,9 +365,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -365,9 +380,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -380,9 +395,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -395,9 +410,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz",
|
||||
"integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
@ -410,9 +425,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -486,9 +501,9 @@
|
|||
"integrity": "sha512-T3yzwKP/JFRYdBUHjDXQfRGp9EOI7+V0uCf9ky1rZXDzMUECxuUqTfBcj60CE3oRLzzSm9fgiEGYLSvzo/S/Fw=="
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-free": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
|
||||
"integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==",
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
|
||||
"integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
@ -1133,9 +1148,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz",
|
||||
"integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
|
@ -1144,28 +1159,29 @@
|
|||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/android-arm": "0.19.2",
|
||||
"@esbuild/android-arm64": "0.19.2",
|
||||
"@esbuild/android-x64": "0.19.2",
|
||||
"@esbuild/darwin-arm64": "0.19.2",
|
||||
"@esbuild/darwin-x64": "0.19.2",
|
||||
"@esbuild/freebsd-arm64": "0.19.2",
|
||||
"@esbuild/freebsd-x64": "0.19.2",
|
||||
"@esbuild/linux-arm": "0.19.2",
|
||||
"@esbuild/linux-arm64": "0.19.2",
|
||||
"@esbuild/linux-ia32": "0.19.2",
|
||||
"@esbuild/linux-loong64": "0.19.2",
|
||||
"@esbuild/linux-mips64el": "0.19.2",
|
||||
"@esbuild/linux-ppc64": "0.19.2",
|
||||
"@esbuild/linux-riscv64": "0.19.2",
|
||||
"@esbuild/linux-s390x": "0.19.2",
|
||||
"@esbuild/linux-x64": "0.19.2",
|
||||
"@esbuild/netbsd-x64": "0.19.2",
|
||||
"@esbuild/openbsd-x64": "0.19.2",
|
||||
"@esbuild/sunos-x64": "0.19.2",
|
||||
"@esbuild/win32-arm64": "0.19.2",
|
||||
"@esbuild/win32-ia32": "0.19.2",
|
||||
"@esbuild/win32-x64": "0.19.2"
|
||||
"@esbuild/aix-ppc64": "0.20.2",
|
||||
"@esbuild/android-arm": "0.20.2",
|
||||
"@esbuild/android-arm64": "0.20.2",
|
||||
"@esbuild/android-x64": "0.20.2",
|
||||
"@esbuild/darwin-arm64": "0.20.2",
|
||||
"@esbuild/darwin-x64": "0.20.2",
|
||||
"@esbuild/freebsd-arm64": "0.20.2",
|
||||
"@esbuild/freebsd-x64": "0.20.2",
|
||||
"@esbuild/linux-arm": "0.20.2",
|
||||
"@esbuild/linux-arm64": "0.20.2",
|
||||
"@esbuild/linux-ia32": "0.20.2",
|
||||
"@esbuild/linux-loong64": "0.20.2",
|
||||
"@esbuild/linux-mips64el": "0.20.2",
|
||||
"@esbuild/linux-ppc64": "0.20.2",
|
||||
"@esbuild/linux-riscv64": "0.20.2",
|
||||
"@esbuild/linux-s390x": "0.20.2",
|
||||
"@esbuild/linux-x64": "0.20.2",
|
||||
"@esbuild/netbsd-x64": "0.20.2",
|
||||
"@esbuild/openbsd-x64": "0.20.2",
|
||||
"@esbuild/sunos-x64": "0.20.2",
|
||||
"@esbuild/win32-arm64": "0.20.2",
|
||||
"@esbuild/win32-ia32": "0.20.2",
|
||||
"@esbuild/win32-x64": "0.20.2"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
|
@ -1705,9 +1721,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/glightbox": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.2.0.tgz",
|
||||
"integrity": "sha512-iit1xYixqL4YVL+I2YJLfMeyJwvLi6FE6kY3qNKeZHEJgRIz80QU8Rm7YCyw1wOTgXvmNDnXGVhHOHRCwnDltQ=="
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.3.0.tgz",
|
||||
"integrity": "sha512-SJukatHBZZ/POMOpLUQ6/dhXf/wJTDx1wZ/FwApjseXw2WrRj3Ze9DzNCFYzca0oU7RjXQhi9o02aIZ9SuCz1A=="
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
|
@ -3303,9 +3319,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.70.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
|
||||
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
|
||||
"version": "1.75.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
|
||||
"integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
@ -4078,136 +4094,142 @@
|
|||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@esbuild/aix-ppc64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-arm": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz",
|
||||
"integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/darwin-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/darwin-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/freebsd-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/freebsd-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-arm": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz",
|
||||
"integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-ia32": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz",
|
||||
"integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-loong64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz",
|
||||
"integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
|
||||
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-mips64el": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz",
|
||||
"integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
|
||||
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-ppc64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz",
|
||||
"integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-riscv64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz",
|
||||
"integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
|
||||
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-s390x": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz",
|
||||
"integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
|
||||
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/netbsd-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/openbsd-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/sunos-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-arm64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz",
|
||||
"integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-ia32": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz",
|
||||
"integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-x64": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz",
|
||||
"integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@eslint-community/eslint-utils": {
|
||||
|
@ -4254,9 +4276,9 @@
|
|||
"integrity": "sha512-T3yzwKP/JFRYdBUHjDXQfRGp9EOI7+V0uCf9ky1rZXDzMUECxuUqTfBcj60CE3oRLzzSm9fgiEGYLSvzo/S/Fw=="
|
||||
},
|
||||
"@fortawesome/fontawesome-free": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
|
||||
"integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ=="
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
|
||||
"integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q=="
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.11.13",
|
||||
|
@ -4733,32 +4755,33 @@
|
|||
}
|
||||
},
|
||||
"esbuild": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz",
|
||||
"integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||
"requires": {
|
||||
"@esbuild/android-arm": "0.19.2",
|
||||
"@esbuild/android-arm64": "0.19.2",
|
||||
"@esbuild/android-x64": "0.19.2",
|
||||
"@esbuild/darwin-arm64": "0.19.2",
|
||||
"@esbuild/darwin-x64": "0.19.2",
|
||||
"@esbuild/freebsd-arm64": "0.19.2",
|
||||
"@esbuild/freebsd-x64": "0.19.2",
|
||||
"@esbuild/linux-arm": "0.19.2",
|
||||
"@esbuild/linux-arm64": "0.19.2",
|
||||
"@esbuild/linux-ia32": "0.19.2",
|
||||
"@esbuild/linux-loong64": "0.19.2",
|
||||
"@esbuild/linux-mips64el": "0.19.2",
|
||||
"@esbuild/linux-ppc64": "0.19.2",
|
||||
"@esbuild/linux-riscv64": "0.19.2",
|
||||
"@esbuild/linux-s390x": "0.19.2",
|
||||
"@esbuild/linux-x64": "0.19.2",
|
||||
"@esbuild/netbsd-x64": "0.19.2",
|
||||
"@esbuild/openbsd-x64": "0.19.2",
|
||||
"@esbuild/sunos-x64": "0.19.2",
|
||||
"@esbuild/win32-arm64": "0.19.2",
|
||||
"@esbuild/win32-ia32": "0.19.2",
|
||||
"@esbuild/win32-x64": "0.19.2"
|
||||
"@esbuild/aix-ppc64": "0.20.2",
|
||||
"@esbuild/android-arm": "0.20.2",
|
||||
"@esbuild/android-arm64": "0.20.2",
|
||||
"@esbuild/android-x64": "0.20.2",
|
||||
"@esbuild/darwin-arm64": "0.20.2",
|
||||
"@esbuild/darwin-x64": "0.20.2",
|
||||
"@esbuild/freebsd-arm64": "0.20.2",
|
||||
"@esbuild/freebsd-x64": "0.20.2",
|
||||
"@esbuild/linux-arm": "0.20.2",
|
||||
"@esbuild/linux-arm64": "0.20.2",
|
||||
"@esbuild/linux-ia32": "0.20.2",
|
||||
"@esbuild/linux-loong64": "0.20.2",
|
||||
"@esbuild/linux-mips64el": "0.20.2",
|
||||
"@esbuild/linux-ppc64": "0.20.2",
|
||||
"@esbuild/linux-riscv64": "0.20.2",
|
||||
"@esbuild/linux-s390x": "0.20.2",
|
||||
"@esbuild/linux-x64": "0.20.2",
|
||||
"@esbuild/netbsd-x64": "0.20.2",
|
||||
"@esbuild/openbsd-x64": "0.20.2",
|
||||
"@esbuild/sunos-x64": "0.20.2",
|
||||
"@esbuild/win32-arm64": "0.20.2",
|
||||
"@esbuild/win32-ia32": "0.20.2",
|
||||
"@esbuild/win32-x64": "0.20.2"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
|
@ -5151,9 +5174,9 @@
|
|||
}
|
||||
},
|
||||
"glightbox": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.2.0.tgz",
|
||||
"integrity": "sha512-iit1xYixqL4YVL+I2YJLfMeyJwvLi6FE6kY3qNKeZHEJgRIz80QU8Rm7YCyw1wOTgXvmNDnXGVhHOHRCwnDltQ=="
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.3.0.tgz",
|
||||
"integrity": "sha512-SJukatHBZZ/POMOpLUQ6/dhXf/wJTDx1wZ/FwApjseXw2WrRj3Ze9DzNCFYzca0oU7RjXQhi9o02aIZ9SuCz1A=="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
|
@ -6262,9 +6285,9 @@
|
|||
}
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.70.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
|
||||
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
|
||||
"version": "1.75.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
|
||||
"integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
|
||||
"requires": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
|
|
@ -29,19 +29,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@fontsource/fira-code": "5.0.2",
|
||||
"@fortawesome/fontawesome-free": "6.4.0",
|
||||
"@fortawesome/fontawesome-free": "6.5.2",
|
||||
"@ledge/is-ie-11": "7.0.0",
|
||||
"bulma": "0.9.4",
|
||||
"elevator.js": "1.0.1",
|
||||
"esbuild": "0.19.2",
|
||||
"glightbox": "3.2.0",
|
||||
"esbuild": "0.20.2",
|
||||
"glightbox": "3.3.0",
|
||||
"htmx.org": "1.9.2",
|
||||
"lite-youtube-embed": "0.3.0",
|
||||
"lodash.clamp": "4.0.3",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"lodash.throttle": "4.1.1",
|
||||
"npm-run-all": "4.1.5",
|
||||
"sass": "1.70.0",
|
||||
"sass": "1.75.0",
|
||||
"shareon": "2.5.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ django-environ==0.11.2
|
|||
whitenoise[brotli]==6.6.0
|
||||
Pygments==2.17.2
|
||||
beautifulsoup4
|
||||
lxml==5.1.0
|
||||
lxml==5.2.1
|
||||
requests
|
||||
wagtail-generic-chooser==0.6
|
||||
django-rq==2.10.1
|
||||
django-redis==5.4.0
|
||||
gunicorn==21.2.0
|
||||
psycopg2==2.9.9
|
||||
gunicorn==22.0.0
|
||||
psycopg==3.1.18
|
||||
djangorestframework
|
||||
django-htmx==1.17.2
|
||||
wagtail-metadata==5.0.0
|
||||
|
@ -32,6 +32,7 @@ wagtail-lite-youtube-embed==0.1.0
|
|||
# DRF OpenAPI dependencies
|
||||
uritemplate
|
||||
PyYAML
|
||||
inflection
|
||||
|
||||
# Use custom `wagtail-favicon` with performance improvements
|
||||
git+https://github.com/RealOrangeOne/wagtail-favicon@b892165e047b35c46d7244109b9ad9226d32a213
|
||||
|
|
|
@ -56,6 +56,9 @@ section.content {
|
|||
.gslide-image img {
|
||||
object-fit: contain !important;
|
||||
|
||||
// Manually set sizes, as mermaid images are very small
|
||||
width: 80vw !important;
|
||||
|
||||
&[src*="mermaid.ink"] {
|
||||
@include dark-mode {
|
||||
filter: invert(100%);
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
body.page-homepage {
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
|
||||
main {
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
color: $white;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
min-width: 90%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -23,7 +33,7 @@ body.page-homepage {
|
|||
min-width: 45%;
|
||||
}
|
||||
|
||||
.latest {
|
||||
.box {
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin-top: 2rem;
|
||||
background-color: color.adjust($dark, $alpha: -0.2);
|
||||
|
@ -59,4 +69,60 @@ body.page-homepage {
|
|||
#to-top {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content-list {
|
||||
width: 90%;
|
||||
|
||||
.card-image {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
transition: filter 0.25s;
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
|
||||
p {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
color: $white;
|
||||
padding: 0.5rem;
|
||||
transition: background-color 0.25s;
|
||||
width: 100%;
|
||||
background-color: rgb(0 0 0 / 55%);
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
img {
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
p {
|
||||
background-color: rgb(0 0 0 / 75%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recent-posts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
flex-grow: unset;
|
||||
margin-top: 2rem;
|
||||
|
||||
.box {
|
||||
margin: 0 auto;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.page-blogpostlistpage {
|
||||
.container.listing {
|
||||
.date-header {
|
||||
font-size: $size-2;
|
||||
font-weight: $weight-bold;
|
||||
|
|
|
@ -3,4 +3,5 @@ from rest_framework.pagination import PageNumberPagination
|
|||
|
||||
class CustomPageNumberPagination(PageNumberPagination):
|
||||
page_size = 10
|
||||
page_size_query_param = "page_size"
|
||||
max_page_size = 25
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.utils import timezone
|
|||
from django.utils.functional import cached_property
|
||||
from modelcluster.fields import ParentalManyToManyField
|
||||
from wagtail.admin.panels import FieldPanel
|
||||
from wagtail.models import PageQuerySet
|
||||
from wagtail.search import index
|
||||
from wagtailautocomplete.edit_handlers import AutocompletePanel
|
||||
|
||||
|
@ -61,6 +62,19 @@ class BlogPostPage(BaseContentPage):
|
|||
def tag_list_page_url(self) -> Optional[str]:
|
||||
return SingletonPageCache.get_url(BlogPostTagListPage)
|
||||
|
||||
@cached_property
|
||||
def tags_list(self) -> models.QuerySet:
|
||||
"""
|
||||
Use this to get a page's tags.
|
||||
"""
|
||||
tags = self.tags.order_by("slug")
|
||||
|
||||
# In drafts, `django-modelcluster` doesn't support these filters
|
||||
if isinstance(tags, PageQuerySet):
|
||||
return tags.public().live()
|
||||
|
||||
return tags
|
||||
|
||||
@cached_property
|
||||
def blog_post_list_page_url(self) -> Optional[str]:
|
||||
return SingletonPageCache.get_url(BlogPostListPage)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block post_content %}
|
||||
<section class="container">
|
||||
<section class="container listing">
|
||||
{% for page in listing_pages %}
|
||||
{% ifchanged %}
|
||||
<h2 id="date-{{ page.date|date:'Y-m' }}" class="date-header">
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{% extends "common/content_page.html" %}
|
||||
|
||||
{% load wagtail_cache %}
|
||||
{% load wagtail_cache navbar_tags %}
|
||||
|
||||
{% block post_content %}
|
||||
{{ block.super }}
|
||||
|
||||
{% if not request.is_preview %}
|
||||
{% include "common/shareon.html" %}
|
||||
|
||||
{% wagtailpagecache FRAGMENT_CACHE_TTL "similar-content" %}
|
||||
<section class="container similar-content" id="similar-content">
|
||||
<h2 class="subtitle is-size-2">Similar content</h2>
|
||||
|
@ -22,5 +22,11 @@
|
|||
|
||||
</section>
|
||||
{% endwagtailpagecache %}
|
||||
|
||||
{% include "common/comments.html" %}
|
||||
|
||||
{% if not request.user.is_authenticated %}
|
||||
{% support_pill %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.http.response import Http404, HttpResponse, HttpResponseBadRequest
|
|||
from django.shortcuts import redirect
|
||||
from django.template.defaultfilters import pluralize
|
||||
from django.utils.functional import cached_property, classproperty
|
||||
from django.utils.text import slugify
|
||||
from django.utils.text import Truncator, slugify
|
||||
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
|
||||
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
||||
from wagtail.contrib.settings.models import BaseGenericSetting, register_setting
|
||||
|
@ -31,12 +31,10 @@ from .serializers import PaginationSerializer
|
|||
from .streamfield import add_heading_anchors, get_blocks, get_content_html
|
||||
from .utils import (
|
||||
TocEntry,
|
||||
count_words,
|
||||
extract_text,
|
||||
get_site_title,
|
||||
get_table_of_contents,
|
||||
get_url_mime_type,
|
||||
truncate_string,
|
||||
)
|
||||
|
||||
|
||||
|
@ -141,16 +139,11 @@ class BaseContentPage(BasePage, MetadataMixin):
|
|||
|
||||
@cached_property
|
||||
def word_count(self) -> int:
|
||||
return count_words(self.plain_text)
|
||||
return len(self.plain_text.split())
|
||||
|
||||
@cached_property
|
||||
def summary(self) -> str:
|
||||
summary = truncate_string(self.plain_text, 50)
|
||||
|
||||
if summary and summary != self.plain_text and not summary.endswith("."):
|
||||
summary += "…"
|
||||
|
||||
return summary
|
||||
return Truncator(self.plain_text).words(50)
|
||||
|
||||
@cached_property
|
||||
def body_html(self) -> str:
|
||||
|
@ -263,7 +256,12 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
|
|||
|
||||
def get_context(self, request: HttpRequest) -> dict:
|
||||
context = super().get_context(request)
|
||||
context["listing_pages"] = self.get_paginator_page()
|
||||
listing_pages = self.get_paginator_page()
|
||||
context["listing_pages"] = listing_pages
|
||||
|
||||
# Show listing images if at least 1 page has an image
|
||||
context["show_listing_images"] = any(p.list_image_url for p in listing_pages)
|
||||
|
||||
return context
|
||||
|
||||
@cached_property
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<figure>
|
||||
<div class="image">
|
||||
<a href="{% image_url value.image 'original' %}" class="glightbox" data-gallery="content" data-height="70vh" data-width="95vw" data-alt="{{ value.caption|richtext|extract_text }}">
|
||||
<a href="{% image_url value.image 'original' %}" class="glightbox" data-gallery="content" data-height="70vh" data-alt="{{ value.caption|richtext|extract_text }}" data-title="{{ value.caption|richtext|extract_text }}">
|
||||
<img src="{% image_url value.image 'width-1500' %}" alt="{{ value.caption|richtext|extract_text }}" loading="lazy" decoding="async" />
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -20,17 +20,50 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.tags.public.live.all %}
|
||||
{% if page.tags_list %}
|
||||
<div class="icon-text is-family-code">
|
||||
<span class="icon">
|
||||
<a href="{{ page.tag_list_page_url }}" title="View all tags">
|
||||
<i class="fas fa-lg fa-tags"></i>
|
||||
</a>
|
||||
</span>
|
||||
{% for tag in page.tags.public.live.all|dictsort:"slug" %}
|
||||
{% for tag in page.tags_list %}
|
||||
<span><a title="{{ tag.name }}" href="{% pageurl tag %}">#{{ tag.slug }}</a></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.slides_url %}
|
||||
<span class="icon-text">
|
||||
<a href="{{ page.slides_url }}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lg fa-images"></i>
|
||||
</span>
|
||||
<span>Slides</span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if page.video_url %}
|
||||
<span class="icon-text">
|
||||
<a href="{{ page.video_url }}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lg fa-film"></i>
|
||||
</span>
|
||||
<span>Video</span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if page.location_name and page.location_url %}
|
||||
<span class="icon-text">
|
||||
<a href="{{ page.location_url }}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lg fa-map-marker-alt"></i>
|
||||
</span>
|
||||
<span>{{ page.location_name }}</span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endwagtailpagecache %}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{% load wagtailcore_tags wagtail_cache util_tags %}
|
||||
|
||||
{% wagtailpagecache FRAGMENT_CACHE_TTL "listing-item" breadcrumbs %}
|
||||
{% wagtailpagecache FRAGMENT_CACHE_TTL "listing-item" breadcrumbs show_listing_images %}
|
||||
<article class="media listing-item">
|
||||
<div class="columns">
|
||||
<figure class="media-left column is-3 image-column">
|
||||
<figure class="media-left column is-{{ show_listing_images|yesno:'3,1' }} image-column">
|
||||
{% if page.list_image_url %}
|
||||
<a href="{% pageurl page %}" class="image" title="{{ page.title }}">
|
||||
<img src="{{ page.list_image_url }}" alt="{{ page.hero_image_alt }}" loading="lazy" decoding="async" referrerpolicy="no-referrer" />
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{% load wagtailcore_tags %}
|
||||
|
||||
{% spaceless %}
|
||||
{{ obj.content_html | truncatewords_html:100 | safe }}
|
||||
|
||||
<p>
|
||||
<a href="{% fullpageurl obj %}">Continue Reading…</a>
|
||||
</p>
|
||||
{% endspaceless %}
|
|
@ -3,7 +3,6 @@ from django.test import SimpleTestCase
|
|||
from wagtail.rich_text import features as richtext_feature_registry
|
||||
|
||||
from website.common.utils import (
|
||||
count_words,
|
||||
extract_text,
|
||||
get_table_of_contents,
|
||||
heading_id,
|
||||
|
@ -97,13 +96,6 @@ class ExtractTextTestCase(SimpleTestCase):
|
|||
self.assertEqual(extract_text("Hello there!"), "Hello there!")
|
||||
|
||||
|
||||
class CountWordsTestCase(SimpleTestCase):
|
||||
def test_counts_words(self) -> None:
|
||||
self.assertEqual(count_words("a b c"), 3)
|
||||
self.assertEqual(count_words("Correct Horse Battery Staple"), 4)
|
||||
self.assertEqual(count_words("Hello there! How are you?"), 5)
|
||||
|
||||
|
||||
class RichTextFeaturesTestCase(SimpleTestCase):
|
||||
def test_features_exist(self) -> None:
|
||||
for editor, editor_config in settings.WAGTAILADMIN_RICH_TEXT_EDITORS.items():
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from dataclasses import dataclass
|
||||
from itertools import islice, pairwise
|
||||
from typing import Iterable, Optional, Type
|
||||
from itertools import pairwise
|
||||
from typing import Optional, Type
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup, SoupStrainer
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.http.request import HttpRequest
|
||||
from django.utils.text import re_words, slugify
|
||||
from django.utils.text import slugify
|
||||
from django_cache_decorator import django_cache_decorator
|
||||
from wagtail.models import Page, Site
|
||||
from wagtail.models import get_page_models as get_wagtail_page_models
|
||||
|
@ -69,19 +69,6 @@ def show_toolbar_callback(request: HttpRequest) -> bool:
|
|||
return settings.DEBUG
|
||||
|
||||
|
||||
def split_words(text: str) -> Iterable[str]:
|
||||
for word in re_words.split(text):
|
||||
if word and word.strip():
|
||||
yield word.strip()
|
||||
|
||||
|
||||
def count_words(text: str) -> int:
|
||||
"""
|
||||
Count the number of words in the text, without duplicating the item in memory
|
||||
"""
|
||||
return len(list(split_words(text)))
|
||||
|
||||
|
||||
def extract_text(html: str) -> str:
|
||||
"""
|
||||
Get the plain text of some HTML.
|
||||
|
@ -91,10 +78,6 @@ def extract_text(html: str) -> str:
|
|||
)
|
||||
|
||||
|
||||
def truncate_string(text: str, words: int) -> str:
|
||||
return " ".join(islice(split_words(text), words))
|
||||
|
||||
|
||||
def heading_id(heading: str) -> str:
|
||||
"""
|
||||
Convert a heading into an identifier which is valid for a HTML id attribute
|
||||
|
|
|
@ -15,6 +15,7 @@ from wagtail.query import PageQuerySet
|
|||
from wagtail_favicon.models import FaviconSettings
|
||||
from wagtail_favicon.utils import get_rendition_url
|
||||
|
||||
from website.blog.models import BlogPostPage
|
||||
from website.common.utils import get_site_title
|
||||
from website.contrib.singleton_page.utils import SingletonPageCache
|
||||
from website.home.models import HomePage
|
||||
|
@ -63,6 +64,7 @@ class KeybaseView(TemplateView):
|
|||
class AllPagesFeed(Feed):
|
||||
feed_type = CustomFeed
|
||||
link = "/"
|
||||
description_template = "feed-description.html"
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.style_tag = f'<?xml-stylesheet href="{static("contrib/pretty-feed-v3.xsl")}" type="text/xsl"?>'.encode()
|
||||
|
@ -122,12 +124,9 @@ class AllPagesFeed(Feed):
|
|||
def item_updateddate(self, item: BasePage) -> datetime:
|
||||
return item.last_published_at
|
||||
|
||||
def item_description(self, item: BasePage) -> str:
|
||||
return getattr(item, "summary", None) or item.title
|
||||
|
||||
def item_categories(self, item: BasePage) -> Optional[list[str]]:
|
||||
if tags := getattr(item, "tags", None):
|
||||
return tags.public().live().order_by("slug").values_list("slug", flat=True)
|
||||
if isinstance(item, BlogPostPage):
|
||||
return item.tags_list.values_list("slug", flat=True)
|
||||
return None
|
||||
|
||||
def item_enclosure_url(self, item: BasePage) -> Optional[str]:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<figure>
|
||||
<div class="image">
|
||||
<a href="https://mermaid.ink/svg/{{ value.pako }}" data-gallery="content" class="glightbox" data-type="image" data-height="60vh" data-width="95vw" data-alt="{{ value.caption|richtext|extract_text }}">
|
||||
<a href="https://mermaid.ink/svg/{{ value.pako }}" data-gallery="content" class="glightbox" data-type="image" data-height="70vh" data-alt="{{ value.caption|richtext|extract_text }}" data-title="{{ value.caption|richtext|extract_text }}">
|
||||
<img src="https://mermaid.ink/svg/{{ value.pako }}" referrerpolicy="no-referrer" alt="{{ value.caption|richtext|extract_text }}" loading="lazy" decoding="async" />
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
from typing import Optional, Tuple
|
||||
|
||||
from django.db import models
|
||||
from django.http.request import HttpRequest
|
||||
from django_cache_decorator import django_cache_decorator
|
||||
from wagtail.admin.panels import FieldPanel
|
||||
from wagtail.images import get_image_model_string
|
||||
from wagtail.images.models import Image
|
||||
|
@ -12,20 +9,6 @@ from website.common.models import BasePage
|
|||
from website.contrib.singleton_page.utils import SingletonPageCache
|
||||
|
||||
|
||||
@django_cache_decorator(time=600)
|
||||
def get_latest_blog_post() -> Optional[Tuple[str, str]]:
|
||||
from website.blog.models import BlogPostPage
|
||||
|
||||
try:
|
||||
latest_blog_post = (
|
||||
BlogPostPage.objects.live().public().defer_streamfields().latest("date")
|
||||
)
|
||||
except BlogPostPage.DoesNotExist:
|
||||
return None
|
||||
|
||||
return latest_blog_post.title, latest_blog_post.get_url()
|
||||
|
||||
|
||||
class HomePage(BasePage, WagtailImageMetadataMixin):
|
||||
max_count = 1
|
||||
|
||||
|
@ -55,9 +38,22 @@ class HomePage(BasePage, WagtailImageMetadataMixin):
|
|||
return self.html_title
|
||||
|
||||
def get_context(self, request: HttpRequest) -> dict:
|
||||
from website.blog.models import BlogPostListPage, BlogPostPage
|
||||
from website.search.models import SearchPage
|
||||
|
||||
context = super().get_context(request)
|
||||
context["latest_blog_post"] = get_latest_blog_post()
|
||||
context["recent_posts"] = list(
|
||||
BlogPostPage.objects.live()
|
||||
.public()
|
||||
.defer_streamfields()
|
||||
.order_by("-date")[:7]
|
||||
)
|
||||
context["latest_blog_post"] = (
|
||||
context["recent_posts"].pop(0) if context["recent_posts"] else None
|
||||
)
|
||||
context["search_page_url"] = SingletonPageCache.get_url(SearchPage, request)
|
||||
context["blog_post_list_url"] = SingletonPageCache.get_url(
|
||||
BlogPostListPage, request
|
||||
)
|
||||
|
||||
return context
|
||||
|
|
|
@ -4,21 +4,38 @@
|
|||
|
||||
{% block main %}
|
||||
<main {% if page.image %}style="background-image: url({% image_url page.image 'width-1200' %})"{% endif %}>
|
||||
<div class="heading-wrapper">
|
||||
<h1>{{ page.heading }}</h1>
|
||||
{% if search_page_url %}
|
||||
<form action="{{ search_page_url }}">
|
||||
<input id="search-input" class="input" type="text" placeholder="Search" name="q" />
|
||||
</form>
|
||||
<div class="top-section">
|
||||
<div class="heading-wrapper">
|
||||
<h1>{{ page.heading }}</h1>
|
||||
{% if search_page_url %}
|
||||
<form action="{{ search_page_url }}">
|
||||
<input id="search-input" class="input" type="text" placeholder="Search" name="q" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if latest_blog_post %}
|
||||
<div class="box latest is-size-5">
|
||||
<strong>Latest Post</strong>:
|
||||
<a href="{% pageurl latest_blog_post %}">{{ latest_blog_post.title }}</a>
|
||||
→
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if latest_blog_post %}
|
||||
<div class="box latest">
|
||||
<strong>Latest Post</strong>:
|
||||
<a href="{{ latest_blog_post.1 }}">{{ latest_blog_post.0 }}</a>
|
||||
→
|
||||
<section class="container content recent-posts">
|
||||
<h2 class="has-text-centered has-text-white is-size-3">Recent Posts</h2>
|
||||
<div class="columns content-list is-multiline">
|
||||
{% for page in recent_posts %}
|
||||
{% include "home/home_page_card.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if blog_post_list_url %}
|
||||
<div class="box">
|
||||
<a href="{{ blog_post_list_url }}">View more →</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{% load wagtail_cache wagtailcore_tags %}
|
||||
|
||||
{% wagtailpagecache FRAGMENT_CACHE_TTL "homepage-card" %}
|
||||
<div class="column is-one-third-widescreen is-half">
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<a href="{% pageurl page %}">
|
||||
<figure class="image is-16by9">
|
||||
<img src="{{ page.list_image_url }}" alt="{{ page.hero_image_alt }}" loading="lazy" decoding="async" referrerpolicy="no-referrer" />
|
||||
<p>{{ page.title }}</p>
|
||||
</figure>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endwagtailpagecache %}
|
|
@ -10,4 +10,5 @@ urlpatterns = [
|
|||
path("tags/<slug:slug>/", views.TagView.as_view()),
|
||||
path("tags/", views.TagsView.as_view()),
|
||||
path("categories/", views.TagsView.as_view()),
|
||||
path("index.json", views.PageLinksView.as_view()),
|
||||
]
|
||||
|
|
|
@ -12,6 +12,12 @@ class AllPagesFeedView(RedirectView):
|
|||
permanent = True
|
||||
|
||||
|
||||
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
|
||||
class PageLinksView(RedirectView):
|
||||
pattern_name = "api:page-links"
|
||||
permanent = True
|
||||
|
||||
|
||||
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
|
||||
class TagView(RedirectView):
|
||||
permanent = True
|
||||
|
|
|
@ -42,6 +42,7 @@ INSTALLED_APPS = [
|
|||
"website.utils",
|
||||
"website.well_known",
|
||||
"website.legacy",
|
||||
"website.talks",
|
||||
"website.contrib.code_block",
|
||||
"website.contrib.mermaid_block",
|
||||
"website.contrib.unsplash",
|
||||
|
@ -374,6 +375,11 @@ LOGGING = {
|
|||
"level": "WARNING",
|
||||
"propagate": False,
|
||||
},
|
||||
"wagtail.images": {
|
||||
"handlers": ["console"],
|
||||
"level": "DEBUG",
|
||||
"propagate": False,
|
||||
},
|
||||
"django.request": {
|
||||
"handlers": ["console"],
|
||||
"level": "ERROR",
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
from datetime import timedelta
|
||||
|
||||
from website.common.factories import BaseContentFactory, BaseListingFactory
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class TalksListPageFactory(BaseListingFactory):
|
||||
class Meta:
|
||||
model = models.TalksListPage
|
||||
|
||||
|
||||
class TalkPageFactory(BaseContentFactory):
|
||||
duration = timedelta(minutes=30)
|
||||
|
||||
class Meta:
|
||||
model = models.TalkPage
|
|
@ -0,0 +1,354 @@
|
|||
# Generated by Django 5.0.1 on 2024-03-01 17:44
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import wagtail.blocks
|
||||
import wagtail.contrib.routable_page.models
|
||||
import wagtail.contrib.typed_table_block.blocks
|
||||
import wagtail.embeds.blocks
|
||||
import wagtail.fields
|
||||
import wagtail.images.blocks
|
||||
import wagtailmetadata.models
|
||||
from django.db import migrations, models
|
||||
|
||||
import website.contrib.code_block.blocks
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("images", "0002_alter_customimage_file_alter_customrendition_file"),
|
||||
("unsplash", "0001_initial"),
|
||||
("wagtailcore", "0089_log_entry_data_json_null_to_object"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="TalkPage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.page",
|
||||
),
|
||||
),
|
||||
("subtitle", wagtail.fields.RichTextField(blank=True)),
|
||||
(
|
||||
"body",
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
("embed", wagtail.embeds.blocks.EmbedBlock()),
|
||||
("rich_text", wagtail.blocks.RichTextBlock()),
|
||||
(
|
||||
"lorem",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"paragraphs",
|
||||
wagtail.blocks.IntegerBlock(min_value=1),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
("html", wagtail.blocks.RawHTMLBlock()),
|
||||
(
|
||||
"image",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"image",
|
||||
wagtail.images.blocks.ImageChooserBlock(),
|
||||
),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"code",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"filename",
|
||||
wagtail.blocks.CharBlock(
|
||||
max_length=128, required=False
|
||||
),
|
||||
),
|
||||
(
|
||||
"language",
|
||||
wagtail.blocks.ChoiceBlock(
|
||||
choices=website.contrib.code_block.blocks.get_language_choices
|
||||
),
|
||||
),
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"tangent",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"name",
|
||||
wagtail.blocks.CharBlock(max_length=64),
|
||||
),
|
||||
(
|
||||
"content",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="simple"
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"mermaid",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"table",
|
||||
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
|
||||
[
|
||||
(
|
||||
"rich_text",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain"
|
||||
),
|
||||
),
|
||||
("numeric", wagtail.blocks.FloatBlock()),
|
||||
("text", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
("date", models.DateField(default=django.utils.timezone.now)),
|
||||
("duration", models.DurationField()),
|
||||
("slides_url", models.URLField(blank=True)),
|
||||
("video_url", models.URLField(blank=True)),
|
||||
("location_name", models.CharField(blank=True, max_length=64)),
|
||||
("location_url", models.URLField(blank=True)),
|
||||
(
|
||||
"hero_image",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="images.customimage",
|
||||
),
|
||||
),
|
||||
(
|
||||
"hero_unsplash_photo",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="unsplash.unsplashphoto",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("wagtailcore.page", wagtailmetadata.models.MetadataMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="TalksListPage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.page",
|
||||
),
|
||||
),
|
||||
(
|
||||
"body",
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
("embed", wagtail.embeds.blocks.EmbedBlock()),
|
||||
("rich_text", wagtail.blocks.RichTextBlock()),
|
||||
(
|
||||
"lorem",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"paragraphs",
|
||||
wagtail.blocks.IntegerBlock(min_value=1),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
("html", wagtail.blocks.RawHTMLBlock()),
|
||||
(
|
||||
"image",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"image",
|
||||
wagtail.images.blocks.ImageChooserBlock(),
|
||||
),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"code",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"filename",
|
||||
wagtail.blocks.CharBlock(
|
||||
max_length=128, required=False
|
||||
),
|
||||
),
|
||||
(
|
||||
"language",
|
||||
wagtail.blocks.ChoiceBlock(
|
||||
choices=website.contrib.code_block.blocks.get_language_choices
|
||||
),
|
||||
),
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"tangent",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"name",
|
||||
wagtail.blocks.CharBlock(max_length=64),
|
||||
),
|
||||
(
|
||||
"content",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="simple"
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"mermaid",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"table",
|
||||
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
|
||||
[
|
||||
(
|
||||
"rich_text",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain"
|
||||
),
|
||||
),
|
||||
("numeric", wagtail.blocks.FloatBlock()),
|
||||
("text", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"hero_image",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="images.customimage",
|
||||
),
|
||||
),
|
||||
(
|
||||
"hero_unsplash_photo",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="unsplash.unsplashphoto",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=(
|
||||
wagtail.contrib.routable_page.models.RoutablePageMixin,
|
||||
"wagtailcore.page",
|
||||
wagtailmetadata.models.MetadataMixin,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,62 @@
|
|||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
|
||||
|
||||
from website.common.models import BaseContentPage, BaseListingPage
|
||||
|
||||
|
||||
class TalksListPage(BaseListingPage):
|
||||
max_count = 1
|
||||
subpage_types = ["talks.TalkPage"]
|
||||
|
||||
|
||||
class TalkPage(BaseContentPage):
|
||||
subpage_types: list[Any] = []
|
||||
parent_page_types = [TalksListPage]
|
||||
|
||||
date = models.DateField(default=timezone.now)
|
||||
|
||||
duration = models.DurationField()
|
||||
|
||||
slides_url = models.URLField(blank=True)
|
||||
video_url = models.URLField(blank=True)
|
||||
|
||||
location_name = models.CharField(max_length=64, blank=True)
|
||||
location_url = models.URLField(blank=True)
|
||||
|
||||
content_panels = BaseContentPage.content_panels + [
|
||||
MultiFieldPanel(
|
||||
[
|
||||
FieldPanel("slides_url"),
|
||||
FieldPanel("video_url"),
|
||||
],
|
||||
heading="Media",
|
||||
),
|
||||
MultiFieldPanel(
|
||||
[
|
||||
FieldPanel("location_name"),
|
||||
FieldPanel("location_url"),
|
||||
],
|
||||
heading="Location",
|
||||
),
|
||||
FieldPanel("duration"),
|
||||
]
|
||||
|
||||
promote_panels = BaseContentPage.promote_panels + [
|
||||
FieldPanel("date"),
|
||||
]
|
||||
|
||||
@property
|
||||
def show_table_of_contents(self) -> bool:
|
||||
return False
|
||||
|
||||
@property
|
||||
def reading_time(self) -> timedelta:
|
||||
return self.duration
|
||||
|
||||
@property
|
||||
def word_count(self) -> int:
|
||||
return 0
|
|
@ -0,0 +1,11 @@
|
|||
{% extends "common/content_page.html" %}
|
||||
|
||||
{% load wagtailembeds_tags %}
|
||||
|
||||
{% block pre_content %}
|
||||
{% if page.video_url %}
|
||||
<section class="container mb-5 content">
|
||||
<div class="block-embed">{% embed page.video_url %}</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,26 @@
|
|||
{% extends "common/listing_page.html" %}
|
||||
|
||||
{% load wagtailroutablepage_tags %}
|
||||
|
||||
{% block post_content %}
|
||||
<section class="container listing">
|
||||
{% for page in listing_pages %}
|
||||
{% ifchanged %}
|
||||
<h2 id="date-{{ page.date.year }}" class="date-header">
|
||||
<time datetime="{{ page.date.year }}" title="{{ page.date.year }}">
|
||||
{{ page.date.year }}
|
||||
</time>
|
||||
</h2>
|
||||
{% endifchanged %}
|
||||
|
||||
{% include "common/listing-item.html" %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{% if listing_pages.has_other_pages %}
|
||||
<section class="container">
|
||||
<hr class="my-5" />
|
||||
{% include "common/pagination.html" with page=listing_pages %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,47 @@
|
|||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from website.home.models import HomePage
|
||||
|
||||
from .factories import TalkPageFactory, TalksListPageFactory
|
||||
|
||||
|
||||
class TalkPageTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls) -> None:
|
||||
cls.home_page = HomePage.objects.get()
|
||||
cls.list_page = TalksListPageFactory(parent=cls.home_page)
|
||||
cls.page = TalkPageFactory(parent=cls.list_page)
|
||||
|
||||
def test_accessible(self) -> None:
|
||||
response = self.client.get(self.page.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_queries(self) -> None:
|
||||
with self.assertNumQueries(34):
|
||||
self.client.get(self.page.url)
|
||||
|
||||
|
||||
class TalksListPageTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls) -> None:
|
||||
cls.home_page = HomePage.objects.get()
|
||||
cls.page = TalksListPageFactory(parent=cls.home_page)
|
||||
|
||||
TalkPageFactory(parent=cls.page)
|
||||
TalkPageFactory(parent=cls.page)
|
||||
|
||||
def test_accessible(self) -> None:
|
||||
response = self.client.get(self.page.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.context["listing_pages"]), 2)
|
||||
|
||||
def test_queries(self) -> None:
|
||||
with self.assertNumQueries(35):
|
||||
self.client.get(self.page.url)
|
||||
|
||||
def test_feed_accessible(self) -> None:
|
||||
response = self.client.get(self.page.url + self.page.reverse_subpage("feed"))
|
||||
self.assertRedirects(
|
||||
response, reverse("feed"), status_code=301, fetch_redirect_response=True
|
||||
)
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import timedelta
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.http.request import HttpRequest
|
||||
|
@ -56,10 +57,13 @@ def activitypub_proxy(request: HttpRequest) -> HttpResponse:
|
|||
if not settings.ACTIVITYPUB_HOST:
|
||||
raise Http404
|
||||
|
||||
activitypub_url = urljoin(
|
||||
"https://" + settings.ACTIVITYPUB_HOST,
|
||||
request.path,
|
||||
allow_fragments=True,
|
||||
)
|
||||
|
||||
try:
|
||||
return proxy_view(
|
||||
request,
|
||||
f"https://{settings.ACTIVITYPUB_HOST}{request.path}",
|
||||
)
|
||||
return proxy_view(request, activitypub_url)
|
||||
except RequestException:
|
||||
return HttpResponse(status_code=502)
|
||||
return HttpResponse(status=502)
|
||||
|
|
Loading…
Reference in New Issue