1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-22 10:54:46 +00:00

Compare commits

...

312 Commits

Author SHA1 Message Date
webplusai
c7dc21aa81 #8868 add login button in header for guest user (#8874) 2025-01-08 13:00:54 +00:00
Jeremy Ruston
19e341c760 Add support for Node.js integrated sqlite
Available in Node.js 22.50 - see https://nodejs.org/api/sqlite.html

Given the Node.js release timeline, I think it will be reasonable to switch to the integrated Node.js sqlite as the default, which will avoid all the npm install kerfuffle too.
2025-01-08 12:47:40 +00:00
Jeremy Ruston
739ba3288b Merge branch 'master' into multi-wiki-support 2025-01-08 12:26:37 +00:00
webplusai
b4583ef9c5 #8867 add login as guest button (#8873) 2024-12-29 17:46:06 +00:00
webplusai
2a9f646ec4 #8858 fix issue with anonymous wiki access (#8869) 2024-12-25 09:29:19 +00:00
webplusai
6d448499c7 #8856 fix profile button rendering for guest users (#8865) 2024-12-24 12:02:10 +00:00
webplusai
b8f2800dab #8862 fix bug with ACL permissions (#8864) 2024-12-24 12:01:18 +00:00
webplusai
d72a4c9826 #8854 fix acl error in anon mode (#8857)
* #8854 fix acl error in anon mode

* #8854 fix test failure
2024-12-23 14:07:43 +00:00
webplusai
ddfc8c469c #8850 relocate Manage ACL button (#8852) 2024-12-23 10:25:33 +00:00
webplusai
faf5d6982e #8849 fixed 403 error on ACL page (#8851) 2024-12-23 10:15:54 +00:00
webplusai
1f1b785524 #8828 update mws documentation (#8848) 2024-12-23 08:30:20 +00:00
webplusai
1d65f0070c #8832 fixed blocked access to view wiki anonymously without admin account setup (#8847) 2024-12-23 08:27:46 +00:00
webplusai
0198eb2a1a #8832 fix favicon and access control issue (#8846) 2024-12-22 20:59:55 +00:00
webplusai
1c5648826e #8833 fix issue with multiple user sessions (#8845) 2024-12-22 20:59:01 +00:00
Jeremy Ruston
02a9fdcd06 Merge branch 'master' into multi-wiki-support 2024-12-21 09:47:47 +00:00
Jeremy Ruston
d0a3c79383 Add MWS documentation wiki 2024-12-21 09:47:14 +00:00
webplusai
e48ea2dbe6 #8818 fix default checkbox checked value issue on anon config modal (#8825) 2024-12-17 14:01:35 +00:00
Jeremy Ruston
060bea89ae Fix Jack's aspect ratio in the favicon 2024-12-14 12:40:07 +00:00
webplusai
67232aab15 Feature/#8812 anon access fix (#8815)
* #8812 resolve issue with anonymous access

* #8812 bug fix with anonymous access
2024-12-14 10:00:14 +00:00
webplusai
ae5bd9d4cd #8812 resolve issue with anonymous access (#8814) 2024-12-11 12:08:09 +00:00
webplusai
a00e3e79d6 #8774 fix add user form errors and error message clean up (#8813) 2024-12-10 20:24:02 +00:00
Jeremy Ruston
ab5a48747f Fix spelling of "anonymous" 2024-12-10 13:18:30 +00:00
Jeremy Ruston
239aacfa03 Fix streaming of attachment tiddlers from recipe URIs 2024-12-10 10:36:08 +00:00
Jeremy Ruston
e3ce053745 Fix attachment size check
I think this was introduced in #8455 sorry it took me so long to notice
2024-12-09 17:51:44 +00:00
Jeremy Ruston
2735ce23d8 Update dependencies 2024-12-09 17:24:30 +00:00
Jeremy Ruston
cb6c471967 Merge branch 'master' into multi-wiki-support 2024-12-09 17:19:31 +00:00
webplusai
8defedd611 #8771 fixed form reset issue on create user form (#8805) 2024-12-06 14:08:05 +00:00
webplusai
d7eb39c88e #8776 resolve server crash on delete role in use (#8796) 2024-12-05 12:29:58 +00:00
Jeremy Ruston
ac59af19dd Add some information about SQLite 2024-11-27 15:43:44 +00:00
webplusai
0cd2190ddf #8756 main page recipes (#8766) 2024-11-21 10:34:45 +00:00
webplusai
266b32bfee #8755 correct READ permission description (#8767) 2024-11-21 10:34:18 +00:00
webplusai
a697a664fa #8754 fix user edit form success/error message cleanup issue (#8764)
* #8754 fix issue with default user role on create, profile navigation issue

* #8754 fix user edit form success/error message cleanup issue
2024-11-20 08:10:36 +00:00
webplusai
9f48e75945 #8757 add exception handling to creating new users & roles functions (#8763) 2024-11-20 08:10:16 +00:00
webplusai
4dc2486d4b Fix issue with default user role on create, profile navigation issue (#8761) 2024-11-19 11:43:07 +00:00
Jeremy Ruston
d9256707f4 Add troubleshooting instructions 2024-11-18 12:25:07 +00:00
Jeremy Ruston
e85cfdefa6 Merge branch 'master' into multi-wiki-support 2024-11-18 11:08:43 +00:00
Jeremy Ruston
b198270cb5 Merge branch 'master' into multi-wiki-support 2024-11-18 09:53:48 +00:00
Jeremy Ruston
3836a5d08c Styles for the improvement banner 2024-11-17 18:06:54 +00:00
Jeremy Ruston
83f7b9023d Retain original tiddler paths to try to get the contribution banner working 2024-11-17 17:40:42 +00:00
Jeremy Ruston
9362121e8d Typo 2024-11-17 12:55:04 +00:00
Mario Pietsch
07c3081e7f [MWS] Move test dependencies to devDep. Update better-sqlite3 to latest (#8717)
* move test dependencies to devDep. Update better-sqlite3 to latest

* update Node version for workflow

* downgrade engines - node

* adjust wasm package
2024-11-17 10:35:49 +00:00
Jeremy Ruston
7c4d0d7f5e Docs: Add table of contents and custom palette 2024-11-16 12:35:04 +00:00
Jeremy Ruston
520188dc2b Tweak favicon 2024-11-16 08:53:20 +00:00
Jeremy Ruston
aa0d59e4ba Docs: Add basic system tiddlers 2024-11-15 21:34:47 +00:00
Jeremy Ruston
c1a441b7a1 Style tweak 2024-11-15 21:25:26 +00:00
Jeremy Ruston
0f558a1442 Move manage-acl page control into multiwikiclient plugin
@webplusai otherwise the control shows up for all wikis built with this version of the core, including those that don't include any MWS plugins. They get an "Undefined widget 'action-managetiddler'" error
2024-11-15 21:24:47 +00:00
Jeremy Ruston
31a7d648e5 Docs: Move documentation from plugin into multiwikidocs edition
@webplusai future docs updates should go to `editions/multiwikidocs`

By the way, it's worth noting that the recent updates in # 8748 were in Markdown syntax, which is slightly different from TiddlyWiki syntax. See the corrections here.
2024-11-15 21:04:15 +00:00
Jeremy Ruston
a6c5b86343 Docs: Copy content from tiddlywiki.com 2024-11-15 20:47:55 +00:00
Jeremy Ruston
ffbf24ae92 CI: We don't need to run the tests when building mws.tiddlywiki.com 2024-11-15 20:30:46 +00:00
webplusai
12e48af372 update multiwiki server documentation (#8748)
* mws authentication

* add more tests and permission checkers

* add logic to ensure that only authenticated users' requests are handled

* add custom login page

* Implement user authentication as well as session handling

* work on user operations authorization

* add middleware to route handlers for bags & tiddlers routes

* add feature that only returns the tiddlers and bags which the user has permission to access on index page

* refactor auth routes & added user management page

* fix Ci Test failure issue

* fix users list page, add manage roles page

* add commands and scripts to create new user & assign roles and permissions

* resolved ci-test failure

* add ACL permissions to bags & tiddlers on creation

* fix comments and access control list bug

* fix indentation issues

* working on user profile edit

* remove list users command & added support for database in server options

* implement user profile update and password change feature

* update plugin readme

* implement command which triggers protected mode on the server

* revert server-wide auth flag. Implement selective authorization

* ACL management feature

* Complete Access control list implementation

* Added support to manage users' assigned role by admin

* fix comments

* fix comment

* Add user profile management and account deletion functionality

* add success and error message feedback for user profile operations

* fix indentation issues

* Add command to create admin user if none exists when the start command is executed

* refactor annonymous user flow with create admin implementation

* remove mws-add-user from start command

* admin configuration for annonymous read-write opearations

* fix comments

* change get-anon handler to POST

* update multiwiki server documentation
2024-11-15 20:29:39 +00:00
Jeremy Ruston
f61a131119 Transparent background for favicon 2024-11-15 18:19:10 +00:00
Jeremy Ruston
8d53a37d34 Docs: Fix CI by including a favicon 2024-11-15 18:09:44 +00:00
Jeremy Ruston
386af03f6d Add multiwikidocs edition and CI to build it 2024-11-15 17:56:00 +00:00
Jeremy Ruston
8ac2730eb1 Merge branch 'master' into multi-wiki-support 2024-11-15 17:43:04 +00:00
webplusai
e873518d6f Admin configuration for anonymous read-write operations (#8736)
* mws authentication

* add more tests and permission checkers

* add logic to ensure that only authenticated users' requests are handled

* add custom login page

* Implement user authentication as well as session handling

* work on user operations authorization

* add middleware to route handlers for bags & tiddlers routes

* add feature that only returns the tiddlers and bags which the user has permission to access on index page

* refactor auth routes & added user management page

* fix Ci Test failure issue

* fix users list page, add manage roles page

* add commands and scripts to create new user & assign roles and permissions

* resolved ci-test failure

* add ACL permissions to bags & tiddlers on creation

* fix comments and access control list bug

* fix indentation issues

* working on user profile edit

* remove list users command & added support for database in server options

* implement user profile update and password change feature

* update plugin readme

* implement command which triggers protected mode on the server

* revert server-wide auth flag. Implement selective authorization

* ACL management feature

* Complete Access control list implementation

* Added support to manage users' assigned role by admin

* fix comments

* fix comment

* Add user profile management and account deletion functionality

* add success and error message feedback for user profile operations

* fix indentation issues

* Add command to create admin user if none exists when the start command is executed

* refactor annonymous user flow with create admin implementation

* remove mws-add-user from start command

* admin configuration for annonymous read-write opearations

* fix comments

* change get-anon handler to POST
2024-11-14 17:47:25 +00:00
webplusai
316bd65296 Add success and error message feedback for user profile operations (#8716)
* mws authentication

* add more tests and permission checkers

* add logic to ensure that only authenticated users' requests are handled

* add custom login page

* Implement user authentication as well as session handling

* work on user operations authorization

* add middleware to route handlers for bags & tiddlers routes

* add feature that only returns the tiddlers and bags which the user has permission to access on index page

* refactor auth routes & added user management page

* fix Ci Test failure issue

* fix users list page, add manage roles page

* add commands and scripts to create new user & assign roles and permissions

* resolved ci-test failure

* add ACL permissions to bags & tiddlers on creation

* fix comments and access control list bug

* fix indentation issues

* working on user profile edit

* remove list users command & added support for database in server options

* implement user profile update and password change feature

* update plugin readme

* implement command which triggers protected mode on the server

* revert server-wide auth flag. Implement selective authorization

* ACL management feature

* Complete Access control list implementation

* Added support to manage users' assigned role by admin

* fix comments

* fix comment

* Add user profile management and account deletion functionality

* add success and error message feedback for user profile operations

* fix indentation issues

* Add command to create admin user if none exists when the start command is executed

* refactor annonymous user flow with create admin implementation

* remove mws-add-user from start command
2024-11-08 10:09:42 +00:00
Jeremy Ruston
3a5f67d4f5 Update dependencies
Tests were breaking in GitHub Actions but work OK on my machine. Problem was better-sqlite3 not being prebuilt, so I am testing to see if an update helps.
2024-11-06 15:31:41 +00:00
Jeremy Ruston
ad901a9f50 Merge branch 'master' into multi-wiki-support 2024-11-06 15:20:44 +00:00
webplusai
c7531e53ab Add user profile management and account deletion functionality (#8712)
* mws authentication

* add more tests and permission checkers

* add logic to ensure that only authenticated users' requests are handled

* add custom login page

* Implement user authentication as well as session handling

* work on user operations authorization

* add middleware to route handlers for bags & tiddlers routes

* add feature that only returns the tiddlers and bags which the user has permission to access on index page

* refactor auth routes & added user management page

* fix Ci Test failure issue

* fix users list page, add manage roles page

* add commands and scripts to create new user & assign roles and permissions

* resolved ci-test failure

* add ACL permissions to bags & tiddlers on creation

* fix comments and access control list bug

* fix indentation issues

* working on user profile edit

* remove list users command & added support for database in server options

* implement user profile update and password change feature

* update plugin readme

* implement command which triggers protected mode on the server

* revert server-wide auth flag. Implement selective authorization

* ACL management feature

* Complete Access control list implementation

* Added support to manage users' assigned role by admin

* fix comments

* fix comment

* Add user profile management and account deletion functionality
2024-10-30 18:38:21 +00:00
webplusai
6a7612ddf8 MWS authentication (#8596)
* mws authentication

* add more tests and permission checkers

* add logic to ensure that only authenticated users' requests are handled

* add custom login page

* Implement user authentication as well as session handling

* work on user operations authorization

* add middleware to route handlers for bags & tiddlers routes

* add feature that only returns the tiddlers and bags which the user has permission to access on index page

* refactor auth routes & added user management page

* fix Ci Test failure issue

* fix users list page, add manage roles page

* add commands and scripts to create new user & assign roles and permissions

* resolved ci-test failure

* add ACL permissions to bags & tiddlers on creation

* fix comments and access control list bug

* fix indentation issues

* working on user profile edit

* remove list users command & added support for database in server options

* implement user profile update and password change feature

* update plugin readme

* implement command which triggers protected mode on the server

* revert server-wide auth flag. Implement selective authorization

* ACL management feature

* Complete Access control list implementation

* Added support to manage users' assigned role by admin

* fix comments

* fix comment
2024-10-30 17:59:44 +00:00
Jeremy Ruston
5d6ddaee7e Fix plugin bag names 2024-10-17 15:51:01 +01:00
Jeremy Ruston
d0575d6e8e Merge branch 'master' into multi-wiki-support 2024-10-17 13:19:19 +01:00
Jeremy Ruston
6dd6b7bef1 Refactor conditional function definitions
Function definitions within a condition do not function as expected thanks to JS hoisting, so it is better to avoid it, and keep ESLint happy as well.

See discussion at #8622 with @pmario
2024-09-30 15:28:37 +01:00
Jeremy Ruston
517dae8242 Merge branch 'master' into multi-wiki-support 2024-09-30 15:20:56 +01:00
Mario Pietsch
d0b5f413a4 MWS icon remove redundant paths (#8621) 2024-09-30 14:37:31 +01:00
Jeremy Ruston
6e7efeb126 Merge branch 'master' into multi-wiki-support 2024-09-20 13:58:19 +01:00
Jeremy Ruston
0f4f60f978 Fix load wiki folder command 2024-09-20 13:57:54 +01:00
Jeremy Ruston
a9c124cffd Whitespace clean up 2024-09-20 13:57:12 +01:00
Jeremy Ruston
4050ba5f92 Avoid optional chaining syntax
So that we can run on older Node.js versions
2024-09-11 19:22:29 +01:00
webplusai
b698b3e9cf revert playwright test (#8573) 2024-08-30 19:55:04 +01:00
webplusai
a930411cfd Handle directory creation if doesn't exist for test store (#8562)
* fix breaking bug in image tiddler attachment

* fix comments

* fix code format

* refactor processIncomingTiddler flow

* remove whitespaces after if statements

* refactor attachment_blob persistence flow

* refactor process tiddler to support different attachments

* add tests for attachment

* add more attachement test cases

* working on adding instanbul for test coverage report

* code coverage report generation

* remove unnecessary packages

* fix comments

* handle directory creation if doesn't exist for test store

* resolve issue with CI tests failure
2024-08-30 13:14:18 +01:00
Jeremy Ruston
90848a1e96 Change for the sake of CI 2024-08-29 10:00:08 +01:00
Jeremy Ruston
c73311684b Merge branch 'master' into multi-wiki-support 2024-08-28 17:17:22 +01:00
Jeremy Ruston
8a5a684218 Clean ups from #8456 2024-08-28 17:16:35 +01:00
webplusai
3287dce40c MWS: fix editing attachment tiddlers (#8455)
* fix breaking bug in image tiddler attachment

* fix comments

* fix code format

* refactor processIncomingTiddler flow

* remove whitespaces after if statements

* refactor attachment_blob persistence flow

* refactor process tiddler to support different attachments

* add tests for attachment

* add more attachement test cases

* working on adding instanbul for test coverage report

* code coverage report generation

* remove unnecessary packages

* fix comments
2024-08-28 17:13:52 +01:00
Jeremy Ruston
eac8a2c3d8 Merge branch 'master' into multi-wiki-support 2024-08-27 09:19:00 +01:00
Jeremy Ruston
535c440741 Remove extraneous storylist tiddler 2024-08-22 11:58:10 +01:00
Jeremy Ruston
9eb28a0e5f Merge branch 'master' into multi-wiki-support 2024-08-22 11:57:38 +01:00
Jeremy Ruston
edc32c5262 Merge branch 'master' into multi-wiki-support 2024-08-19 13:11:04 +01:00
Jeremy Ruston
6492ed36bf Merge branch 'master' into multi-wiki-support 2024-08-06 21:38:38 +01:00
Jeremy Ruston
2819b53c04 Merge branch 'master' into multi-wiki-support 2024-07-31 14:03:38 +01:00
Jeremy Ruston
adb2ffcd4d Update package.json 2024-07-31 14:03:35 +01:00
Jeremy Ruston
97db75e741 Merge branch 'master' into multi-wiki-support 2024-07-26 14:00:34 +01:00
Jeremy Ruston
066771e6e9 Allow attachment handling to be switched off 2024-07-16 10:32:34 +01:00
Jeremy Ruston
b4664bd7d1 getBagTiddlerStream should return bagname 2024-07-16 10:31:13 +01:00
Jeremy Ruston
882438db14 Merge branch 'master' into multi-wiki-support 2024-07-15 20:58:31 +01:00
Jeremy Ruston
e9f314579d Fix failing tests 2024-07-10 14:42:56 +01:00
Jeremy Ruston
9e2962b24e Merge branch 'master' into multi-wiki-support 2024-07-10 14:37:30 +01:00
Jeremy Ruston
5b53a14903 Use $tw.utils.encodeURIComponentExtended for consistency with encodeuricomponent operator 2024-07-10 13:16:47 +01:00
poc2go
39b7a4fb71 Adds 'created' field to attachment meta.json file (#8356) 2024-07-09 16:35:18 +01:00
Jeremy Ruston
f1d0e52ff7 New commands 2024-07-05 18:40:59 +01:00
Jeremy Ruston
d1edf6424d Merge branch 'master' into multi-wiki-support 2024-07-05 16:52:41 +01:00
Jeremy Ruston
e9cbb51f68 Merge branch 'master' into multi-wiki-support 2024-06-28 14:55:50 +01:00
Jeremy Ruston
0ff3875b8a Update readme.tid 2024-06-24 11:42:59 +01:00
Jeremy Ruston
38e1ea8f90 Merge branch 'master' into multi-wiki-support 2024-06-22 16:56:46 +01:00
Jeremy Ruston
2b2fd4bdb7 Merge branch 'master' into multi-wiki-support 2024-06-08 14:59:17 +01:00
Jeremy Ruston
b8f463d8ba Merge branch 'master' into multi-wiki-support 2024-05-31 22:39:14 +01:00
Jeremy Ruston
3f30cb4991 Merge branch 'master' into multi-wiki-support 2024-05-31 14:05:30 +01:00
Jeremy Ruston
049951e269 Merge branch 'master' into multi-wiki-support 2024-05-31 13:46:36 +01:00
Jeremy Ruston
3c36e4bd55 Merge branch 'master' into multi-wiki-support 2024-05-31 13:32:53 +01:00
Jeremy Ruston
d03ad0bca6 Merge branch 'master' into multi-wiki-support 2024-05-29 21:42:34 +01:00
Jeremy Ruston
109d92ae68 Update readme 2024-05-28 15:36:34 +01:00
Jeremy Ruston
e396334b20 Disable SSE by default
The current implementation is still broken, and actually more broken than it was before a37d50166f.

It seems that we should be exposing the SSE events to the syncer so that the resulting updates can be handled by the syncers existing task scheduler
2024-05-28 13:15:34 +01:00
Jeremy Ruston
d8fd9af63a Fix string vs. number confusion for revisions 2024-05-28 09:08:17 +01:00
Jeremy Ruston
9b6d677726 Fix revision comparisons 2024-05-27 17:33:27 +01:00
Jeremy Ruston
a37d50166f Tentative fix for SSE issue
See https://github.com/Jermolene/TiddlyWiki5/pull/7915#issuecomment-2124341900
2024-05-27 15:01:15 +01:00
Jeremy Ruston
b4564e31bd Merge branch 'master' into multi-wiki-support 2024-05-26 10:40:19 +01:00
Jeremy Ruston
33fb857d6b Merge branch 'master' into multi-wiki-support 2024-05-22 15:42:22 +01:00
Jeremy Ruston
4c2c7266d7 Make usage of SSE be configurable 2024-05-22 10:39:02 +01:00
Jeremy Ruston
3c7f06009b Temporarily Disable SSE 2024-05-22 10:33:35 +01:00
Jeremy Ruston
87f4a525f8 Mark MWS plugins as experimental 2024-05-21 12:07:01 +01:00
Jeremy Ruston
92a1c56d31 Merge branch 'master' into multi-wiki-support 2024-05-21 12:05:26 +01:00
Jeremy Ruston
d8eb5cb459 Merge branch 'master' into multi-wiki-support 2024-05-20 14:42:11 +01:00
Jeremy Ruston
268aaebaf0 Logging 2024-04-16 14:51:26 +01:00
Jeremy Ruston
ea318bab6e Fix file separator for Windows 2024-04-16 13:47:15 +01:00
Jeremy Ruston
aafe775779 Debugging 2024-04-16 12:49:09 +01:00
Jeremy Ruston
129bbe421c Debugging 2024-04-16 12:45:35 +01:00
Jeremy Ruston
471ba99a5d Debugging 2024-04-16 12:43:41 +01:00
Jeremy Ruston
516a17a6f0 More debugging 2024-04-16 12:41:50 +01:00
Jeremy Ruston
2e4980bb97 Add some error checking 2024-04-16 12:38:05 +01:00
Jeremy Ruston
db9978f8c2 Syncer should only save existing tiddlers if they have changed 2024-04-16 11:59:52 +01:00
Jeremy Ruston
cc4cb04900 Turn on WAL mode for better-sqlite3 2024-04-14 19:12:21 +01:00
Jeremy Ruston
9ba4556250 Add support for the plugin library
We create a system bag to contain each plugin/theme/language. It seems wasteful because it results in lots of bags, but the semantics are exactly right and so it seems like the right approach
2024-04-14 18:41:34 +01:00
Jeremy Ruston
131a5abeb8 Protect against syncing client state tiddlers to the server
The default sync filter omits $:/state/, but users can customise it
2024-04-12 10:58:45 +01:00
Jeremy Ruston
ce79a4add8 Add bag indicator to tiddler info panel 2024-04-12 10:28:26 +01:00
Jeremy Ruston
28a831489b Merge branch 'master' into multi-wiki-support 2024-04-12 09:59:12 +01:00
Jeremy Ruston
51cdca6841 Merge branch 'master' into multi-wiki-support 2024-04-03 21:51:51 +01:00
Jeremy Ruston
d51ad80f80 Incoming updates filter should exclude tiddlers prefixed $:/StoryList and $:/HistoryList
Thanks @pmario
2024-03-28 17:32:10 +00:00
Jeremy Ruston
ad528d6b1f Fix get wiki crash when serving a recipe with no tiddlers in it
Fixes #8110
2024-03-27 14:41:03 +00:00
Jeremy Ruston
f2947e73b3 Filter updates from the server
We don't want $:/StoryList etc.
2024-03-26 17:16:14 +00:00
Jeremy Ruston
baee0bb301 Fix broken updating of last_known_tiddler_id from streamed data 2024-03-26 12:25:49 +00:00
Jeremy Ruston
8a2111f150 Update GettingStarted 2024-03-26 12:25:13 +00:00
Jeremy Ruston
fcffff3964 npm start should use mws-listen 2024-03-25 22:38:10 +00:00
Jeremy Ruston
cca1f21d02 Undo unneeded changes to image widget 2024-03-25 22:35:42 +00:00
Jeremy Ruston
37f6930bf2 Quit command should abort any pending commands 2024-03-25 22:33:41 +00:00
Jeremy Ruston
4b1affee50 Load streamed tiddlers immediately, rather than scheduling a load 2024-03-25 17:49:34 +00:00
Jeremy Ruston
464d17b522 Update last known tiddler ID for events delivered via SSE 2024-03-25 17:43:29 +00:00
Jeremy Ruston
d1bb7159b8 Expose the connection status in the UI 2024-03-25 17:07:36 +00:00
Jeremy Ruston
b58cfe6324 SSE client: better state management to avoid multiple connections 2024-03-25 16:43:41 +00:00
Jeremy Ruston
7a0c43436f First pass at SSE support 2024-03-25 08:36:42 +00:00
Jeremy Ruston
708e21951f Fix syncing 2024-03-24 21:15:59 +00:00
Jeremy Ruston
8198574087 Remove code that is unneeded for the moment 2024-03-24 21:15:31 +00:00
Jeremy Ruston
6c9b92400e GET recipes/name/tiddlers.json should use include_deleted parameter 2024-03-24 18:32:56 +00:00
Jeremy Ruston
8091db37e8 Merge branch 'master' into multi-wiki-support 2024-03-23 17:32:03 +00:00
Jeremy Ruston
e66b67dedc Only turn binary tiddlers into attachments 2024-03-23 09:54:15 +00:00
Jeremy Ruston
a2012dcff8 Typo in comment 2024-03-23 09:54:03 +00:00
Jeremy Ruston
08649dd1eb More efficient syncing
Thank you @PotOfCoffee2Go I ended up taking some of your code from #8101 to get this up and running. There's still some stuff missing (like the tests!) but it gets things moving.
2024-03-23 09:27:54 +00:00
Jeremy Ruston
52f76380c7 Remove code relating to revision and bag as fields 2024-03-22 17:58:52 +00:00
Jeremy Ruston
1f63bcbbd0 Remove some templates that are no longer needed 2024-03-20 19:24:21 +00:00
Jeremy Ruston
3aa5607a3a Stop storing bag and revision details as tiddler fields
Instead we store them as dictionary tiddlers
2024-03-20 19:22:12 +00:00
Jeremy Ruston
eaebeb87c9 Include bagname in tiddler Etags 2024-03-20 18:52:54 +00:00
Jeremy Ruston
808b94468e Fix multipart form data POST test 2024-03-20 18:52:37 +00:00
Jeremy Ruston
60e6c8bcb2 Fix etag handling 2024-03-20 17:56:47 +00:00
Jeremy Ruston
891f0fd599 Fix page body class in static pages
So that we get the TW background colour
2024-03-20 17:55:50 +00:00
Jeremy Ruston
6154de0d2c Rename misnamed route 2024-03-20 15:53:49 +00:00
Jeremy Ruston
cae9dbf5d1 Fix colours in static renderings 2024-03-20 15:51:58 +00:00
Jeremy Ruston
ae8ef305fa Get rid of those annoying missing image icons
By adding a `?fallback=url` parameter to tiddler requests
2024-03-20 15:40:38 +00:00
Jeremy Ruston
9b3ca525ee Introduce multiwikiclient plugin
Routes are now rationalised, too.
2024-03-20 15:13:50 +00:00
Jeremy Ruston
38ee942d8f Don't do full debug logging during tests 2024-03-20 15:12:35 +00:00
Jeremy Ruston
957329d515 Add store directories to gitignore 2024-03-20 13:10:30 +00:00
Jeremy Ruston
6063256439 Create new static index route with ability to create/update bags and recipes
Also introduces a new /.system/filename route for stylesheets, scripts etc.
2024-03-20 09:44:52 +00:00
Jeremy Ruston
1c64646393 Fix quit command to work gracefully 2024-03-19 10:04:32 +00:00
Jeremy Ruston
259b3dca1b Add a new static index route 2024-03-18 22:26:24 +00:00
Jeremy Ruston
6a673e6aea Stop syncing state tiddlers to the admin wiki 2024-03-18 21:08:48 +00:00
Jeremy Ruston
f606e33415 Stop using the existing core server infrastructure
Too much of a constraint now that we're starting work on client-server sync
2024-03-18 08:44:45 +00:00
Jeremy Ruston
09de91940e Typo 2024-03-17 19:30:22 +00:00
Jeremy Ruston
3d485f0706 Expose tiddler_ids in bag and recipe listings 2024-03-17 16:34:45 +00:00
Jeremy Ruston
7eaa9b8aec Database methods that mutate tables should return IDs 2024-03-17 15:06:36 +00:00
Jeremy Ruston
faa4b9700a More consistent variable naming 2024-03-17 14:54:06 +00:00
Jeremy Ruston
dea739ff07 Introduce delete markers for deleted tiddlers 2024-03-17 14:18:47 +00:00
Jeremy Ruston
69cc45bf5c Refactor the database engine specific code 2024-03-17 13:27:00 +00:00
Jeremy Ruston
347aa4d546 Tests should cover tiddler_ids 2024-03-17 13:07:24 +00:00
Jeremy Ruston
b4855b25c4 Merge branch 'master' into multi-wiki-support 2024-03-16 22:06:02 +00:00
Jeremy Ruston
d518675e03 Get rid of some console.logs 2024-03-15 16:49:24 +00:00
Jeremy Ruston
9b59ae2b73 Add an HTTP test for multipart form data upload 2024-03-15 16:40:22 +00:00
Jeremy Ruston
f67573315e POST /wiki/:bag_name/bags/:bag_name/tiddlers/ should also return JSON 2024-03-15 16:39:59 +00:00
Jeremy Ruston
501f57499e Update readme 2024-03-14 12:25:11 +00:00
Jeremy Ruston
2916cb6fd9 Clarify comment
Thanks @pmario
2024-03-14 11:55:55 +00:00
Jeremy Ruston
e553539b2a Clarify that process.exit() is a hack 2024-03-13 22:12:41 +00:00
Jeremy Ruston
3da773c27f Add HTTP tests to npm test command 2024-03-13 18:39:42 +00:00
Jeremy Ruston
b923be5e94 Merge branch 'master' into multi-wiki-support 2024-03-13 18:03:35 +00:00
Jeremy Ruston
c9ab184c65 TODOs before merging 2024-03-12 21:32:22 +00:00
Jeremy Ruston
bc45a16f40 Fix readme build 2024-03-12 21:21:49 +00:00
Jeremy Ruston
8b6642b56d Update the root readme that is visible on GitHub 2024-03-12 17:33:07 +00:00
Jeremy Ruston
d6807cb471 Update MWS plugin readme 2024-03-12 17:32:54 +00:00
Jeremy Ruston
f9064428c5 Add npm start command 2024-03-12 17:32:35 +00:00
Jeremy Ruston
a443e5f0ad Add new command to test local or remote server via HTTP 2024-03-11 21:52:40 +00:00
Jeremy Ruston
24413c53dd The listen command shouldn't return until the server is listening 2024-03-11 21:45:27 +00:00
Jeremy Ruston
8b5c3746f8 Refactor command module filenames 2024-03-11 09:40:40 +00:00
Jeremy Ruston
9df625c44d Reorganise JS modules into subdirectories 2024-03-11 09:10:01 +00:00
Rob Hoelz
54ff0446c6 MWS: store level tests (#8021)
* Fix a few typos

The "database instead of store" change isn't a typo fix, per se, but
these tests are testing the lower-level database layer, and I'm about
to introduce some tests for the higher-level store layer, so I want to
avoid any confusion in the test names

* Start on SQL store-level tests

* Add some tests for createBag

* Add test for getBagTiddler and getBagTiddlers

* Add basic recipe tests

* Add a test for saving a tiddler within a recipe

* Add store test for deleteTiddler
2024-03-11 09:01:43 +00:00
Jeremy Ruston
0f5dfb89ad Refactor multipart form handling for more reusability 2024-03-10 20:20:06 +00:00
Jeremy Ruston
e3b27768d2 Update comment 2024-03-10 20:19:41 +00:00
Jeremy Ruston
580283433e Remove unneeded highlight plugin from multiwikiserver edition 2024-03-10 18:07:02 +00:00
Jeremy Ruston
f4ac2b92e7 Remove unneeded plugins from tw5.com edition 2024-03-10 18:06:44 +00:00
Jeremy Ruston
e35584843d Fix mws-save-archive command name 2024-03-10 18:06:26 +00:00
Jeremy Ruston
3335e87ef4 Remove unneeded option 2024-03-10 17:49:52 +00:00
Jeremy Ruston
abde67e5df MWS: Add support for large tiddlers to be stored as attachment files
Fixes #8022
2024-03-10 17:45:33 +00:00
Jeremy Ruston
2ba3643a0c Merge branch 'master' into multi-wiki-support 2024-03-07 08:26:10 +00:00
Jeremy Ruston
89ae2012c7 Merge branch 'master' into multi-wiki-support 2024-03-04 14:14:06 +00:00
Jeremy Ruston
8a209d643f Fix typo 2024-03-01 18:58:17 +00:00
Jeremy Ruston
1a28ec7ea4 Rename upload manager to multipart form manager 2024-02-28 18:36:19 +00:00
Jeremy Ruston
5fe41fc896 Reorder test tiddlers 2024-02-28 18:19:22 +00:00
Jeremy Ruston
4f9ff1ae81 Missed closing tag 2024-02-28 18:19:01 +00:00
Jeremy Ruston
d97ddf1eec Merge branch 'master' into multi-wiki-support 2024-02-28 18:04:45 +00:00
Jeremy Ruston
9facf4a067 Fix incorrect encoding of description when creating bags and recipes 2024-02-28 17:46:19 +00:00
Rob Hoelz
de4fe132a7 Unconditionally decrement transaction depth (#8008)
…otherwise we may end up in a situation where we're always stuck in an
"already in a transaction" state and often neglect to actually enter a
real transaction!
2024-02-26 11:02:55 +00:00
Jeremy Ruston
d7d0733177 Remove accidentally committed database file
Thanks @hoelzro
2024-02-25 22:27:36 +00:00
Jeremy Ruston
e614e291a2 Default to better-sqlite3 2024-02-25 18:08:59 +00:00
Jeremy Ruston
dd9a3bfeeb Re-enable loading the tw5.com docs tiddlers 2024-02-25 18:04:54 +00:00
Jeremy Ruston
83229ace63 Add a config tiddler for selecting the SQLite engine 2024-02-25 18:04:43 +00:00
Jeremy Ruston
6724fa804b Run the tests on both node-sqlite3-wasm and better-sqlite3 2024-02-25 17:58:53 +00:00
Jeremy Ruston
630b98520f Add note about transaction handling 2024-02-25 09:48:24 +00:00
Jeremy Ruston
1c0341de51 Merge branch 'master' into multi-wiki-support 2024-02-24 09:38:42 +00:00
Jeremy Ruston
d5aa74d9af Improve manual transaction handling
Thanks @hoelzro
2024-02-24 09:31:59 +00:00
Jeremy Ruston
343cc33bbe Add a test tiddler with emoji title
Useful for testing
2024-02-24 09:30:20 +00:00
Jeremy Ruston
b1edbed6a5 Fix bag static HTML to show emoji correctly 2024-02-23 17:47:00 +00:00
Jeremy Ruston
066e553f84 Introduce command to load tiddler folders into a bag 2024-02-23 12:51:29 +00:00
Jeremy Ruston
61b54125be Rename mws-load|save to mws-load|save-archive 2024-02-23 12:51:07 +00:00
Jeremy Ruston
3276703edd Fix failing tests 2024-02-23 09:30:12 +00:00
Jeremy Ruston
f9265169fd Commands to load and save tiddlers, bags and recipes as a JSON archive
@linonetwo the resulting archive should be suitable for storing in git
2024-02-23 09:27:53 +00:00
Jeremy Ruston
2361880c45 createBag should optionally set access control data 2024-02-23 09:26:45 +00:00
Jeremy Ruston
3ad87df154 Allow wiki engine to be configured 2024-02-22 17:58:30 +00:00
Jeremy Ruston
3c58788e37 Merge branch 'master' into multi-wiki-support 2024-02-22 17:39:51 +00:00
Jeremy Ruston
310b5f058a Whitespace 2024-02-22 12:13:53 +00:00
Jeremy Ruston
a33705e348 Fix error
It appears that not all statements can be finalized.
2024-02-22 12:09:36 +00:00
Jeremy Ruston
3fca82321e MWS: Add support for node-sqlite-wasm alongside better-sqlite3 (#7996)
* Switch from better-sqlite3 to node-sqlite3-wasm

Seems to be slower, but might make cloud deployments easier by not having any binary dependencies

* More logging

* Temporarily use a memory database

We will make this configurable

* Revert "More logging"

* Resume loading demo tiddlers

* Cache prepared statements

Gives a 20% reduction in startup time on my machine

* Some more logging

* Update package-lock

* More logging

* Route regexps should allow for proxies that automatically decode URLs

Astonishingly, Azure does this

* Go back to a file-based database

* Less logging

* Update package-lock.json

* Simplify startup by not loading the docs edition

* Tiddler database layer should mark statements as having been removed

* Re-introduce better-sqlite3

* Make the SQLite provider be switchable

* Support switchable SQL engines

I am not intending to make this a long term feature. We will choose one engine and stick with it until we choose to change to another.

* Adjust dependency versions

* Setting up default engine

* Make transaction handling compatible with node-sqlite3-wasm

https://github.com/tndrle/node-sqlite3-wasm doesn't have transaction support so I've tried to implement it using SQL statements directly.

@hoelzro do you think this is right? Should we be rolling back the transaction in the finally clause? It would be nice to have tests in this area...

I looked at better-sqlite3's implementation - https://github.com/WiseLibs/better-sqlite3/blob/master/lib/methods/transaction.js

* Default to better-sqlite3 for compatibility after merging
2024-02-22 11:57:41 +00:00
Rob Hoelz
790f431df0 MWS: Use transactions when modifying multiple resources (#7991)
* Use transactions when modifying multiple resources

This prevents partial changes from entering the database, and also
nets a nice speed-up.

* Keep track of transaction depth

…so we could someday potentially leverage SQL implementations that don't
implement nested transactions
2024-02-22 10:48:39 +00:00
Jeremy Ruston
0d22bf8418 Update to latest better-sqlite3 2024-02-22 10:47:08 +00:00
Jeremy Ruston
1eecfb6b3a Less logging 2024-02-21 17:55:13 +00:00
Jeremy Ruston
b8c1c6c8de Allow backslashes in trailing API path components
To make us more tolerant of proxies that "helpfully" decodeuricomponent for us (looking at you Azure)
2024-02-21 17:54:56 +00:00
Jeremy Ruston
6503fb4a04 Simple performance logging 2024-02-20 09:33:39 +00:00
Jeremy Ruston
bab14b7053 Logging 2024-02-19 16:29:14 +00:00
Jeremy Ruston
2d4b3341f6 Merge branch 'master' into multi-wiki-support 2024-02-19 09:55:57 +00:00
Jeremy Ruston
6f8a3b9261 mws-load command: more validation tiddler files 2024-02-16 16:41:39 +00:00
Jeremy Ruston
8edefffbc5 WIP: Support for streaming multipart form data to the file system 2024-02-16 16:02:40 +00:00
Jeremy Ruston
59b425fd5c Update to better-sqlite3 v9.4.1 2024-02-16 16:00:46 +00:00
Jeremy Ruston
f2267e2af0 Merge branch 'master' into multi-wiki-support 2024-02-16 15:58:08 +00:00
Jeremy Ruston
c26acfdb42 Add NOT NULL constraint to all columns
Thanks @hoelzro
2024-02-05 16:15:35 +00:00
Jeremy Ruston
f925f036c9 Introduce $tw.mws for MWS globals 2024-02-05 14:49:08 +00:00
Jeremy Ruston
2c810faeeb Add barebones support for timing HTTP response generation times 2024-02-02 15:42:47 +00:00
Jeremy Ruston
6675358e85 WIP: Add a multipart/form-data convenience function
This is the start of adding support for large attachments.

We have a new endpoint for POSTing tiddler data. The idea is that it will take any kind of data and figure out how to extract tiddlers from the upload and save them in the nominated bag.

The next step is to move the attachment files into a special folder and reference them from the database so that we can construct _canonical_uris for them.
2024-02-02 15:42:02 +00:00
Jeremy Ruston
262a730534 Move the database file into a "store" directory inside the wiki folder 2024-01-29 18:11:50 +00:00
Jeremy Ruston
4b6872aa42 Fix typo 2024-01-29 08:29:26 +00:00
Jeremy Ruston
3283d38867 First draft of a command to read tiddlers, bags and recipes from an archive
The archive format is a legacy format that I used with Xememex, and will need to be updated to iron out the wrinkles
2024-01-28 21:27:12 +00:00
Jeremy Ruston
4204ff367e A few more tests 2024-01-28 17:11:53 +00:00
Jeremy Ruston
51e646690c Allow tilde character in bag and recipe names 2024-01-28 17:11:44 +00:00
Jeremy Ruston
85607f7846 Query fixes 2024-01-28 17:11:23 +00:00
Jeremy Ruston
41a5bcc3a1 Fix canonical URI handling 2024-01-26 15:48:39 +00:00
Jeremy Ruston
84c8a9be9b Fix typo 2024-01-26 15:01:07 +00:00
Jeremy Ruston
62b2fe3e2f Add an error when creating a recipe with no bags 2024-01-26 14:42:43 +00:00
Jeremy Ruston
f5fdd79c7f Refresh when creating bags and recipes to get the change instantly 2024-01-26 14:42:17 +00:00
Jeremy Ruston
14752ccb0c Missing comma 2024-01-26 14:04:54 +00:00
Jeremy Ruston
541c166863 Error handling for bag and recipe handling 2024-01-26 14:03:32 +00:00
Jeremy Ruston
270f62bbb2 Merge branch 'master' into multi-wiki-support 2024-01-26 13:38:02 +00:00
Jeremy Ruston
8290d853c9 Merge branch 'master' into multi-wiki-support 2024-01-26 12:54:40 +00:00
Jeremy Ruston
b0a67300cc Add support for bag descriptions, validate bags and recipes, and complete the UI for creating bags and recipes
Styling to come
2024-01-24 22:24:24 +00:00
Jeremy Ruston
0b9749f3a4 Add favicon to bag listing page 2024-01-24 20:58:57 +00:00
Jeremy Ruston
3ad3e19392 Merge branch 'master' into multi-wiki-support 2024-01-24 16:21:58 +00:00
Jeremy Ruston
ed71adac7e Merge branch 'master' into multi-wiki-support 2024-01-24 12:54:39 +00:00
Jeremy Ruston
8d95c92bfb Use $:/state/ tiddler for server entity state tiddlers 2024-01-24 10:56:23 +00:00
Jeremy Ruston
41ab94978f Renaming tiddlers for consistency 2024-01-24 10:55:14 +00:00
Jeremy Ruston
26e198b7d8 First pass at MWS icon 2024-01-23 22:05:58 +00:00
Jeremy Ruston
d16746ab38 Extend image widget to support alternate content if a remote image fails to load
...and use it to add an image for bags/recipes that do not have a $:/favicon.ico
2024-01-23 22:05:22 +00:00
Jeremy Ruston
627c3e20cc Add docs bags 2024-01-23 16:53:12 +00:00
Jeremy Ruston
4d42d4a190 Escape less than sign
Otherwise tiddlers containing `</script>` will break TiddlyWiki
2024-01-23 16:52:49 +00:00
Jeremy Ruston
ff184822ca Don't wikify recipe descriptions 2024-01-23 16:52:11 +00:00
Jeremy Ruston
ddbd6d1e82 Fix favicon aspect ratio 2024-01-23 16:51:57 +00:00
Jeremy Ruston
f6d6478944 Add support for recipe descriptions 2024-01-23 14:29:50 +00:00
Jeremy Ruston
138c7f2665 Don't use the filesystem plugin
Otherwise changes to _multiwikiserver/ tiddlers are saved back to the file system

Perhaps it would work better to configure the wiki to sync state tiddlers to the browser.
2024-01-23 14:29:08 +00:00
Jeremy Ruston
239ace0c07 Avoid clients of sqlTiddlerStore having to call updateAdminWIki() explicitly 2024-01-23 12:53:06 +00:00
Jeremy Ruston
c1312100aa Admin UI styling 2024-01-23 12:52:40 +00:00
Jeremy Ruston
e343eccdc3 Refactor _canonical_uri handling out of route handlers 2024-01-23 10:51:12 +00:00
Jeremy Ruston
da5b316358 Split SqlTiddlerStore into SqlTiddlerStore and SqlTiddlerDatabase
The motivation is to encapsulate knowledge of the SQL queries
2024-01-22 22:08:55 +00:00
Jeremy Ruston
dc8692044c Use SQLite's AUTOINCREMENT to give us tiddler version identifiers
This commit fixes sync within hosted wikis
2024-01-21 18:18:29 +00:00
Jeremy Ruston
4f9ba11489 Update to newest better-sqlite3 2024-01-21 18:17:23 +00:00
Jeremy Ruston
f7914db019 Add bag and recipe favicons to dashboard 2024-01-20 21:50:12 +00:00
Jeremy Ruston
11ecaff7db Fix typo 2024-01-20 21:48:40 +00:00
Jeremy Ruston
d832bbcc70 Order the results of getRecipeTiddlers 2024-01-20 21:48:33 +00:00
Jeremy Ruston
59aed49e98 Make getRecipeTiddlers return the bagname as well 2024-01-20 20:22:46 +00:00
Jeremy Ruston
e9f83ca735 Add missing LIMIT 1 2024-01-19 22:03:07 +00:00
Jeremy Ruston
afa9ad3cde Update store.getRecipeTiddler to also return the bag from which the tiddler came 2024-01-19 20:35:47 +00:00
Jeremy Ruston
01d29ed11e get bag tiddler and put recipe tiddler should return the bag name 2024-01-19 20:12:29 +00:00
Jeremy Ruston
8f9ae7e4d5 Clarify method name 2024-01-19 19:52:57 +00:00
Jeremy Ruston
70b048f356 Fix bag links 2024-01-19 19:36:36 +00:00
Jeremy Ruston
5fddd3b104 Add support for retrieving tiddlers from bags 2024-01-19 19:33:58 +00:00
Jeremy Ruston
02afbb4000 Rename some of the routes more logically 2024-01-19 19:27:54 +00:00
Jeremy Ruston
54432485e7 Add an HTML view of bag listings 2024-01-19 19:25:58 +00:00
Jeremy Ruston
26ede2839b Add support for _canonical_uri tiddlers
Currently hard wired to kick in for tiddlers over 10MB (in base64 representation for binary tiddlers)
2024-01-19 14:46:21 +00:00
Jeremy Ruston
4b0df1a7ae Basic support for creating bags and recipes
Cannot yet specify the bags for the new recipe
2024-01-19 11:03:58 +00:00
Jeremy Ruston
9767e7d3b7 Update entity state tiddlers on startup to read bag and recipe info 2024-01-19 11:03:27 +00:00
Jeremy Ruston
4133e7d6d6 Stream wiki generation
Avoids "string too long" errors when working with big tiddlers (>100MB)
2024-01-19 10:52:12 +00:00
Jeremy Ruston
4f37355a9f Tests should use a dummy admin wiki 2024-01-19 10:28:04 +00:00
Jeremy Ruston
82fae45656 Admin styling 2024-01-18 21:48:09 +00:00
Jeremy Ruston
2f09c32d2d Fix getTiddler query 2024-01-18 21:47:57 +00:00
Jeremy Ruston
a16338ce11 Merge branch 'master' into multi-wiki-support 2024-01-18 16:43:43 +00:00
Jeremy Ruston
50d0b1412d Fix CI tests 2024-01-18 09:02:41 +00:00
Jeremy Ruston
8941bd1747 Server extension framework
May not actually be needed
2024-01-17 22:42:01 +00:00
Jeremy Ruston
615dc0c4a3 First pass at admin user interface 2024-01-17 22:41:41 +00:00
Jeremy Ruston
1fb8b2e279 Fix broken test 2024-01-05 15:45:40 +00:00
Jeremy Ruston
0799177cf4 Add another recipe, improve docs 2024-01-05 15:40:39 +00:00
Jeremy Ruston
1eed61397b Fix create recipe SQL bug 2024-01-05 15:37:48 +00:00
Jeremy Ruston
3f1f7c7ef7 Remove debugging code 2024-01-05 11:08:33 +00:00
Jeremy Ruston
8543dda4aa Fix broken test 2024-01-05 11:01:10 +00:00
Jeremy Ruston
68a89b615d Use a persistent disk-based database 2024-01-05 10:58:07 +00:00
Jeremy Ruston
e9d3f67c5c Add new multiwikiserver edition 2024-01-03 16:47:20 +00:00
Jeremy Ruston
a980390870 Implement APIs for client wikis to sync with the server
It is now possible to create and edit tiddlers, using the existing tiddlywebadaptor syncing mechanism. There are a lot of hacks and lumpiness to make things compatible, so I think I will end up with an independent implementation
2024-01-03 16:27:13 +00:00
Jeremy Ruston
299781bdba Update docs 2024-01-02 21:47:08 +00:00
Jeremy Ruston
f42d3e0536 Update usage instructions 2024-01-02 21:41:25 +00:00
Jeremy Ruston
993eb5c90d Tests need npm install 2024-01-02 21:41:06 +00:00
Jeremy Ruston
f8f8319324 Add dependencies to package.json
This is needed in order for our CI to be able to run the tests
2024-01-02 14:47:32 +00:00
Jeremy Ruston
12d84c43c9 Initial Commit 2024-01-02 14:39:14 +00:00
172 changed files with 12374 additions and 13 deletions

View File

@@ -4,8 +4,9 @@ on:
branches:
- master
- tiddlywiki-com
- multi-wiki-support
env:
NODE_VERSION: "22"
NODE_VERSION: "23"
jobs:
test:
runs-on: ubuntu-latest
@@ -82,3 +83,14 @@ jobs:
- run: "./bin/build-tw-org.sh"
env:
GH_TOKEN: ${{ secrets.GITHUBPUSHTOKEN }}
build-mws-tiddlywiki-com:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/multi-wiki-support'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: "${{ env.NODE_VERSION }}"
- run: "./bin/build-mws-site.sh"
env:
GH_TOKEN: ${{ secrets.GITHUBPUSHTOKEN }}

2
.gitignore vendored
View File

@@ -5,7 +5,9 @@
tmp/
output/
node_modules/
store/
/test-results/
/playwright-report/
/playwright/.cache/
$__StoryList.tid
/editions/test/test-store/*

View File

@@ -0,0 +1,4 @@
The `multi-wiki-support` branch includes some changes that are not intended to be merged into the `master` branch:
* Readme update (see `editions/tw5.com/tiddlers/readme/ReadMe.tid`)
* Remove `multiwikiserver` plugin from `readme-bld.sh` (see `bin/readme-bld.sh`)

97
bin/build-mws-site.sh Executable file
View File

@@ -0,0 +1,97 @@
#!/bin/bash
# Build mws.tiddlywiki.com assets.
# Default to the version of TiddlyWiki installed in this repo
if [ -z "$MWSTWCOM_BUILD_TIDDLYWIKI" ]; then
MWSTWCOM_BUILD_TIDDLYWIKI=./tiddlywiki.js
fi
echo "Using MWSTWCOM_BUILD_TIDDLYWIKI as [$MWSTWCOM_BUILD_TIDDLYWIKI]"
# Set up the build details
if [ -z "$MWSTWCOM_BUILD_DETAILS" ]; then
MWSTWCOM_BUILD_DETAILS="$(git symbolic-ref --short HEAD)-$(git rev-parse HEAD) from $(git remote get-url origin)"
fi
echo "Using MWSTWCOM_BUILD_DETAILS as [$MWSTWCOM_BUILD_DETAILS]"
if [ -z "$MWSTWCOM_BUILD_COMMIT" ]; then
MWSTWCOM_BUILD_COMMIT="$(git rev-parse HEAD)"
fi
echo "Using MWSTWCOM_BUILD_COMMIT as [$MWSTWCOM_BUILD_COMMIT]"
# Set up the build output directory
if [ -z "$MWSTWCOM_BUILD_OUTPUT" ]; then
MWSTWCOM_BUILD_OUTPUT=$(mktemp -d)
fi
mkdir -p $MWSTWCOM_BUILD_OUTPUT
if [ ! -d "$MWSTWCOM_BUILD_OUTPUT" ]; then
echo 'A valid MWSTWCOM_BUILD_OUTPUT environment variable must be set'
exit 1
fi
echo "Using MWSTWCOM_BUILD_OUTPUT as [$MWSTWCOM_BUILD_OUTPUT]"
# Pull existing GitHub pages content
git clone --depth=1 --branch=main "https://github.com/TiddlyWiki/mws.tiddlywiki.com-gh-pages.git" $MWSTWCOM_BUILD_OUTPUT
# Make the CNAME file that GitHub Pages requires
echo "mws.tiddlywiki.com" > $MWSTWCOM_BUILD_OUTPUT/CNAME
# Delete any existing static content
mkdir -p $MWSTWCOM_BUILD_OUTPUT/static
rm $MWSTWCOM_BUILD_OUTPUT/static/*
# Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script)
echo -e -n "title: $:/build\ncommit: $MWSTWCOM_BUILD_COMMIT\n\n$MWSTWCOM_BUILD_DETAILS\n" > $MWSTWCOM_BUILD_OUTPUT/build.tid
######################################################
#
# mws.tiddlywiki.com distribution
#
######################################################
# /index.html Main site
# /favicon.ico Favicon for main site
# /static.html Static rendering of default tiddlers
# /alltiddlers.html Static rendering of all tiddlers
# /static/* Static single tiddlers
# /static/static.css Static stylesheet
# /static/favicon.ico Favicon for static pages
node $MWSTWCOM_BUILD_TIDDLYWIKI \
editions/multiwikidocs \
--verbose \
--version \
--load $MWSTWCOM_BUILD_OUTPUT/build.tid \
--output $MWSTWCOM_BUILD_OUTPUT \
--build favicon static index \
|| exit 1
# Delete the temporary build tiddler
rm $MWSTWCOM_BUILD_OUTPUT/build.tid || exit 1
# Push output back to GitHub
# Exit script immediately if any command fails
set -e
pushd $MWSTWCOM_BUILD_OUTPUT
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
git add -A .
git commit --message "GitHub build: $GITHUB_RUN_NUMBER of $TW5_BUILD_BRANCH ($(date +'%F %T %Z'))"
git remote add deploy "https://$GH_TOKEN@github.com/TiddlyWiki/mws.tiddlywiki.com-gh-pages.git" &>/dev/null
git push deploy main &>/dev/null
popd

View File

@@ -2,6 +2,8 @@
# test TiddlyWiki5 for tiddlywiki.com
npm install
node ./tiddlywiki.js \
./editions/test \
--verbose \

View File

@@ -10,6 +10,7 @@ fi
# tw5.com readmes
node $TW5_BUILD_TIDDLYWIKI \
+plugins/tiddlywiki/multiwikiserver \
editions/tw5.com \
--verbose \
--output . \

View File

@@ -38,6 +38,13 @@ Commander.prototype.log = function(str) {
}
};
/*
Clear pending commands
*/
Commander.prototype.clearCommands = function() {
this.commandTokens = this.commandTokens.slice(0,this.nextToken);
};
/*
Write a string if verbose flag is set
*/

View File

@@ -16,7 +16,7 @@ var Server = require("$:/core/modules/server/server.js").Server;
exports.info = {
name: "listen",
synchronous: true,
synchronous: false,
namedParameterMode: true,
mandatoryParameters: []
};
@@ -38,7 +38,11 @@ Command.prototype.execute = function() {
wiki: this.commander.wiki,
variables: self.params
});
var nodeServer = this.server.listen();
var nodeServer = this.server.listen(null,null,null,{
callback: function() {
self.callback();
}
});
$tw.hooks.invokeHook("th-server-command-post-start",this.server,nodeServer,"tiddlywiki");
return null;
};

View File

@@ -0,0 +1,37 @@
/*\
title: $:/core/modules/commands/quit.js
type: application/javascript
module-type: command
Immediately ends the TiddlyWiki process
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "quit",
synchronous: true
};
var Command = function(params,commander,callback) {
var self = this;
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
// Clear any pending commands
this.commander.clearCommands();
// We don't actually quit, we just issue the "th-quit" hook to give listeners a chance to exit
$tw.hooks.invokeHook("th-quit");
return null;
};
exports.Command = Command;
})();

View File

@@ -364,6 +364,11 @@ Server.prototype.listen = function(port,host,prefix) {
}
// Display the port number after we've started listening (the port number might have been specified as zero, in which case we will get an assigned port)
server.on("listening",function() {
// Stop listening when we get the "th-quit" hook
$tw.hooks.addHook("th-quit",function() {
server.close();
});
// Log listening details
var address = server.address(),
url = self.protocol + "://" + (address.family === "IPv6" ? "[" + address.address + "]" : address.address) + ":" + address.port + prefix;
$tw.utils.log("Serving on " + url,"brown/orange");

View File

@@ -257,7 +257,11 @@ Save an incoming tiddler in the store, and updates the associated tiddlerInfo
Syncer.prototype.storeTiddler = function(tiddlerFields) {
// Save the tiddler
var tiddler = new $tw.Tiddler(tiddlerFields);
this.wiki.addTiddler(tiddler);
// Only save the tiddler if it has changed
var existingTiddler = this.wiki.getTiddler(tiddlerFields.title);
if(!existingTiddler || !existingTiddler.isEqual(tiddler)) {
this.wiki.addTiddler(tiddler);
}
// Save the tiddler revision and changeCount details
this.tiddlerInfo[tiddlerFields.title] = {
revision: this.getTiddlerRevision(tiddlerFields.title),
@@ -556,6 +560,7 @@ SaveTiddlerTask.prototype.run = function(callback) {
// Invoke the callback
callback(null);
},{
syncer: self.syncer,
tiddlerInfo: self.syncer.tiddlerInfo[self.title]
});
} else {
@@ -586,6 +591,7 @@ DeleteTiddlerTask.prototype.run = function(callback) {
// Invoke the callback
callback(null);
},{
syncer: self.syncer,
tiddlerInfo: self.syncer.tiddlerInfo[this.title]
});
};
@@ -614,6 +620,8 @@ LoadTiddlerTask.prototype.run = function(callback) {
}
// Invoke the callback
callback(null);
},{
syncer: self.syncer
});
};

View File

@@ -163,4 +163,4 @@ ImageWidget.prototype.refresh = function(changedTiddlers) {
exports.image = ImageWidget;
})();
})();

View File

@@ -0,0 +1,41 @@
created: 20240309135835396
modified: 20240309142156125
title: Bags and Recipes
type: text/vnd.tiddlywiki
The bags and recipes model is a reference architecture for how tiddlers can be shared between multiple wikis. It was first introduced by TiddlyWeb in 2008.
The principles of bags and recipes can be simply stated:
# Tiddlers are stored in named "bags"
# Bags have access controls that determines which users can read or write to them
# Recipes are named lists of bags, ordered from lowest priority to highest
# The tiddlers within a recipe are accumulated in turn from each bag in the recipe in order of increasing priority. Thus, if there are multiple tiddlers with the same title in different bags then the one from the highest priority bag will be used as the recipe tiddler
# Wikis are composed by splicing the tiddlers from the corresponding recipe into the standard TW5 HTML template
A very simple example of the recipe/bag model might be for a single user who maintains the following bags:
* ''recipes'' - tiddlers related to cooking recipes
* ''work'' - tiddlers related to work
* ''app'' - common tiddlers for customising TiddlyWiki
Those bags would be used with the following recipes:
* ''recipes'' --> recipes, app - wiki for working with recipes, with common custom components
* ''work'' --> work, app - wiki for working with work, with common custom components
* ''app'' --> app - wiki for maintaining custom components
All of this will work dynamically, so changes to the app bag will instantly ripple into the affected hosted wikis.
A more complex example might be for a teacher working with a group of students:
* ''student-{name}'' bag for each students work
* ''teacher-course'' bag for the coursework, editable by the teacher
* ''teacher-tools'' bag for custom tools used by the teacher
Those bags would be exposed through the following hosted wikis:
* ''student-{name}'' hosted wiki for each students work, including the coursework material
* ''teacher-course'' hosted wiki for the coursework, editable by the teacher
* ''teacher'' hosted wiki for the teacher, bringing together all the bags, giving them an overview of all the students work

View File

@@ -0,0 +1,15 @@
title: Database Engines
tags: Reference
MWS uses [[SQLite]] for data storage. It supports choosing between two "database engines" that are based on two different npm modules:
* [[better-sqlite3|https://www.npmjs.com/package/better-sqlite3]] is written partially in C/C++ and so requires compilation for the target platform
* [[node-sqlite3-wasm|https://www.npmjs.com/package/node-sqlite3-wasm]] is written entirely in JavaScript and does not require compilation, but does require a WebAssembly-capable Node.js host. This is not currently possible on some platforms such as iOS/iPadOS
By default `npm install` will install both database engines. By default it will use `better-sqlite3`. To switch to using `node-sqlite3-wasm`, set the system configuration tiddler `$:/config/MultiWikiServer/Engine` to `wasm` (the default value is `better`). Note that this tiddler resides in the [[Administration Wiki]].
!! Avoiding Installation Errors
If you encounter errors during `npm install` related to `gyp` or `prebuild`, you may be able to avoid them by using `node-sqlite3-wasm` instead of `better-sqlite3`. However, it will be necessary to manually install `node-sqlite3-wasm` and its dependencies. This can be done by running the following commands in your terminal:
<<.copy-code-to-clipboard "npm install node-sqlite3-wasm">>

View File

@@ -0,0 +1,6 @@
title: $:/DefaultTiddlers
HelloThere
Installation
Usage
Reference

View File

@@ -0,0 +1,19 @@
title: HelloThere
tags: TableOfContents
!! ~TiddlyWiki is Growing Up
<span class="tc-float-right">[img width=200 [MWS Banner.png]]</span>
~MultiWikiServer is a new development that drastically improves ~TiddlyWiki's capabilities when running as a server under Node.js. It brings ~TiddlyWiki up to par with common web-based tools like ~WordPress or ~MediaWiki by supporting multiple wikis and multiple users at the same time.
Features under development include:
* Hosting multiple wikis at once, using the [[Bags and Recipes]] mechanism for sharing data between them
* Based on [[SQLite|MWS and SQLite]] for performance and reliability
* Robust built-in synchronisation handlers for syncing data to the filesystem
* Flexible authentication and authorisation options
* Improved handling of file uploads and attachments, allowing gigabyte video files to be uploaded and streamed
* Instantaneous synchronisation of changes between the server and all connected clients
* Workflow processing on the server, for example to automatically compress images, or to archive webpages
MWS is currently [[under development at GitHub|https://github.com/TiddlyWiki/TiddlyWiki5/pull/7915]] but it is already functional and usable.

View File

@@ -0,0 +1,19 @@
title: Installation using Git
tags: Installation
modified: 20241105133737778
created: 20241105133737778
These instructions require basic knowledge both of the terminal and of Git. There are also [[alternative instructions without using Git|Installation]].
# Clone the code from GitHub with: <<.copy-code-to-clipboard "git clone -b multi-wiki-support --single-branch https://github.com/TiddlyWiki/TiddlyWiki5">>
# Open a terminal window and set the current directory to the root of the downloaded folder
# Install the dependencies with the command: <<.copy-code-to-clipboard "npm install">>
# Start the server with the command: <<.copy-code-to-clipboard "npm start">>
# To use MWS, visit http://localhost:8080 in a browser on the same computer
# When you have finished using MWS, stop the server with <kbd>ctrl-C</kbd>
See [[Troubleshooting]] if you encounter any errors.
To update your copy of MWS with newer changes from ~GitHub, run the following command:
<<.copy-code-to-clipboard "git pull">>

View File

@@ -0,0 +1,18 @@
title: Installation
tags: TableOfContents
modified: 20241105133737778
created: 20241105133737778
These instructions require minimal knowledge of the terminal. There are also [[alternative instructions for those using Git|Installation using Git]].
# Download the code [[direct from GitHub|https://github.com/TiddlyWiki/TiddlyWiki5/archive/refs/pull/7915/head.zip]]
# Open a terminal window and set the current directory to the root of the downloaded folder
# Install the dependencies with the command: <<.copy-code-to-clipboard "npm install">>
# To verify that MWS is working correctly, start the server with the command: <<.copy-code-to-clipboard "npm start">> and then visit http://localhost:8080 in a browser on the same computer
# When you have finished using MWS, stop the server with <kbd>ctrl-C</kbd>
See [[Troubleshooting]] if you encounter any errors.
To update your copy of MWS in the future with newer changes will require re-downloading the code, taking care not to lose any changes you might have made.

View File

@@ -0,0 +1,2 @@
title: Architecture
tags: TableOfContents

View File

@@ -0,0 +1,25 @@
title: MWS and SQLite
tags: Architecture
! Introduction
SQLite is a very popular open source embedded SQL database with some [[unusual characteristics|https://www.sqlite.org/different.html]]. It has proved itself to be robust, fast and scalable, and has been widely adopted in a range of applications including web browsers, mobile devices, and embedded systems.
The "embedded" part means that developers access SQLite as a library of C functions that run as part of a larger application. This contrasts with more familiar database applications like Microsoft's SQL Server or Oracle that are accessed as network services.
MWS uses SQLite for the tiddler store and associated data. It brings many advantages:
* ''Performance'': the optimising query engine inside SQLite makes it much faster and more efficient than could be achieved in plain JavaScript. In some cases, it is [[faster than writing directly to the file system||https://www.sqlite.org/fasterthanfs.html]]
* ''Reliability'': SQLite uses protocols that [[ensure data integrity and consistency|https://www.sqlite.org/hirely.html]], even when the application crashes
* ''Scalability'': SQLite can handle extremely [[large datasets and complex queries|https://www.sqlite.org/limits.html]]
* ''Portability'': SQLite databases are stored as [[a single file|https://www.sqlite.org/fileformat.html]] that can be easily copied and moved between systems
! Misconceptions
TiddlyWiki 5 has always incorporated a database. Until MWS, that database has always been a custom tiddler database written in JavaScript. Over the years it has been enhanced and optimised with indexes and other database features that have given us reasonably decent performance for a range of common operations.
In terms of the traditional architecture of TiddlyWiki, MWS uses SQLite as the basis for an internal API that is equivalent to that of the `$tw.Wiki` object: basic CRUD operations on a database of tiddlers stored by their titles.
In the context of MWS, SQLite is just a fast and efficient equivalent of TiddlyWiki's existing JavaScript database engine. It gives us the option of persisting the database in file storage, but we also retain the option to run the database entirely within memory and rely on a file synchronisation process to save changes as individual `.tid` files in the file system, just as we do today.
One particular misconception to avoid is the idea that SQLite replaces the folders of `.tid` files that characterise the Node.js configuration of TiddlyWiki. These are separate components with a different purpose. The tiddler files are the result of syncing a database to the filesystem, and that database can be conceptually interchanged between our custom JavaScript database or the new SQLite implementation in MWS.

View File

@@ -0,0 +1,230 @@
title: Reference
tags: TableOfContents
! Authentication & Authorization
!! Overview
Our application has transitioned from a conventional username/password authentication system to a more robust Authentication and Authorization implementation. This new system supports multiple user accounts, roles, permissions, and a comprehensive access control list.
!! Key Features
# Multiple User Accounts
# Role-based Access Control
# Granular Permissions
# Access Control List (ACL)
!! Application Access & Security
!!! Initial Setup
When you first launch the Multiwiki Server, it operates in an unauthenticated mode to facilitate initial configuration. During this initial state, the system creates a temporary anonymous administrator account. Upon accessing the landing page, you'll receive a prominent security warning with instructions to establish a permanent ADMIN account. It's crucial to create this account immediately to secure your installation.
!!! User Types and Permissions
!!!! Administrator (ADMIN)
* Full system access and configuration rights
* Can create, modify, and delete user accounts
* Manages role assignments and permissions
* Controls global system settings
* Can configure guest access policies
* Has complete control over all recipes and tiddlers
!!!! Regular Users
* Custom accounts created by administrators
* Permissions determined by assigned roles
* Access limited to specific recipes based on role permissions
* Can have READ and/or WRITE permissions
!!!! Guest Users
* Default anonymous access level
* No inherent permissions
* Can only access recipes without Access Control Lists (ACLs)
* Read/write capabilities configurable by ADMIN
* Useful for public wikis or documentation sites
!!! Access Control System
!!!! Recipe-Level Security
* Access control is implemented at the recipe level
* Each recipe can have its own Access Control List (ACL)
* Permissions are granular:
** READ: Allows viewing recipe contents
** WRITE: Allows modifications to recipe contents
!!!! Role-Based Access Control (RBAC)
* Administrators can create custom roles
* Roles can be assigned specific READ/WRITE permissions
* Users inherit permissions from their assigned roles
* Multiple roles can be assigned to a single user
* Provides flexible and scalable access management
!!!! Permission Inheritance
* Users receive combined permissions from all assigned roles
* When roles grant different permission levels for the same resource, the higher access level is granted. For example, if one role grants "read" and another grants "write" access to a recipe, the user receives "write" access since it includes all lower-level permissions.
* Guest access is overridden by recipe ACLs
* When different permission rules conflict, the system follows a "most restrictive wins" principle: if any applicable rule denies access or requires a higher security level, that restriction takes precedence over more permissive rules. This ensures security is maintained even when users have multiple overlapping role assignments or inherited permissions.
This security model allows for fine-grained control over content access while maintaining flexibility for both private and public wiki deployments.
!! User Management & Security Architecture
!!! User Account Management
Users can be administered through two interfaces:
# Web-based Administrative Interface
#* Accessible only to ADMIN users
#* Provides graphical interface for user operations
#* Real-time validation and feedback
# Command-line Interface (CLI) Tools
#* Suitable for automation and scripting
#* Enables batch operations
#* Useful for system initialization
Each user account contains the following essential components:
* ''Username''
** Must be unique across the system
** Case-sensitive
** Used for authentication and audit trails
* ''Password''
** Stored using secure hashing algorithms
** Never stored in plaintext
** Subject to complexity requirements
* ''Role Assignments''
** Multiple roles can be assigned
** Inherited permissions from all assigned roles
** Dynamic permission calculation based on role combination
!!! Role & Permission Framework
!!!! Role Management
Roles serve as permission containers and provide organized access control. The system includes:
Built-in Roles:
* `ADMIN`
** Highest privilege level
** Full system access
** Cannot be modified or deleted
** Can create and manage other roles
** Controls guest access policies
* `USER`
** Basic access rights
** Typically limited to specific recipes
**Custom Roles (Examples):**
* `MANAGER`
** Intermediate access level
** Can manage subset of resources
** Custom roles as needed for specific use cases
!!!! Permission Architecture
Core Permissions:
* `READ` Permission
** View recipe contents
** Access tiddler data
** View metadata
** Export capabilities
* `WRITE` Permission
** Create new tiddlers
** Modify existing content
** Delete resources
** Manage recipe contents
**Guest Access:**
* No default permissions
* Access limited to non-ACL recipes
* Configurable by ADMIN users
* Suitable for public wikis
!!! Access Control List (ACL) Implementation
The ACL system provides granular security control through:
!!!! Entity-Level Control
* Recipe-based access control
* Individual resource protection
* Hierarchical permission inheritance
!!! Authentication Process Flow
* Initial Authentication
** User submits credentials
** System validates username existence
** Password hash comparison
** Session token generation
* Session Management
** Secure session storage
** Token-based authentication
** Automatic session expiration
** Re-authentication requirements
!!! Authorization Workflow
* Request Processing
** Capture user action request
** Identify target resource
** Extract required permissions
* Permission Validation
** Check user roles
** Aggregate permissions
** Verify ACL entries
** Apply guest policies if applicable
* Access Decision
** Compare required vs. available permissions
** Apply most restrictive policy
** Return access decision
!!! System Extension Guidelines
!!!! Adding New Roles
# Access administrative interface
# Define role identifier
# Assign base permissions
# Configure ACL mappings
# Test role functionality
!!!! Permission Expansion
# Define new permission type
# Update ACL structure
# Implement permission checks
# Update validation logic
# Document new permission
!!!! Security Considerations
* Regular permission audits
* Role assignment reviews
* Guest access monitoring
* Security log analysis
* Access pattern monitoring
This comprehensive security model provides flexible, maintainable, and secure access control while supporting both authenticated and guest users within the Multiwiki Server environment.
! HTTP API
The ~MultiWikiServer HTTP API provides access to resources hosted by the MWS store. It is based on [[the API of TiddlyWeb|https://tank.peermore.com/tanks/tiddlyweb/HTTP%20API]], first developed in 2008 by Chris Dent.
The design goals of the API are:
* To follow the principles of REST where practical
* To present resources as nouns, not verbs
General points about the design:
* In MWS there are no resources that end with / (except for the root path which is /)

View File

@@ -0,0 +1,19 @@
title: Troublesheeting gyp/prebuild Installation Errors
tags: Troubleshooting
modified: 20241105133737778
created: 20241105133737778
Installation may fail with errors related to `gyp` or `prebuild`. These errors are caused by missing dependencies or incorrect versions of dependencies.
Note that in most cases, these errors occur because of the use of the npm module [[better-sqlite3|https://www.npmjs.com/package/better-sqlite3]]. This module is mostly written in C, and thus requires compilation for the target platform. MWS supports switchable database engines, and also supports the use of the [[node-sqlite3-wasm|https://www.npmjs.com/package/node-sqlite3-wasm]] module which is written in ~JavaScript and does not require compilation and so may avoid these errors. See [[Database Engines]] for more details of how to switch between engines.
The following steps may help resolve errors involving `gyp` or `prebuild`:
# Ensure that you have the latest version of Node.js installed. You can download the latest version from the [[Node.js website|https://nodejs.org/]].
# Update npm to the latest version by running the following command in your terminal: <<.copy-code-to-clipboard "npm install -g npm@latest">>
# Clear the npm cache by running the following command in your terminal: <<.copy-code-to-clipboard "npm cache clean --force">>
# Delete the `node_modules` folder in your TiddlyWiki directory by running the following command in your terminal: <<.copy-code-to-clipboard "rm -rf node_modules">>
# Reinstall the dependencies by running the following command in your terminal: <<.copy-code-to-clipboard "npm install">>
# If you continue to encounter errors, try running the following command in your terminal: <<.copy-code-to-clipboard "npm rebuild">>
# If you are still experiencing issues, you may need to manually install the `gyp` and `prebuild` dependencies. You can do this by running the following commands in your terminal: <<.copy-code-to-clipboard "npm install -g node-gyp">> <<.copy-code-to-clipboard "npm install -g prebuild">>
# Once you have installed the dependencies, try reinstalling the TiddlyWiki dependencies by running the following command in your terminal: <<.copy-code-to-clipboard "npm install">>

View File

@@ -0,0 +1,4 @@
title: Troubleshooting
tags: TableOfContents
<<list-links "[tag[Troubleshooting]]">>

View File

@@ -0,0 +1,13 @@
title: Usage
tags: TableOfContents
modified: 20241105133737778
created: 20241105133737778
Once MWS is successfully [[installed|Installation]], you can access it by visiting http://localhost:8080 in a browser on the same computer.
By default, MWS is installed with full anonymous access enabled, meaning that anyone with access to the server has full access to read and modify anything. However, also by default, the server is only accessible to browsers on the same machine.
If you intend to put an MWS installation on a public network like the Internet, the server will need to be secured with the following steps:
* Create and login with a new administrator account
* Disable anonymouse access

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

View File

@@ -0,0 +1,4 @@
title: MWS Banner.png
type: image/png
tags: picture
alt-text: Banner for the new Multi Wiki Server plugin for TiddlyWiki

View File

@@ -0,0 +1,16 @@
list-after: $:/core/ui/EditTemplate/title
tags: $:/tags/EditTemplate
title: $:/ContributionBanner
\define base-github()
https://github.com/TiddlyWiki/TiddlyWiki5/edit/multi-wiki-support/editions/multiwikidocs/tiddlers/
\end
<$set name="draft-of" value={{{ [<currentTiddler>get[draft.of]] }}}>
<$list filter="[[$:/config/OriginalTiddlerPaths]getindex<draft-of>]" variable="target" >
<div class="tc-improvement-banner">
{{$:/core/images/star-filled}} Can you help us improve this documentation? [[Find out how|Improving TiddlyWiki Documentation]] to
<a href={{{ [<target>addprefix<base-github>] }}} class="tc-tiddlylink-external" target="_blank" rel="noopener noreferrer">edit it directly on ~GitHub</a>
</div>
</$list>
</$set>

View File

@@ -0,0 +1,3 @@
title: $:/SiteDomain
mws.tiddlywiki.com

View File

@@ -0,0 +1,3 @@
title: $:/SitePreviewUrl
https://tiddlywiki.com/images/Introduction%2520Video%2520Thumbnail.jpg

View File

@@ -0,0 +1,3 @@
title: $:/SiteSubtitle
~TiddlyWiki for the People

View File

@@ -0,0 +1,4 @@
title: $:/SiteTitle
type: text/vnd.tiddlywiki
~TiddlyWiki ~MultiWikiServer

View File

@@ -0,0 +1,3 @@
title: $:/SiteUrl
https://mws.tiddlywiki.com/

View File

@@ -0,0 +1,129 @@
tags: $:/tags/RawMarkupWikified/TopBody
title: $:/SplashScreen
type: text/vnd.tiddlywiki
\import [subfilter{$:/core/config/GlobalImportFilter}]
\procedure show-icon(title)
<$wikify name="icon" text={{{ [<title>addprefix[{{]addsuffix[}}]] }}} output="html">
<$text text=<<icon>>/>
</$wikify>
\end
\rules only filteredtranscludeinline transcludeinline macrocallinline
<div class="tc-remove-when-wiki-loaded">
<style scoped>
.tc-splash-text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
margin: 20px auto 0;
width: 200px;
text-align: center;
color: <<colour foreground>>;
fill: <<colour foreground>>;
}
.tc-splash-text img {
width: 150px;
<<box-shadow "2px 2px 10px rgba(0, 0, 0, 0.5)">>
}
html body.tc-body {
background: <<colour page-background>>;
}
/*
Spinner from https://github.com/tobiasahlin/SpinKit/ by Tobias Ahlin
The MIT License (MIT)
Copyright (c) 2015 Tobias Ahlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
.tc-splash-spinner {
margin: 20px auto 0;
width: 70px;
text-align: center;
}
.tc-splash-spinner > div {
width: 18px;
height: 18px;
border-radius: 100%;
display: inline-block;
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
background-color: #f88;
}
.tc-splash-spinner .tc-splash-bounce-1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
background-color: #8f8;
}
.tc-splash-spinner .tc-splash-bounce-2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
background-color: #88f;
}
@-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bouncedelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}
</style>
<div class="tc-splash-spinner">
<div class="tc-splash-bounce-1"></div>
<div class="tc-splash-bounce-2"></div>
<div class="tc-splash-bounce-3"></div>
</div>
<div class="tc-splash-text">
<strong>{{$:/SiteTitle}}</strong>
<br/>
is loading
</div>
<!-- Demonstrating how to embed a bitmap graphic -->
<div class="tc-splash-text">
<img src="data:image/jpeg;base64,{{MWS Banner.png||$:/core/templates/plain-text-tiddler}}" width="100"/>
</div>
<!-- Demonstrating how to embed a wikitext SVG graphic -->
<div class="tc-splash-text">
<<show-icon "$:/core/icon">>
</div>
</div>

View File

@@ -0,0 +1,3 @@
title: $:/StaticBanner
<div class="tc-static-alert"><div class="tc-static-alert-inner">This page is part of a static HTML representation of the ~TiddlyWiki at https://tiddlywiki.com/</div></div>

View File

@@ -0,0 +1,49 @@
tags: $:/tags/Stylesheet
title: $:/_styles
type: text/vnd.tiddlywiki
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
.tc-improvement-banner {
font-size: 0.7em;
background: #fcc;
padding-left: 5px;
margin-top: 6px;
margin-bottom: 12px;
<<box-shadow "2px 2px 2px rgba(0,0,0,0.4)">>
}
@media (max-width: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarbreakpoint}}) {
.tc-improvement-banner {
}
}
@media (min-width: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarbreakpoint}}) {
.tc-improvement-banner {
margin-right: -53px;
margin-left: -53px;
}
.tc-improvement-banner:before {
display: block;
position: absolute;
width: 0;
height: 0;
content: " ";
margin-left: -5px;
margin-top: -10px;
border-top: 5px solid transparent;
border-left: 5px solid transparent;
border-right: 5px solid #C07E7E;
border-bottom: 5px solid #C07E7E;
}
}
.tc-improvement-banner svg {
width: 1em;
height: 1em;
}

View File

@@ -0,0 +1,9 @@
tags: $:/tags/SideBar
title: TableOfContents
list-before:
<div class="tc-table-of-contents">
<<toc-selective-expandable 'TableOfContents'>>
</div>

View File

@@ -0,0 +1,3 @@
title: $:/config/DefaultSidebarTab
TableOfContents

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,2 @@
title: $:/favicon.ico
type: image/png

View File

@@ -0,0 +1,11 @@
code-body: yes
tags: $:/tags/Macro
title: $:/editions/multiwikidocs/doc-macros
type: text/vnd.tiddlywiki
\procedure .copy-code-to-clipboard(text)
<div>
<$transclude $variable="copy-to-clipboard-above-right" src=<<text>>/>
<$codeblock code=<<text>>/>
</div>
\end .copy-code-to-clipboard

View File

@@ -0,0 +1,149 @@
title: $:/palettes/MWS
name: MWS Palette
description: Palette for mws.tiddlywiki.com
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
alert-background: #ffe476
alert-border: #b99e2f
alert-highlight: #881122
alert-muted-foreground: #b99e2f
background: #ffffff
blockquote-bar: <<colour muted-foreground>>
button-background:
button-foreground:
button-border:
code-background: #f7f7f9
code-border: #e1e1e8
code-foreground: #dd1144
diff-delete-background: #ffc9c9
diff-delete-foreground: <<colour foreground>>
diff-equal-background:
diff-equal-foreground: <<colour foreground>>
diff-insert-background: #aaefad
diff-insert-foreground: <<colour foreground>>
diff-invisible-background:
diff-invisible-foreground: <<colour muted-foreground>>
dirty-indicator: #ff0000
download-background: #34c734
download-foreground: <<colour background>>
dragger-background: <<colour foreground>>
dragger-foreground: <<colour background>>
dropdown-background: <<colour background>>
dropdown-border: <<colour muted-foreground>>
dropdown-tab-background-selected: #ffffff
dropdown-tab-background: #ececec
dropzone-background: rgba(0,200,0,0.7)
external-link-background-hover: inherit
external-link-background-visited: inherit
external-link-background: inherit
external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
footnote-target-background: #ecf2ff
foreground: #333333
highlight-background: #ffff00
highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
modal-backdrop: <<colour foreground>>
modal-background: <<colour background>>
modal-border: #999999
modal-footer-background: #f5f5f5
modal-footer-border: #dddddd
modal-header-border: #eeeeee
muted-foreground: #bbbbbb
network-activity-foreground: #448844
notification-background: #ffffdd
notification-border: #999999
page-background: #eddee7
pre-background: #f5f5f5
pre-border: #cccccc
primary: #5778d8
selection-background:
selection-foreground:
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #aaaaaa
sidebar-foreground-shadow: rgba(255,255,255, 0.8)
sidebar-foreground: #acacac
sidebar-muted-foreground-hover: #444444
sidebar-muted-foreground: #c0c0c0
sidebar-tab-background-selected: #eddee7
sidebar-tab-background: #c7b7bf
sidebar-tab-border-selected: <<colour tab-border-selected>>
sidebar-tab-border: <<colour tab-border>>
sidebar-tab-divider: #e4e4e4
sidebar-tab-foreground-selected:
sidebar-tab-foreground: <<colour tab-foreground>>
sidebar-tiddler-link-foreground-hover: #444444
sidebar-tiddler-link-foreground: #999999
site-title-foreground: <<colour tiddler-title-foreground>>
stability-stable: #008000
stability-experimental: #c07c00
stability-deprecated: #ff0000
stability-legacy: #0000ff
static-alert-foreground: #aaaaaa
tab-background-selected: #ffffff
tab-background: #d8d8d8
tab-border-selected: #d8d8d8
tab-border: #cccccc
tab-divider: #d8d8d8
tab-foreground-selected: <<colour tab-foreground>>
tab-foreground: #666666
table-border: #dddddd
table-footer-background: #a8a8a8
table-header-background: #f0f0f0
tag-background: #eecc66
tag-foreground: #ffffff
testcase-accent-level-1: #c1eaff
testcase-accent-level-2: #E3B740
testcase-accent-level-3: #5FD564
tiddler-background: <<colour background>>
tiddler-border: <<colour background>>
tiddler-controls-foreground-hover: #888888
tiddler-controls-foreground-selected: #444444
tiddler-controls-foreground: #cccccc
tiddler-editor-background: #f8f8f8
tiddler-editor-border-image: #ffffff
tiddler-editor-border: #cccccc
tiddler-editor-fields-even: #e0e8e0
tiddler-editor-fields-odd: #f0f4f0
tiddler-info-background: #f8f8f8
tiddler-info-border: #dddddd
tiddler-info-tab-background: #f8f8f8
tiddler-link-background: <<colour background>>
tiddler-link-foreground: <<colour primary>>
tiddler-subtitle-foreground: #c0c0c0
tiddler-title-foreground: #182955
toolbar-new-button:
toolbar-options-button:
toolbar-save-button:
toolbar-info-button:
toolbar-edit-button:
toolbar-close-button:
toolbar-delete-button:
toolbar-cancel-button:
toolbar-done-button:
untagged-background: #999999
very-muted-foreground: #888888
wikilist-background: #e5e5e5
wikilist-item: #ffffff
wikilist-info: #000000
wikilist-title: #666666
wikilist-title-svg: <<colour wikilist-title>>
wikilist-url: #aaaaaa
wikilist-button-open: #4fb82b
wikilist-button-open-hover: green
wikilist-button-reveal: #5778d8
wikilist-button-reveal-hover: blue
wikilist-button-remove: #d85778
wikilist-button-remove-hover: red
wikilist-toolbar-background: #d3d3d3
wikilist-toolbar-foreground: #888888
wikilist-droplink-dragover: rgba(255,192,192,0.5)
wikilist-button-background: #acacac
wikilist-button-foreground: #000000

View File

@@ -0,0 +1,3 @@
title: $:/palette
$:/palettes/MWS

View File

@@ -0,0 +1,3 @@
title: $:/themes/tiddlywiki/vanilla/options/sidebarlayout
fluid-fixed

View File

@@ -0,0 +1,18 @@
title: $:/core/templates/static.content
\define tv-wikilink-template() https://tiddlywiki.com/static/$uri_doubleencoded$.html
<!-- For Google, and people without JavaScript-->
<$reveal default="yes" text=<<savingEmpty>> type="nomatch">
It looks like this browser doesn't run JavaScript. You can use one of these static HTML versions to browse the same content:
* https://mws.tiddlywiki.com/static.html - browse individual tiddlers as separate pages
* https://mws.tiddlywiki.com/alltiddlers.html#HelloThere - single file containing all tiddlers
---
{{HelloThere}}
</$reveal>

View File

@@ -0,0 +1,26 @@
{
"description": "Multiple wiki server documentation edition",
"plugins": [
],
"themes": [
"tiddlywiki/vanilla",
"tiddlywiki/snowwhite"
],
"build": {
"index": [
"--savetiddlers","[tag[external-image]]","images",
"--setfield","[tag[external-image]]","_canonical_uri","$:/core/templates/canonical-uri-external-image","text/plain",
"--setfield","[tag[external-image]]","text","","text/plain",
"--render","$:/core/save/all","index.html","text/plain"],
"favicon": [
"--savetiddler","$:/favicon.ico","favicon.ico"],
"static": [
"--render","$:/core/templates/static.template.html","static.html","text/plain",
"--render","$:/core/templates/alltiddlers.template.html","alltiddlers.html","text/plain",
"--render","[!is[system]]","[encodeuricomponent[]addprefix[static/]addsuffix[.html]]","text/plain","$:/core/templates/static.tiddler.html",
"--render","$:/core/templates/static.template.css","static/static.css","text/plain"]
},
"config": {
"retain-original-tiddler-path": true
}
}

View File

@@ -0,0 +1,2 @@
list: GettingStarted
title: $:/StoryList

View File

@@ -0,0 +1,2 @@
title: $:/config/MultiWikiServer/Engine
text: better

View File

@@ -0,0 +1,39 @@
{
"description": "Multiple wiki client-server edition",
"plugins": [
"tiddlywiki/tiddlyweb",
"tiddlywiki/filesystem",
"tiddlywiki/multiwikiclient",
"tiddlywiki/multiwikiserver"
],
"themes": [
"tiddlywiki/vanilla",
"tiddlywiki/snowwhite"
],
"build": {
"load-mws-demo-data": [
"--mws-load-wiki-folder","./editions/multiwikidocs","mws-docs", "MWS Documentation from https://mws.tiddlywiki.com","mws-docs","MWS Documentation from https://mws.tiddlywiki.com",
"--mws-load-wiki-folder","./editions/tw5.com","docs", "TiddlyWiki Documentation from https://tiddlywiki.com","docs","TiddlyWiki Documentation from https://tiddlywiki.com",
"--mws-load-wiki-folder","./editions/dev","dev","TiddlyWiki Developer Documentation from https://tiddlywiki.com/dev","dev-docs", "TiddlyWiki Developer Documentation from https://tiddlywiki.com/dev",
"--mws-load-wiki-folder","./editions/tour","tour","TiddlyWiki Interactive Tour from https://tiddlywiki.com","tour", "TiddlyWiki Interactive Tour from https://tiddlywiki.com",
"--mws-create-bag","bag-alpha","A test bag",
"--mws-create-bag","bag-beta","Another test bag",
"--mws-create-bag","bag-gamma","A further test bag",
"--mws-create-recipe","recipe-rho","bag-alpha bag-beta","First wiki",
"--mws-create-recipe","recipe-sigma","bag-alpha bag-gamma","Second Wiki",
"--mws-create-recipe","recipe-tau","bag-alpha","Third Wiki",
"--mws-create-recipe","recipe-upsilon","bag-alpha bag-gamma bag-beta","Fourth Wiki",
"--mws-save-tiddler-text","bag-alpha","$:/SiteTitle","bag-alpha",
"--mws-save-tiddler-text","bag-alpha","😀😃😄😁😆🥹😅😂","bag-alpha",
"--mws-save-tiddler-text","bag-beta","$:/SiteTitle","bag-beta",
"--mws-save-tiddler-text","bag-gamma","$:/SiteTitle","bag-gamma",
"--mws-add-permission", "READ", "Allows user to read recipes and bags",
"--mws-add-permission", "WRITE", "Gives the user the permission to edit and delete tiddlers",
"--mws-add-role", "ADMIN", "System Administrator",
"--mws-add-role", "USER", "Basic User",
"--mws-assign-role-permission", "ADMIN", "READ",
"--mws-assign-role-permission", "ADMIN", "WRITE",
"--mws-assign-role-permission", "USER", "READ"
]
}
}

View File

@@ -12,9 +12,9 @@ test('get started link', async ({ page }) => {
// Load the generated test TW html
await page.goto(`file:///${crossPlatformIndexPath}`);
const title = await page.title();
// Sanity check
await expect(page.locator('.tc-site-title'), "Expected correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5');
await expect(title, "Expected correct page title to verify the test page was loaded").toContain('TiddlyWiki5');
// Wait for jasmine results bar to appear
await expect(page.locator('.jasmine-overall-result'), "Expected jasmine test results bar to be present").toBeVisible({timeout});

View File

@@ -2,6 +2,7 @@
"description": "TiddlyWiki core tests",
"plugins": [
"tiddlywiki/jasmine",
"tiddlywiki/multiwikiserver",
"tiddlywiki/geospatial"
],
"themes": [

View File

@@ -6,7 +6,23 @@ type: text/vnd.tiddlywiki
\define tv-wikilink-template() https://tiddlywiki.com/static/$uri_doubleencoded$.html
\import [subfilter{$:/core/config/GlobalImportFilter}]
! Welcome
---
! ~TiddlyWiki ~MultiWikiServer
UNDER DEVELOPMENT
This is a branch of TiddlyWiki that adds the ~MultiWikiServer plugin.
!! Readme
{{$:/plugins/tiddlywiki/multiwikiserver/readme}}
!! Docs
{{$:/plugins/tiddlywiki/multiwikiserver/docs}}
---
Welcome to TiddlyWiki, a non-linear personal web notebook that anyone can use and keep forever, independently of any corporation.

1478
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -24,18 +24,26 @@
"wiki"
],
"devDependencies": {
"@playwright/test": "^1.47.2",
"eslint": "^9.12.0",
"@eslint/js": "^9.12.0"
"@eslint/js": "^9.12.0",
"playwright": "^1.47.2"
},
"bundleDependencies": [],
"license": "BSD",
"engines": {
"node": ">=0.8.2"
},
"scripts": {
"dev": "node ./tiddlywiki.js ./editions/tw5.com-server --listen",
"test": "node ./tiddlywiki.js ./editions/test --verbose --version --build index",
"start": "node ./tiddlywiki.js ./editions/multiwikiserver --mws-load-plugin-bags --build load-mws-demo-data --mws-listen",
"build:test-edition": "node ./tiddlywiki.js ./editions/test --verbose --version --build index",
"test:multiwikiserver-edition": "node ./tiddlywiki.js ./editions/multiwikiserver/ --build load-mws-demo-data --mws-listen --mws-test-server http://127.0.0.1:8080/ --quit",
"mws-add-user": "node ./tiddlywiki.js ./editions/multiwikiserver --build load-mws-demo-data --mws-listen --build mws-add-user --quit",
"test": "npm run build:test-edition && npm run test:multiwikiserver-edition",
"lint:fix": "eslint . --fix",
"lint": "eslint ."
},
"dependencies": {
"better-sqlite3": "^11.5.0",
"node-sqlite3-wasm": "^0.8.25"
}
}

View File

@@ -0,0 +1,9 @@
title: GettingStarted
tags: $:/tags/GettingStarted
caption: Step 1<br>Syncing
! ~TiddlyWiki ~MultiWikiServer
Welcome to ~TiddlyWiki and the ~TiddlyWiki community.
Please note that ~MultiWikiServer is under active development, and may not be fully robust. Do not use it for anything critical.

View File

@@ -0,0 +1,3 @@
title: $:/config/SaveWikiButton/Template
$:/plugins/tiddlywiki/multiwikiclient/save/offline

View File

@@ -0,0 +1,2 @@
title: $:/config/multiwikiclient/incoming-updates-filter
text: [all[]] -[[$:/isEncrypted]] -[prefix[$:/temp/]] -[prefix[$:/status/]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/library/sjcl.js]] -[[$:/core]] -[prefix[$:/StoryList]] -[prefix[$:/HistoryList]]

View File

@@ -0,0 +1,2 @@
title: $:/config/Server/ExternalFilters/[all[tiddlers]] -[[$:/isEncrypted]] -[prefix[$:/temp/]] -[prefix[$:/status/]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/library/sjcl.js]] -[[$:/core]]
text: yes

View File

@@ -0,0 +1,2 @@
title: $:/config/multiwikiclient/use-server-sent-events
text: no

View File

@@ -0,0 +1,7 @@
title: $:/config/OfficialPluginLibrary
tags: $:/tags/PluginLibrary
url: https://tiddlywiki.com/library/v5.1.23/index.html
caption: {{$:/language/OfficialPluginLibrary}}
enabled: no
The official plugin library is disabled when using the client-server configuration. Instead, plugins should be installed via the `tiddlywiki.info` file, as described [[here|https://tiddlywiki.com/#Installing%20a%20plugin%20from%20the%20plugin%20library]].

View File

@@ -0,0 +1,4 @@
title: $:/plugins/tiddlywiki/multiwikiclient/icon/cloud
tags: $:/tags/Image
<svg class="tc-image-cloud tc-image-button" width="22pt" height="22pt" viewBox="0 0 128 128"><g><path d="M24 103C10.745 103 0 92.255 0 79c0-9.697 5.75-18.05 14.027-21.836A24.787 24.787 0 0114 56c0-13.255 10.745-24 24-24 1.373 0 2.718.115 4.028.337C48.628 24.2 58.707 19 70 19c19.882 0 36 16.118 36 36v.082c12.319 1.016 22 11.336 22 23.918 0 12.239-9.16 22.337-20.999 23.814L107 103H24z"/><path class="tc-image-cloud-idle" d="M57.929 84.698a6 6 0 01-8.485 0L35.302 70.556a6 6 0 118.485-8.485l9.9 9.9L81.97 43.686a6 6 0 018.485 8.486L57.929 84.698z"/><path class="tc-image-cloud-progress tc-animate-rotate-slow" d="M44.8 40a3.6 3.6 0 100 7.2h2.06A23.922 23.922 0 0040 64c0 13.122 10.531 23.785 23.603 23.997L64 88l.001-7.2c-9.171 0-16.626-7.348-16.798-16.477L47.2 64c0-5.165 2.331-9.786 5.999-12.868L53.2 55.6a3.6 3.6 0 107.2 0v-12a3.6 3.6 0 00-3.6-3.6h-12zM64 40v7.2c9.278 0 16.8 7.522 16.8 16.8 0 5.166-2.332 9.787-6 12.869V72.4a3.6 3.6 0 10-7.2 0v12a3.6 3.6 0 003.6 3.6h12a3.6 3.6 0 100-7.2l-2.062.001A23.922 23.922 0 0088 64c0-13.255-10.745-24-24-24z"/></g></svg>

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/multiwikiclient/info-segment
tags: $:/tags/TiddlerInfoSegment
<$reveal type="nomatch" state=<<folded-state>> text="hide" tag="div" retain="yes" animate="yes">
<div class="tc-subtitle">
Bag: <$view tiddler="$:/state/multiwikiclient/tiddlers/bag" index=<<currentTiddler>>>(none)</$view>
</div>
</$reveal>

View File

@@ -0,0 +1,65 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiclient/managetiddleraction.js
type: application/javascript
module-type: widget
A widget to manage tiddler actions.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var ManageTiddlerAction = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
ManageTiddlerAction.prototype = new Widget();
/*
Render this widget into the DOM
*/
ManageTiddlerAction.prototype.render = function(parent,nextSibling) {
this.computeAttributes();
this.execute();
};
/*
Compute the internal state of the widget
*/
ManageTiddlerAction.prototype.execute = function() {
this.tiddler = this.getAttribute("tiddler");
};
/*
Invoke the action associated with this widget
*/
ManageTiddlerAction.prototype.invokeAction = function(triggeringWidget,event) {
var pathname = window.location.pathname;
var paths = pathname.split("/");
var recipeName = paths[paths.length - 1];
var bagName = document.querySelector("h1.tc-site-title").innerHTML;
window.location.href = "/admin/acl/"+recipeName+"/"+bagName;
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
ManageTiddlerAction.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.tiddler) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports["action-managetiddler"] = ManageTiddlerAction;
})();

View File

@@ -0,0 +1,399 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiclient/multiwikiclientadaptor.js
type: application/javascript
module-type: syncadaptor
A sync adaptor module for synchronising with MultiWikiServer-compatible servers. It has three key areas of concern:
* Basic operations like put, get, and delete a tiddler on the server
* Real time updates from the server (handled by SSE)
* Managing login/logout (not yet implemeneted)
* Bags and recipes, which are unknown to the syncer
A key aspect of the design is that the syncer never overlaps basic server operations; it waits for the
previous operation to complete before sending a new one.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var CONFIG_HOST_TIDDLER = "$:/config/multiwikiclient/host",
DEFAULT_HOST_TIDDLER = "$protocol$//$host$/",
MWC_STATE_TIDDLER_PREFIX = "$:/state/multiwikiclient/",
BAG_STATE_TIDDLER = "$:/state/multiwikiclient/tiddlers/bag",
REVISION_STATE_TIDDLER = "$:/state/multiwikiclient/tiddlers/revision",
CONNECTION_STATE_TIDDLER = "$:/state/multiwikiclient/connection",
INCOMING_UPDATES_FILTER_TIDDLER = "$:/config/multiwikiclient/incoming-updates-filter",
ENABLE_SSE_TIDDLER = "$:/config/multiwikiclient/use-server-sent-events";
var SERVER_NOT_CONNECTED = "NOT CONNECTED",
SERVER_CONNECTING_SSE = "CONNECTING SSE",
SERVER_CONNECTED_SSE = "CONNECTED SSE",
SERVER_POLLING = "SERVER POLLING";
function MultiWikiClientAdaptor(options) {
this.wiki = options.wiki;
this.host = this.getHost();
this.recipe = this.wiki.getTiddlerText("$:/config/multiwikiclient/recipe");
this.useServerSentEvents = this.wiki.getTiddlerText(ENABLE_SSE_TIDDLER) === "yes";
this.last_known_tiddler_id = $tw.utils.parseNumber(this.wiki.getTiddlerText("$:/state/multiwikiclient/recipe/last_tiddler_id","0"));
this.outstandingRequests = Object.create(null); // Hashmap by title of outstanding request object: {type: "PUT"|"GET"|"DELETE"}
this.lastRecordedUpdate = Object.create(null); // Hashmap by title of last recorded update via SSE: {type: "update"|"detetion", tiddler_id:}
this.logger = new $tw.utils.Logger("MultiWikiClientAdaptor");
this.isLoggedIn = false;
this.isReadOnly = false;
this.logoutIsAvailable = true;
// Compile the dirty tiddler filter
this.incomingUpdatesFilterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(INCOMING_UPDATES_FILTER_TIDDLER));
this.setUpdateConnectionStatus(SERVER_NOT_CONNECTED);
}
MultiWikiClientAdaptor.prototype.setUpdateConnectionStatus = function(status) {
this.serverUpdateConnectionStatus = status;
this.wiki.addTiddler({
title: CONNECTION_STATE_TIDDLER,
text: status
});
};
MultiWikiClientAdaptor.prototype.name = "multiwikiclient";
MultiWikiClientAdaptor.prototype.supportsLazyLoading = true;
MultiWikiClientAdaptor.prototype.setLoggerSaveBuffer = function(loggerForSaving) {
this.logger.setSaveBuffer(loggerForSaving);
};
MultiWikiClientAdaptor.prototype.isReady = function() {
return true;
};
MultiWikiClientAdaptor.prototype.getHost = function() {
var text = this.wiki.getTiddlerText(CONFIG_HOST_TIDDLER,DEFAULT_HOST_TIDDLER),
substitutions = [
{name: "protocol", value: document.location.protocol},
{name: "host", value: document.location.host},
{name: "pathname", value: document.location.pathname}
];
for(var t=0; t<substitutions.length; t++) {
var s = substitutions[t];
text = $tw.utils.replaceString(text,new RegExp("\\$" + s.name + "\\$","mg"),s.value);
}
return text;
};
MultiWikiClientAdaptor.prototype.getTiddlerInfo = function(tiddler) {
var title = tiddler.fields.title,
revision = this.wiki.extractTiddlerDataItem(REVISION_STATE_TIDDLER,title),
bag = this.wiki.extractTiddlerDataItem(BAG_STATE_TIDDLER,title);
if(revision && bag) {
return {
title: title,
revision: revision,
bag: bag
};
} else {
return undefined;
}
};
MultiWikiClientAdaptor.prototype.getTiddlerBag = function(title) {
return this.wiki.extractTiddlerDataItem(BAG_STATE_TIDDLER,title);
};
MultiWikiClientAdaptor.prototype.getTiddlerRevision = function(title) {
return this.wiki.extractTiddlerDataItem(REVISION_STATE_TIDDLER,title);
};
MultiWikiClientAdaptor.prototype.setTiddlerInfo = function(title,revision,bag) {
this.wiki.setText(BAG_STATE_TIDDLER,null,title,bag,{suppressTimestamp: true});
this.wiki.setText(REVISION_STATE_TIDDLER,null,title,revision,{suppressTimestamp: true});
};
MultiWikiClientAdaptor.prototype.removeTiddlerInfo = function(title) {
this.wiki.setText(BAG_STATE_TIDDLER,null,title,undefined,{suppressTimestamp: true});
this.wiki.setText(REVISION_STATE_TIDDLER,null,title,undefined,{suppressTimestamp: true});
};
/*
Get the current status of the server connection
*/
MultiWikiClientAdaptor.prototype.getStatus = function(callback) {
// Invoke the callback if present
if(callback) {
callback(
null, // Error
true, // Is logged in
this.username, // Username
false, // Is read only
true // Is anonymous
);
}
};
/*
Get details of changed tiddlers from the server
*/
MultiWikiClientAdaptor.prototype.getUpdatedTiddlers = function(syncer,callback) {
if(this.useServerSentEvents) {
var self = this;
// Do nothing if there's already a connection in progress.
if(this.serverUpdateConnectionStatus !== SERVER_NOT_CONNECTED) {
return callback(null,{
modifications: [],
deletions: []
});
}
// Try to connect a server stream
this.setUpdateConnectionStatus(SERVER_CONNECTING_SSE);
this.connectServerStream({
syncer: syncer,
onerror: function(err) {
self.logger.log("Error connecting SSE stream",err);
// If the stream didn't work, try polling
self.setUpdateConnectionStatus(SERVER_POLLING);
self.pollServer({
callback: function(err,changes) {
self.setUpdateConnectionStatus(SERVER_NOT_CONNECTED);
callback(null,changes);
}
});
},
onopen: function() {
self.setUpdateConnectionStatus(SERVER_CONNECTED_SSE);
// The syncer is expecting a callback but we don't have any data to send
callback(null,{
modifications: [],
deletions: []
});
}
});
} else {
this.pollServer({
callback: function(err,changes) {
callback(null,changes);
}
});
}
};
/*
Attempt to establish an SSE stream with the server and transfer tiddler changes. Options include:
syncer: reference to syncer object used for storing data
onopen: invoked when the stream is successfully opened
onerror: invoked if there is an error
*/
MultiWikiClientAdaptor.prototype.connectServerStream = function(options) {
var self = this;
const eventSource = new EventSource("/recipes/" + this.recipe + "/events?last_known_tiddler_id=" + this.last_known_tiddler_id);
eventSource.onerror = function(event) {
if(options.onerror) {
options.onerror(event);
}
}
eventSource.onopen = function(event) {
if(options.onopen) {
options.onopen(event);
}
}
eventSource.addEventListener("change", function(event) {
const data = $tw.utils.parseJSONSafe(event.data);
if(data) {
console.log("SSE data",data)
// Update last seen tiddler_id
if(data.tiddler_id > self.last_known_tiddler_id) {
self.last_known_tiddler_id = data.tiddler_id;
}
// Record the last update to this tiddler
self.lastRecordedUpdate[data.title] = {
type: data.is_deleted ? "deletion" : "update",
tiddler_id: data.tiddler_id
};
console.log(`Oustanding requests is ${JSON.stringify(self.outstandingRequests[data.title])}`)
// Process the update if the tiddler is not the subject of an outstanding request
if(!self.outstandingRequests[data.title]) {
if(data.is_deleted) {
self.removeTiddlerInfo(data.title);
delete options.syncer.tiddlerInfo[data.title];
options.syncer.logger.log("Deleting tiddler missing from server:",data.title);
options.syncer.wiki.deleteTiddler(data.title);
options.syncer.processTaskQueue();
} else {
var result = self.incomingUpdatesFilterFn.call(self.wiki,self.wiki.makeTiddlerIterator([data.title]));
if(result.length > 0) {
self.setTiddlerInfo(data.title,data.tiddler_id.toString(),data.bag_name);
options.syncer.storeTiddler(data.tiddler);
}
}
}
}
});
};
/*
Poll the server for changes. Options include:
callback: invoked on completion as (err,changes)
*/
MultiWikiClientAdaptor.prototype.pollServer = function(options) {
var self = this;
$tw.utils.httpRequest({
url: this.host + "recipes/" + this.recipe + "/tiddlers.json",
data: {
last_known_tiddler_id: this.last_known_tiddler_id,
include_deleted: "true"
},
callback: function(err,data) {
// Check for errors
if(err) {
return options.callback(err);
}
var modifications = [],
deletions = [];
var tiddlerInfoArray = $tw.utils.parseJSONSafe(data);
$tw.utils.each(tiddlerInfoArray,function(tiddlerInfo) {
if(tiddlerInfo.tiddler_id > self.last_known_tiddler_id) {
self.last_known_tiddler_id = tiddlerInfo.tiddler_id;
}
if(tiddlerInfo.is_deleted) {
deletions.push(tiddlerInfo.title);
} else {
modifications.push(tiddlerInfo.title);
}
});
// Invoke the callback with the results
options.callback(null,{
modifications: modifications,
deletions: deletions
});
// If Browswer Storage tiddlers were cached on reloading the wiki, add them after sync from server completes in the above callback.
if($tw.browserStorage && $tw.browserStorage.isEnabled()) {
$tw.browserStorage.addCachedTiddlers();
}
}
});
};
/*
Queue a load for a tiddler if there has been an update for it since the specified revision
*/
MultiWikiClientAdaptor.prototype.checkLastRecordedUpdate = function(title,revision,syncer) {
var lru = this.lastRecordedUpdate[title];
if(lru) {
var numRevision = $tw.utils.getInt(revision);
console.log(`Checking for updates to ${title} since ${JSON.stringify(revision)} comparing to ${numRevision}`)
if(lru.tiddler_id > numRevision) {
options.syncer.enqueueLoadTiddler(title);
}
}
}
/*
Save a tiddler and invoke the callback with (err,adaptorInfo,revision)
*/
MultiWikiClientAdaptor.prototype.saveTiddler = function(tiddler,callback,options) {
var self = this,
title = tiddler.fields.title;
if(this.isReadOnly || title.substr(0,MWC_STATE_TIDDLER_PREFIX.length) === MWC_STATE_TIDDLER_PREFIX) {
return callback(null);
}
self.outstandingRequests[title] = {type: "PUT"};
$tw.utils.httpRequest({
url: this.host + "recipes/" + encodeURIComponent(this.recipe) + "/tiddlers/" + encodeURIComponent(title),
type: "PUT",
headers: {
"Content-type": "application/json"
},
data: JSON.stringify(tiddler.getFieldStrings()),
callback: function(err,data,request) {
delete self.outstandingRequests[title];
if(err) {
return callback(err);
}
//If Browser-Storage plugin is present, remove tiddler from local storage after successful sync to the server
if($tw.browserStorage && $tw.browserStorage.isEnabled()) {
$tw.browserStorage.removeTiddlerFromLocalStorage(title)
}
// Save the details of the new revision of the tiddler
var revision = request.getResponseHeader("X-Revision-Number"),
bag_name = request.getResponseHeader("X-Bag-Name");
console.log(`Saved ${title} with revision ${revision} and bag ${bag_name}`)
// If there has been a more recent update from the server then enqueue a load of this tiddler
self.checkLastRecordedUpdate(title,revision,options.syncer);
// Invoke the callback
self.setTiddlerInfo(title,revision,bag_name);
callback(null,{bag: bag_name},revision);
}
});
};
/*
Load a tiddler and invoke the callback with (err,tiddlerFields)
*/
MultiWikiClientAdaptor.prototype.loadTiddler = function(title,callback,options) {
var self = this;
self.outstandingRequests[title] = {type: "GET"};
$tw.utils.httpRequest({
url: this.host + "recipes/" + encodeURIComponent(this.recipe) + "/tiddlers/" + encodeURIComponent(title),
callback: function(err,data,request) {
delete self.outstandingRequests[title];
if(err === 404) {
return callback(null,null);
} else if(err) {
return callback(err);
}
var revision = request.getResponseHeader("X-Revision-Number"),
bag_name = request.getResponseHeader("X-Bag-Name");
// If there has been a more recent update from the server then enqueue a load of this tiddler
self.checkLastRecordedUpdate(title,revision,options.syncer);
// Invoke the callback
self.setTiddlerInfo(title,revision,bag_name);
callback(null,$tw.utils.parseJSONSafe(data));
}
});
};
/*
Delete a tiddler and invoke the callback with (err)
options include:
tiddlerInfo: the syncer's tiddlerInfo for this tiddler
*/
MultiWikiClientAdaptor.prototype.deleteTiddler = function(title,callback,options) {
var self = this;
if(this.isReadOnly) {
return callback(null);
}
// If we don't have a bag it means that the tiddler hasn't been seen by the server, so we don't need to delete it
var bag = this.getTiddlerBag(title);
if(!bag) {
return callback(null,options.tiddlerInfo.adaptorInfo);
}
self.outstandingRequests[title] = {type: "DELETE"};
// Issue HTTP request to delete the tiddler
$tw.utils.httpRequest({
url: this.host + "bags/" + encodeURIComponent(bag) + "/tiddlers/" + encodeURIComponent(title),
type: "DELETE",
callback: function(err,data,request) {
delete self.outstandingRequests[title];
if(err) {
return callback(err);
}
var revision = request.getResponseHeader("X-Revision-Number");
// If there has been a more recent update from the server then enqueue a load of this tiddler
self.checkLastRecordedUpdate(title,revision,options.syncer);
self.removeTiddlerInfo(title);
// Invoke the callback & return null adaptorInfo
callback(null,null);
}
});
};
if($tw.browser && document.location.protocol.substr(0,4) === "http" ) {
exports.adaptorClass = MultiWikiClientAdaptor;
}
})();

View File

@@ -0,0 +1,8 @@
{
"title": "$:/plugins/tiddlywiki/multiwikiclient",
"name": "MultiWikiClient",
"description": "Synchronise changes from the browser to TiddlyWiki ~MultiWikiServer",
"list": "readme",
"plugin-priority": 10,
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/multiwikiclient/readme
This plugin runs in the browser to synchronise tiddler changes to and from a TiddlyWiki server running ~MultiWikiServer.
This plugin is inert when run under Node.js. Disabling this plugin via the browser can not be undone via the browser since this plugin provides the mechanism to synchronize settings with the server.
Changes made while offline are saved in memory and automatically synchonised with the server when the connection is re-established. However, if the browser tab is closed or another URL is loaded, the in-memory changes will be lost. The [[https://tiddlywiki.com/#BrowserStorage Plugin]] may be added to provide temporary filesystem storage of tiddler changes made while offline and enable them to be synchronised with the server the next time the wiki is loaded in the same browser.

View File

@@ -0,0 +1,27 @@
title: $:/plugins/tiddlywiki/multiwikiclient/readonly
tags: [[$:/tags/Stylesheet]]
\define button-selector(title)
button.$title$, .tc-drop-down button.$title$, div.$title$
\end
\define hide-edit-controls()
<$reveal state="$:/status/IsReadOnly" type="match" text="yes" default="yes">
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fclone>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fdelete>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fedit>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fnew-here>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fnew-journal-here>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fimport>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fmanager>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fnew-image>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fnew-journal>>`,`
<<button-selector tc-btn-\%24\%3A\%2Fcore\%2Fui\%2FButtons\%2Fnew-tiddler>> `{
display: none;
}`
</$reveal>
\end
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
<<hide-edit-controls>>

View File

@@ -0,0 +1,7 @@
title: $:/plugins/tiddlywiki/multiwikiclient/save/offline
\import [subfilter{$:/core/config/GlobalImportFilter}]
\define saveTiddlerFilter()
[is[tiddler]] -[[$:/boot/boot.css]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/plugins/tiddlywiki/filesystem]] -[[$:/plugins/tiddlywiki/multiwikiclient]] -[prefix[$:/temp/]] +[sort[title]] $(publishFilter)$
\end
{{$:/core/templates/tiddlywiki5.html}}

View File

@@ -0,0 +1,26 @@
title: $:/core/ui/Buttons/save-wiki
tags: $:/tags/PageControls
caption: {{$:/plugins/tiddlywiki/multiwikiclient/icon/cloud}} Server status
description: Status of synchronisation with server
\whitespace trim
\define config-title()
$:/config/PageControlButtons/Visibility/$(listItem)$
\end
<$button popup=<<qualify "$:/state/popup/save-wiki">> tooltip="Status of synchronisation with server" aria-label="Server status" class=<<tv-config-toolbar-class>> selectedClass="tc-selected">
<span class="tc-dirty-indicator">
<$list filter="[<tv-config-toolbar-icons>match[yes]]">
{{$:/plugins/tiddlywiki/multiwikiclient/icon/cloud}}
</$list>
<$list filter="[<tv-config-toolbar-text>match[yes]]">
<span class="tc-btn-text"><$text text="Server status"/></span>
</$list>
</span>
</$button>
<$reveal state=<<qualify "$:/state/popup/save-wiki">> type="popup" position="belowleft" animate="yes">
<div class="tc-drop-down">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/SyncerDropdown]!has[draft.of]]" variable="listItem">
<$transclude tiddler=<<listItem>>/>
</$list>
</div>
</$reveal>

View File

@@ -0,0 +1,9 @@
title: $:/plugins/multiwikiclient/SideBarSegment
tags: $:/tags/SideBarSegment
list-before: $:/core/ui/SideBarSegments/page-controls
<%if [{$:/config/multiwikiclient/use-server-sent-events}match[yes]] %>
MWS Connection Status: {{$:/state/multiwikiclient/connection}}
<%endif%>

View File

@@ -0,0 +1,44 @@
title: $:/plugins/tiddlywiki/multiwikiclient/styles
tags: [[$:/tags/Stylesheet]]
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
body.tc-dirty span.tc-dirty-indicator svg {
transition: fill 250ms ease-in-out;
}
body .tc-image-cloud-idle {
fill: <<colour background>>;
transition: opacity 250ms ease-in-out;
opacity: 1;
display: unset;
}
body.tc-dirty .tc-image-cloud-idle {
opacity: 0;
display: none;
}
body .tc-image-cloud-progress {
transition: opacity 250ms ease-in-out;
transform-origin: 50% 50%;
transform: rotate(359deg);
animation: animation-rotate-slow 2s infinite linear;
fill: <<colour background>>;
display: none;
opacity: 0;
}
body.tc-dirty .tc-image-cloud-progress {
opacity: 1;
display: unset;
}
@keyframes animation-rotate-slow {
from {
transform: rotate(0deg);
}
to {
transform: scale(359deg);
}
}

View File

@@ -0,0 +1,6 @@
title: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/copy-logs
tags: $:/tags/SyncerDropdown
<$button message="tm-copy-syncer-logs-to-clipboard" class="tc-btn-invisible">
{{$:/core/images/copy-clipboard}} Copy syncer logs to clipboard
</$button>

View File

@@ -0,0 +1,9 @@
title: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/login-status
tags: $:/tags/SyncerDropdown
<$reveal state="$:/status/IsLoggedIn" type="match" text="yes">
<div class="tc-drop-down-info">
You are logged in<$reveal state="$:/status/UserName" type="nomatch" text="" default=""> as <strong><$text text={{$:/status/UserName}}/></strong></$reveal><$reveal state="$:/status/IsReadOnly" type="match" text="yes" default="no"> (read-only)</$reveal>
</div>
<hr/>
</$reveal>

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/login
tags: $:/tags/SyncerDropdown
<$reveal state="$:/status/IsLoggedIn" type="nomatch" text="yes">
<$button message="tm-login" class="tc-btn-invisible">
{{$:/core/images/unlocked-padlock}} Login
</$button>
</$reveal>

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/logout
tags: $:/tags/SyncerDropdown
<$reveal state="$:/status/IsLoggedIn" type="match" text="yes">
<$button message="tm-logout" class="tc-btn-invisible">
{{$:/core/images/cancel-button}} Logout
</$button>
</$reveal>

View File

@@ -0,0 +1,9 @@
title: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/refresh
tags: $:/tags/SyncerDropdown
<$reveal state="$:/status/IsLoggedIn" type="match" text="yes">
<$button tooltip="Get latest changes from the server" aria-label="Refresh from server" class="tc-btn-invisible">
<$action-sendmessage $message="tm-server-refresh"/>
{{$:/core/images/refresh-button}}<span class="tc-btn-text"><$text text="Get latest changes from the server"/></span>
</$button>
</$reveal>

View File

@@ -0,0 +1,9 @@
title: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/save-snapshot
tags: $:/tags/SyncerDropdown
<$button class="tc-btn-invisible">
<$wikify name="site-title" text={{$:/config/SaveWikiButton/Filename}}>
<$action-sendmessage $message="tm-download-file" $param={{$:/config/SaveWikiButton/Template}} filename=<<site-title>>/>
</$wikify>
{{$:/core/images/download-button}} Save snapshot for offline use
</$button>

View File

@@ -0,0 +1,2 @@
title: $:/tags/SyncerDropdown
list: $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/login-status $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/login $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/refresh $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/logout $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/save-snapshot $:/plugins/tiddlywiki/multiwikiclient/syncer-actions/copy-logs

View File

@@ -0,0 +1,43 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/auth/authentication.js
type: application/javascript
module-type: library
Handles authentication related operations
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var crypto = require("crypto");
function Authenticator(database) {
if(!(this instanceof Authenticator)) {
return new Authenticator(database);
}
this.sqlTiddlerDatabase = database;
}
Authenticator.prototype.verifyPassword = function(inputPassword, storedHash) {
var hashedInput = this.hashPassword(inputPassword);
return hashedInput === storedHash;
};
Authenticator.prototype.hashPassword = function(password) {
return crypto.createHash("sha256").update(password).digest("hex");
};
Authenticator.prototype.createSession = function(userId) {
var sessionId = crypto.randomBytes(16).toString("hex");
// Store the session in your database or in-memory store
this.sqlTiddlerDatabase.createUserSession(userId, sessionId);
return sessionId;
};
exports.Authenticator = Authenticator;
})();

View File

@@ -0,0 +1,19 @@
title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login
tags: $:/tags/ServerRoute
route-method: GET
route-path: /login
<$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/styles"/>
<html>
<head>
<$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/head"/>
</head>
<body>
<div class="login-container">
<$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/header" mode="block"/>
<$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/form" mode="block"/>
<$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message" mode="block"/>
</div>
</body>
</html>

View File

@@ -0,0 +1,7 @@
title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message
<$list filter="[[$:/temp/mws/login/error]!is[missing]]" variable="errorTiddler">
<div class="tc-error-message">
{{$:/temp/mws/login/error}}
</div>
</$list>

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/form
<form class="login-form" method="POST" action="/login">
<input type="hidden" name="returnUrl" value=<<returnUrl>>/>
<input type="text" name="username" placeholder="Username"/>
<input type="password" name="password" placeholder="Password"/>
<input type="submit" value="Log In"/>
</form>

View File

@@ -0,0 +1,3 @@
title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/head
<title>TiddlyWiki Login</title>

View File

@@ -0,0 +1,41 @@
title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/header
<div class="login-header">
<h1>Be our Guest</h1>
<a class="" href="/">Explore as Guest</a>
<h2>TiddlyWiki Login</h2>
</div>
<style>
.login-header {
display: flex;
flex-direction: column;
gap: 5px;
align-items: center;
justify-content: center;
}
.login-header h1 {
font-size: 30px;
}
.login-header h2 {
font-size: 28px;
}
.login-header h1,
.login-header h2 {
font-weight: bold;
}
.login-header a {
padding: 10px 20px;
background-color: #1366a8;
color: #FFF;
text-decoration: none;
cursor: pointer;
border-radius: 5px;
width: 85%;
text-align: center;
}
</style>

View File

@@ -0,0 +1,48 @@
title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/styles
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.login-container {
max-width: 300px;
padding: 20px;
background-color: #ffffff;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.login-container h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
.login-form input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.login-form input[type="submit"] {
background-color: #4CAF50;
color: white;
cursor: pointer;
border: none;
}
.login-form input[type="submit"]:hover {
background-color: #45a049;
}
.tc-error-message {
color: #ff0000;
text-align: center;
margin-top: 10px;
}
</style>

View File

@@ -0,0 +1,4 @@
title: $:/config/MultiWikiServer/AllowAnonymousReads
text: no
description: Controls whether anonymous users can read wiki content
type: text/plain

View File

@@ -0,0 +1,4 @@
title: $:/config/MultiWikiServer/AllowAnonymousWrites
text: no
description: Controls whether anonymous users can write to the wiki
type: text/plain

View File

@@ -0,0 +1,2 @@
title: $:/config/MultiWikiServer/AttachmentSizeLimit
text: 204800

View File

@@ -0,0 +1,2 @@
title: $:/config/MultiWikiServer/EnableAttachments
text: yes

View File

@@ -0,0 +1,5 @@
title: $:/plugins/tiddlywiki/multiwikiserver/readme
This plugin extends the TiddlyWiki 5 server running on Node.js to be able to host multiple wikis that can share content or be independent.
See https://mws.tiddlywiki.com/ for more information.

View File

@@ -0,0 +1,4 @@
title: $:/plugins/tiddlywiki/multiwikiserver/icon
type: image/svg+xml
<svg style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" version="1.1" viewBox="0 0 635 635" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m336 19.7a16 16 0 0 1 20.6-9.59l132 48a16 16 0 0 1 9.6 20.6l-3.1 8.51a16 16 0 0 0 23.1 19.4l7.85-4.53a16 16 0 0 1 21.9 5.87l70.2 122a16.1 16.1 0 0 1-5.87 21.9l-7.85 4.53a16.1 16.1 0 0 0 5.24 29.7l8.92 1.57a16 16 0 0 1 13 18.6l-24.4 138a16 16 0 0 1-18.6 13l-8.92-1.58a16.1 16.1 0 0 0-15.1 26.1l5.82 6.94a16 16 0 0 1-1.97 22.6l-108 90.2a16 16 0 0 1-22.6-1.97l-5.82-6.93a16 16 0 0 0-28.3 10.3v9.05c0 8.87-7.19 16-16 16h-140a16 16 0 0 1-16-16v-9.05a16.1 16.1 0 0 0-28.3-10.3l-5.82 6.93a16 16 0 0 1-22.6 1.97l-108-90.2a16 16 0 0 1-1.97-22.6l5.82-6.94a16.1 16.1 0 0 0-15.1-26.1l-8.92 1.58a16.1 16.1 0 0 1-18.6-13l-24.4-138a16 16 0 0 1 13-18.6l8.92-1.57a16 16 0 0 0 5.24-29.7l-7.85-4.53a16.1 16.1 0 0 1-5.87-21.9l70.2-122a16 16 0 0 1 21.9-5.87l7.85 4.53a16.1 16.1 0 0 0 23.1-19.4l-3.1-8.51a16 16 0 0 1 9.6-20.6l132-48a16 16 0 0 1 20.6 9.59l3.1 8.51a16.1 16.1 0 0 0 30.2 0zm108 459 4.29-0.55c3.15-1.16 6.11-2.22 8.04-5.19 6.32-9-9.97-12.6-15.8-22.6-5.83-9.97-29.9-64.7-11.7-80 18.2-15.3 37.9-21.6 45.5-36 3.26-6.47 6.65-13 7.5-20.3 4.44 3.61 7.92 6.99 13.9 7.19 15.8 0.972 29.4-18 36.5-36 3.77-11 6.82-22.7 6.1-33.5-1.93-20.8 0.363-43.3 16.8-56.5 0.157-0.177 0.432-0.265 0.481-0.53-16.7 8.26-32.6 18.9-51.5 21.6-10.4-3.11-20.9-5.91-32-5.48l-0.0196 0.01c-5.42-6.92-12.7-19.9-22.2-21.4-16.2 5.68-16.7 30.2-23.5 63.5-10.5-2.82-21.3-4.79-32.2-5.29-15.1-0.972-39.6 3.65-56.9 3.16-17.3-0.481-51.2-15.4-79.5-12.1-11.4 1.37-22.8 3.27-33.4 7.96-6.45 2.86-15.8 11.5-20.5 12.8-47.2 12.9-124 12.4-108-57.5 1.23-5.37 1.57-10.2-0.854-11.4-3.03-1.5-4.73 1.65-6.52 5.11-27.5 58.5 21.3 96.1 76.8 96.3 17.4-1.32 9.03-0.196 25.2-3.22l-0.412 2.24c-1.14 19.4 7.68 36.5 24.1 47-7.55 21.1-22.5 38-32.7 57.4 3.86 24.1 28.8 56.3 39.1 60.8 9.07 4.04 28.9 4.08 33.5-2.42 1.64-3.05 0.56-9.61-1.25-13 7.75 8.42 18.2 20.1 28.3 25.8 6.05 2.83 12.1 4.66 18.9 4.89l8.62-0.412c4.31-0.904 9.39-1.59 12.3-5.34 3.26-5.44 0.216-20.7-13.3-21.8-13.5-1.09-22.2-6.09-34.6-24.1-10.2-17.5 2.14-41.5 6.3-58.7 21.3 3.36 42.8 6.01 64.5 6.07 9.38-0.471 18.7-0.805 27.9-2.54 2.95 7.39 11.2 28 11.3 33.9 0.206 16.5-6.11 33-3.39 49.4 1.7 4.84 0.147 14 9.8 14.4 5.76 0.285 2.16 0.108 10.8 0.491l7.43-0.206c9.54 15.7 17.8 15.6 36.5 16.2zm-208-40.9-1.66-1.69c-4.84-5.98-17.3-16.4-15-26.1 1.95-7.17 5.6-13.8 8.47-20.6l-0.0982 0.707c-0.521 8.5-1.54 16.9 0.854 25.2 7.28 6.88 17.4 17.8 24.1 25.2-3.64-1.16-10.5-3.52-13.9-1.99z"/></svg>

View File

@@ -0,0 +1,47 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-add-permission.js
type: application/javascript
module-type: command
Command to create a permission
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-add-permission",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(this.params.length < 2) {
return "Usage: --mws-add-permission <permission_name> <description>";
}
if(!$tw.mws || !$tw.mws.store || !$tw.mws.store.sqlTiddlerDatabase) {
return "Error: MultiWikiServer or SQL database not initialized.";
}
var permission_name = this.params[0];
var description = this.params[1];
$tw.mws.store.sqlTiddlerDatabase.createPermission(permission_name, description);
self.callback();
return null;
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,47 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-add-role.js
type: application/javascript
module-type: command
Command to create a role
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-add-role",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(this.params.length < 2) {
return "Usage: --mws-add-role <role_name> <description>";
}
if(!$tw.mws || !$tw.mws.store || !$tw.mws.store.sqlTiddlerDatabase) {
return "Error: MultiWikiServer or SQL database not initialized.";
}
var role_name = this.params[0];
var description = this.params[1];
$tw.mws.store.sqlTiddlerDatabase.createRole(role_name, description);
self.callback(null, "Role Created Successfully!");
return null;
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,56 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-add-user.js
type: application/javascript
module-type: command
Command to create users and grant permission
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
if($tw.node) {
var crypto = require("crypto");
}
exports.info = {
name: "mws-add-user",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(this.params.length < 2) {
return "Usage: --mws-add-user <username> <password> [email]";
}
if(!$tw.mws || !$tw.mws.store || !$tw.mws.store.sqlTiddlerDatabase) {
return "Error: MultiWikiServer or SQL database not initialized.";
}
var username = this.params[0];
var password = this.params[1];
var email = this.params[2] || username + "@example.com";
var hashedPassword = crypto.createHash("sha256").update(password).digest("hex");
var user = $tw.mws.store.sqlTiddlerDatabase.getUserByUsername(username);
if(!user) {
$tw.mws.store.sqlTiddlerDatabase.createUser(username, email, hashedPassword);
console.log("User Account Created Successfully with username: " + username + " and password: " + password);
self.callback();
}
return null;
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,60 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-assign-role-permission.js
type: application/javascript
module-type: command
Command to assign permission to a role
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-assign-role-permission",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(this.params.length < 2) {
return "Usage: --mws-assign-role-permission <role_name> <permission_name>";
}
if(!$tw.mws || !$tw.mws.store || !$tw.mws.store.sqlTiddlerDatabase) {
return "Error: MultiWikiServer or SQL database not initialized.";
}
var role_name = this.params[0];
var permission_name = this.params[1];
var role = $tw.mws.store.sqlTiddlerDatabase.getRoleByName(role_name);
var permission = $tw.mws.store.sqlTiddlerDatabase.getPermissionByName(permission_name);
if(!role) {
return "Error: Unable to find Role: "+role_name;
}
if(!permission) {
return "Error: Unable to find Permission: "+permission_name;
}
var permission = $tw.mws.store.sqlTiddlerDatabase.getPermissionByName(permission_name);
$tw.mws.store.sqlTiddlerDatabase.addPermissionToRole(role.role_id, permission.permission_id);
self.callback();
return null;
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,59 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-assign-user-role.js
type: application/javascript
module-type: command
Command to assign a role to a user
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-assign-user-role",
synchronous: false
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(this.params.length < 2) {
return "Usage: --mws-assign-user-role <username> <role_name>";
}
if(!$tw.mws || !$tw.mws.store || !$tw.mws.store.sqlTiddlerDatabase) {
return "Error: MultiWikiServer or SQL database not initialized.";
}
var username = this.params[0];
var role_name = this.params[1];
var role = $tw.mws.store.sqlTiddlerDatabase.getRoleByName(role_name);
var user = $tw.mws.store.sqlTiddlerDatabase.getUserByUsername(username);
if(!role) {
return "Error: Unable to find Role: "+role_name;
}
if(!user) {
return "Error: Unable to find user with the username "+username;
}
$tw.mws.store.sqlTiddlerDatabase.addRoleToUser(user.user_id, role.role_id);
console.log(role_name+" role has been assigned to user with username "+username)
self.callback();
return null;
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,47 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-create-bag.js
type: application/javascript
module-type: command
Command to load archive of recipes, bags and tiddlers from a directory
--mws-create-bag <name> <description>
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-create-bag",
synchronous: true
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
// Check parameters
if(this.params.length < 1) {
return "Missing bag name";
}
var bagName = this.params[0],
bagDescription = this.params[1] || bagName;
// Create bag
var result = $tw.mws.store.createBag(bagName,bagDescription);
if(result) {
return result.message;
} else {
return null;
}
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,50 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-create-recipe.js
type: application/javascript
module-type: command
Command to load archive of recipes, bags and tiddlers from a directory
--mws-create-recipe <name> <bag-list> <description>
The parameter "bag-list" should be a space delimited list of bags
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-create-recipe",
synchronous: true
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
// Check parameters
if(this.params.length < 1) {
return "Missing recipe name";
}
var recipeName = this.params[0],
bagList = (this.params[1] || "").split(" "),
recipeDescription = this.params[2] || recipeNameName;
// Create recipe
var result = $tw.mws.store.createRecipe(recipeName,bagList,recipeDescription);
if(result) {
return result.message;
} else {
return null;
}
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,49 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-listen.js
type: application/javascript
module-type: command
Listen for HTTP requests and serve tiddlers
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-listen",
synchronous: false,
namedParameterMode: true,
mandatoryParameters: []
};
var Command = function(params,commander,callback) {
var self = this;
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(!$tw.boot.wikiTiddlersPath) {
$tw.utils.warning("Warning: Wiki folder '" + $tw.boot.wikiPath + "' does not exist or is missing a tiddlywiki.info file");
}
// Set up server
this.server = $tw.mws.serverManager.createServer({
wiki: $tw.wiki,
variables: self.params
});
this.server.listen(null,null,null,{
callback: function() {
self.callback();
}
});
return null;
};
exports.Command = Command;
})();

View File

@@ -0,0 +1,93 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-load-archive.js
type: application/javascript
module-type: command
Command to load archive of recipes, bags and tiddlers from a directory
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-load-archive",
synchronous: true
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
// Check parameters
if(this.params.length < 1) {
return "Missing pathname";
}
var archivePath = this.params[0];
loadBackupArchive(archivePath);
return null;
};
function loadBackupArchive(archivePath) {
const fs = require("fs"),
path = require("path");
// Iterate the bags
const bagNames = fs.readdirSync(path.resolve(archivePath,"bags")).filter(filename => filename !== ".DS_Store");
for(const bagFilename of bagNames) {
const bagName = decodeURIComponent(bagFilename);
console.log(`Reading bag ${bagName}`);
const bagInfo = JSON.parse(fs.readFileSync(path.resolve(archivePath,"bags",bagFilename,"meta.json"),"utf8"));
$tw.mws.store.createBag(bagName,bagInfo.description,bagInfo.accesscontrol);
if(fs.existsSync(path.resolve(archivePath,"bags",bagFilename,"tiddlers"))) {
const tiddlerFilenames = fs.readdirSync(path.resolve(archivePath,"bags",bagFilename,"tiddlers"));
for(const tiddlerFilename of tiddlerFilenames) {
if(tiddlerFilename.endsWith(".json")) {
const tiddlerPath = path.resolve(archivePath,"bags",bagFilename,"tiddlers",tiddlerFilename),
jsonTiddler = fs.readFileSync(tiddlerPath,"utf8"),
tiddler = sanitiseTiddler(JSON.parse(jsonTiddler));
if(tiddler && tiddler.title) {
$tw.mws.store.saveBagTiddler(tiddler,bagName);
} else {
console.log(`Malformed JSON tiddler in file ${tiddlerPath}`);
}
}
}
}
}
// Iterate the recipes
const recipeNames = fs.readdirSync(path.resolve(archivePath,"recipes"));
for(const recipeFilename of recipeNames) {
if(recipeFilename.endsWith(".json")) {
const recipeName = decodeURIComponent(recipeFilename.substring(0,recipeFilename.length - ".json".length));
const jsonInfo = JSON.parse(fs.readFileSync(path.resolve(archivePath,"recipes",recipeFilename),"utf8"));
$tw.mws.store.createRecipe(recipeName,jsonInfo.bag_names,jsonInfo.description,jsonInfo.accesscontrol);
}
}
};
function sanitiseTiddler(tiddler) {
var sanitisedFields = Object.create(null);
for(const fieldName in tiddler) {
const fieldValue = tiddler[fieldName];
let sanitisedValue = "";
if(typeof fieldValue === "string") {
sanitisedValue = fieldValue;
} else if($tw.utils.isDate(fieldValue)) {
sanitisedValue = $tw.utils.stringifyDate(fieldValue);
} else if($tw.utils.isArray(fieldValue)) {
sanitisedValue = $tw.utils.stringifyList(fieldValue);
}
sanitisedFields[fieldName] = sanitisedValue;
}
return sanitisedFields;
}
exports.Command = Command;
})();

View File

@@ -0,0 +1,81 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/commands/mws-load-plugin-bags.js
type: application/javascript
module-type: command
Command to create and load a bag for each plugin in the repo
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "mws-load-plugin-bags",
synchronous: true
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
loadPluginBags();
return null;
};
function loadPluginBags() {
const path = require("path"),
fs = require("fs");
// Copy plugins
var makePluginBagName = function(type,publisher,name) {
return "$:/" + type + "/" + (publisher ? publisher + "/" : "") + name;
},
savePlugin = function(pluginFields,type,publisher,name) {
const bagName = makePluginBagName(type,publisher,name);
const result = $tw.mws.store.createBag(bagName,pluginFields.description || "(no description)",{allowPrivilegedCharacters: true});
if(result) {
console.log(`Error creating plugin bag ${bagname}: ${JSON.stringify(result)}`);
}
$tw.mws.store.saveBagTiddler(pluginFields,bagName);
},
collectPlugins = function(folder,type,publisher) {
var pluginFolders = $tw.utils.getSubdirectories(folder) || [];
for(var p=0; p<pluginFolders.length; p++) {
const pluginFolderName = pluginFolders[p];
if(!$tw.boot.excludeRegExp.test(pluginFolderName)) {
var pluginFields = $tw.loadPluginFolder(path.resolve(folder,pluginFolderName));
if(pluginFields && pluginFields.title) {
savePlugin(pluginFields,type,publisher,pluginFolderName);
}
}
}
},
collectPublisherPlugins = function(folder,type) {
var publisherFolders = $tw.utils.getSubdirectories(folder) || [];
for(var t=0; t<publisherFolders.length; t++) {
const publisherFolderName = publisherFolders[t];
if(!$tw.boot.excludeRegExp.test(publisherFolderName)) {
collectPlugins(path.resolve(folder,publisherFolderName),type,publisherFolderName);
}
}
};
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.pluginsPath,$tw.config.pluginsEnvVar),function(folder) {
collectPublisherPlugins(folder,"plugins");
});
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.themesPath,$tw.config.themesEnvVar),function(folder) {
collectPublisherPlugins(folder,"themes");
});
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.languagesPath,$tw.config.languagesEnvVar),function(folder) {
collectPlugins(folder,"languages");
});
}
exports.Command = Command;
})();

Some files were not shown because too many files have changed in this diff Show More