@geekity/cms 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/README.md +2091 -214
  2. package/admin/components/fields.njk +165 -0
  3. package/admin/layouts/base.njk +1 -1
  4. package/admin/layouts/settings-page.njk +26 -0
  5. package/admin/layouts/shell.njk +17 -3
  6. package/admin/pages/account/forgot.njk +47 -0
  7. package/admin/pages/account/login.njk +26 -0
  8. package/admin/pages/account/reset.njk +38 -0
  9. package/admin/pages/account/setup.njk +28 -0
  10. package/admin/pages/appearance/themes.njk +69 -0
  11. package/admin/pages/comments/all.njk +133 -0
  12. package/admin/{layouts/dashboard.njk → pages/dashboard/home.njk} +8 -15
  13. package/admin/{layouts/document-conflict.njk → pages/documents/conflict.njk} +1 -0
  14. package/admin/pages/documents/editor.njk +196 -0
  15. package/admin/{layouts/document-list.njk → pages/documents/list.njk} +19 -2
  16. package/admin/pages/documents/taxonomy.njk +95 -0
  17. package/admin/pages/federation/followers.njk +238 -0
  18. package/admin/pages/media/library.njk +160 -0
  19. package/admin/pages/messages/all.njk +110 -0
  20. package/admin/{layouts → pages}/placeholder.njk +5 -1
  21. package/admin/pages/settings/discussion.njk +99 -0
  22. package/admin/pages/settings/email.njk +203 -0
  23. package/admin/pages/settings/federation.njk +75 -0
  24. package/admin/pages/settings/general.njk +29 -0
  25. package/admin/pages/settings/permalinks.njk +50 -0
  26. package/admin/pages/settings/reading.njk +72 -0
  27. package/admin/pages/users/edit.njk +239 -0
  28. package/admin/pages/users/list.njk +48 -0
  29. package/admin/pages/users/new.njk +41 -0
  30. package/admin/static/admin.css +498 -3
  31. package/admin/static/copy.js +52 -0
  32. package/admin/static/editor.js +16 -16
  33. package/admin/static/slug.js +61 -0
  34. package/dist/admin/accounts.d.ts +413 -0
  35. package/dist/admin/accounts.d.ts.map +1 -0
  36. package/dist/admin/accounts.js +680 -0
  37. package/dist/admin/accounts.js.map +1 -0
  38. package/dist/admin/appearance.d.ts +80 -0
  39. package/dist/admin/appearance.d.ts.map +1 -0
  40. package/dist/admin/appearance.js +136 -0
  41. package/dist/admin/appearance.js.map +1 -0
  42. package/dist/admin/comments.d.ts +90 -0
  43. package/dist/admin/comments.d.ts.map +1 -0
  44. package/dist/admin/comments.js +244 -0
  45. package/dist/admin/comments.js.map +1 -0
  46. package/dist/admin/credentials.d.ts +26 -2
  47. package/dist/admin/credentials.d.ts.map +1 -1
  48. package/dist/admin/credentials.js +35 -2
  49. package/dist/admin/credentials.js.map +1 -1
  50. package/dist/admin/documents.d.ts +127 -10
  51. package/dist/admin/documents.d.ts.map +1 -1
  52. package/dist/admin/documents.js +360 -41
  53. package/dist/admin/documents.js.map +1 -1
  54. package/dist/admin/federation.d.ts +151 -50
  55. package/dist/admin/federation.d.ts.map +1 -1
  56. package/dist/admin/federation.js +225 -93
  57. package/dist/admin/federation.js.map +1 -1
  58. package/dist/admin/formatting.d.ts +20 -0
  59. package/dist/admin/formatting.d.ts.map +1 -0
  60. package/dist/admin/formatting.js +72 -0
  61. package/dist/admin/formatting.js.map +1 -0
  62. package/dist/admin/headers.d.ts +65 -0
  63. package/dist/admin/headers.d.ts.map +1 -0
  64. package/dist/admin/headers.js +104 -0
  65. package/dist/admin/headers.js.map +1 -0
  66. package/dist/admin/index.d.ts +38 -12
  67. package/dist/admin/index.d.ts.map +1 -1
  68. package/dist/admin/index.js +25 -7
  69. package/dist/admin/index.js.map +1 -1
  70. package/dist/admin/media.d.ts +168 -0
  71. package/dist/admin/media.d.ts.map +1 -0
  72. package/dist/admin/media.js +295 -0
  73. package/dist/admin/media.js.map +1 -0
  74. package/dist/admin/menu.d.ts +79 -0
  75. package/dist/admin/menu.d.ts.map +1 -0
  76. package/dist/admin/menu.js +126 -0
  77. package/dist/admin/menu.js.map +1 -0
  78. package/dist/admin/messages.d.ts +80 -0
  79. package/dist/admin/messages.d.ts.map +1 -0
  80. package/dist/admin/messages.js +152 -0
  81. package/dist/admin/messages.js.map +1 -0
  82. package/dist/admin/preview.d.ts.map +1 -1
  83. package/dist/admin/preview.js +31 -7
  84. package/dist/admin/preview.js.map +1 -1
  85. package/dist/admin/recovery.d.ts +92 -0
  86. package/dist/admin/recovery.d.ts.map +1 -0
  87. package/dist/admin/recovery.js +229 -0
  88. package/dist/admin/recovery.js.map +1 -0
  89. package/dist/admin/routes.d.ts +0 -20
  90. package/dist/admin/routes.d.ts.map +1 -1
  91. package/dist/admin/routes.js +155 -105
  92. package/dist/admin/routes.js.map +1 -1
  93. package/dist/admin/settings-discussion.d.ts +37 -0
  94. package/dist/admin/settings-discussion.d.ts.map +1 -0
  95. package/dist/admin/settings-discussion.js +117 -0
  96. package/dist/admin/settings-discussion.js.map +1 -0
  97. package/dist/admin/settings-email.d.ts +54 -0
  98. package/dist/admin/settings-email.d.ts.map +1 -0
  99. package/dist/admin/settings-email.js +189 -0
  100. package/dist/admin/settings-email.js.map +1 -0
  101. package/dist/admin/settings-federation.d.ts +39 -0
  102. package/dist/admin/settings-federation.d.ts.map +1 -0
  103. package/dist/admin/settings-federation.js +103 -0
  104. package/dist/admin/settings-federation.js.map +1 -0
  105. package/dist/admin/settings-general.d.ts +12 -0
  106. package/dist/admin/settings-general.d.ts.map +1 -0
  107. package/dist/admin/settings-general.js +48 -0
  108. package/dist/admin/settings-general.js.map +1 -0
  109. package/dist/admin/settings-page.d.ts +102 -0
  110. package/dist/admin/settings-page.d.ts.map +1 -0
  111. package/dist/admin/settings-page.js +126 -0
  112. package/dist/admin/settings-page.js.map +1 -0
  113. package/dist/admin/settings-pages.d.ts +19 -0
  114. package/dist/admin/settings-pages.d.ts.map +1 -0
  115. package/dist/admin/settings-pages.js +33 -0
  116. package/dist/admin/settings-pages.js.map +1 -0
  117. package/dist/admin/settings-permalinks.d.ts +16 -0
  118. package/dist/admin/settings-permalinks.d.ts.map +1 -0
  119. package/dist/admin/settings-permalinks.js +32 -0
  120. package/dist/admin/settings-permalinks.js.map +1 -0
  121. package/dist/admin/settings-reading.d.ts +33 -0
  122. package/dist/admin/settings-reading.d.ts.map +1 -0
  123. package/dist/admin/settings-reading.js +48 -0
  124. package/dist/admin/settings-reading.js.map +1 -0
  125. package/dist/admin/settings.d.ts +325 -96
  126. package/dist/admin/settings.d.ts.map +1 -1
  127. package/dist/admin/settings.js +650 -308
  128. package/dist/admin/settings.js.map +1 -1
  129. package/dist/admin/store.d.ts +606 -152
  130. package/dist/admin/store.d.ts.map +1 -1
  131. package/dist/admin/store.js +863 -268
  132. package/dist/admin/store.js.map +1 -1
  133. package/dist/admin/taxonomy.d.ts +114 -0
  134. package/dist/admin/taxonomy.d.ts.map +1 -0
  135. package/dist/admin/taxonomy.js +289 -0
  136. package/dist/admin/taxonomy.js.map +1 -0
  137. package/dist/admin/templates.d.ts +44 -14
  138. package/dist/admin/templates.d.ts.map +1 -1
  139. package/dist/admin/templates.js +44 -14
  140. package/dist/admin/templates.js.map +1 -1
  141. package/dist/admin/throttle.d.ts +84 -0
  142. package/dist/admin/throttle.d.ts.map +1 -0
  143. package/dist/admin/throttle.js +143 -0
  144. package/dist/admin/throttle.js.map +1 -0
  145. package/dist/admin/uploads.d.ts +28 -2
  146. package/dist/admin/uploads.d.ts.map +1 -1
  147. package/dist/admin/uploads.js +43 -4
  148. package/dist/admin/uploads.js.map +1 -1
  149. package/dist/admin/users.d.ts +100 -13
  150. package/dist/admin/users.d.ts.map +1 -1
  151. package/dist/admin/users.js +412 -39
  152. package/dist/admin/users.js.map +1 -1
  153. package/dist/cache.d.ts +141 -0
  154. package/dist/cache.d.ts.map +1 -0
  155. package/dist/cache.js +207 -0
  156. package/dist/cache.js.map +1 -0
  157. package/dist/cli.d.ts +14 -2
  158. package/dist/cli.d.ts.map +1 -1
  159. package/dist/cli.js +424 -21
  160. package/dist/cli.js.map +1 -1
  161. package/dist/comments/akismet.d.ts +130 -0
  162. package/dist/comments/akismet.d.ts.map +1 -0
  163. package/dist/comments/akismet.js +274 -0
  164. package/dist/comments/akismet.js.map +1 -0
  165. package/dist/comments/conversation.d.ts +29 -0
  166. package/dist/comments/conversation.d.ts.map +1 -0
  167. package/dist/comments/conversation.js +60 -0
  168. package/dist/comments/conversation.js.map +1 -0
  169. package/dist/comments/form.d.ts +75 -0
  170. package/dist/comments/form.d.ts.map +1 -0
  171. package/dist/comments/form.js +64 -0
  172. package/dist/comments/form.js.map +1 -0
  173. package/dist/comments/index.d.ts +15 -0
  174. package/dist/comments/index.d.ts.map +1 -0
  175. package/dist/comments/index.js +9 -0
  176. package/dist/comments/index.js.map +1 -0
  177. package/dist/comments/markdown.d.ts +8 -0
  178. package/dist/comments/markdown.d.ts.map +1 -0
  179. package/dist/comments/markdown.js +85 -0
  180. package/dist/comments/markdown.js.map +1 -0
  181. package/dist/comments/moderate.d.ts +71 -0
  182. package/dist/comments/moderate.d.ts.map +1 -0
  183. package/dist/comments/moderate.js +88 -0
  184. package/dist/comments/moderate.js.map +1 -0
  185. package/dist/comments/policy.d.ts +57 -0
  186. package/dist/comments/policy.d.ts.map +1 -0
  187. package/dist/comments/policy.js +80 -0
  188. package/dist/comments/policy.js.map +1 -0
  189. package/dist/comments/records.d.ts +266 -0
  190. package/dist/comments/records.d.ts.map +1 -0
  191. package/dist/comments/records.js +487 -0
  192. package/dist/comments/records.js.map +1 -0
  193. package/dist/comments/routes.d.ts +58 -0
  194. package/dist/comments/routes.d.ts.map +1 -0
  195. package/dist/comments/routes.js +177 -0
  196. package/dist/comments/routes.js.map +1 -0
  197. package/dist/comments/submission.d.ts +263 -0
  198. package/dist/comments/submission.d.ts.map +1 -0
  199. package/dist/comments/submission.js +241 -0
  200. package/dist/comments/submission.js.map +1 -0
  201. package/dist/config.d.ts +190 -5
  202. package/dist/config.d.ts.map +1 -1
  203. package/dist/config.js +118 -6
  204. package/dist/config.js.map +1 -1
  205. package/dist/contact/delivery.d.ts +60 -0
  206. package/dist/contact/delivery.d.ts.map +1 -0
  207. package/dist/contact/delivery.js +87 -0
  208. package/dist/contact/delivery.js.map +1 -0
  209. package/dist/contact/form.d.ts +130 -0
  210. package/dist/contact/form.d.ts.map +1 -0
  211. package/dist/contact/form.js +163 -0
  212. package/dist/contact/form.js.map +1 -0
  213. package/dist/contact/index.d.ts +17 -0
  214. package/dist/contact/index.d.ts.map +1 -0
  215. package/dist/contact/index.js +13 -0
  216. package/dist/contact/index.js.map +1 -0
  217. package/dist/contact/records.d.ts +124 -0
  218. package/dist/contact/records.d.ts.map +1 -0
  219. package/dist/contact/records.js +218 -0
  220. package/dist/contact/records.js.map +1 -0
  221. package/dist/contact/routes.d.ts +17 -0
  222. package/dist/contact/routes.d.ts.map +1 -0
  223. package/dist/contact/routes.js +127 -0
  224. package/dist/contact/routes.js.map +1 -0
  225. package/dist/contact/submission.d.ts +103 -0
  226. package/dist/contact/submission.d.ts.map +1 -0
  227. package/dist/contact/submission.js +166 -0
  228. package/dist/contact/submission.js.map +1 -0
  229. package/dist/content/document.d.ts +7 -2
  230. package/dist/content/document.d.ts.map +1 -1
  231. package/dist/content/document.js +1 -0
  232. package/dist/content/document.js.map +1 -1
  233. package/dist/content/index.d.ts +7 -2
  234. package/dist/content/index.d.ts.map +1 -1
  235. package/dist/content/index.js +4 -1
  236. package/dist/content/index.js.map +1 -1
  237. package/dist/content/markdown.d.ts.map +1 -1
  238. package/dist/content/markdown.js +24 -2
  239. package/dist/content/markdown.js.map +1 -1
  240. package/dist/content/parser.d.ts.map +1 -1
  241. package/dist/content/parser.js +11 -4
  242. package/dist/content/parser.js.map +1 -1
  243. package/dist/content/save.d.ts +5 -0
  244. package/dist/content/save.d.ts.map +1 -1
  245. package/dist/content/save.js +44 -1
  246. package/dist/content/save.js.map +1 -1
  247. package/dist/content/schedule.d.ts +125 -0
  248. package/dist/content/schedule.d.ts.map +1 -0
  249. package/dist/content/schedule.js +207 -0
  250. package/dist/content/schedule.js.map +1 -0
  251. package/dist/content/search.d.ts +64 -0
  252. package/dist/content/search.d.ts.map +1 -0
  253. package/dist/content/search.js +136 -0
  254. package/dist/content/search.js.map +1 -0
  255. package/dist/content/store.d.ts +202 -7
  256. package/dist/content/store.d.ts.map +1 -1
  257. package/dist/content/store.js +374 -67
  258. package/dist/content/store.js.map +1 -1
  259. package/dist/content/sync.d.ts +7 -1
  260. package/dist/content/sync.d.ts.map +1 -1
  261. package/dist/content/sync.js +18 -10
  262. package/dist/content/sync.js.map +1 -1
  263. package/dist/content/time.d.ts +56 -0
  264. package/dist/content/time.d.ts.map +1 -0
  265. package/dist/content/time.js +141 -0
  266. package/dist/content/time.js.map +1 -0
  267. package/dist/content/writer.d.ts.map +1 -1
  268. package/dist/content/writer.js +3 -0
  269. package/dist/content/writer.js.map +1 -1
  270. package/dist/env.d.ts +58 -4
  271. package/dist/env.d.ts.map +1 -1
  272. package/dist/federation/actor.d.ts +95 -41
  273. package/dist/federation/actor.d.ts.map +1 -1
  274. package/dist/federation/actor.js +180 -53
  275. package/dist/federation/actor.js.map +1 -1
  276. package/dist/federation/article.d.ts +44 -17
  277. package/dist/federation/article.d.ts.map +1 -1
  278. package/dist/federation/article.js +98 -46
  279. package/dist/federation/article.js.map +1 -1
  280. package/dist/federation/delivery.d.ts +63 -15
  281. package/dist/federation/delivery.d.ts.map +1 -1
  282. package/dist/federation/delivery.js +154 -80
  283. package/dist/federation/delivery.js.map +1 -1
  284. package/dist/federation/federation.d.ts +22 -17
  285. package/dist/federation/federation.d.ts.map +1 -1
  286. package/dist/federation/federation.js +98 -77
  287. package/dist/federation/federation.js.map +1 -1
  288. package/dist/federation/followers.d.ts +3 -2
  289. package/dist/federation/followers.d.ts.map +1 -1
  290. package/dist/federation/followers.js +6 -5
  291. package/dist/federation/followers.js.map +1 -1
  292. package/dist/federation/import-wordpress.d.ts +128 -0
  293. package/dist/federation/import-wordpress.d.ts.map +1 -0
  294. package/dist/federation/import-wordpress.js +426 -0
  295. package/dist/federation/import-wordpress.js.map +1 -0
  296. package/dist/federation/inbox.d.ts +36 -13
  297. package/dist/federation/inbox.d.ts.map +1 -1
  298. package/dist/federation/inbox.js +120 -49
  299. package/dist/federation/inbox.js.map +1 -1
  300. package/dist/federation/index.d.ts +22 -10
  301. package/dist/federation/index.d.ts.map +1 -1
  302. package/dist/federation/index.js +13 -8
  303. package/dist/federation/index.js.map +1 -1
  304. package/dist/federation/keys.d.ts +98 -20
  305. package/dist/federation/keys.d.ts.map +1 -1
  306. package/dist/federation/keys.js +269 -39
  307. package/dist/federation/keys.js.map +1 -1
  308. package/dist/federation/mount.d.ts +42 -10
  309. package/dist/federation/mount.d.ts.map +1 -1
  310. package/dist/federation/mount.js +266 -26
  311. package/dist/federation/mount.js.map +1 -1
  312. package/dist/federation/paths.d.ts +23 -35
  313. package/dist/federation/paths.d.ts.map +1 -1
  314. package/dist/federation/paths.js +26 -39
  315. package/dist/federation/paths.js.map +1 -1
  316. package/dist/federation/records.d.ts +223 -0
  317. package/dist/federation/records.d.ts.map +1 -0
  318. package/dist/federation/records.js +506 -0
  319. package/dist/federation/records.js.map +1 -0
  320. package/dist/federation/relays.d.ts +114 -0
  321. package/dist/federation/relays.d.ts.map +1 -0
  322. package/dist/federation/relays.js +290 -0
  323. package/dist/federation/relays.js.map +1 -0
  324. package/dist/federation/replies.d.ts +80 -0
  325. package/dist/federation/replies.d.ts.map +1 -0
  326. package/dist/federation/replies.js +166 -0
  327. package/dist/federation/replies.js.map +1 -0
  328. package/dist/federation/wordpress.d.ts +131 -0
  329. package/dist/federation/wordpress.d.ts.map +1 -0
  330. package/dist/federation/wordpress.js +341 -0
  331. package/dist/federation/wordpress.js.map +1 -0
  332. package/dist/files/atomic.d.ts +103 -0
  333. package/dist/files/atomic.d.ts.map +1 -0
  334. package/dist/files/atomic.js +150 -0
  335. package/dist/files/atomic.js.map +1 -0
  336. package/dist/files/index.d.ts +3 -0
  337. package/dist/files/index.d.ts.map +1 -0
  338. package/dist/files/index.js +2 -0
  339. package/dist/files/index.js.map +1 -0
  340. package/dist/forms/protection.d.ts +85 -0
  341. package/dist/forms/protection.d.ts.map +1 -0
  342. package/dist/forms/protection.js +116 -0
  343. package/dist/forms/protection.js.map +1 -0
  344. package/dist/images/icons.d.ts +59 -0
  345. package/dist/images/icons.d.ts.map +1 -0
  346. package/dist/images/icons.js +131 -0
  347. package/dist/images/icons.js.map +1 -0
  348. package/dist/images/index.d.ts +7 -0
  349. package/dist/images/index.d.ts.map +1 -0
  350. package/dist/images/index.js +4 -0
  351. package/dist/images/index.js.map +1 -0
  352. package/dist/images/markup.d.ts +47 -0
  353. package/dist/images/markup.d.ts.map +1 -0
  354. package/dist/images/markup.js +175 -0
  355. package/dist/images/markup.js.map +1 -0
  356. package/dist/images/paths.d.ts +34 -0
  357. package/dist/images/paths.d.ts.map +1 -0
  358. package/dist/images/paths.js +41 -0
  359. package/dist/images/paths.js.map +1 -0
  360. package/dist/images/variants.d.ts +104 -0
  361. package/dist/images/variants.d.ts.map +1 -0
  362. package/dist/images/variants.js +320 -0
  363. package/dist/images/variants.js.map +1 -0
  364. package/dist/index.d.ts +123 -19
  365. package/dist/index.d.ts.map +1 -1
  366. package/dist/index.js +327 -25
  367. package/dist/index.js.map +1 -1
  368. package/dist/init.d.ts +18 -0
  369. package/dist/init.d.ts.map +1 -1
  370. package/dist/init.js +34 -3
  371. package/dist/init.js.map +1 -1
  372. package/dist/mail/brevo.d.ts +35 -0
  373. package/dist/mail/brevo.d.ts.map +1 -0
  374. package/dist/mail/brevo.js +85 -0
  375. package/dist/mail/brevo.js.map +1 -0
  376. package/dist/mail/credentials.d.ts +62 -0
  377. package/dist/mail/credentials.d.ts.map +1 -0
  378. package/dist/mail/credentials.js +86 -0
  379. package/dist/mail/credentials.js.map +1 -0
  380. package/dist/mail/index.d.ts +15 -0
  381. package/dist/mail/index.d.ts.map +1 -0
  382. package/dist/mail/index.js +8 -0
  383. package/dist/mail/index.js.map +1 -0
  384. package/dist/mail/memory.d.ts +35 -0
  385. package/dist/mail/memory.d.ts.map +1 -0
  386. package/dist/mail/memory.js +29 -0
  387. package/dist/mail/memory.js.map +1 -0
  388. package/dist/mail/provider.d.ts +80 -0
  389. package/dist/mail/provider.d.ts.map +1 -0
  390. package/dist/mail/provider.js +23 -0
  391. package/dist/mail/provider.js.map +1 -0
  392. package/dist/mail/service.d.ts +156 -0
  393. package/dist/mail/service.d.ts.map +1 -0
  394. package/dist/mail/service.js +265 -0
  395. package/dist/mail/service.js.map +1 -0
  396. package/dist/mail/smtp.d.ts +39 -0
  397. package/dist/mail/smtp.d.ts.map +1 -0
  398. package/dist/mail/smtp.js +58 -0
  399. package/dist/mail/smtp.js.map +1 -0
  400. package/dist/mail/templates.d.ts +91 -0
  401. package/dist/mail/templates.d.ts.map +1 -0
  402. package/dist/mail/templates.js +69 -0
  403. package/dist/mail/templates.js.map +1 -0
  404. package/dist/notifications/comments.d.ts +66 -0
  405. package/dist/notifications/comments.d.ts.map +1 -0
  406. package/dist/notifications/comments.js +151 -0
  407. package/dist/notifications/comments.js.map +1 -0
  408. package/dist/notifications/digest.d.ts +135 -0
  409. package/dist/notifications/digest.d.ts.map +1 -0
  410. package/dist/notifications/digest.js +287 -0
  411. package/dist/notifications/digest.js.map +1 -0
  412. package/dist/notifications/index.d.ts +13 -0
  413. package/dist/notifications/index.d.ts.map +1 -0
  414. package/dist/notifications/index.js +8 -0
  415. package/dist/notifications/index.js.map +1 -0
  416. package/dist/notifications/links.d.ts +60 -0
  417. package/dist/notifications/links.d.ts.map +1 -0
  418. package/dist/notifications/links.js +63 -0
  419. package/dist/notifications/links.js.map +1 -0
  420. package/dist/notifications/optouts.d.ts +48 -0
  421. package/dist/notifications/optouts.d.ts.map +1 -0
  422. package/dist/notifications/optouts.js +93 -0
  423. package/dist/notifications/optouts.js.map +1 -0
  424. package/dist/notifications/preferences.d.ts +158 -0
  425. package/dist/notifications/preferences.d.ts.map +1 -0
  426. package/dist/notifications/preferences.js +195 -0
  427. package/dist/notifications/preferences.js.map +1 -0
  428. package/dist/notifications/routes.d.ts +28 -0
  429. package/dist/notifications/routes.d.ts.map +1 -0
  430. package/dist/notifications/routes.js +184 -0
  431. package/dist/notifications/routes.js.map +1 -0
  432. package/dist/notifications/tokens.d.ts +60 -0
  433. package/dist/notifications/tokens.d.ts.map +1 -0
  434. package/dist/notifications/tokens.js +125 -0
  435. package/dist/notifications/tokens.js.map +1 -0
  436. package/dist/notify.d.ts +84 -0
  437. package/dist/notify.d.ts.map +1 -0
  438. package/dist/notify.js +149 -0
  439. package/dist/notify.js.map +1 -0
  440. package/dist/web/archive.d.ts +49 -0
  441. package/dist/web/archive.d.ts.map +1 -0
  442. package/dist/web/archive.js +59 -0
  443. package/dist/web/archive.js.map +1 -0
  444. package/dist/web/assets.d.ts +6 -5
  445. package/dist/web/assets.d.ts.map +1 -1
  446. package/dist/web/assets.js +5 -7
  447. package/dist/web/assets.js.map +1 -1
  448. package/dist/web/authors.d.ts +143 -0
  449. package/dist/web/authors.d.ts.map +1 -0
  450. package/dist/web/authors.js +178 -0
  451. package/dist/web/authors.js.map +1 -0
  452. package/dist/web/comments.d.ts +60 -0
  453. package/dist/web/comments.d.ts.map +1 -0
  454. package/dist/web/comments.js +207 -0
  455. package/dist/web/comments.js.map +1 -0
  456. package/dist/web/context.d.ts +162 -28
  457. package/dist/web/context.d.ts.map +1 -1
  458. package/dist/web/context.js +123 -26
  459. package/dist/web/context.js.map +1 -1
  460. package/dist/web/conversation.d.ts +209 -0
  461. package/dist/web/conversation.d.ts.map +1 -0
  462. package/dist/web/conversation.js +557 -0
  463. package/dist/web/conversation.js.map +1 -0
  464. package/dist/web/documents.d.ts +36 -6
  465. package/dist/web/documents.d.ts.map +1 -1
  466. package/dist/web/documents.js +52 -10
  467. package/dist/web/documents.js.map +1 -1
  468. package/dist/web/feed-atom.d.ts +22 -0
  469. package/dist/web/feed-atom.d.ts.map +1 -0
  470. package/dist/web/feed-atom.js +83 -0
  471. package/dist/web/feed-atom.js.map +1 -0
  472. package/dist/web/feed-item.d.ts +145 -0
  473. package/dist/web/feed-item.d.ts.map +1 -0
  474. package/dist/web/feed-item.js +120 -0
  475. package/dist/web/feed-item.js.map +1 -0
  476. package/dist/web/feed-json.d.ts +79 -0
  477. package/dist/web/feed-json.d.ts.map +1 -0
  478. package/dist/web/feed-json.js +53 -0
  479. package/dist/web/feed-json.js.map +1 -0
  480. package/dist/web/feed-rss.d.ts +26 -0
  481. package/dist/web/feed-rss.d.ts.map +1 -0
  482. package/dist/web/feed-rss.js +203 -0
  483. package/dist/web/feed-rss.js.map +1 -0
  484. package/dist/web/feed-source.d.ts +244 -0
  485. package/dist/web/feed-source.d.ts.map +1 -0
  486. package/dist/web/feed-source.js +201 -0
  487. package/dist/web/feed-source.js.map +1 -0
  488. package/dist/web/feed-xml.d.ts +63 -0
  489. package/dist/web/feed-xml.d.ts.map +1 -0
  490. package/dist/web/feed-xml.js +116 -0
  491. package/dist/web/feed-xml.js.map +1 -0
  492. package/dist/web/feeds.d.ts +31 -96
  493. package/dist/web/feeds.d.ts.map +1 -1
  494. package/dist/web/feeds.js +94 -198
  495. package/dist/web/feeds.js.map +1 -1
  496. package/dist/web/health.d.ts +31 -0
  497. package/dist/web/health.d.ts.map +1 -0
  498. package/dist/web/health.js +44 -0
  499. package/dist/web/health.js.map +1 -0
  500. package/dist/web/index.d.ts +30 -10
  501. package/dist/web/index.d.ts.map +1 -1
  502. package/dist/web/index.js +18 -7
  503. package/dist/web/index.js.map +1 -1
  504. package/dist/web/navigation.d.ts +73 -0
  505. package/dist/web/navigation.d.ts.map +1 -0
  506. package/dist/web/navigation.js +110 -0
  507. package/dist/web/navigation.js.map +1 -0
  508. package/dist/web/negotiate.d.ts +9 -0
  509. package/dist/web/negotiate.d.ts.map +1 -1
  510. package/dist/web/negotiate.js +6 -1
  511. package/dist/web/negotiate.js.map +1 -1
  512. package/dist/web/recent.d.ts +43 -0
  513. package/dist/web/recent.d.ts.map +1 -0
  514. package/dist/web/recent.js +37 -0
  515. package/dist/web/recent.js.map +1 -0
  516. package/dist/web/render.d.ts +197 -9
  517. package/dist/web/render.d.ts.map +1 -1
  518. package/dist/web/render.js +334 -17
  519. package/dist/web/render.js.map +1 -1
  520. package/dist/web/routes.d.ts +25 -8
  521. package/dist/web/routes.d.ts.map +1 -1
  522. package/dist/web/routes.js +845 -93
  523. package/dist/web/routes.js.map +1 -1
  524. package/dist/web/sanitize.d.ts +19 -0
  525. package/dist/web/sanitize.d.ts.map +1 -0
  526. package/dist/web/sanitize.js +272 -0
  527. package/dist/web/sanitize.js.map +1 -0
  528. package/dist/web/search.d.ts +79 -0
  529. package/dist/web/search.d.ts.map +1 -0
  530. package/dist/web/search.js +94 -0
  531. package/dist/web/search.js.map +1 -0
  532. package/dist/web/sitemap.d.ts +120 -0
  533. package/dist/web/sitemap.d.ts.map +1 -0
  534. package/dist/web/sitemap.js +199 -0
  535. package/dist/web/sitemap.js.map +1 -0
  536. package/dist/web/taxonomy.d.ts +152 -0
  537. package/dist/web/taxonomy.d.ts.map +1 -0
  538. package/dist/web/taxonomy.js +237 -0
  539. package/dist/web/taxonomy.js.map +1 -0
  540. package/dist/web/templates.d.ts +54 -16
  541. package/dist/web/templates.d.ts.map +1 -1
  542. package/dist/web/templates.js +109 -28
  543. package/dist/web/templates.js.map +1 -1
  544. package/dist/web/themes.d.ts +205 -0
  545. package/dist/web/themes.d.ts.map +1 -0
  546. package/dist/web/themes.js +277 -0
  547. package/dist/web/themes.js.map +1 -0
  548. package/dist/webmention/discovery.d.ts +51 -0
  549. package/dist/webmention/discovery.d.ts.map +1 -0
  550. package/dist/webmention/discovery.js +176 -0
  551. package/dist/webmention/discovery.js.map +1 -0
  552. package/dist/webmention/html.d.ts +71 -0
  553. package/dist/webmention/html.d.ts.map +1 -0
  554. package/dist/webmention/html.js +285 -0
  555. package/dist/webmention/html.js.map +1 -0
  556. package/dist/webmention/index.d.ts +12 -0
  557. package/dist/webmention/index.d.ts.map +1 -0
  558. package/dist/webmention/index.js +8 -0
  559. package/dist/webmention/index.js.map +1 -0
  560. package/dist/webmention/links.d.ts +18 -0
  561. package/dist/webmention/links.d.ts.map +1 -0
  562. package/dist/webmention/links.js +64 -0
  563. package/dist/webmention/links.js.map +1 -0
  564. package/dist/webmention/microformats.d.ts +93 -0
  565. package/dist/webmention/microformats.d.ts.map +1 -0
  566. package/dist/webmention/microformats.js +376 -0
  567. package/dist/webmention/microformats.js.map +1 -0
  568. package/dist/webmention/receive.d.ts +141 -0
  569. package/dist/webmention/receive.d.ts.map +1 -0
  570. package/dist/webmention/receive.js +264 -0
  571. package/dist/webmention/receive.js.map +1 -0
  572. package/dist/webmention/routes.d.ts +37 -0
  573. package/dist/webmention/routes.d.ts.map +1 -0
  574. package/dist/webmention/routes.js +75 -0
  575. package/dist/webmention/routes.js.map +1 -0
  576. package/dist/webmention/service.d.ts +94 -0
  577. package/dist/webmention/service.d.ts.map +1 -0
  578. package/dist/webmention/service.js +207 -0
  579. package/dist/webmention/service.js.map +1 -0
  580. package/package.json +11 -6
  581. package/templates/site/content/_data/site.json +1 -0
  582. package/templates/site/content/posts/2026-01-01-hello-world.md +5 -3
  583. package/templates/site/geekity.config.ts +11 -1
  584. package/templates/site/tsconfig.json +1 -1
  585. package/themes/default/README.md +1096 -50
  586. package/themes/default/layouts/404.njk +2 -3
  587. package/themes/default/layouts/author.njk +37 -0
  588. package/themes/default/layouts/base.njk +172 -31
  589. package/themes/default/layouts/category.njk +27 -0
  590. package/themes/default/layouts/front-page.njk +57 -0
  591. package/themes/default/layouts/home.njk +17 -1
  592. package/themes/default/layouts/page.njk +57 -5
  593. package/themes/default/layouts/post.njk +78 -19
  594. package/themes/default/layouts/search.njk +57 -0
  595. package/themes/default/layouts/tag.njk +3 -6
  596. package/themes/default/mail/comment-digest.html.njk +37 -0
  597. package/themes/default/mail/comment-digest.subject.njk +3 -0
  598. package/themes/default/mail/comment-digest.txt.njk +40 -0
  599. package/themes/default/mail/comment-pending.html.njk +27 -0
  600. package/themes/default/mail/comment-pending.subject.njk +2 -0
  601. package/themes/default/mail/comment-pending.txt.njk +24 -0
  602. package/themes/default/mail/comment-reply.html.njk +21 -0
  603. package/themes/default/mail/comment-reply.subject.njk +2 -0
  604. package/themes/default/mail/comment-reply.txt.njk +22 -0
  605. package/themes/default/mail/contact-message.html.njk +24 -0
  606. package/themes/default/mail/contact-message.subject.njk +5 -0
  607. package/themes/default/mail/contact-message.txt.njk +20 -0
  608. package/themes/default/mail/password-changed.html.njk +18 -0
  609. package/themes/default/mail/password-changed.subject.njk +2 -0
  610. package/themes/default/mail/password-changed.txt.njk +17 -0
  611. package/themes/default/mail/password-reset.html.njk +22 -0
  612. package/themes/default/mail/password-reset.subject.njk +2 -0
  613. package/themes/default/mail/password-reset.txt.njk +18 -0
  614. package/themes/default/mail/test.html.njk +14 -0
  615. package/themes/default/mail/test.subject.njk +2 -0
  616. package/themes/default/mail/test.txt.njk +12 -0
  617. package/themes/default/partials/archive.njk +27 -0
  618. package/themes/default/partials/bio.njk +77 -0
  619. package/themes/default/partials/comment-form.njk +109 -0
  620. package/themes/default/partials/contact-form.njk +88 -0
  621. package/themes/default/partials/conversation.njk +139 -0
  622. package/themes/default/partials/feeds.njk +26 -0
  623. package/themes/default/partials/jsonld.njk +120 -0
  624. package/themes/default/partials/pagination.njk +12 -6
  625. package/themes/default/partials/post-list.njk +51 -18
  626. package/themes/default/partials/search-form.njk +17 -0
  627. package/themes/default/partials/tags.njk +26 -5
  628. package/themes/default/static/highlight.js +4 -0
  629. package/themes/default/static/style.css +1119 -194
  630. package/themes/default/theme.json +5 -0
  631. package/admin/layouts/document-editor.njk +0 -167
  632. package/admin/layouts/federation.njk +0 -145
  633. package/admin/layouts/login.njk +0 -23
  634. package/admin/layouts/settings.njk +0 -116
  635. package/admin/layouts/setup.njk +0 -27
  636. package/admin/layouts/users.njk +0 -91
  637. /package/admin/{layouts/_flash.njk → components/flash.njk} +0 -0
@@ -0,0 +1,160 @@
1
+ {# /admin/media: everything under content/uploads, what links to it, and the
2
+ two things that can be done to it. The directory is walked on every request,
3
+ so a file copied in by hand is here without a restart.
4
+
5
+ Everything works with JavaScript switched off. The URL and the Markdown are
6
+ readonly fields, which select and copy like any other text; copy.js adds a
7
+ button beside each of them and nothing else. #}
8
+ {% extends "layouts/shell.njk" %}
9
+
10
+ {% block title %}{{ heading }}{% endblock %}
11
+
12
+ {% block content %}
13
+ <h1>{{ heading }}</h1>
14
+
15
+ {% if confirm %}
16
+ <form class="admin-settings" method="post" action="{{ deleteUrl }}">
17
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
18
+ <input type="hidden" name="{{ fields.path }}" value="{{ confirm.path }}" />
19
+ <input type="hidden" name="{{ fields.confirm }}" value="1" />
20
+
21
+ <h2>Delete {{ confirm.url }}?</h2>
22
+
23
+ <p>
24
+ {{ confirm.references.length }}
25
+ {% if confirm.references.length == 1 %}document still points{% else %}documents still point{% endif %}
26
+ at it. Deleting the file does not change them, so they will point at nothing.
27
+ </p>
28
+
29
+ <ul class="admin-references">
30
+ {% for reference in confirm.references %}
31
+ <li>
32
+ <a href="{{ reference.editUrl }}">{{ reference.title }}</a>
33
+ {% if reference.trashed %}<span class="admin-status admin-status-trashed">Trash</span>{% endif %}
34
+ <code>{{ reference.path }}</code>
35
+ </li>
36
+ {% endfor %}
37
+ </ul>
38
+
39
+ <p class="admin-settings-buttons">
40
+ <button type="submit">Delete it anyway</button>
41
+ <a href="{{ listUrl }}">Cancel</a>
42
+ </p>
43
+ </form>
44
+ {% endif %}
45
+
46
+ <form class="admin-settings admin-media-upload" method="post" action="{{ uploadUrl }}" enctype="multipart/form-data">
47
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
48
+ <label for="media-file">Add a file</label>
49
+ <input id="media-file" name="{{ fields.file }}" type="file" accept="{{ accepts }}" required />
50
+ <p class="admin-hint">
51
+ This site accepts {{ accepts | replace(',', ', ') }}, up to {{ maxBytes }} bytes. The same
52
+ rules the editor’s upload control enforces.
53
+ </p>
54
+ <p class="admin-settings-buttons">
55
+ <button type="submit">Upload</button>
56
+ </p>
57
+ </form>
58
+
59
+ {% if total == 0 %}
60
+ <p class="admin-empty">Nothing has been uploaded yet.</p>
61
+ {% else %}
62
+ <table class="admin-list admin-media">
63
+ <thead>
64
+ <tr>
65
+ <th scope="col">File</th>
66
+ <th scope="col">Uploaded</th>
67
+ <th scope="col">Size</th>
68
+ <th scope="col">Link</th>
69
+ <th scope="col">Used by</th>
70
+ <th scope="col">Actions</th>
71
+ </tr>
72
+ </thead>
73
+ <tbody>
74
+ {% for file in files %}
75
+ <tr>
76
+ <td class="admin-media-file">
77
+ {% if file.image %}
78
+ <a href="{{ file.url }}">
79
+ <img class="admin-media-thumbnail" src="{{ file.url }}" alt="" width="60" height="60" />
80
+ </a>
81
+ {% else %}
82
+ <span class="admin-media-icon" aria-hidden="true">{{ file.extension | replace('.', '') | upper }}</span>
83
+ {% endif %}
84
+ <a href="{{ file.url }}">{{ file.path }}</a>
85
+ </td>
86
+ <td>{{ file.modified | date('html') }}</td>
87
+ <td>{{ file.bytes }}</td>
88
+ <td class="admin-media-links">
89
+ <label class="admin-visually-hidden" for="media-url-{{ loop.index }}">
90
+ URL of {{ file.name }}
91
+ </label>
92
+ <span class="admin-copy">
93
+ <input id="media-url-{{ loop.index }}" type="text" value="{{ file.url }}" readonly />
94
+ <button type="button" class="admin-copy-button" data-copy="media-url-{{ loop.index }}" hidden>
95
+ Copy
96
+ </button>
97
+ </span>
98
+
99
+ <label class="admin-visually-hidden" for="media-markdown-{{ loop.index }}">
100
+ Markdown for {{ file.name }}
101
+ </label>
102
+ <span class="admin-copy">
103
+ <input id="media-markdown-{{ loop.index }}" type="text" value="{{ file.markdown }}" readonly />
104
+ <button type="button" class="admin-copy-button" data-copy="media-markdown-{{ loop.index }}" hidden>
105
+ Copy
106
+ </button>
107
+ </span>
108
+ </td>
109
+ <td>
110
+ {% if file.references.length %}
111
+ <ul class="admin-references">
112
+ {% for reference in file.references %}
113
+ <li>
114
+ <a href="{{ reference.editUrl }}">{{ reference.title }}</a>
115
+ {% if reference.trashed %}
116
+ <span class="admin-status admin-status-trashed">Trash</span>
117
+ {% endif %}
118
+ </li>
119
+ {% endfor %}
120
+ </ul>
121
+ {% else %}
122
+ <span class="admin-status">Nothing</span>
123
+ {% endif %}
124
+ </td>
125
+ <td class="admin-actions">
126
+ <form method="post" action="{{ deleteUrl }}">
127
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
128
+ <input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
129
+ <button type="submit">Delete</button>
130
+ </form>
131
+ </td>
132
+ </tr>
133
+ {% endfor %}
134
+ </tbody>
135
+ </table>
136
+
137
+ {% if previousUrl or nextUrl %}
138
+ <nav class="admin-pagination" aria-label="Pages">
139
+ {% if previousUrl %}
140
+ <a rel="prev" href="{{ previousUrl }}">Newer</a>
141
+ {% endif %}
142
+ <span>Page {{ page }} of {{ pages }}</span>
143
+ {% if nextUrl %}
144
+ <a rel="next" href="{{ nextUrl }}">Older</a>
145
+ {% endif %}
146
+ </nav>
147
+ {% endif %}
148
+
149
+ <p class="admin-hint">
150
+ The list is the <code>content/uploads</code> directory itself, read afresh on every request,
151
+ so a file copied in by hand is here without a restart. Deleting one removes the file and
152
+ whatever was derived from it; it does not touch the documents that link to it, which is why
153
+ a file something still points at asks first.
154
+ </p>
155
+ {% endif %}
156
+
157
+ {# The one enhancement on this screen. The fields above are readonly inputs
158
+ and already copy by hand; this adds a button beside each of them. #}
159
+ <script defer src="{{ assetPrefix }}copy.js"></script>
160
+ {% endblock %}
@@ -0,0 +1,110 @@
1
+ {# /admin/messages: what the contact form left behind (TASK-56).
2
+
3
+ A message is a file under data/contact/ and nothing else, so the two buttons
4
+ here are a rewrite and an unlink. Every button is a form of its own, so the
5
+ whole screen works with JavaScript switched off. #}
6
+ {% extends "layouts/shell.njk" %}
7
+
8
+ {% block title %}{{ heading }}{% endblock %}
9
+
10
+ {% block content %}
11
+ <h1>{{ heading }}</h1>
12
+
13
+ <nav class="admin-filters" aria-label="Message status">
14
+ <ul>
15
+ {% for tab in tabs %}
16
+ <li>
17
+ <a href="{{ tab.url }}"{% if tab.current %} aria-current="page"{% endif %}>
18
+ {{ tab.label }} ({{ tab.count }})
19
+ </a>
20
+ </li>
21
+ {% endfor %}
22
+ </ul>
23
+ </nav>
24
+
25
+ {% if rows.length == 0 %}
26
+ <p class="admin-empty">Nothing here.</p>
27
+ {% else %}
28
+ <ul class="admin-comments">
29
+ {% for row in rows %}
30
+ <li class="admin-comment{% if not row.read %} admin-comment-unread{% endif %}">
31
+ <div class="admin-comment-meta">
32
+ <span class="admin-comment-author">{{ row.author }}</span>
33
+ <a class="admin-comment-email" href="mailto:{{ row.email }}">{{ row.email }}</a>
34
+ {% if not row.read %}
35
+ <span class="admin-status admin-status-pending">unread</span>
36
+ {% endif %}
37
+ {% if row.status == 'spam' %}
38
+ <span class="admin-status admin-status-spam">spam</span>
39
+ {% endif %}
40
+ </div>
41
+
42
+ <p class="admin-comment-where">
43
+ <time>{{ row.received }}</time>
44
+ from
45
+ {% if row.pageUrl %}
46
+ <a href="{{ row.pageUrl }}">{{ row.page }}</a>
47
+ {% else %}
48
+ {{ row.page }}
49
+ {% endif %}
50
+ {% if row.address %}
51
+ &middot; <code title="A salted hash of the address it came from">{{ row.address }}</code>
52
+ {% endif %}
53
+ </p>
54
+
55
+ {% if row.subject %}
56
+ <p class="admin-message-subject"><strong>{{ row.subject }}</strong></p>
57
+ {% endif %}
58
+
59
+ {# Printed, never rendered: a contact message is plain text, and
60
+ nothing a stranger typed is turned into markup here. #}
61
+ <div class="admin-comment-body admin-message-body">{{ row.message }}</div>
62
+
63
+ <div class="admin-actions">
64
+ <a class="admin-button admin-button-quiet" href="{{ row.replyUrl }}">Reply</a>
65
+
66
+ <form method="post" action="{{ readUrl }}">
67
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
68
+ <input type="hidden" name="{{ fields.id }}" value="{{ row.id }}" />
69
+ <input type="hidden" name="{{ fields.status }}" value="{{ status }}" />
70
+ {% if row.read %}
71
+ <button type="submit" name="{{ fields.read }}" value="">Mark unread</button>
72
+ {% else %}
73
+ <button type="submit" name="{{ fields.read }}" value="1">Mark read</button>
74
+ {% endif %}
75
+ </form>
76
+
77
+ <form method="post" action="{{ deleteUrl }}">
78
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
79
+ <input type="hidden" name="{{ fields.id }}" value="{{ row.id }}" />
80
+ <input type="hidden" name="{{ fields.status }}" value="{{ status }}" />
81
+ <button type="submit">Delete</button>
82
+ </form>
83
+ </div>
84
+ </li>
85
+ {% endfor %}
86
+ </ul>
87
+
88
+ {% if previousUrl or nextUrl %}
89
+ <nav class="admin-pagination" aria-label="Pages">
90
+ {% if previousUrl %}
91
+ <a rel="prev" href="{{ previousUrl }}">Newer</a>
92
+ {% endif %}
93
+ <span>Page {{ page }} of {{ pages }}</span>
94
+ {% if nextUrl %}
95
+ <a rel="next" href="{{ nextUrl }}">Older</a>
96
+ {% endif %}
97
+ </nav>
98
+ {% endif %}
99
+ {% endif %}
100
+
101
+ <p class="admin-hint">
102
+ A page carrying <code>contact: true</code> in its front matter offers a contact form, and every
103
+ message it takes is one JSON file under <code>data/contact/</code> &mdash; written before
104
+ anything is emailed, so a message survives a mail outage and a site with no mail configured
105
+ still collects them. They are under <code>data/</code> rather than <code>content/</code>
106
+ because they carry somebody&rsquo;s address and were never meant to be published. Where a
107
+ message is sent is the contact address on the Settings screen; deleting one here deletes its
108
+ file.
109
+ </p>
110
+ {% endblock %}
@@ -4,7 +4,11 @@
4
4
 
5
5
  {% block content %}
6
6
  {# A screen a later task builds. It exists so the navigation goes somewhere
7
- and the guard covers it from the start. #}
7
+ and the guard covers it from the start.
8
+
9
+ It sits at the top of `pages/` rather than inside one of the section
10
+ folders because it is not any one section's screen: it stands in for
11
+ whichever section has nothing built yet, and the route heads it. #}
8
12
  <h1>{{ heading }}{% if slug %}: {{ slug }}{% endif %}</h1>
9
13
  <p class="admin-note">This screen is not built yet.</p>
10
14
  {% endblock %}
@@ -0,0 +1,99 @@
1
+ {# /admin/settings/discussion: comments, webmentions, and what checks them. #}
2
+ {% extends "layouts/settings-page.njk" %}
3
+ {% import "components/fields.njk" as field %}
4
+
5
+ {% block settingsFields %}
6
+ <h2>Comments</h2>
7
+
8
+ {% set commentsHint %}
9
+ Clear this and no post shows a comment form and every submission is
10
+ refused. Fediverse replies keep arriving and keep being shown either way:
11
+ a remote server sends those, and nothing here can stop it.
12
+ {% endset %}
13
+ {{ field.checkbox('settings-comments', fields.comments, 'Take comments on this site',
14
+ checked=form.comments, hint=commentsHint) }}
15
+
16
+ {% set closeHint %}
17
+ Days after a post's date that it stops taking comments; <code>0</code>
18
+ never closes one. A post overrides this for itself with
19
+ <code>comments: true</code> or <code>comments: false</code>, which the
20
+ editor offers as a field. Pages are closed unless they say otherwise.
21
+ {% endset %}
22
+ {{ field.text('settings-comments-close', fields.commentsCloseAfterDays, 'Close comments after',
23
+ form.commentsCloseAfterDays, type='number', min='0', step='1',
24
+ error=problems.commentsCloseAfterDays, hint=closeHint) }}
25
+
26
+ <h2>Webmentions</h2>
27
+
28
+ {% set sendHint %}
29
+ When a post is published or edited, every external page it links to is
30
+ asked whether it takes webmentions and told about the post if it does.
31
+ The Resend button on the federation screen sends them again.
32
+ {% endset %}
33
+ {{ field.checkbox('settings-webmentions-send', fields.webmentionsSend,
34
+ 'Tell the pages a post links to', checked=form.webmentionsSend, hint=sendHint) }}
35
+
36
+ {% set receiveHint %}
37
+ Every post advertises an endpoint, and a page that links to one and says
38
+ so lands in the comment queue like a comment. Clear this and the endpoint
39
+ is gone from the pages and answers nothing; what has already arrived
40
+ stays where it is.
41
+ {% endset %}
42
+ {{ field.checkbox('settings-webmentions-receive', fields.webmentionsReceive,
43
+ 'Accept webmentions sent here', checked=form.webmentionsReceive, hint=receiveHint) }}
44
+ {% endblock %}
45
+
46
+ {% block settingsPanels %}
47
+ {# Akismet is its own pair of forms for the reason the avatar is: the key is
48
+ a credential in data/akismet.json rather than a setting in site.json, and
49
+ a key Akismet will not take must not lose an edit to the fields above. #}
50
+ <section class="admin-settings admin-akismet-panel">
51
+ <h2>Spam checking</h2>
52
+
53
+ <p class="admin-akismet-state admin-akismet-{{ akismetState }}">
54
+ {% if akismetState == 'valid' %}
55
+ <strong>Connected</strong> &mdash; key {{ akismetKeyHint }}, checked {{ akismetCheckedAt }}.
56
+ {% elif akismetState == 'invalid' %}
57
+ <strong>Akismet does not recognise this key</strong> &mdash; key {{ akismetKeyHint }}, checked {{ akismetCheckedAt }}.
58
+ {% elif akismetState == 'unchecked' %}
59
+ <strong>Akismet could not be reached</strong> to check key {{ akismetKeyHint }}.
60
+ {% else %}
61
+ <strong>Not connected</strong> &mdash; this site has no key.
62
+ {% endif %}
63
+ </p>
64
+ <p class="admin-hint">{{ akismetHint }}</p>
65
+
66
+ <form method="post" action="{{ akismetUrl }}">
67
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
68
+ {% if akismetPresent %}
69
+ {% set akismetPlaceholder = 'Replace the stored key' %}
70
+ {% else %}
71
+ {% set akismetPlaceholder = 'Paste your key' %}
72
+ {% endif %}
73
+ {{ field.password('settings-akismet-key', akismetFields.key, 'Akismet API key',
74
+ autocomplete='off', spellcheck='false', placeholder=akismetPlaceholder) }}
75
+ <p class="admin-settings-buttons">
76
+ <button type="submit">{% if akismetPresent %}Replace key{% else %}Save key{% endif %}</button>
77
+ </p>
78
+ </form>
79
+
80
+ {% if akismetPresent %}
81
+ <form method="post" action="{{ akismetUrl }}">
82
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
83
+ <input type="hidden" name="{{ akismetFields.action }}" value="{{ akismetRemove }}" />
84
+ <p class="admin-settings-buttons">
85
+ <button type="submit">Remove key</button>
86
+ </p>
87
+ </form>
88
+ {% endif %}
89
+
90
+ <p class="admin-hint">
91
+ A key from <a href="https://akismet.com/">akismet.com</a> turns on spam
92
+ checking for native comments and incoming webmentions. It is checked with
93
+ Akismet when it is saved and kept in <code>data/akismet.json</code>, never
94
+ in <code>site.json</code> and never on the public site. Marking something
95
+ spam or not spam on the Comments screen tells Akismet, which is the only
96
+ way it learns. Fediverse replies are never sent to it.
97
+ </p>
98
+ </section>
99
+ {% endblock %}
@@ -0,0 +1,203 @@
1
+ {# /admin/settings/email: how the site sends mail, and where a message written
2
+ to it goes. #}
3
+ {% extends "layouts/settings-page.njk" %}
4
+ {% import "components/fields.njk" as field %}
5
+
6
+ {% block settingsFields %}
7
+ {% set providerHint %}
8
+ The key or password each one needs is a credential and is saved below,
9
+ in <code>data/mail.json</code> rather than in <code>site.json</code>.
10
+ {% endset %}
11
+ {% call field.select('settings-mail-provider', fields.mailProvider, 'Provider',
12
+ error=problems.mailProvider, hint=providerHint) %}
13
+ {% for provider in mailProviders %}
14
+ <option value="{{ provider }}"{% if form.mailProvider == provider %} selected{% endif %}>
15
+ {% if provider == 'none' %}None &mdash; this site sends no email{% elif provider == 'brevo' %}Brevo (transactional API){% else %}SMTP{% endif %}
16
+ </option>
17
+ {% endfor %}
18
+ {% endcall %}
19
+
20
+ {{ field.text('settings-mail-from-name', fields.mailFromName, 'From name', form.mailFromName,
21
+ hint='The name on the From line. Empty uses the site title.') }}
22
+
23
+ {% set fromAddressHint %}
24
+ Providers only send from an address they have verified, so this should be
25
+ one you own. Empty falls back to <code>no-reply@</code> at the site's host.
26
+ {% endset %}
27
+ {{ field.text('settings-mail-from-address', fields.mailFromAddress, 'From address',
28
+ form.mailFromAddress, error=problems.mailFromAddress, hint=fromAddressHint,
29
+ inputmode='email') }}
30
+
31
+ {{ field.text('settings-mail-reply-to', fields.mailReplyTo, 'Reply-to', form.mailReplyTo,
32
+ error=problems.mailReplyTo, inputmode='email',
33
+ hint='Where a reply goes. Empty replies to the From address.') }}
34
+
35
+ {% set contactHint %}
36
+ Where a message from a page&rsquo;s contact form is sent, with reply-to set to whoever
37
+ wrote it. Empty writes to the first admin with an email address. It is read when a
38
+ message arrives and never appears on the public site.
39
+ {% endset %}
40
+ {{ field.text('settings-contact-email', fields.contactEmail, 'Contact address',
41
+ form.contactEmail, error=problems.contactEmail, hint=contactHint, inputmode='email') }}
42
+ {% endblock %}
43
+
44
+ {% block settingsPanels %}
45
+ {# The mail credential is its own pair of forms for the reason the Akismet
46
+ key is: an API key and an SMTP password belong in data/mail.json, not in
47
+ the public site.json, and a mistyped one must not lose an edit to the
48
+ From line. #}
49
+ <section class="admin-settings admin-mail-panel">
50
+ <h2>Mail credentials</h2>
51
+
52
+ <p class="admin-mail-state admin-mail-{{ mailProvider }}">
53
+ {% if mailConfigured %}
54
+ <strong>Configured</strong> &mdash; sending through {{ mailProvider }}.
55
+ {% elif mailProvider == 'none' %}
56
+ <strong>Not sending</strong> &mdash; no provider is chosen.
57
+ {% else %}
58
+ <strong>Not configured</strong> &mdash; {{ mailProvider }} is chosen but has no credential.
59
+ {% endif %}
60
+ </p>
61
+ <p class="admin-hint">{{ mailHint }}</p>
62
+
63
+ {# One provider's boxes, never both. A site sending through Brevo has no
64
+ use for an SMTP port and a site sending through SMTP has none for an API
65
+ key, and seven boxes where one is wanted is how a screen teaches people
66
+ to stop reading it.
67
+
68
+ The provider branched on is the saved one, from `site.json`, not
69
+ whatever the select above happens to be showing: that select belongs to
70
+ the settings form and nothing it shows is true until Save settings is
71
+ pressed. Saying so is the paragraph below — without it, somebody who
72
+ picks SMTP, looks straight down for a Host box and finds an API key
73
+ would reasonably call this broken. Doing it the other way round would
74
+ take JavaScript, and no other admin screen needs any. #}
75
+ {% if mailProvider == 'none' %}
76
+ <p class="admin-hint">
77
+ No provider is <em>saved</em>, so there is no credential to give and no
78
+ box here to give it in. Choose one under <strong>Provider</strong> above
79
+ and press <strong>Save settings</strong> &mdash; picking it in the select
80
+ alone changes nothing &mdash; and the boxes it needs appear here.
81
+ </p>
82
+ {% else %}
83
+ <p class="admin-hint">
84
+ These are the boxes
85
+ <strong>{% if mailProvider == 'brevo' %}Brevo{% else %}SMTP{% endif %}</strong>
86
+ needs, and they follow the provider this site has <em>saved</em> &mdash; not
87
+ the one the select above is showing. Change <strong>Provider</strong> and
88
+ press <strong>Save settings</strong> first, and the boxes here change with it.
89
+ </p>
90
+
91
+ <form method="post" action="{{ mailUrl }}">
92
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
93
+
94
+ {% if mailProvider == 'brevo' %}
95
+ <h3>Brevo</h3>
96
+ {% if mailBrevoPresent %}
97
+ <p class="admin-hint">A key is stored, ending {{ mailBrevoKeyHint }}.</p>
98
+ {% set brevoPlaceholder = 'Leave blank to keep the stored key' %}
99
+ {% else %}
100
+ {% set brevoPlaceholder = 'Paste your key' %}
101
+ {% endif %}
102
+ {{ field.password('settings-mail-brevo-key', mailFields.brevoApiKey, 'API key',
103
+ autocomplete='off', spellcheck='false', placeholder=brevoPlaceholder) }}
104
+ {% else %}
105
+ <h3>SMTP</h3>
106
+ {{ field.text('settings-mail-smtp-host', mailFields.smtpHost, 'Host', mailSmtp.host,
107
+ autocomplete='off') }}
108
+
109
+ {{ field.text('settings-mail-smtp-port', mailFields.smtpPort, 'Port',
110
+ mailSmtp.port if mailSmtp else 587, inputmode='numeric') }}
111
+
112
+ {# Its own box with the sentence beside it, like every other switch in
113
+ the admin. It used to be a label wrapped round the tick in a class the
114
+ stylesheet has never heard of, so it was the one switch here that
115
+ rendered unstyled. #}
116
+ {{ field.checkbox('settings-mail-smtp-secure', mailFields.smtpSecure,
117
+ 'TLS from the first byte (port 465)', checked=mailSmtp.secure,
118
+ hint='Leave this off for port 587, which upgrades with STARTTLS.') }}
119
+
120
+ {{ field.text('settings-mail-smtp-user', mailFields.smtpUser, 'Username', mailSmtp.user,
121
+ autocomplete='off') }}
122
+
123
+ {% if mailSmtp.hasPassword %}
124
+ {% set smtpPlaceholder = 'Leave blank to keep the stored password' %}
125
+ {% else %}
126
+ {% set smtpPlaceholder = 'The SMTP password' %}
127
+ {% endif %}
128
+ {{ field.password('settings-mail-smtp-password', mailFields.smtpPassword, 'Password',
129
+ autocomplete='off', spellcheck='false', placeholder=smtpPlaceholder) }}
130
+ {% endif %}
131
+
132
+ <p class="admin-settings-buttons">
133
+ <button type="submit">Save credentials</button>
134
+ </p>
135
+ </form>
136
+ {% endif %}
137
+
138
+ {# A credential kept for a provider that is not the chosen one. Its boxes
139
+ are gone but the secret is still on disk, and this panel is the only
140
+ place in the admin that says so, so it is reported in words instead.
141
+ Hiding a stored API key would be the screen lying about what the site
142
+ holds. #}
143
+ {% set brevoStoredElsewhere = mailBrevoPresent and mailProvider != 'brevo' %}
144
+ {% set smtpStoredElsewhere = mailSmtpPresent and mailProvider != 'smtp' %}
145
+ {% if brevoStoredElsewhere or smtpStoredElsewhere %}
146
+ <h3>Also stored</h3>
147
+
148
+ {% if brevoStoredElsewhere %}
149
+ <p class="admin-field-static">
150
+ A <strong>Brevo</strong> API key, ending {{ mailBrevoKeyHint }}.
151
+ </p>
152
+ {% endif %}
153
+ {% if smtpStoredElsewhere %}
154
+ <p class="admin-field-static">
155
+ An <strong>SMTP</strong> server:
156
+ {% if mailSmtp.user %}{{ mailSmtp.user }} at {% endif %}{{ mailSmtp.host }}
157
+ port {{ mailSmtp.port }}{% if mailSmtp.hasPassword %}, with a password{% endif %}.
158
+ </p>
159
+ {% endif %}
160
+
161
+ <p class="admin-hint">
162
+ Kept in <code>data/mail.json</code> and used by nothing while
163
+ {% if mailProvider == 'none' %}no provider is chosen{% else %}{% if mailProvider == 'brevo' %}Brevo{% else %}SMTP{% endif %} is the chosen provider{% endif %}.
164
+ Choose that provider above to edit it, or press
165
+ <strong>Remove credentials</strong> to forget every credential this site
166
+ holds, this one included.
167
+ </p>
168
+ {% endif %}
169
+
170
+ {% if mailBrevoPresent or mailSmtpPresent %}
171
+ <form method="post" action="{{ mailUrl }}">
172
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
173
+ <input type="hidden" name="{{ mailFields.action }}" value="{{ mailRemove }}" />
174
+ <p class="admin-settings-buttons">
175
+ <button type="submit">Remove credentials</button>
176
+ </p>
177
+ </form>
178
+ {% endif %}
179
+
180
+ {# Only where there is something to send with, the way the notice switches
181
+ on a user's page are drawn only where there is both a provider and an
182
+ address (TASK-97). A Send test email button on a site with no credential
183
+ can do one thing, which is say it did nothing. #}
184
+ {% if mailConfigured %}
185
+ <form method="post" action="{{ mailTestUrl }}">
186
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
187
+ {{ field.text('settings-mail-test-to', mailTestFields.to, 'Send a test message to', '',
188
+ inputmode='email', placeholder='you@example.com') }}
189
+ <p class="admin-settings-buttons">
190
+ <button type="submit">Send test email</button>
191
+ </p>
192
+ </form>
193
+ {% endif %}
194
+
195
+ <p class="admin-hint">
196
+ Neither the API key nor the SMTP password is ever printed back into this
197
+ page. Both are kept in <code>data/mail.json</code> at mode
198
+ <code>0600</code>, beside the password hashes and the actor's private
199
+ keys, and never in <code>site.json</code>, which is public and in git.
200
+ Leaving a secret blank keeps the one already stored.
201
+ </p>
202
+ </section>
203
+ {% endblock %}
@@ -0,0 +1,75 @@
1
+ {# /admin/settings/federation: which relays boost the site, and whether it is
2
+ still answering the paths a WordPress site arrived with. Who the site's
3
+ people are on the fediverse is a user's profile now (decision-14); the
4
+ Federation section in the menu is where the actors and their followers are. #}
5
+ {% extends "layouts/settings-page.njk" %}
6
+ {% import "components/fields.njk" as field %}
7
+
8
+ {% block settingsFields %}
9
+ {% set relaysHint %}
10
+ One relay inbox per line, such as
11
+ <code>https://tags.pub/user/_____relay_____/inbox</code>. Adding one sends
12
+ it a follow; every public post then goes to it as well as to the
13
+ followers, and it boosts them on to instances nobody here follows. The
14
+ Federation screen says where each subscription stands.
15
+ {% endset %}
16
+ {{ field.textarea('settings-relays', fields.relays, 'Relays', form.relays,
17
+ error=problems.relays, hint=relaysHint, rows='3', spellcheck='false', autofocus=true) }}
18
+
19
+ <h2>WordPress</h2>
20
+
21
+ {% set wordpressHint %}
22
+ Answer the inbox and collection paths the WordPress ActivityPub plugin
23
+ published, under <code>/wp-json/activitypub/1.0/</code>, for the users
24
+ carrying its numeric actor id. Only a site that moved here from that plugin
25
+ needs this: a follower's server keeps delivering to the inbox URL it cached
26
+ until it next refetches the actor, and this is what catches those
27
+ deliveries in the meantime.
28
+ {% endset %}
29
+ {{ field.checkbox('settings-wordpress-activitypub', fields.wordpressActivityPub,
30
+ 'WordPress ActivityPub compatibility', checked=form.wordpressActivityPub,
31
+ hint=wordpressHint) }}
32
+ {% endblock %}
33
+
34
+ {% block settingsPanels %}
35
+ {# When each compatibility path was last asked for. It is the whole reason
36
+ the instants are recorded: once every follower's server has refetched the
37
+ actor, nothing asks any more and the switch can go off. #}
38
+ <section class="admin-settings admin-wordpress-panel">
39
+ <h2>WordPress paths</h2>
40
+
41
+ {% if wordpressRoutes.length %}
42
+ <table class="admin-table">
43
+ <thead>
44
+ <tr><th scope="col">Path</th><th scope="col">For</th><th scope="col">Last asked for</th></tr>
45
+ </thead>
46
+ <tbody>
47
+ {% for route in wordpressRoutes %}
48
+ <tr>
49
+ <td><code>{{ route.path }}</code></td>
50
+ <td>{{ route.username or 'every user' }}</td>
51
+ <td>
52
+ {% if route.lastRequestedAt %}
53
+ {{ route.lastRequestedAt | date('readable') }}
54
+ {% else %}
55
+ Never
56
+ {% endif %}
57
+ </td>
58
+ </tr>
59
+ {% endfor %}
60
+ </tbody>
61
+ </table>
62
+ <p class="admin-hint">
63
+ Once every one of these has been quiet for long enough that every
64
+ follower's server has refetched the actor, turn the switch off: these
65
+ paths are a cache other servers hold, not this site's identity, and the
66
+ CMS carries them only until the caches have moved on.
67
+ </p>
68
+ {% else %}
69
+ <p class="admin-hint">
70
+ No user carries a WordPress actor id, so the switch would serve nothing.
71
+ The <code>geekity import wordpress-actor</code> command sets one.
72
+ </p>
73
+ {% endif %}
74
+ </section>
75
+ {% endblock %}