Compare commits

..

316 Commits

Author SHA1 Message Date
Dan Brown
7e44b195c5 Updated version and assets for release v23.10 2023-10-30 12:15:59 +00:00
Dan Brown
5b45eac5e1 Merge branch 'development' into release 2023-10-30 12:14:23 +00:00
Dan Brown
49b286cd34 Demo mode: Updated my account access to be more selective 2023-10-30 12:07:18 +00:00
Dan Brown
e006f9674f Langs: Updated translators and locale list pre v23.10 2023-10-30 11:41:36 +00:00
Dan Brown
8bffcebd64 Updated translations with latest Crowdin changes (#4523) 2023-10-30 11:16:19 +00:00
Dan Brown
7c4dc981cd Middlware: Prevented caching of all app requests
Previously we'd prevent caching of authed responses for security
(prevent back cache or proxy caching) but caching could still be an
issue in non-auth scenarios due to CSRF (eg. returning to login screen after
session expiry).

For #4600
2023-10-23 13:32:15 +01:00
Dan Brown
9b4f1fb981 Styles: Aligned empty state alignment & consistency
- Fixed inital empty state margins/paddings to be aligned and not differ
when lists are empty.
- Aligned button/action display when viewing empty entities.
- Fixed use of non-existing permission in books for book empty state
  button.

Fixes #4563
2023-10-23 11:53:19 +01:00
Dan Brown
d42af4affc Shortcuts: Prevented help shown when in inputs
For #4606
2023-10-23 11:04:09 +01:00
Dan Brown
8375d341ea Deps: Updated npm and composer packages 2023-10-20 16:39:40 +01:00
Dan Brown
f5756ff28a Security: Swapped twitter for mastodon link 2023-10-19 16:53:02 +01:00
Dan Brown
c513cdaebe Merge pull request #4618 from radiantwave/patch-1
Remove huntr from SECURITY.md
2023-10-19 16:50:30 +01:00
Dan Brown
995b7d61e9 Merge pull request #4615 from BookStackApp/user_account
User preferences/options cleanup
2023-10-19 16:49:06 +01:00
Dan Brown
02bfaffeb4 My Acount: Updated old preference url reference for watches 2023-10-19 16:37:55 +01:00
Daniel
38fe40809b Update SECURITY.md
Remove huntr
2023-10-19 17:12:18 +02:00
Dan Brown
ce53f641ad My Account: Covered profile and auth pages with tests 2023-10-19 16:06:59 +01:00
Dan Brown
f55e7ca3c9 User Account: Ensured page titles for pages and api tokens 2023-10-19 15:24:48 +01:00
Dan Brown
fabc854390 My Account: Updated and started adding to tests
- Updated existing tests now affected by my-account changes.
- Updated some existing tests to more accuractly check the scenario.
- Updated some code styling in SocialController.
- Fixed redirects for social account flows to fit my-account.
- Added test for social account attaching.
- Added test for api token redirect handling.
2023-10-19 14:18:42 +01:00
Dan Brown
12946414b0 API Tokens: Updated interfaces to return to correct location
Since management of API tokens can be accessed via two routes, this adds
tracking and handling to reutrn the user to the correct place.
2023-10-19 11:31:45 +01:00
Dan Brown
f9422dff18 My Account: Added self-delete flow 2023-10-19 10:48:27 +01:00
Dan Brown
cf72e48d2a User form: Always show external auth field, update access control
Updated old user management routes to only be accessible with permission
to manage users, so also removed old content controls checking for that
permission.
2023-10-19 10:20:04 +01:00
Dan Brown
e4ea73ee25 My Account: Cleaned-up/reorganised user header dropdown 2023-10-18 17:57:14 +01:00
Dan Brown
03c44b3992 My Account: Extracted/tweaked profile text, removed old index 2023-10-18 17:53:58 +01:00
Dan Brown
c1b01639c1 My Account: Built out profile page & endpoints
Text currently hard-coded, needs finalising and extracting.
2023-10-18 12:39:57 +01:00
Dan Brown
a868012048 Users: Built out auth page for my-account section 2023-10-17 17:38:07 +01:00
Dan Brown
a9d0f36766 User: Started cleanup of user self-management
- Moved preference views to more general "my-account" area.
- Started new layout for my-account with sidebar.
- Added MFA to prefeences view (to be moved).
2023-10-17 13:11:10 +01:00
Dan Brown
3274181e14 Merge pull request #4604 from BookStackApp/editor_trim_enhancement
Editor design update
2023-10-14 17:30:04 +01:00
Dan Brown
8166e27f2b Editors: Properly aligned edit area border radius 2023-10-14 17:18:09 +01:00
Dan Brown
8ffa436f3d Editors: Adjusted new design for mobile and dark mode
Tested new design across FF, Chrome, and Gnome web (webkit)
2023-10-14 17:10:29 +01:00
Dan Brown
8c10959339 Editors: Tightened up new design, adjusted for MD editor 2023-10-14 16:33:48 +01:00
Dan Brown
45c7409092 Editor: Started toying with more singificant design update 2023-10-13 17:33:11 +01:00
Dan Brown
a12b60e1ad Editor: Started attempts to improve design elements 2023-10-08 15:04:07 +01:00
Dan Brown
ccb3c2516a Homepage: Made much nicer at ipad-like widths
Updated default homepage layout to be much nicer at ipad-like widths by
switching to css-column approach at those breakpoints.
Also neated top actions by switching to simpler flexbox layout.

Fixes #4596
2023-10-07 12:38:54 +01:00
Dan Brown
2e2272343b Merge branch 'LawssssCat/development' into development 2023-10-06 12:03:38 +01:00
Dan Brown
031067745b Layout: Restructured tri-layout for sidebar control
Restructured tri-layout grid system, so the sidebars are contained in
their own child grid system, mimicking the parent grid, so we can treat
them as part of the same parent scroll container at smaller screen
sizes for consistent scroll/sticky behavior.

Tested on Firefox, Chromium, Gnome Web and Safari (MacOS).

For #4394
Changes made during review of #4562
2023-10-06 12:03:38 +01:00
Dan Brown
1267068d9c CI: Added path filtering to actions
In the hope we can make the CI runs a bit more efficient and energy
conscious, by only running when relevant files have changed.
2023-10-04 09:18:24 +01:00
Dan Brown
0241032f06 Tags: Fixed enter press clearing field
For #4570
2023-10-04 09:08:10 +01:00
Dan Brown
bd7c7eb8d6 Print Styles: Removed use of seperate style sheet
Seemed a bit redundant and complicated, since we're only adding a few
extra styles. Just merged into main styles instead.
2023-10-03 15:14:21 +01:00
Dan Brown
c5d5b6e3c1 Print Styles: Fixed header/footer content showing
Extra bits were showing due to recent changes.
Done a quick pass through major display views.

Fixes #4594
2023-10-03 14:59:35 +01:00
Dan Brown
1005f4bd7a Testing: Added favicon cleanup
Was leaving a changed favicon leaving other test to fail.
2023-10-03 14:50:54 +01:00
Dan Brown
b24296e0c9 Added NETWAYS to readme sponsor list
Related to:
edceda5342
2023-10-03 12:10:45 +01:00
Dan Brown
d1f28ed245 Merge branch 'basic-pwa-support' into development 2023-10-02 15:58:07 +01:00
Dan Brown
1d91b4d8a6 PWA Manifest: Tweaks during review of PR #4430
- Updated to go through HomeController with the builder as a helper
  class.
- Extracted some reapeated items into variables in manifest.
- Updated background color to match those used by BookStack.
- Removed reference of icon.ico since its not intended to be used.
- Added tests to cover functionality.

Review of #4430
2023-10-02 15:54:39 +01:00
Dan Brown
8bba5dd5a0 Merge pull request #4578 from BookStackApp/upload_handling
Improvements to file/image upload handling UX
2023-10-01 17:20:10 +00:00
Dan Brown
ffb04a8be6 JS: Fixed ESLint issues 2023-10-01 18:13:54 +01:00
Dan Brown
b2d48d9a7f Images: Rolled out image memory handling to image actions
- Moved thumnbail loading out of repo into ImageResizer.
- Updated gallery and editor image handling to show errors where
  possible to indicate memory issues for resizing/thumbs.
- Updated gallery to load image data in a per-image basis via edit form
  for more resiliant thumb/data fetching. Data was previously provided
  via gallery listing, which could be affected by failing generation
  of other images.
- Updated image manager double click handling to be more pleasant and
  not flash away the edit form.
- Updated editor handlers to use main URL when thumbs fail to load.
2023-10-01 13:05:18 +01:00
Dan Brown
20bcbd76ef Images: Extracted out image resizing to its own class 2023-09-30 20:00:48 +01:00
Dan Brown
e703009d7f Images: Added thin wrapper around image filesystem instances
Extracts duplicated required handling (Like path adjustment) out to
simpler storage disk instance which can be passed around.
2023-09-30 19:12:22 +01:00
Dan Brown
7247e31936 Images: Started refactor of image service
To break it up.
Also added better memory handling to other parts of the app.
2023-09-30 18:28:42 +01:00
Dan Brown
40721433f7 Image manager: Tweaked grid sizing to prevent massive items 2023-09-30 12:43:51 +01:00
Dan Brown
97274a8140 Images: Added test to cover thubmnail regen endpoint 2023-09-30 12:29:49 +01:00
Dan Brown
5c318a45b8 Images: Reverted some thumbnails to be on-demand generated
Added since we can't always be sure of future image usage, and in many
cases we don't generate ahead-of-time.
Also:
- Simplified image handling on certain models.
- Updated various string handling operations to use newer functions.
2023-09-30 12:09:29 +01:00
Dan Brown
5af3041b9b Thumbnails: Added OOM handling and regen endpoint
- Added some level of app out-of-memory handling so we can show a proper
  error message upon OOM events.
- Added endpoint and image-manager button/action for regenerating
  thumbnails for an image so they can be re-created upon failure.
2023-09-29 13:54:08 +01:00
Dan Brown
cc0827ff28 Images: Updated to create thumbnails at specific events 2023-09-29 11:46:32 +01:00
Dan Brown
59da7666b5 Uploads: Added user-facing message for Laravel post limit handling
Uploads over the post max size Would previously error without a
clean user facing message. This catches that error to provide a
user friendly message, compatible with our common error handling.

Tested on image manager handling.
Added test to cover.
2023-09-25 13:48:23 +01:00
JonatanRek
287ed4ff3b Remove Dumps 2023-09-24 20:19:53 +02:00
Dan Brown
21badde4ef Editors: Updated entity link select to pre-fill with selection
Updated all uses across both editors, so the entity link selector popup
now initates a search with the selection text if existing.

For #4571
2023-09-24 18:33:33 +01:00
Dan Brown
e9664dc678 Exports: Fixed issues with book text export format
- Fixed missing page content for direct page children
- Fixed lack of book description.
- Fixed inconsistent spacing between items.
- Fixed lack of spacing between HTML items when HTML on same line.

For #4557
2023-09-24 18:03:37 +01:00
Dan Brown
d5a3bdb7aa Header: Simplified, split and re-orgranised view file(s)
- Moved "common" template partials, that are only used in layouts, to
  layouts/parts folder.
- Simplified HTML structure of header template.
- Extracted logo and links from header template to simplify.
- Added header-links-start template for easier extension/customization
  without needing to override full list of links.
  - Added test to cover usage of this.

For #4564
2023-09-24 10:29:51 +01:00
Dan Brown
c3b4128a38 Homepage: Added tags button to non-default home views
For #4558
2023-09-24 09:31:44 +01:00
Dan Brown
f77bb01b51 Search: Added further backslash handling
Added due to now not being able to perform an exact search where
contains a trailing backslash.
Now all backslashes in exact terms are consided escape chars
and require escaping themselves.
Potential breaking change due to search syntax handling change.

Related to #4535.
2023-09-23 13:41:10 +01:00
Dan Brown
fb417828a4 Readme: Updated badges, sponsors and top links 2023-09-23 12:47:24 +01:00
JonatanRek
57791c1466 Fix Reloading changes on dark mode switch 2023-09-22 11:31:24 +02:00
JonatanRek
46e3b2ceb3 Merge branch 'basic-pwa-support' of https://github.com/GamerClassN7/BookStack into basic-pwa-support 2023-09-22 11:19:38 +02:00
JonatanRek
10e8e1a88d New line fix 2023-09-22 11:19:34 +02:00
JonatanRek
7e09c9a147 Update HomeController.php 2023-09-22 11:19:17 +02:00
JonatanRek
2a2f893fcc Formating Fixes 2023-09-22 11:18:10 +02:00
JonatanRek
9b99664bff Additional Tweaks and FIxes 2023-09-22 11:15:13 +02:00
JonatanRek
f910424fa3 Implementation of required changes 2023-09-22 11:00:41 +02:00
JonatanRek
6e19a8a4bb Merge branch 'basic-pwa-support' of https://github.com/GamerClassN7/BookStack into basic-pwa-support 2023-09-22 10:49:53 +02:00
JonatanRek
cb9c3fc9f5 Fix Dark theme 2023-09-22 10:49:37 +02:00
JonatanRek
effc03e99e Merge branch 'BookStackApp:development' into basic-pwa-support 2023-09-22 10:48:48 +02:00
Dan Brown
8964575973 Search: Added support for escaped exact terms
Also prevented use of empty exact matches.
Prevents issues when attempting to use exact search terms in inputs for
just search terms, and use of single " chars within search terms since
these would get auto-promoted to exacts.

For #4535
2023-09-19 20:09:33 +01:00
Dan Brown
4b4d8ba2a1 Avatar Commend: Simplified and updated during review
During review of #4560.

- Simplified command to share as much log as possible across different
  run options.
- Extracted out user handling to share with MFA command.
- Added specific handling for disabled avatar fetching.
- Added mention of avatar endpoint, to make it clear where these avatars
  are coming from (Protect against user expectation of LDAP avatar sync).
- Simplified a range of the testing.
- Tweaked wording and code formatting.
2023-09-19 15:53:01 +01:00
lawsssscat
588ed785d2 fix Sidebar scrolling at mid-range sceen 2023-09-19 22:12:33 +08:00
Marc Hagen
ca98155373 fix: Actually check if we have correct data 2023-09-18 20:04:59 +02:00
Marc Hagen
ea7592509f feat: Artisan command for updating avatars for existing users 2023-09-18 20:04:28 +02:00
Dan Brown
95b9ea1a21 Dev: Reviewed and expanded on PHP testing docs 2023-09-17 23:41:02 +01:00
Dan Brown
684a9dee8e Merge branch 'tusharnain4578/development' into development 2023-09-17 22:29:06 +01:00
Dan Brown
c42cd29ed3 Notifications: Updated comment notif. prefs. test
Combined testcases, updated to use actual text strings, and set comments
setting via correct method.

Made during review of #4552
2023-09-17 22:26:51 +01:00
Dan Brown
35813e818d Merge pull request #4555 from BookStackApp/language_cleanup
Language cleanup
2023-09-17 22:15:38 +01:00
Dan Brown
78bf11cf65 Locales: Removed a lot of existing locale handling
There was a lot of locale handling to get correct/expected date
formatting within the app.
Carbon now has built-in locale content rather than us needing to target
specific system locales.

This also removes setting locale via Carbon directly.
Carbon registers its own Laravel service provider which seems to
accurately pull the correct locale from the app.

For #4555
2023-09-17 22:02:12 +01:00
Tushar Nain
baa957d980 Update UserPreferencesTest.php
Added Testcases for preferences menu of Comment Notifications visibility when comments are enabled/disabled.
2023-09-17 23:31:01 +05:30
Dan Brown
b42e8cdb63 Locales: Fixed errors occuring for PHP < 8.2 2023-09-17 17:35:00 +01:00
Dan Brown
8994c1b9d9 Locales: More use of locale objects, Addressed failing tests 2023-09-17 16:20:21 +01:00
Dan Brown
ac9a65945f Locales: Performed cleanup and alignment of locale handling
- Reduced app settings down to what's required.
- Used new view-shared $locale object instead of using globals via
  config.
- Aligned language used to default on "locale" instead of mixing
  locale/language.

For #4501
2023-09-17 13:31:38 +01:00
Dan Brown
b292cf7090 Extracted icon helper, aligned container resolution
Also updated breadcrumb view composer to current standards.
Closes #4553
2023-09-16 18:26:28 +01:00
Dan Brown
54791c8627 Merge pull request #4554 from BookStackApp/guest_user_cleanup
Guest control: Cleaned methods involved in fetching/handling
2023-09-16 13:59:16 +01:00
Dan Brown
e16bdf443c Removed redundant null check 2023-09-16 13:49:03 +01:00
Dan Brown
b90033a730 Guest control: Cleaned methods involved in fetching/handling
- Moves guest user caching from User class to app container for
  simplicity.
- Updates test to use simpler $this->users->guest() method for
  consistency.
- Streamlined helpers to avoid function overlap for simplicity.
- Extracted user profile dropdown while doing changes.
2023-09-16 13:18:35 +01:00
Dan Brown
9ac932fc28 Merge branch 'v23-08' into development 2023-09-16 11:55:57 +01:00
Tushar Nain
6a5361d853 Fixed : Comment notification settings are visible even if comments are disabled
Added a UX condition to display comment notification settings, only if the user has enabled the comment notifications.
2023-09-16 14:00:08 +05:30
Dan Brown
c1d30341e7 Updated version and assets for release v23.08.3 2023-09-15 13:49:40 +01:00
Dan Brown
80d2b4913b Merge branch 'v23-08' into release 2023-09-15 13:49:12 +01:00
Dan Brown
45b8d6cd0c Comments: Fixed wrong identification of parent comment
Would cause comment reply notifications to not be sent to expected user.
Updated test to cover problem case.

For #4548
2023-09-15 13:38:02 +01:00
Dan Brown
dfaf6f7c13 Cleanup Command: Allowed running non-interactively
For #4541
2023-09-14 14:17:20 +01:00
Dan Brown
417705651c Dark Mode: Fixed not toggle action when dark by default
Added test to cover.
For #4543.
2023-09-14 13:53:24 +01:00
Dan Brown
4ec600adfa Langs: Added Uzbek, Updated translator attribution
For #4527
2023-09-13 10:52:12 +01:00
Dan Brown
709c182bda Merge branch 'Bajszi97/development' into development 2023-09-13 10:12:49 +01:00
Dan Brown
a452092e40 Reviewed #4533, formatting and tweaks
- Updating formatting.
- Tweaked truncation to roughly match elipsis char to width used.
- Updated testing to use existing helpers, and ran check as admin user
  to avoid name conflicts.
2023-09-13 10:09:33 +01:00
Bajszi
83028f3fbe Test comment creator name truncation 2023-09-12 21:10:25 +02:00
Bajszi
f4deb13301 Truncate with three dots 2023-09-12 21:10:25 +02:00
Dan Brown
6e098905d4 Theme: Added handling for functions.php file load error
This adds specific handling for functions.php error loading to re-throw
errors wrapped in a more descriptive message, to make it clear the error
is due to an issue in their functions.php file.

Decided to throw and stop, rather than ignore & continue, to be on the
safe side in the event auth-level (or other security level) customizations
have been made via functions.php.

Adds test to cover.
Closes #4504
2023-09-12 12:34:02 +01:00
Bajszi
f997d3e0bb Trimmed name last resort 2023-09-11 21:27:15 +02:00
Dan Brown
8e3f8de627 Notifications: Reorgranised classes into domain specific folders
Closes #4500
2023-09-11 19:27:36 +01:00
Dan Brown
18f396c21b Views: Rolled out ID to similar recent activity lists
To allow for easier customization.
Related to #4530
2023-09-11 18:50:39 +01:00
Dan Brown
ec86576e1e Merge branch 'v23-08' into development 2023-09-11 18:43:23 +01:00
Dan Brown
99eb3e5f71 Comments: Fixed JS error when lacking commenting permissions
The page comments component would throw an error due to references to
form elements/content, when form elements may not exist due to
permisisons.

For #4531
2023-09-11 18:40:40 +01:00
Dan Brown
4985e39db4 Slack auth: Switched from community to laravel library
Tested locally before & after change, and looked at code to compare.
Nothing seen or experienced that should affect things, from testing all
is working as expected with no difference from before.
- Update composer requirement of socialite to that which included slack.
- Updated PHP depds while there.
- Updated format of socialite events to align with current documentation
  and to use class references instead of strings.
2023-09-11 15:26:04 +01:00
Dan Brown
05f2ec40cc OIDC: Moved name claim option handling from config to service
Closes #4494
2023-09-11 11:50:58 +01:00
Dan Brown
564dc70ac4 Fixed php8 compat issue, updated readme & templates
- Changed use of array spread since it was not supported in PHP8.0.
- Updated issue templates based to reduce less valueable fields, update
  some details, and try to help bug reports be more focused on bugs.
- Updated readme with peertube link and attribution advistory for
  translations PRs.
2023-09-11 11:37:07 +01:00
Dan Brown
2fbf5527c7 Simplified and aligned handling of mixed entity endpoints
Fixes #4444
2023-09-10 15:19:23 +01:00
Dan Brown
3928cbac18 Mail: changed default "MAIL_FROM" address
Used an "example.com" address so we're using a propoer reserved domain,
and to avoid these trying to be delivered to the main bookstackapp
domain.

Closes #4518
2023-09-09 12:41:37 +01:00
Dan Brown
8659ee0936 Merge pull request #4525 from BookStackApp/http_alignment
HTTP calling logic alignment
2023-09-08 17:30:44 +01:00
Dan Brown
06490f624c Removed use of HttpFetcher
- Fixed some existing issues in new aligned process.
- Manually tested each external call scenario.
2023-09-08 17:16:57 +01:00
Dan Brown
a8b5652210 Started aligning app-wide outbound http calling behaviour 2023-09-08 14:16:09 +01:00
Dan Brown
15da4b98ef Updated translations with latest Crowdin changes (#4512)
Last translation merge for possible continued v23.08 branch
2023-09-07 15:57:59 +01:00
Dan Brown
21cd2d17f6 Updated sponsors and dev version 2023-09-07 14:43:29 +01:00
Dan Brown
3f473528b1 Updated version and assets for release v23.08.2 2023-09-04 12:06:50 +01:00
Dan Brown
d0dcd4f61b Merge branch 'development' into release 2023-09-04 12:06:15 +01:00
Dan Brown
ad60517536 Updated translations with latest Crowdin changes (#4506) 2023-09-04 11:48:25 +01:00
Dan Brown
2c20abc872 WYSIWYG: Fixed filtering issue causing broken page edits
Could error upon div elements without classes, including drawings.

Related to #4510 and #4509
2023-09-04 11:25:05 +01:00
Dan Brown
bde66a1396 Updated version and assets for release v23.08.1 2023-09-03 17:40:19 +01:00
Dan Brown
4de5a2d9bf Merge branch 'development' into release 2023-09-03 17:39:56 +01:00
Dan Brown
2abbcf5c0f Updated translator attribution before release v23.08.1 2023-09-03 17:35:57 +01:00
Dan Brown
7a48516bf4 Updated translations with latest Crowdin changes (#4481) 2023-09-03 17:23:40 +01:00
Dan Brown
e31b50dabd Preferences: Fixed section screen flexibility
Improved wrapping and flex control to prevent button text force wrapping
to newlines.

For #4502
2023-09-03 16:58:29 +01:00
Dan Brown
817581aa0c Watching: Prevent issues when watchable or user is deleted
- Adds filtering to the watched items list in notification preferences
  so that deleted (recycle bin) items are removed via query.
- Adds relations and logic to properly remove watches upon user and
  entity delete events, to old watches in database do not linger.
- Adds testing to cover the above.

Did not add migration for existing data, since patch will be close to
introduction, and lingering DB entries don't open a security concern,
just some potential confusion in specific potential scenarios.
Probably not work extra migration risk, although could add in future if
concerns/issues are found.

Related to #4499
2023-09-03 14:19:43 +01:00
Dan Brown
1cd19c76ba Merge pull request #4497 from BookStackApp/notification_language
Notifications: User language for notification text
2023-09-02 15:47:26 +01:00
Dan Brown
5d38ae3c97 Merge pull request #4484 from omahs/patch-1
Fix typos
2023-09-02 15:44:01 +01:00
Dan Brown
a720b3725d Testing: Added entity decode flag and phpunit env option
- Passed decode flags to provide consistent behaviour across PHP
  versions during testing.
- Added env option to prevent local option taking action in PHPunit
  tests.
2023-09-02 15:39:45 +01:00
Dan Brown
3847a76134 Notifications: Aligned how user language is used
- This ensures content notifications are not translated to receiver
  language.
- This adds actual plaintext support for content notifications (Was
  previously just HTML as text view).
- Shares same base class across all mail notifications.
- Also cleaned up existing notification classes.

Future cleanup requested via #4501
2023-09-02 15:11:42 +01:00
Dan Brown
f91049a3f2 Notifications: Add test to check notification language 2023-09-01 16:30:37 +01:00
Dan Brown
4e6b74f2a1 WYSIWYG: Added filtering of page pointer elements
For #4474
2023-09-01 13:50:55 +01:00
omahs
976f241ae0 fix typo 2023-08-31 10:01:56 +02:00
omahs
415dab9936 fix typos 2023-08-31 10:00:45 +02:00
omahs
54715d40ef fix typo 2023-08-31 09:58:59 +02:00
Dan Brown
27bf4299cf Updated version and assets for release v23.08 2023-08-30 12:38:48 +01:00
Dan Brown
164f01bb25 Merge branch 'development' into release 2023-08-30 12:38:22 +01:00
Dan Brown
c6d0e690f9 Updated translations with latest Crowdin changes (#4462) 2023-08-30 12:35:10 +01:00
Dan Brown
77d65d1ca1 Updated translator attribution before v23.08 2023-08-30 11:49:45 +01:00
Dan Brown
dc77233ec3 MD Editor: Fixed scroll on mobile widths
Added min-height to flex elements to ensure they properly flex within
the container rathen than adjust to content.

For #4466
2023-08-30 02:41:51 +01:00
Dan Brown
3622c440d7 SSR: Added new option to complete env example file 2023-08-30 02:31:36 +01:00
Dan Brown
642210ab4c Merge branch 'srr_host_allowlist' into development 2023-08-27 12:45:00 +01:00
Dan Brown
e176aae940 Updated translations with latest Crowdin changes (#4380) 2023-08-27 12:43:59 +01:00
Dan Brown
903895814a SSR: Updated allow list handling & covered webhook usage
- Covered webhook SSR allow list useage via test.
- Updated allow list handling to use trailing slash, or hash, or end of
  line as late anchor for better handling for hosts (prevent .co.uk
passing for .co domain host)
2023-08-26 20:13:37 +01:00
Dan Brown
c324ad928d Security: Added new SSR allow list and validator
Included unit tests to cover validator functionality.
Added to webhooks.
Still need to do testing specifically for webhooks.
2023-08-26 15:28:29 +01:00
Dan Brown
9100a82b47 Guests: Prevented access to profile routes
Prevention of action on certain routes for guest user when public access
is enabled. Could not see a way this could be a security issue, beyond a
mild nuisance that'd only be visible if public users can edit, which
would present larger potential nuisance anyway.
2023-08-26 14:07:48 +01:00
Dan Brown
32516f7b68 Merge pull request #4457 from BookStackApp/drawing_backup_store
Browser-based drawing backup storage system
2023-08-23 19:12:29 +01:00
Dan Brown
69ac425903 Updated readme attribution and fixed eslint issues 2023-08-23 19:02:23 +01:00
Dan Brown
3917e50c90 Drawio: Tweaked fail backup handling during testing
- Tweaked wording of popup title.
- Updated WYSIWYG create handling to properly remove drawing container
  on failure.

Tested across FF and chrome, in both editors for create & editing.
2023-08-23 18:50:37 +01:00
Dan Brown
dd71658d70 Drawio: Added unsaved restore prompt and logic 2023-08-23 14:16:20 +01:00
Dan Brown
a4fbde9185 Drawio: Started browser drawing backup store system
Adds just the part to store image data, and remove on successfull save.
Alters save events to properly throw upon error.
Adds IDB-Keyval library for local large-size store.
For #4421
2023-08-22 19:30:39 +01:00
Dan Brown
cbcec189fd RTL: Fixed screen-reader-only elements pushout out view
For #4429
2023-08-22 18:25:14 +01:00
Dan Brown
0628c28f66 Cache: Increases database cache value size
Upped from text to medium text.
Aligns with modern Laravel default.
Fixes #4453 where were reaching the limit of TEXT.
2023-08-21 23:01:42 +01:00
Dan Brown
391478465a Merge branch 'add-priority' into development 2023-08-21 15:43:16 +01:00
Dan Brown
9ca1139ab0 API: Reviewed changes for API priority control
Review of #4313
- Made constructor changes while reviewing some classes.
- Updated API examples for consistency.
- Tweaked formatting for some array changes.
- Simplified added tests.
- Tweaked chapter/page repo priority handling to be simpler.

Performed manual API endpoint testing of page/chapter create/update.
2023-08-21 15:42:47 +01:00
Dan Brown
7bf5425c6b Updated PHP and npm deps, Upped node version 2023-08-19 20:22:19 +01:00
Dan Brown
e44ef57219 Status: Updated cache check to use unique key
Updated status endpoint cache check to include a random component in the
key to avoid conflict during simultaneous checks.
For #4396
2023-08-17 21:24:35 +01:00
Dan Brown
fef433a9cb Merge pull request #4390 from BookStackApp/content_notifications
Content user notifications
2023-08-17 21:09:52 +01:00
Dan Brown
e709caa005 Notifications: Switched testing from string to reference levels 2023-08-17 18:10:34 +01:00
Dan Brown
38829f8a38 Notifications: Fixed send content permission checking
Added test and changed logic to properly check the view permissions for
the notification receiver before sending.
Required change to permissions applicator to allow the user to be
manually determined, and a service provider update to provide the class
as a singleton without a specific user, so it checks the current logged
in user on demand.
2023-08-17 17:57:31 +01:00
Dan Brown
ee9e342b58 Notifications: Fixed issues causing failing tests
- Ensured watch options passed in all meta template usage to fix failing
  scenarios where watch options did not exist.
- Fixed testing issue caused by guest user permission caching.
2023-08-17 14:59:28 +01:00
Dan Brown
79470ea4b7 Notifications: Made improvements from manual testing
- Added titles for preference pages.
- Added extra check for non-guest for notifications on preferences page.
2023-08-16 20:15:49 +01:00
Dan Brown
565908ef52 Notifications: Add phpunit test for notification sending
Covers core case scenarios, and check of notification content.
2023-08-16 16:02:00 +01:00
Dan Brown
bc6e19b2a1 Notifications: Added testing to cover controls 2023-08-15 20:08:27 +01:00
Dan Brown
615741af9d Notifications: Cleaned up mails, added debounce for updates
- Updated mail notification design to be a bit prettier, and extracted
  text to new lang file for translation.
- Added debounce logic for page update notifications.
- Fixed watch options not being filtered to current user.
2023-08-15 14:39:39 +01:00
Dan Brown
371779205a Notifications: Added new preferences view and access control
- Added general user preferences view and updated link in profile menu
  to suit.
- Made notification permission required for notification preferences
  view, added test to cover.
2023-08-14 17:29:12 +01:00
Dan Brown
d9fdecd902 Notifications: User watch list and differnt page watch options
- Adds option filtering and alternative text for page watch options.
- Adds "Watched & Ignored Items" list to user notification preferences
  page to show existing watched items.
2023-08-14 13:11:18 +01:00
JonatanRek
2b604b5af9 Move Manifest Definition to Separate Config File 2023-08-10 17:02:31 +02:00
JonatanRek
08ea97fd83 Manifest Tweaks 2023-08-10 16:43:14 +02:00
JonatanRek
601491b275 Add Color 2023-08-10 15:51:09 +02:00
JonatanRek
88e148ba00 Initial Draft 2023-08-10 15:44:27 +02:00
Dan Brown
c47b3f805a Notifications: Updated watch control to show parent status 2023-08-09 14:53:31 +01:00
Dan Brown
ecab2c8e42 Notifications: Added logic and classes for remaining notification types 2023-08-05 14:19:23 +01:00
Dan Brown
18ae67a138 Notifications: Got core notification logic working for new pages
Also rolled out watch UI to chapter and page views
2023-08-04 16:51:29 +01:00
Dan Brown
9779c1a357 Notifications: Started core user notification logic
Put together an initial notification.
Started logic to query and identify watchers.
2023-08-04 12:27:29 +01:00
Dan Brown
9d149e4d36 Notifications: Linked watch functionality to UI
Got watch system working to an initial base state.
Moved some existing logic where it makes sense.
2023-08-02 13:14:00 +01:00
Dan Brown
8cdf3203ef Notifications: Started back-end for watch system
Added DB and started controller method.
2023-07-31 16:08:29 +01:00
Dan Brown
6100b99828 Notifications: Extracted watch options, updated UI further 2023-07-31 15:23:28 +01:00
Dan Brown
730f539029 Notifications: Started entity watch UI 2023-07-27 14:27:45 +01:00
Dan Brown
ff2674c464 Notifications: Added role receive-notifications permission 2023-07-25 17:59:04 +01:00
Dan Brown
100b28707c Notifications: added user preference UI & logic
Includes testing to cover.
Also added file missing from previous commit.
2023-07-25 17:08:40 +01:00
Dan Brown
45e75edf05 Notifications: Started activity->notification core framework 2023-07-19 11:03:05 +01:00
Dan Brown
1c922be4c7 Comments: Added text for new activity types 2023-07-19 10:11:53 +01:00
Dan Brown
0359e2490a Comments: Updated testing to check for new activities 2023-07-19 10:09:08 +01:00
Dan Brown
422e50302a Comments: Added extra comment-specific activities
Kept existing "COMMENTED_ON" activity for upgrade compatibility,
specifically for existing webhook usage and for showing comment
activities in activity lists.

Precursor to content notifications.
Currently untested.
Also applied some type updates.
2023-07-18 15:07:31 +01:00
Dan Brown
f563a005f5 Updated version and assets for release v23.06.2 2023-07-12 22:34:25 +01:00
Dan Brown
a14d8e30cc Merge branch 'development' into release 2023-07-12 22:34:15 +01:00
Dan Brown
7504ad32a7 Updated translator attribution before release v23.06.2 2023-07-12 22:34:04 +01:00
Dan Brown
fca18862d2 Updated translations with latest Crowdin changes (#4367) 2023-07-12 22:22:43 +01:00
Dan Brown
ae834050f5 Shelf permissions: reverted create removal
Reverted work in 847a57a49a.
Left test in but updated to new expectation.
Left migration in but removed content to prevent new pre-v23.06
upgraders loosing shelf create permission status.
Added note to permission to describe use-case.

For #4375
2023-07-12 22:04:05 +01:00
Dan Brown
a83150131a Webhooks: Fixed failing delete-based events
Due to queue serialization.
Added a test to check a couple of delete events.
Added ApiTokenFactory to support.
Also made a couple of typing/doc updates while there.

Related to #4373
2023-07-12 16:16:12 +01:00
Jean-René ROUET
3a36d3c847 add tests for priority 2023-07-11 14:11:13 +02:00
Jean-René ROUET
4d399f6ba7 add priority on page and chapter create 2023-07-11 13:28:20 +02:00
Jean-René Rouet
b1b8067cbe Merge branch 'BookStackApp:development' into add-priority 2023-07-11 08:57:14 +02:00
Dan Brown
a9194ffb63 Updated version and assets for release v23.06.1 2023-07-05 13:04:51 +01:00
Dan Brown
2f9c1b7127 Merge branch 'development' into release 2023-07-05 13:04:30 +01:00
Dan Brown
18979e84d6 Updated tranlsator attribution and sponsors 2023-07-05 12:40:49 +01:00
Dan Brown
bf5e886d76 Updated translations with latest Crowdin changes (#4352) 2023-07-05 12:28:19 +01:00
Dan Brown
e04a1af444 Merge pull request #4344 from devdot/update-api-docs
Update API Docs
2023-07-05 12:08:51 +01:00
Dan Brown
eb2c5d00cb Audit log: Added IP address wrapping
Primarily to support long ipv6 addresses which would overflow over the
activity date.
For #4349
2023-07-05 11:37:49 +01:00
Dan Brown
96819b7bd9 Images: Updated image timestamp upon file change
For #4354
2023-07-05 11:28:03 +01:00
Dan Brown
18ee80a743 Roles: fixed error upon created_at sorting
Added test to cover core role sorting functionality.
For #4350
2023-07-04 21:52:46 +01:00
Dan Brown
1a56de6cb4 Testing: Split out role tests to management and permissions 2023-07-04 21:40:05 +01:00
Dan Brown
465989efa9 Mail: Updated to forked symfony/mailer to allow assurance of tls
Related to #4358
2023-07-04 15:21:31 +01:00
Dan Brown
bbea76668b Updated version and assets for release v23.06 2023-06-30 11:06:19 +01:00
Dan Brown
becc630acf Merge branch 'development' into release 2023-06-30 11:05:57 +01:00
Dan Brown
80635144b1 Meta: Updated dev version and translation attribution 2023-06-30 10:55:54 +01:00
Thomas Kuschan
d293171da2 API Docs: Add Missing Fields in Example Responses 2023-06-30 09:36:46 +02:00
Thomas Kuschan
174cd5a893 API Docs: Add Missing editor fields in Example Responses 2023-06-30 09:35:47 +02:00
Thomas Kuschan
ccfe38e963 API Docs: Add book_slug to Example Responses
Remove the book attribute in responses because it is never returned by the API. Currently, Chapters Create does not return book_slug! (The example response is consistent with the inconsistent API behavior)
2023-06-30 09:33:53 +02:00
Thomas Kuschan
23ae332c1b API Docs: Sort a few example responses 2023-06-30 09:27:18 +02:00
Thomas Kuschan
3a39f13420 API Docs: Remove Dates from Tags in Example Responses 2023-06-30 09:24:46 +02:00
Thomas Kuschan
ca2d2c97d4 API Docs: Add User Slugs to Example Responses 2023-06-30 09:23:02 +02:00
Dan Brown
d23cfc3d32 Updated test to match German translation 2023-06-28 23:46:59 +01:00
Dan Brown
5ea2d0c57b WYSIWYG: Fixed growing rows on Firefox
Occured when the cell contained any block content with a differnt line
height to the table cell itself.
In firefox, cells with a height would end up with an actual greater
real cell height, which messed up TinyMCE resize calculations, causing
tables to grow.
Adding default vertical-align: top, changes this behaviour to get proper
cell heights.
Related to Firefox issue: https://bugzilla.mozilla.org/show_bug.cgi?id=569645
Have tested that editor cell text align options can still be used with
this.

For #4337
2023-06-28 23:28:31 +01:00
Dan Brown
b425d0f65c Updated tinymce to v6.5.1 2023-06-28 22:45:21 +01:00
Dan Brown
63f03046b3 Updated translations with latest Crowdin changes (#4256) 2023-06-28 17:54:32 +01:00
Dan Brown
7f98906b0f Comments: Tweaked design to be more consistent and compact 2023-06-28 13:41:14 +01:00
Dan Brown
b24246085f CSS: Tweaked css heading font to fall back to body font 2023-06-28 09:35:30 +01:00
Thomas Kuschan
e47870794d API Docs: Add Missing Type in Response
Type is always returned when pages/chapters are in a contents array.
2023-06-26 10:14:10 +02:00
Thomas Kuschan
e43d85b801 API Docs: Remove id from Tag in Response 2023-06-26 10:13:02 +02:00
Dan Brown
bae0e80cee Merge pull request #4320 from devdot/improve-api-auth-exception
Improve ApiAuthException control flow
2023-06-25 23:35:19 +01:00
Dan Brown
847a57a49a Shelf permissions: Removed unused 'create' permission from view
Was causing confusion.
Added test to cover.
Also added migration to remove existing create entries to pre-emptively
avoid issues in future if 'create' is used again.
2023-06-25 23:22:49 +01:00
Dan Brown
c74a2608c4 Updated php dependencies 2023-06-24 11:32:54 +01:00
Dan Brown
dbb6c87580 Mail Config: Updated how TLS is configured
After full review of current MAIL_ENCRYPTION usage in laravel and
smyfony mailer, this updates the options in BookStack to be simplified
and specific in usage:

- Removed mail.mailers.smtp.encryption option since it did not actually
  affect anything in the current state of dependancies.
- Updated MAIL_ENCRYPTION so values of tls OR ssl will force-enable tls
  via 'scheme' option with laravel passes to the SMTP transfport, which
  Smyfony uses as an indicator to force TLS.

When MAIL_ENCRYPTION is not used, STARTTLS will still be attempted by
symfony mailer.
Updated .env files to refer to BookStack docs (which was updated for
this) and to reflect correct default port.
Related to #4342
2023-06-24 11:32:07 +01:00
Dan Brown
9ae17efce9 Shelf view: Updated books to be database sorted
Fixes issue where sorting would not match other database-sorted parts of
app due to case sensitivity differences.
Added test to cover.

For #4341
2023-06-23 16:42:40 +01:00
Dan Brown
0a485baf8b Merge pull request #4332 from BookStackApp/api_docs_tweaks
API Docs: Allowed multi-paragraph descriptions
2023-06-20 23:47:58 +01:00
Dan Brown
38883e8d46 API Docs: Allowed multi-paragraph descriptions
Added support for mulit-line endpoint descriptions via blank
intermediate lines in php controller method docblocks.

Also tweaks endpoint header design for better flexing and alignment.
2023-06-20 23:44:39 +01:00
Dan Brown
4bb2cf5c5f Pages API: Added extra helper text to read endpoint 2023-06-20 17:15:32 +01:00
Dan Brown
8b935e71d1 Pages API: Made raw_html available on page responses
To provide a way to see the original un-pre-processed database HTML
content.

For #4310
2023-06-20 17:07:46 +01:00
Dan Brown
41c3ed154b Content Permissions API: Fixed param combination bug
Fixes issue where providing owner_id alongside certain
fallback_permissions would cause the owner change not to take affect,
due to bad variable shadowing.

For #4323
2023-06-20 14:13:26 +01:00
Dan Brown
f5396ecaf0 Merge pull request #4317 from devdot/http-fetch-improve-exception-logging
Modify HttpFetchException flow to log the exception
2023-06-20 13:49:23 +01:00
Thomas Kuschan
97d46f43a7 Revert some changes to HttpFetchException 2023-06-19 08:47:47 +02:00
Dan Brown
22fc720c22 Merge pull request #4318 from devdot/improve-json-debug-exception
Change JsonDebugException to Responsable interface
2023-06-18 17:52:57 +01:00
Dan Brown
eb44748084 Merge branch 'development' of github.com:BookStackApp/BookStack into development 2023-06-17 18:22:01 +01:00
Dan Brown
00b5dd7852 Users API: Fixed incorrect created_at date on index endpoint
For #4325
2023-06-17 18:18:17 +01:00
Dan Brown
9f4450fea9 Merge pull request #4322 from BookStackApp/comments_in_editor
Added read-only comments listing into page editor
2023-06-16 13:23:40 +01:00
Dan Brown
88aae5b004 Comments: Fixed failing tests due to unset template variable 2023-06-16 13:17:11 +01:00
Dan Brown
9a2ef7ef44 Comments: Added read-only listing into page editor 2023-06-16 13:08:04 +01:00
Thomas Kuschan
74097bd47c Simplify ApiAuthException control flow
Remove unnecessary UnauthorizedException
and make ApiAuthException compatible with HttpExceptionInterface.

Move the creation of a rsponse for the exception
from ApiAuthenticate middleware into the application exception handler.
2023-06-16 10:00:02 +02:00
Thomas Kuschan
7249d947ec Change JsonDebugException to Responsable interface
In all other exceptions, when a Response is supposed to be returned,
the Responsable interface is used instead of render.
2023-06-16 09:53:12 +02:00
Thomas Kuschan
c35080d6ce Modify HttpFetchException handle to log exception
Within the flow of HttpFetchException, the actual exception from curl is preserved and logged. Make HttpFetchException a pretty exception for when it is shown to users.
2023-06-16 09:21:25 +02:00
Dan Brown
ec775aec02 Merge branch 'fix-api-404' into development 2023-06-15 17:08:51 +01:00
Dan Brown
e72cf61f7e Exceptions: Added some types, simplified some classes
During review of #4291
2023-06-15 17:07:40 +01:00
Jean-René Rouet
bb3ce845b4 Merge branch 'BookStackApp:development' into add-priority 2023-06-15 16:55:14 +02:00
Dan Brown
70be2e8c9e CSS: Reduced styles used in export formats
Extracted many main page content styles to own scss partial.
Styles could do with a more general clean-up.

Closes #4303
2023-06-14 13:19:29 +01:00
Dan Brown
610ad0d613 Updated fonts to be defined via CSS variables
Exports system remains separate due to lacking css variable support.
2023-06-14 12:53:48 +01:00
Thomas Kuschan
34d8268b2b Refactor notify exception to clean up api exception handling 2023-06-14 11:08:20 +02:00
Thomas Kuschan
321a459421 Refactor exception handling by using interface 2023-06-13 18:52:02 +02:00
Dan Brown
56a40f1b23 Merge pull request #4301 from BookStackApp/css_color_variables
CSS: Updated status colors to be CSS variables, Added dark variants
2023-06-13 15:54:27 +01:00
Dan Brown
f7ad387a10 CSS: Updated status colors to be CSS variables, Added dark variants
Needed some level of harcoding though due to callouts using colors,
which can't be css colors as DOMPDF won't understand these.
Use css variables elsewhere and added new dark variants to fit a bit
better.
2023-06-13 15:52:33 +01:00
Dan Brown
b01bbf9c89 Page Drafts: Added new "Delete Draft" action to draft menu
Provides a way for users to actually delte their user drafts where
required.
For #3927

Added test to cover new endpoint.

Makes update to MD editor #setText so that new selection is within new
range, otherwise it errors and fails operation.
2023-06-13 15:13:07 +01:00
Dan Brown
f39938c4e3 Added activity text for each activity type
Ensures some sensible text is always in webhook text data.
Also aligned some notification reporting to use centralised activity
system instead of custom success events.

For #4216
2023-06-12 16:47:36 +01:00
Jean-René ROUET
458cea3644 [API] add priority in book read
[API] add priority in chapter create and update
[API] add priority in page create and update
2023-06-12 15:12:46 +02:00
Dan Brown
af0b4fa851 Search: Updated popular items query, load parent book for chapters/pages
Primarily intended to show parent book for chapters when moving/copying
pages, since the default parent selector interfaces, which used the
entity-selector search endpoint, would run this popular query when no
term was present as a default backup.

For #4264
2023-06-10 15:08:07 +01:00
Dan Brown
777027bc48 Permissions: Updated guest user handling so additional roles apply
Previously additional roles would only partially apply (system or "all"
permissions). This aligns the query-handling of permissions so that
additional roles will be used for permission queries.

Adds migration to detach existing roles as a safety precaution since
this is likely to widen permissions in scenarios that the public user
has other roles assigned already.

For #1229
2023-06-10 11:37:01 +01:00
Dan Brown
1e220c473f API: Fixed misaligned image datetime format
For #4294
2023-06-10 10:54:56 +01:00
Dan Brown
59c7077fd9 Fixed error on pages without comments 2023-06-09 19:21:49 +01:00
Dan Brown
07de6ecdc5 Merge pull request #4286 from BookStackApp/comment_threads
Comment threads
2023-06-09 17:39:02 +01:00
Dan Brown
19e39ddd1f Comments: Updated reply-to and general styling
Updated reply inidicator to fit with new nesting system, only showing on
view when nest within nesting structure.

Updated the general design to be a bit cleaner and better adapt on
mobile.

Tested on FF+Chrome, inc. dark mode.
2023-06-09 17:36:30 +01:00
Dan Brown
3bede42121 Comments: Added visual nesting limit, added nesting test 2023-06-09 11:12:39 +01:00
Dan Brown
3b46b92bb9 Comments: Updated to show form in expected location
Includes a change of create response to use a branch as a template.
2023-06-08 15:09:54 +01:00
Thomas Kuschan
9ba7d1e6c5 Fix "HTTP 500 on not found" bug #4290 2023-06-08 10:50:12 +02:00
Thomas Kuschan
ecf99fa0ed Add test showing the "HTTP 500 on not found" bug 2023-06-08 09:53:53 +02:00
Dan Brown
154924cc0c Comments: updated component and split out code
Split out comment component code so single-comment actions (delete, edit) are handled within their own compontent.
Modernised existing component code.
2023-06-07 17:47:37 +01:00
Dan Brown
4b9f6beb37 Comments: Updated to show as nested threads
Initial functional implementation, a lot of tweaking and adapting to be
done.
2023-06-07 13:24:49 +01:00
Dan Brown
88785aa71b Page display pointer: Considerably improved accessibility
- Updated pointer to move within content DOM so that you can back-focus
  into the pointer if desired.
- Added new "Section select mode" which toggles focusabiltiy for main
  content sections, with ability to show pointer via enter press on
  these.
- Updated pointer with proper input/button labelling.

Tested via orca screen reader on Firefox/Fedora/Gnome.
For #3975
2023-05-31 16:44:20 +01:00
Dan Brown
0323ebccd3 Chapters API: Allowed move via book_id property
Aligns it with pages and with the book_id property already being part of
the API.
For #4272.
2023-05-30 20:55:24 +01:00
Dan Brown
3f5dc10cd4 Altered ldap_connect usage, cleaned up LDAP classes
Primarily updated ldap_connect to avoid usage of deprecated syntax.
Updated tests and service to handle as expected.
Cleaned up syntax and types in classes while there.

Closes #4274
2023-05-30 13:12:00 +01:00
Dan Brown
242d23788d Merge pull request #4265 from BookStackApp/image_manager_responsive
Enhanced Responsive Image Manager
2023-05-29 16:52:55 +01:00
Dan Brown
08c73f02c9 Removed forced initial image manager display 2023-05-29 16:23:37 +01:00
Dan Brown
a139c2a8a2 Image manager: Improved screen reader usage
Added extra labels, or removed duplicate info, to improve screen reader
ux after testing via gnome/fedora/firefox screen reader usage testing.
2023-05-29 16:21:44 +01:00
Dan Brown
f5ef52ca59 Image manager: cleaned up style changes, dark mode support
- Updated tab handling to be smarter on initial tab selection, to first
  target non-hidden tab panels where they may be handled server-side.
- Extracted contained search box handling styles to _forms.scss, after
  merging with image-manager-specific styles since this is only usage of
  contained variant.
- Aligned focus handling on image manager UI elements.
2023-05-29 15:50:36 +01:00
Dan Brown
948e95e1ad Updated test to align with image manager HTML changes 2023-05-29 15:16:16 +01:00
Dan Brown
cd4b612019 Image update API: added update image file ability 2023-05-29 15:06:17 +01:00
Dan Brown
f78c0635ee Fixed bad /api docs redirection on sub path
Direct route redirect does not seem to go via standard URL generator so
misses off generation via base URL.
2023-05-29 14:41:59 +01:00
Dan Brown
e3c4a9d167 Added the ability to replace existing image files
- Updated UI with image form dropdown containing delete and replace
  image actions.
- Adds new endpoint and service/repo handling for replacing existing
  image.
- Includes tests to cover.
2023-05-28 17:32:22 +01:00
Dan Brown
9ff7c97911 Image manager: Added extra detail below image edit form 2023-05-28 12:07:19 +01:00
Dan Brown
89d6d862fa Image manager: extracted lang text, updated anims and search cancel
- Updated search cancel to only show when a search is active.
- Updated gallery image load animation to be much faster.
2023-05-28 11:37:49 +01:00
Dan Brown
946c9ae804 Image manager: supported a tabbed interface on mobile
Makes interface relatively usable now on mobile sizes.
Required updating of tab handling to support tabs being active at only
mobile screen sizes, include change on resize, upon support for
potentially nested tab usage.
Tab component will now search within sensible depths for finding its own
tabs and panels to control.
2023-05-27 16:58:10 +01:00
Dan Brown
dc6133c4c4 Image manager: added ability to trigger load more via scroll 2023-05-26 18:05:29 +01:00
Dan Brown
6c91e09c73 Image manager: Redesigned header bar(s) 2023-05-26 14:30:59 +01:00
Dan Brown
e467324658 Updated image manager to use grid-based css 2023-05-24 17:07:32 +01:00
Dan Brown
4c726201f9 Merge pull request #4262 from BookStackApp/command_cleanup
Command cleanup & alignment
2023-05-24 13:22:25 +01:00
Dan Brown
431aeefdda Updated command classes to include "Command" in name 2023-05-24 13:21:46 +01:00
Dan Brown
c0620da9f8 Aligned command class code
- Aligned usage of injecting through handler.
- Aligned handler return type.
- Aligned argument and arg desc format.
- Aligned lack of constructor.
2023-05-24 12:59:50 +01:00
Dan Brown
0704f1bd0d Covered untested commands with testing 2023-05-24 10:34:43 +01:00
Dan Brown
3b31ac75ec Merge pull request #4247 from BookStackApp/controller_cleanup
Revised `app/` folder layout/structure
2023-05-24 09:12:49 +01:00
Dan Brown
df6326e5ab Fixed failing references after controller/file reshuffle 2023-05-24 09:06:15 +01:00
Dan Brown
4ac8ecad6b Updated version and assets for release v23.05.2 2023-05-23 12:36:46 +01:00
Dan Brown
903e88c700 Merge branch 'development' into release 2023-05-23 12:36:29 +01:00
Dan Brown
c0d5e158d7 Updated translation attribution before v23.05.2 2023-05-23 12:32:39 +01:00
Dan Brown
99377d43c1 Updated php deps 2023-05-22 20:52:50 +01:00
Dan Brown
ebb1942fb8 Updated translations with latest Crowdin changes (#4239) 2023-05-22 20:51:22 +01:00
Dan Brown
152f7f3ad0 Merge pull request #4252 from BookStackApp/cli_update_2
Updated System CLI
2023-05-22 20:45:32 +01:00
Dan Brown
8a03442b5b Merge pull request #4254 from BookStackApp/code_active_line
Updated code view block line highlighting to only show on focus
2023-05-22 20:44:05 +01:00
Dan Brown
e591f4896e Allowed attachment drag via main text link
Enables easier sorting and dragging of box into content.
Related to #591
2023-05-22 20:23:19 +01:00
Dan Brown
6a7bc68b61 Allowed button-based multi-file uploads
Likely something that worked via dropzone before.
This adds support for our custom dropzone file handling.
Related to #4241
2023-05-22 14:20:20 +01:00
Dan Brown
924f517217 Updated code view block line highlighting to only show on focus
The default 1st line highlighting confused users when existing on
read-only blocks as it was not clear this represented the active line.
This changes the highlight to only show when the block is focused upon.
2023-05-22 14:05:07 +01:00
Dan Brown
150b40edc1 Updated System CLI
- Fixed a range of additional issues involving symlinks.
- Fixed incorrect app locating relative to system cli.
2023-05-22 10:28:12 +01:00
Dan Brown
141eecb858 Cleaned up namespacing in routes
Also moved home controller and moved controllers up a level in http.
2023-05-18 20:57:05 +01:00
Dan Brown
295cd01605 Played around with a new app structure 2023-05-17 17:56:55 +01:00
Dan Brown
ed96aa820e Updated version and assets for release v23.05.1 2023-05-08 16:05:50 +01:00
Dan Brown
63ec079b7b Merge branch 'development' into release 2023-05-08 16:04:51 +01:00
Dan Brown
c17906c758 Updated translator attribution before release v23.05.1 2023-05-08 16:04:02 +01:00
Dan Brown
62d5701578 Merge pull request #4229 from BookStackApp/cli-update
Updated system CLI
2023-05-08 15:21:04 +01:00
Dan Brown
9f1a6947ab Updated system CLI
- Fixed wrong env details being used on restore.
- Updated update-url on restore actually work.
- Added better support for symlinked locations.
- Added warning against updating in docker-like (non git controlled)
  environments.
2023-05-08 15:16:30 +01:00
Dan Brown
ae90776927 Updated translations with latest Crowdin changes (#4211) 2023-05-08 14:49:01 +01:00
Dan Brown
4489f65371 Fixed code block line-number bar showing in exports
Also fixed in print view.
Likely crept in during CM6 changes.

For #4215
2023-05-08 14:45:45 +01:00
Dan Brown
ee1e047964 Updated php deps, formatted command changes 2023-05-08 14:37:01 +01:00
Dan Brown
8846f7d255 Prevented shorcuts activating when in codemirror areas
For #4227
2023-05-08 14:28:03 +01:00
Dan Brown
2523cee0e2 WYSWIYG code blocks: copied head styles into shadow root
Currently only link-based styles are made available in the shadow root
code editor environment, this adds normal styles to apply any user-added
via custom head content.

Fixes #4228
2023-05-08 12:21:53 +01:00
Dan Brown
b5cc0a8e38 Fixed added padding around hr tags in details blocks
Due to manual handling & wrapping of non-block content in details block
not taking hr elements into account.
For #3963
2023-05-08 12:01:52 +01:00
Dan Brown
3bcbf6b9c5 Added WYSWIYG editor code editor cancel focus return
Focus now returns to the editor properly when you quit out the code
editor without saving.
This also sets the return location to be correct on normal saving (Would
sometimes jump to the end of the document).

For #4109.
2023-05-07 19:36:10 +01:00
Dan Brown
573bc3ec45 Added force option for update-url command
Includes test to cover.
Closes #4223
2023-05-06 23:05:25 +01:00
1026 changed files with 27818 additions and 9949 deletions

View File

@@ -37,8 +37,10 @@ MAIL_FROM=bookstack@example.com
# SMTP mail options
# These settings can be checked using the "Send a Test Email"
# feature found in the "Settings > Maintenance" area of the system.
# For more detailed documentation on mail options, refer to:
# https://www.bookstackapp.com/docs/admin/email-webhooks/#email-configuration
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_PORT=587
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

View File

@@ -69,23 +69,19 @@ DB_PASSWORD=database_user_password
# certificate itself (Common Name or Subject Alternative Name).
MYSQL_ATTR_SSL_CA="/path/to/ca.pem"
# Mail system to use
# Can be 'smtp' or 'sendmail'
# Mail configuration
# Refer to https://www.bookstackapp.com/docs/admin/email-webhooks/#email-configuration
MAIL_DRIVER=smtp
# Mail sending options
MAIL_FROM=mail@bookstackapp.com
MAIL_FROM=bookstack@example.com
MAIL_FROM_NAME=BookStack
# SMTP mail options
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_PORT=587
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_VERIFY_SSL=true
# Command to use when email is sent via sendmail
MAIL_SENDMAIL_COMMAND="/usr/sbin/sendmail -bs"
# Cache & Session driver to use
@@ -363,6 +359,15 @@ ALLOWED_IFRAME_HOSTS=null
# Current host and source for the "DRAWIO" setting will be auto-appended to the sources configured.
ALLOWED_IFRAME_SOURCES="https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com"
# A list of the sources/hostnames that can be reached by application SSR calls.
# This is used wherever users can provide URLs/hosts in-platform, like for webhooks.
# Host-specific functionality (usually controlled via other options) like auth
# or user avatars for example, won't use this list.
# Space seperated if multiple. Can use '*' as a wildcard.
# Values will be compared prefix-matched, case-insensitive, against called SSR urls.
# Defaults to allow all hosts.
ALLOWED_SSR_HOSTS="*"
# The default and maximum item-counts for listing API requests.
API_DEFAULT_ITEM_COUNT=100
API_MAX_ITEM_COUNT=500

View File

@@ -1,7 +1,14 @@
name: Bug Report
description: Create a report to help us improve or fix things
description: Create a report to help us fix bugs & issues in existing supported functionality
labels: [":bug: Bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out a bug report!
Please note that this form is for reporting bugs in existing supported functionality.
If you are reporting something that's not an issue in functionality we've previously supported and/or is simply something different to your expectations, then it may be more appropriate to raise via a feature or support request instead.
- type: textarea
id: description
attributes:
@@ -13,7 +20,7 @@ body:
id: reproduction
attributes:
label: Steps to Reproduce
description: Detail the steps that would replicate this issue
description: Detail the steps that would replicate this issue.
placeholder: |
1. Go to '...'
2. Click on '....'
@@ -32,7 +39,7 @@ body:
id: context
attributes:
label: Screenshots or Additional Context
description: Provide any additional context and screenshots here to help us solve this issue
description: Provide any additional context and screenshots here to help us solve this issue.
validations:
required: false
- type: input
@@ -48,23 +55,7 @@ body:
id: bsversion
attributes:
label: Exact BookStack Version
description: This can be found in the settings view of BookStack. Please provide an exact version.
placeholder: (eg. v21.08.5)
validations:
required: true
- type: input
id: phpversion
attributes:
label: PHP Version
description: Keep in mind your command-line PHP version may differ to that of your webserver. Provide that relevant to the issue.
placeholder: (eg. 7.4)
validations:
required: false
- type: textarea
id: hosting
attributes:
label: Hosting Environment
description: Describe your hosting environment as much as possible including any proxies used (If applicable).
placeholder: (eg. Ubuntu 20.04 VPS, installed using official installation script)
description: This can be found in the settings view of BookStack. Please provide an exact version(s) you've tested on.
placeholder: (eg. v23.06.7)
validations:
required: true

View File

@@ -33,9 +33,9 @@ body:
attributes:
label: Have you searched for an existing open/closed issue?
description: |
To help us keep these issues under control, please ensure you have first [searched our issue list](https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue) for any existing issues that cover the fundemental benefit/goal of your request.
To help us keep these issues under control, please ensure you have first [searched our issue list](https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue) for any existing issues that cover the fundamental benefit/goal of your request.
options:
- label: I have searched for existing issues and none cover my fundemental request
- label: I have searched for existing issues and none cover my fundamental request
required: true
- type: dropdown
id: existing_usage
@@ -43,8 +43,8 @@ body:
label: How long have you been using BookStack?
options:
- Not using yet, just scoping
- 0 to 6 months
- 6 months to 1 year
- Under 3 months
- 3 months to 1 year
- 1 to 5 years
- Over 5 years
validations:

View File

@@ -33,7 +33,7 @@ body:
attributes:
label: Exact BookStack Version
description: This can be found in the settings view of BookStack. Please provide an exact version.
placeholder: (eg. v21.08.5)
placeholder: (eg. v23.06.7)
validations:
required: true
- type: textarea
@@ -44,19 +44,11 @@ body:
placeholder: Be sure to remove any confidential details in your logs
validations:
required: false
- type: input
id: phpversion
attributes:
label: PHP Version
description: Keep in mind your command-line PHP version may differ to that of your webserver. Provide that most relevant to the issue.
placeholder: (eg. 7.4)
validations:
required: false
- type: textarea
id: hosting
attributes:
label: Hosting Environment
description: Describe your hosting environment as much as possible including any proxies used (If applicable).
placeholder: (eg. Ubuntu 20.04 VPS, installed using official installation script)
placeholder: (eg. PHP8.1 on Ubuntu 22.04 VPS, installed using official installation script)
validations:
required: true

15
.github/SECURITY.md vendored
View File

@@ -15,18 +15,13 @@ If you'd like to be notified of new potential security concerns you can [sign-up
If you've found an issue that likely has no impact to existing users (For example, in a development-only branch)
feel free to raise it via a standard GitHub bug report issue.
If the issue could have a security impact to BookStack instances, please use one of the below
methods to report the vulnerability:
- Directly contact the lead maintainer [@ssddanbrown](https://github.com/ssddanbrown).
- You will need to login to be able to see the email address on the [GitHub profile page](https://github.com/ssddanbrown).
- Alternatively you can send a DM via Twitter to [@ssddanbrown](https://twitter.com/ssddanbrown).
- [Disclose via huntr.dev](https://huntr.dev/bounties/disclose)
- Bounties may be available to you through this platform.
- Be sure to use `https://github.com/BookStackApp/BookStack` as the repository URL.
If the issue could have a security impact to BookStack instances,
please directly contact the lead maintainer [@ssddanbrown](https://github.com/ssddanbrown).
You will need to log in to be able to see the email address on the [GitHub profile page](https://github.com/ssddanbrown).
Alternatively you can send a DM via Mastodon to [@danb@fosstodon.org](https://fosstodon.org/@danb).
Please be patient while the vulnerability is being reviewed. Deploying the fix to address the vulnerability
can often take a little time due to the amount of preparation required, to ensure the vulnerability has
been covered, and to create the content required to adequately notify the user-base.
Thank you for keeping BookStack instances safe!
Thank you for keeping BookStack instances safe!

View File

@@ -57,6 +57,7 @@ Name :: Languages
@Jokuna :: Korean
@smartshogu :: German; German Informal
@samadha56 :: Persian
@mrmuminov :: Uzbek
cipi1965 :: Italian
Mykola Ronik (Mantikor) :: Ukrainian
furkanoyk :: Turkish
@@ -176,7 +177,7 @@ Alexander Predl (Harveyhase68) :: German
Rem (Rem9000) :: Dutch
Michał Stelmach (stelmach-web) :: Polish
arniom :: French
REMOVED_USER :: ; French; Dutch; Turkish
REMOVED_USER :: French; Dutch; Turkish;
林祖年 (contagion) :: Chinese Traditional
Siamak Guodarzi (siamakgoudarzi88) :: Persian
Lis Maestrelo (lismtrl) :: Portuguese, Brazilian
@@ -269,7 +270,7 @@ mcgong (GongMingCai) :: Chinese Simplified; Chinese Traditional
Nanang Setia Budi (sefidananang) :: Indonesian
Андрей Павлов (andrei.pavlov) :: Russian
Alex Navarro (alex.n.navarro) :: Portuguese, Brazilian
Ji-Hyeon Gim (PotatoGim) :: Korean
Jihyeon Gim (PotatoGim) :: Korean
Mihai Ochian (soulstorm19) :: Romanian
HeartCore :: German Informal; German
simon.pct :: French
@@ -289,7 +290,7 @@ Ismael Mesquita (mesquitoliveira) :: Portuguese, Brazilian
LiZerui (CNLiZerui) :: Chinese Traditional
Fabrice Boyer (FabriceBoyer) :: French
mikael (bitcanon) :: Swedish
Matthias Mai (schnapsidee) :: German; German Informal
Matthias Mai (schnapsidee) :: German Informal; German
Ufuk Ayyıldız (ufukayyildiz) :: Turkish
Jan Mitrof (jan.kachlik) :: Czech
edwardsmirnov :: Russian
@@ -327,3 +328,42 @@ H.-H. Peng (Hsins) :: Chinese Traditional
Mosi Wang (mosiwang) :: Chinese Traditional
骆言 (LawssssCat) :: Chinese Simplified
Stickers Gaming Shøw (StickerSGSHOW) :: French
Le Van Chinh (Chino) (lvanchinh86) :: Vietnamese
Rubens nagios (rubenix) :: Catalan
Patrick Dantas (pa-tiq) :: Portuguese, Brazilian
Michal (michalgurcik) :: Slovak
Nepomacs :: German
Rubens (rubenix) :: Catalan
m4z :: German; German Informal
TheRazvy :: Romanian
Yossi Zilber (lortens) :: Hebrew; Uzbek
desdinova :: French
Ingus Rūķis (ingus.rukis) :: Latvian
Eugene Pershin (SilentEugene) :: Russian
周盛道 (zhoushengdao) :: Chinese Simplified
hamidreza amini (hamidrezaamini2022) :: Persian
Tomislav Kraljević (tomislav.kraljevic) :: Croatian
Taygun Yıldırım (yildirimtaygun) :: Turkish
robing29 :: German
Bruno Eduardo de Jesus Barroso (brunoejb) :: Portuguese, Brazilian
Igor V Belousov (biv) :: Russian
David Bauer (davbauer) :: German
Guttorm Hveem (guttormhveem) :: Norwegian Bokmal; Norwegian Nynorsk
Minh Giang Truong (minhgiang1204) :: Vietnamese
Ioannis Ioannides (i.ioannides) :: Greek
Vadim (vadrozh) :: Russian
Flip333 :: German Informal; German
Paulo Henrique (paulohsantos114) :: Portuguese, Brazilian
Dženan (Dzenan) :: Swedish
Péter Péli (peter.peli) :: Hungarian
TWME :: Chinese Traditional
Sascha (Man-in-Black) :: German
Mohammadreza Madadi (madadi.efl) :: Persian
Konstantin Kovacheli (kkovacheli) :: Ukrainian
link1183 :: French
Renan (rfpe) :: Portuguese, Brazilian
Lowkey (bbsweb) :: Chinese Simplified
ZZnOB (zznobzz) :: Russian
rupus :: Swedish
developernecsys :: Norwegian Nynorsk
xuan LI (xuanli233) :: Chinese Simplified

View File

@@ -1,6 +1,12 @@
name: analyse-php
on: [push, pull_request]
on:
push:
paths:
- '**.php'
pull_request:
paths:
- '**.php'
jobs:
build:

View File

@@ -1,6 +1,14 @@
name: lint-js
on: [push, pull_request]
on:
push:
paths:
- '**.js'
- '**.json'
pull_request:
paths:
- '**.js'
- '**.json'
jobs:
build:

View File

@@ -1,6 +1,12 @@
name: lint-php
on: [push, pull_request]
on:
push:
paths:
- '**.php'
pull_request:
paths:
- '**.php'
jobs:
build:

View File

@@ -1,6 +1,14 @@
name: test-migrations
on: [push, pull_request]
on:
push:
paths:
- '**.php'
- 'composer.*'
pull_request:
paths:
- '**.php'
- 'composer.*'
jobs:
build:

View File

@@ -1,6 +1,14 @@
name: test-php
on: [push, pull_request]
on:
push:
paths:
- '**.php'
- 'composer.*'
pull_request:
paths:
- '**.php'
- 'composer.*'
jobs:
build:

View File

@@ -1,14 +1,14 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\EmailConfirmationService;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\UserRepo;
use BookStack\Access\EmailConfirmationService;
use BookStack\Access\LoginService;
use BookStack\Exceptions\ConfirmationEmailException;
use BookStack\Exceptions\UserTokenExpiredException;
use BookStack\Exceptions\UserTokenNotFoundException;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use BookStack\Users\UserRepo;
use Exception;
use Illuminate\Http\Request;

View File

@@ -1,9 +1,9 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Actions\ActivityType;
use BookStack\Http\Controllers\Controller;
use BookStack\Activity\ActivityType;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;

View File

@@ -1,10 +1,10 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\User;
use BookStack\Access\LoginService;
use BookStack\Exceptions\NotFoundException;
use BookStack\Users\Models\User;
trait HandlesPartialLogins
{

View File

@@ -1,13 +1,13 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Access\LoginService;
use BookStack\Access\SocialAuthService;
use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Facades\Activity;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

View File

@@ -1,14 +1,14 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\Mfa\BackupCodeService;
use BookStack\Auth\Access\Mfa\MfaSession;
use BookStack\Auth\Access\Mfa\MfaValue;
use BookStack\Access\LoginService;
use BookStack\Access\Mfa\BackupCodeService;
use BookStack\Access\Mfa\MfaSession;
use BookStack\Access\Mfa\MfaValue;
use BookStack\Activity\ActivityType;
use BookStack\Exceptions\NotFoundException;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

View File

@@ -1,10 +1,10 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\Mfa\MfaValue;
use BookStack\Http\Controllers\Controller;
use BookStack\Access\Mfa\MfaValue;
use BookStack\Activity\ActivityType;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
class MfaController extends Controller

View File

@@ -1,15 +1,15 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\Mfa\MfaSession;
use BookStack\Auth\Access\Mfa\MfaValue;
use BookStack\Auth\Access\Mfa\TotpService;
use BookStack\Auth\Access\Mfa\TotpValidationRule;
use BookStack\Access\LoginService;
use BookStack\Access\Mfa\MfaSession;
use BookStack\Access\Mfa\MfaValue;
use BookStack\Access\Mfa\TotpService;
use BookStack\Access\Mfa\TotpValidationRule;
use BookStack\Activity\ActivityType;
use BookStack\Exceptions\NotFoundException;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

View File

@@ -1,10 +1,10 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\Oidc\OidcException;
use BookStack\Auth\Access\Oidc\OidcService;
use BookStack\Http\Controllers\Controller;
use BookStack\Access\Oidc\OidcException;
use BookStack\Access\Oidc\OidcService;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
class OidcController extends Controller

View File

@@ -1,13 +1,13 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Access\LoginService;
use BookStack\Access\RegistrationService;
use BookStack\Access\SocialAuthService;
use BookStack\Exceptions\StoppedAuthenticationException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use Illuminate\Contracts\Validation\Validator as ValidatorContract;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

View File

@@ -1,11 +1,11 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\User;
use BookStack\Http\Controllers\Controller;
use BookStack\Access\LoginService;
use BookStack\Activity\ActivityType;
use BookStack\Http\Controller;
use BookStack\Users\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

View File

@@ -1,9 +1,9 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\Saml2Service;
use BookStack\Http\Controllers\Controller;
use BookStack\Access\Saml2Service;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

View File

@@ -1,37 +1,27 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Access\LoginService;
use BookStack\Access\RegistrationService;
use BookStack\Access\SocialAuthService;
use BookStack\Exceptions\SocialDriverNotConfigured;
use BookStack\Exceptions\SocialSignInAccountNotUsed;
use BookStack\Exceptions\SocialSignInException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\User as SocialUser;
class SocialController extends Controller
{
protected SocialAuthService $socialAuthService;
protected RegistrationService $registrationService;
protected LoginService $loginService;
/**
* SocialController constructor.
*/
public function __construct(
SocialAuthService $socialAuthService,
RegistrationService $registrationService,
LoginService $loginService
protected SocialAuthService $socialAuthService,
protected RegistrationService $registrationService,
protected LoginService $loginService,
) {
$this->middleware('guest')->only(['register']);
$this->socialAuthService = $socialAuthService;
$this->registrationService = $registrationService;
$this->loginService = $loginService;
}
/**
@@ -112,7 +102,7 @@ class SocialController extends Controller
$this->socialAuthService->detachSocialAccount($socialDriver);
session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => Str::title($socialDriver)]));
return redirect(user()->getEditUrl());
return redirect('/my-account/auth#social-accounts');
}
/**

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use Illuminate\Cache\RateLimiter;
use Illuminate\Http\Request;
@@ -71,7 +71,7 @@ trait ThrottlesLogins
*/
protected function limiter(): RateLimiter
{
return app(RateLimiter::class);
return app()->make(RateLimiter::class);
}
/**

View File

@@ -1,12 +1,12 @@
<?php
namespace BookStack\Http\Controllers\Auth;
namespace BookStack\Access\Controllers;
use BookStack\Auth\Access\UserInviteService;
use BookStack\Auth\UserRepo;
use BookStack\Access\UserInviteService;
use BookStack\Exceptions\UserTokenExpiredException;
use BookStack\Exceptions\UserTokenNotFoundException;
use BookStack\Http\Controllers\Controller;
use BookStack\Http\Controller;
use BookStack\Users\UserRepo;
use Exception;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

View File

@@ -1,10 +1,10 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\User;
use BookStack\Access\Notifications\ConfirmEmailNotification;
use BookStack\Exceptions\ConfirmationEmailException;
use BookStack\Notifications\ConfirmEmail;
use BookStack\Users\Models\User;
class EmailConfirmationService extends UserTokenService
{
@@ -26,7 +26,7 @@ class EmailConfirmationService extends UserTokenService
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
$user->notify(new ConfirmEmail($token));
$user->notify(new ConfirmEmailNotification($token));
}
/**

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;

View File

@@ -1,9 +1,9 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\Role;
use BookStack\Auth\User;
use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
use Illuminate\Support\Collection;
class GroupSyncService

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Guards;
namespace BookStack\Access\Guards;
/**
* Saml2 Session Guard.

View File

@@ -1,8 +1,8 @@
<?php
namespace BookStack\Auth\Access\Guards;
namespace BookStack\Access\Guards;
use BookStack\Auth\Access\RegistrationService;
use BookStack\Access\RegistrationService;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\StatefulGuard;

View File

@@ -1,15 +1,15 @@
<?php
namespace BookStack\Auth\Access\Guards;
namespace BookStack\Access\Guards;
use BookStack\Auth\Access\LdapService;
use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\User;
use BookStack\Access\LdapService;
use BookStack\Access\RegistrationService;
use BookStack\Exceptions\JsonDebugException;
use BookStack\Exceptions\LdapException;
use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Users\Models\User;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Str;

110
app/Access/Ldap.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
namespace BookStack\Access;
/**
* Class Ldap
* An object-orientated thin abstraction wrapper for common PHP LDAP functions.
* Allows the standard LDAP functions to be mocked for testing.
*/
class Ldap
{
/**
* Connect to an LDAP server.
*
* @return resource|\LDAP\Connection|false
*/
public function connect(string $hostName)
{
return ldap_connect($hostName);
}
/**
* Set the value of an LDAP option for the given connection.
*
* @param resource|\LDAP\Connection|null $ldapConnection
*/
public function setOption($ldapConnection, int $option, mixed $value): bool
{
return ldap_set_option($ldapConnection, $option, $value);
}
/**
* Start TLS on the given LDAP connection.
*/
public function startTls($ldapConnection): bool
{
return ldap_start_tls($ldapConnection);
}
/**
* Set the version number for the given LDAP connection.
*
* @param resource|\LDAP\Connection $ldapConnection
*/
public function setVersion($ldapConnection, int $version): bool
{
return $this->setOption($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, $version);
}
/**
* Search LDAP tree using the provided filter.
*
* @param resource|\LDAP\Connection $ldapConnection
*
* @return resource|\LDAP\Result
*/
public function search($ldapConnection, string $baseDn, string $filter, array $attributes = null)
{
return ldap_search($ldapConnection, $baseDn, $filter, $attributes);
}
/**
* Get entries from an LDAP search result.
*
* @param resource|\LDAP\Connection $ldapConnection
* @param resource|\LDAP\Result $ldapSearchResult
*/
public function getEntries($ldapConnection, $ldapSearchResult): array|false
{
return ldap_get_entries($ldapConnection, $ldapSearchResult);
}
/**
* Search and get entries immediately.
*
* @param resource|\LDAP\Connection $ldapConnection
*/
public function searchAndGetEntries($ldapConnection, string $baseDn, string $filter, array $attributes = null): array|false
{
$search = $this->search($ldapConnection, $baseDn, $filter, $attributes);
return $this->getEntries($ldapConnection, $search);
}
/**
* Bind to LDAP directory.
*
* @param resource|\LDAP\Connection $ldapConnection
*/
public function bind($ldapConnection, string $bindRdn = null, string $bindPassword = null): bool
{
return ldap_bind($ldapConnection, $bindRdn, $bindPassword);
}
/**
* Explode an LDAP dn string into an array of components.
*/
public function explodeDn(string $dn, int $withAttrib): array|false
{
return ldap_explode_dn($dn, $withAttrib);
}
/**
* Escape a string for use in an LDAP filter.
*/
public function escape(string $value, string $ignore = '', int $flags = 0): string
{
return ldap_escape($value, $ignore, $flags);
}
}

View File

@@ -1,11 +1,11 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\User;
use BookStack\Exceptions\JsonDebugException;
use BookStack\Exceptions\LdapException;
use BookStack\Uploads\UserAvatars;
use BookStack\Users\Models\User;
use ErrorException;
use Illuminate\Support\Facades\Log;
@@ -15,26 +15,19 @@ use Illuminate\Support\Facades\Log;
*/
class LdapService
{
protected Ldap $ldap;
protected GroupSyncService $groupSyncService;
protected UserAvatars $userAvatars;
/**
* @var resource
* @var resource|\LDAP\Connection
*/
protected $ldapConnection;
protected array $config;
protected bool $enabled;
/**
* LdapService constructor.
*/
public function __construct(Ldap $ldap, UserAvatars $userAvatars, GroupSyncService $groupSyncService)
{
$this->ldap = $ldap;
$this->userAvatars = $userAvatars;
$this->groupSyncService = $groupSyncService;
public function __construct(
protected Ldap $ldap,
protected UserAvatars $userAvatars,
protected GroupSyncService $groupSyncService
) {
$this->config = config('services.ldap');
$this->enabled = config('auth.method') === 'ldap';
}
@@ -59,7 +52,7 @@ class LdapService
// Clean attributes
foreach ($attributes as $index => $attribute) {
if (strpos($attribute, 'BIN;') === 0) {
if (str_starts_with($attribute, 'BIN;')) {
$attributes[$index] = substr($attribute, strlen('BIN;'));
}
}
@@ -82,7 +75,7 @@ class LdapService
* Get the details of a user from LDAP using the given username.
* User found via configurable user filter.
*
* @throws LdapException
* @throws LdapException|JsonDebugException
*/
public function getUserDetails(string $userName): ?array
{
@@ -126,7 +119,7 @@ class LdapService
*/
protected function getUserResponseProperty(array $userDetails, string $propertyKey, $defaultValue)
{
$isBinary = strpos($propertyKey, 'BIN;') === 0;
$isBinary = str_starts_with($propertyKey, 'BIN;');
$propertyKey = strtolower($propertyKey);
$value = $defaultValue;
@@ -170,11 +163,11 @@ class LdapService
* Bind the system user to the LDAP connection using the given credentials
* otherwise anonymous access is attempted.
*
* @param resource $connection
* @param resource|\LDAP\Connection $connection
*
* @throws LdapException
*/
protected function bindSystemUser($connection)
protected function bindSystemUser($connection): void
{
$ldapDn = $this->config['dn'];
$ldapPass = $this->config['pass'];
@@ -197,7 +190,7 @@ class LdapService
*
* @throws LdapException
*
* @return resource
* @return resource|\LDAP\Connection
*/
protected function getConnection()
{
@@ -216,8 +209,8 @@ class LdapService
$this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
}
$serverDetails = $this->parseServerString($this->config['server']);
$ldapConnection = $this->ldap->connect($serverDetails['host'], $serverDetails['port']);
$ldapHost = $this->parseServerString($this->config['server']);
$ldapConnection = $this->ldap->connect($ldapHost);
if ($ldapConnection === false) {
throw new LdapException(trans('errors.ldap_cannot_connect'));
@@ -242,23 +235,16 @@ class LdapService
}
/**
* Parse a LDAP server string and return the host and port for a connection.
* Parse an LDAP server string and return the host suitable for a connection.
* Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'.
*/
protected function parseServerString(string $serverString): array
protected function parseServerString(string $serverString): string
{
$serverNameParts = explode(':', $serverString);
// If we have a protocol just return the full string since PHP will ignore a separate port.
if ($serverNameParts[0] === 'ldaps' || $serverNameParts[0] === 'ldap') {
return ['host' => $serverString, 'port' => 389];
if (str_starts_with($serverString, 'ldaps://') || str_starts_with($serverString, 'ldap://')) {
return $serverString;
}
// Otherwise, extract the port out
$hostName = $serverNameParts[0];
$ldapPort = (count($serverNameParts) > 1) ? intval($serverNameParts[1]) : 389;
return ['host' => $hostName, 'port' => $ldapPort];
return "ldap://{$serverString}";
}
/**
@@ -386,7 +372,7 @@ class LdapService
* @throws LdapException
* @throws JsonDebugException
*/
public function syncGroups(User $user, string $username)
public function syncGroups(User $user, string $username): void
{
$userLdapGroups = $this->getUserGroups($username);
$this->groupSyncService->syncUserWithFoundGroups($user, $userLdapGroups, $this->config['remove_from_groups']);

View File

@@ -1,15 +1,15 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\Mfa\MfaSession;
use BookStack\Auth\User;
use BookStack\Access\Mfa\MfaSession;
use BookStack\Activity\ActivityType;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Exceptions\StoppedAuthenticationException;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
use Exception;
class LoginService

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Mfa;
namespace BookStack\Access\Mfa;
use Illuminate\Support\Str;

View File

@@ -1,8 +1,8 @@
<?php
namespace BookStack\Auth\Access\Mfa;
namespace BookStack\Access\Mfa;
use BookStack\Auth\User;
use BookStack\Users\Models\User;
class MfaSession
{

View File

@@ -1,8 +1,8 @@
<?php
namespace BookStack\Auth\Access\Mfa;
namespace BookStack\Access\Mfa;
use BookStack\Auth\User;
use BookStack\Users\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Mfa;
namespace BookStack\Access\Mfa;
use BaconQrCode\Renderer\Color\Rgb;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
@@ -8,7 +8,7 @@ use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\Fill;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use BookStack\Auth\User;
use BookStack\Users\Models\User;
use PragmaRX\Google2FA\Google2FA;
use PragmaRX\Google2FA\Support\Constants;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Mfa;
namespace BookStack\Access\Mfa;
use Illuminate\Contracts\Validation\Rule;

View File

@@ -0,0 +1,26 @@
<?php
namespace BookStack\Access\Notifications;
use BookStack\App\MailNotification;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class ConfirmEmailNotification extends MailNotification
{
public function __construct(
public string $token
) {
}
public function toMail(User $notifiable): MailMessage
{
$appName = ['appName' => setting('app-name')];
return $this->newMailMessage()
->subject(trans('auth.email_confirm_subject', $appName))
->greeting(trans('auth.email_confirm_greeting', $appName))
->line(trans('auth.email_confirm_text'))
->action(trans('auth.email_confirm_action'), url('/register/confirm/' . $this->token));
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace BookStack\Access\Notifications;
use BookStack\App\MailNotification;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class ResetPasswordNotification extends MailNotification
{
public function __construct(
public string $token
) {
}
public function toMail(User $notifiable): MailMessage
{
return $this->newMailMessage()
->subject(trans('auth.email_reset_subject', ['appName' => setting('app-name')]))
->line(trans('auth.email_reset_text'))
->action(trans('auth.reset_password'), url('password/reset/' . $this->token))
->line(trans('auth.email_reset_not_requested'));
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace BookStack\Access\Notifications;
use BookStack\App\MailNotification;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class UserInviteNotification extends MailNotification
{
public function __construct(
public string $token
) {
}
public function toMail(User $notifiable): MailMessage
{
$appName = ['appName' => setting('app-name')];
$locale = $notifiable->getLocale();
return $this->newMailMessage($locale)
->subject($locale->trans('auth.user_invite_email_subject', $appName))
->greeting($locale->trans('auth.user_invite_email_greeting', $appName))
->line($locale->trans('auth.user_invite_email_text'))
->action($locale->trans('auth.user_invite_email_action'), url('/register/invite/' . $this->token));
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use InvalidArgumentException;
use League\OAuth2\Client\Token\AccessToken;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use Exception;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
class OidcIdToken
{

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
class OidcInvalidKeyException extends \Exception
{

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use Exception;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use Exception;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use phpseclib3\Crypt\Common\PublicKey;
use phpseclib3\Crypt\PublicKeyLoader;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use League\OAuth2\Client\Grant\AbstractGrant;
use League\OAuth2\Client\Provider\AbstractProvider;
@@ -20,15 +20,8 @@ class OidcOAuthProvider extends AbstractProvider
{
use BearerAuthorizationTrait;
/**
* @var string
*/
protected $authorizationEndpoint;
/**
* @var string
*/
protected $tokenEndpoint;
protected string $authorizationEndpoint;
protected string $tokenEndpoint;
/**
* Scopes to use for the OIDC authorization call.
@@ -60,7 +53,7 @@ class OidcOAuthProvider extends AbstractProvider
}
/**
* Add an additional scope to this provider upon the default.
* Add another scope to this provider upon the default.
*/
public function addScope(string $scope): void
{

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use GuzzleHttp\Psr7\Request;
use Illuminate\Contracts\Cache\Repository;
@@ -59,7 +59,7 @@ class OidcProviderSettings
}
}
if (strpos($this->issuer, 'https://') !== 0) {
if (!str_starts_with($this->issuer, 'https://')) {
throw new InvalidArgumentException('Issuer value must start with https://');
}
}

View File

@@ -1,21 +1,21 @@
<?php
namespace BookStack\Auth\Access\Oidc;
namespace BookStack\Access\Oidc;
use BookStack\Auth\Access\GroupSyncService;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\User;
use BookStack\Access\GroupSyncService;
use BookStack\Access\LoginService;
use BookStack\Access\RegistrationService;
use BookStack\Exceptions\JsonDebugException;
use BookStack\Exceptions\StoppedAuthenticationException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Theme;
use BookStack\Http\HttpRequestService;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Psr\Http\Client\ClientInterface as HttpClient;
/**
* Class OpenIdConnectService
@@ -26,7 +26,7 @@ class OidcService
public function __construct(
protected RegistrationService $registrationService,
protected LoginService $loginService,
protected HttpClient $httpClient,
protected HttpRequestService $http,
protected GroupSyncService $groupService
) {
}
@@ -94,7 +94,7 @@ class OidcService
// Run discovery
if ($config['discover'] ?? false) {
try {
$settings->discoverFromIssuer($this->httpClient, Cache::store(null), 15);
$settings->discoverFromIssuer($this->http->buildClient(5), Cache::store(null), 15);
} catch (OidcIssuerDiscoveryException $exception) {
throw new OidcException('OIDC Discovery Error: ' . $exception->getMessage());
}
@@ -111,7 +111,7 @@ class OidcService
protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider
{
$provider = new OidcOAuthProvider($settings->arrayForProvider(), [
'httpClient' => $this->httpClient,
'httpClient' => $this->http->buildClient(5),
'optionProvider' => new HttpBasicAuthOptionProvider(),
]);
@@ -142,10 +142,11 @@ class OidcService
*/
protected function getUserDisplayName(OidcIdToken $token, string $defaultValue): string
{
$displayNameAttr = $this->config()['display_name_claims'];
$displayNameAttrString = $this->config()['display_name_claims'] ?? '';
$displayNameAttrs = explode('|', $displayNameAttrString);
$displayName = [];
foreach ($displayNameAttr as $dnAttr) {
foreach ($displayNameAttrs as $dnAttr) {
$dnComponent = $token->getClaim($dnAttr) ?? '';
if ($dnComponent !== '') {
$displayName[] = $dnComponent;

View File

@@ -1,15 +1,14 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Actions\ActivityType;
use BookStack\Auth\SocialAccount;
use BookStack\Auth\User;
use BookStack\Auth\UserRepo;
use BookStack\Activity\ActivityType;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
use BookStack\Users\UserRepo;
use Exception;
use Illuminate\Support\Str;

View File

@@ -1,12 +1,12 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\User;
use BookStack\Exceptions\JsonDebugException;
use BookStack\Exceptions\SamlException;
use BookStack\Exceptions\StoppedAuthenticationException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Users\Models\User;
use Exception;
use OneLogin\Saml2\Auth;
use OneLogin\Saml2\Constants;

View File

@@ -1,9 +1,10 @@
<?php
namespace BookStack\Auth;
namespace BookStack\Access;
use BookStack\Interfaces\Loggable;
use BookStack\Model;
use BookStack\Activity\Models\Loggable;
use BookStack\App\Model;
use BookStack\Users\Models\User;
/**
* Class SocialAccount.

View File

@@ -1,12 +1,12 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\SocialAccount;
use BookStack\Auth\User;
use BookStack\Auth\Access\handler;
use BookStack\Exceptions\SocialDriverNotConfigured;
use BookStack\Exceptions\SocialSignInAccountNotUsed;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Users\Models\User;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\Factory as Socialite;
@@ -154,21 +154,21 @@ class SocialAuthService
$currentUser->socialAccounts()->save($account);
session()->flash('success', trans('settings.users_social_connected', ['socialAccount' => $titleCaseDriver]));
return redirect($currentUser->getEditUrl());
return redirect('/my-account/auth#social_accounts');
}
// When a user is logged in and the social account exists and is already linked to the current user.
if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) {
session()->flash('error', trans('errors.social_account_existing', ['socialAccount' => $titleCaseDriver]));
return redirect($currentUser->getEditUrl());
return redirect('/my-account/auth#social_accounts');
}
// When a user is logged in, A social account exists but the users do not match.
if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) {
session()->flash('error', trans('errors.social_account_already_used_existing', ['socialAccount' => $titleCaseDriver]));
return redirect($currentUser->getEditUrl());
return redirect('/my-account/auth#social_accounts');
}
// Otherwise let the user know this social account is not used by anyone.
@@ -214,6 +214,7 @@ class SocialAuthService
/**
* Gets the names of the active social drivers.
* @returns array<string, string>
*/
public function getActiveDrivers(): array
{

View File

@@ -1,9 +1,9 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\User;
use BookStack\Notifications\UserInvite;
use BookStack\Access\Notifications\UserInviteNotification;
use BookStack\Users\Models\User;
class UserInviteService extends UserTokenService
{
@@ -18,6 +18,6 @@ class UserInviteService extends UserTokenService
{
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
$user->notify(new UserInvite($token));
$user->notify(new UserInviteNotification($token));
}
}

View File

@@ -1,10 +1,10 @@
<?php
namespace BookStack\Auth\Access;
namespace BookStack\Access;
use BookStack\Auth\User;
use BookStack\Exceptions\UserTokenExpiredException;
use BookStack\Exceptions\UserTokenNotFoundException;
use BookStack\Users\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

View File

@@ -1,60 +0,0 @@
<?php
namespace BookStack\Actions;
use BookStack\Model;
use BookStack\Traits\HasCreatorAndUpdater;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property int $id
* @property string $text
* @property string $html
* @property int|null $parent_id
* @property int $local_id
*/
class Comment extends Model
{
use HasFactory;
use HasCreatorAndUpdater;
protected $fillable = ['text', 'parent_id'];
protected $appends = ['created', 'updated'];
/**
* Get the entity that this comment belongs to.
*/
public function entity(): MorphTo
{
return $this->morphTo('entity');
}
/**
* Check if a comment has been updated since creation.
*/
public function isUpdated(): bool
{
return $this->updated_at->timestamp > $this->created_at->timestamp;
}
/**
* Get created date as a relative diff.
*
* @return mixed
*/
public function getCreatedAttribute()
{
return $this->created_at->diffForHumans();
}
/**
* Get updated date as a relative diff.
*
* @return mixed
*/
public function getUpdatedAttribute()
{
return $this->updated_at->diffForHumans();
}
}

View File

@@ -1,82 +0,0 @@
<?php
namespace BookStack\Actions;
use BookStack\Auth\User;
use BookStack\Facades\Theme;
use BookStack\Interfaces\Loggable;
use BookStack\Theming\ThemeEvents;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class DispatchWebhookJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected Webhook $webhook;
protected string $event;
protected User $initiator;
protected int $initiatedTime;
/**
* @var string|Loggable
*/
protected $detail;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Webhook $webhook, string $event, $detail)
{
$this->webhook = $webhook;
$this->event = $event;
$this->detail = $detail;
$this->initiator = user();
$this->initiatedTime = time();
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$themeResponse = Theme::dispatch(ThemeEvents::WEBHOOK_CALL_BEFORE, $this->event, $this->webhook, $this->detail, $this->initiator, $this->initiatedTime);
$webhookData = $themeResponse ?? WebhookFormatter::getDefault($this->event, $this->webhook, $this->detail, $this->initiator, $this->initiatedTime)->format();
$lastError = null;
try {
$response = Http::asJson()
->withOptions(['allow_redirects' => ['strict' => true]])
->timeout($this->webhook->timeout)
->post($this->webhook->endpoint, $webhookData);
} catch (\Exception $exception) {
$lastError = $exception->getMessage();
Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with error \"{$lastError}\"");
}
if (isset($response) && $response->failed()) {
$lastError = "Response status from endpoint was {$response->status()}";
Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with status {$response->status()}");
}
$this->webhook->last_called_at = now();
if ($lastError) {
$this->webhook->last_errored_at = now();
$this->webhook->last_error = $lastError;
}
$this->webhook->save();
}
}

View File

@@ -1,13 +1,14 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity;
use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Auth\User;
use BookStack\Activity\Models\Activity;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Users\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity;
class ActivityType
{
@@ -27,6 +27,10 @@ class ActivityType
const BOOKSHELF_DELETE = 'bookshelf_delete';
const COMMENTED_ON = 'commented_on';
const COMMENT_CREATE = 'comment_create';
const COMMENT_UPDATE = 'comment_update';
const COMMENT_DELETE = 'comment_delete';
const PERMISSIONS_UPDATE = 'permissions_update';
const REVISION_RESTORE = 'revision_restore';

View File

@@ -1,32 +1,20 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity;
use BookStack\Activity\Models\Comment;
use BookStack\Entities\Models\Entity;
use BookStack\Facades\Activity as ActivityService;
use League\CommonMark\CommonMarkConverter;
/**
* Class CommentRepo.
*/
class CommentRepo
{
/**
* @var Comment
*/
protected $comment;
public function __construct(Comment $comment)
{
$this->comment = $comment;
}
/**
* Get a comment by ID.
*/
public function getById(int $id): Comment
{
return $this->comment->newQuery()->findOrFail($id);
return Comment::query()->findOrFail($id);
}
/**
@@ -35,7 +23,7 @@ class CommentRepo
public function create(Entity $entity, string $text, ?int $parent_id): Comment
{
$userId = user()->id;
$comment = $this->comment->newInstance();
$comment = new Comment();
$comment->text = $text;
$comment->html = $this->commentToHtml($text);
@@ -45,6 +33,7 @@ class CommentRepo
$comment->parent_id = $parent_id;
$entity->comments()->save($comment);
ActivityService::add(ActivityType::COMMENT_CREATE, $comment);
ActivityService::add(ActivityType::COMMENTED_ON, $entity);
return $comment;
@@ -60,6 +49,8 @@ class CommentRepo
$comment->html = $this->commentToHtml($text);
$comment->save();
ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
return $comment;
}
@@ -69,6 +60,8 @@ class CommentRepo
public function delete(Comment $comment): void
{
$comment->delete();
ActivityService::add(ActivityType::COMMENT_DELETE, $comment);
}
/**
@@ -82,7 +75,7 @@ class CommentRepo
'allow_unsafe_links' => false,
]);
return $converter->convertToHtml($commentText);
return $converter->convert($commentText);
}
/**
@@ -90,9 +83,8 @@ class CommentRepo
*/
protected function getNextLocalId(Entity $entity): int
{
/** @var Comment $comment */
$comment = $entity->comments(false)->orderBy('local_id', 'desc')->first();
$currentMaxId = $entity->comments()->max('local_id');
return ($comment->local_id ?? 0) + 1;
return $currentMaxId + 1;
}
}

View File

@@ -1,12 +1,12 @@
<?php
namespace BookStack\Http\Controllers;
namespace BookStack\Activity\Controllers;
use BookStack\Actions\Activity;
use BookStack\Actions\ActivityType;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\Activity;
use BookStack\Http\Controller;
use BookStack\Util\SimpleListOptions;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AuditLogController extends Controller
{

View File

@@ -1,19 +1,18 @@
<?php
namespace BookStack\Http\Controllers;
namespace BookStack\Activity\Controllers;
use BookStack\Actions\CommentRepo;
use BookStack\Activity\CommentRepo;
use BookStack\Entities\Models\Page;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class CommentController extends Controller
{
protected $commentRepo;
public function __construct(CommentRepo $commentRepo)
{
$this->commentRepo = $commentRepo;
public function __construct(
protected CommentRepo $commentRepo
) {
}
/**
@@ -42,7 +41,13 @@ class CommentController extends Controller
$this->checkPermission('comment-create-all');
$comment = $this->commentRepo->create($page, $request->get('text'), $request->get('parent_id'));
return view('comments.comment', ['comment' => $comment]);
return view('comments.comment-branch', [
'readOnly' => false,
'branch' => [
'comment' => $comment,
'children' => [],
]
]);
}
/**
@@ -62,7 +67,7 @@ class CommentController extends Controller
$comment = $this->commentRepo->update($comment, $request->get('text'));
return view('comments.comment', ['comment' => $comment]);
return view('comments.comment', ['comment' => $comment, 'readOnly' => false]);
}
/**

View File

@@ -1,15 +1,22 @@
<?php
namespace BookStack\Http\Controllers;
namespace BookStack\Activity\Controllers;
use BookStack\Activity\Models\Favouritable;
use BookStack\App\Model;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Queries\TopFavourites;
use BookStack\Interfaces\Favouritable;
use BookStack\Model;
use BookStack\Entities\Tools\MixedEntityRequestHelper;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
class FavouriteController extends Controller
{
public function __construct(
protected MixedEntityRequestHelper $entityHelper,
) {
}
/**
* Show a listing of all favourite items for the current user.
*/
@@ -35,13 +42,14 @@ class FavouriteController extends Controller
*/
public function add(Request $request)
{
$favouritable = $this->getValidatedModelFromRequest($request);
$favouritable->favourites()->firstOrCreate([
$modelInfo = $this->validate($request, $this->entityHelper->validationRules());
$entity = $this->entityHelper->getVisibleEntityFromRequestData($modelInfo);
$entity->favourites()->firstOrCreate([
'user_id' => user()->id,
]);
$this->showSuccessNotification(trans('activities.favourite_add_notification', [
'name' => $favouritable->name,
'name' => $entity->name,
]));
return redirect()->back();
@@ -52,48 +60,16 @@ class FavouriteController extends Controller
*/
public function remove(Request $request)
{
$favouritable = $this->getValidatedModelFromRequest($request);
$favouritable->favourites()->where([
$modelInfo = $this->validate($request, $this->entityHelper->validationRules());
$entity = $this->entityHelper->getVisibleEntityFromRequestData($modelInfo);
$entity->favourites()->where([
'user_id' => user()->id,
])->delete();
$this->showSuccessNotification(trans('activities.favourite_remove_notification', [
'name' => $favouritable->name,
'name' => $entity->name,
]));
return redirect()->back();
}
/**
* @throws \Illuminate\Validation\ValidationException
* @throws \Exception
*/
protected function getValidatedModelFromRequest(Request $request): Entity
{
$modelInfo = $this->validate($request, [
'type' => ['required', 'string'],
'id' => ['required', 'integer'],
]);
if (!class_exists($modelInfo['type'])) {
throw new \Exception('Model not found');
}
/** @var Model $model */
$model = new $modelInfo['type']();
if (!$model instanceof Favouritable) {
throw new \Exception('Model not favouritable');
}
$modelInstance = $model->newQuery()
->where('id', '=', $modelInfo['id'])
->first(['id', 'name', 'owned_by']);
$inaccessibleEntity = ($modelInstance instanceof Entity && !userCan('view', $modelInstance));
if (is_null($modelInstance) || $inaccessibleEntity) {
throw new \Exception('Model instance not found');
}
return $modelInstance;
}
}

View File

@@ -1,8 +1,9 @@
<?php
namespace BookStack\Http\Controllers;
namespace BookStack\Activity\Controllers;
use BookStack\Actions\TagRepo;
use BookStack\Activity\TagRepo;
use BookStack\Http\Controller;
use BookStack\Util\SimpleListOptions;
use Illuminate\Http\Request;

View File

@@ -0,0 +1,29 @@
<?php
namespace BookStack\Activity\Controllers;
use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Tools\MixedEntityRequestHelper;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
class WatchController extends Controller
{
public function update(Request $request, MixedEntityRequestHelper $entityHelper)
{
$this->checkPermission('receive-notifications');
$this->preventGuestAccess();
$requestData = $this->validate($request, array_merge([
'level' => ['required', 'string'],
], $entityHelper->validationRules()));
$watchable = $entityHelper->getVisibleEntityFromRequestData($requestData);
$watchOptions = new UserEntityWatchOptions(user(), $watchable);
$watchOptions->updateLevelByName($requestData['level']);
$this->showSuccessNotification(trans('activities.watch_update_level_notification'));
return redirect()->back();
}
}

View File

@@ -1,10 +1,11 @@
<?php
namespace BookStack\Http\Controllers;
namespace BookStack\Activity\Controllers;
use BookStack\Actions\ActivityType;
use BookStack\Actions\Queries\WebhooksAllPaginatedAndSorted;
use BookStack\Actions\Webhook;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\Webhook;
use BookStack\Activity\Queries\WebhooksAllPaginatedAndSorted;
use BookStack\Http\Controller;
use BookStack\Util\SimpleListOptions;
use Illuminate\Http\Request;

View File

@@ -0,0 +1,84 @@
<?php
namespace BookStack\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Models\Webhook;
use BookStack\Activity\Tools\WebhookFormatter;
use BookStack\Facades\Theme;
use BookStack\Http\HttpRequestService;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
use BookStack\Util\SsrUrlValidator;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class DispatchWebhookJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected Webhook $webhook;
protected User $initiator;
protected int $initiatedTime;
protected array $webhookData;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Webhook $webhook, string $event, Loggable|string $detail)
{
$this->webhook = $webhook;
$this->initiator = user();
$this->initiatedTime = time();
$themeResponse = Theme::dispatch(ThemeEvents::WEBHOOK_CALL_BEFORE, $event, $this->webhook, $detail, $this->initiator, $this->initiatedTime);
$this->webhookData = $themeResponse ?? WebhookFormatter::getDefault($event, $this->webhook, $detail, $this->initiator, $this->initiatedTime)->format();
}
/**
* Execute the job.
*
* @return void
*/
public function handle(HttpRequestService $http)
{
$lastError = null;
try {
(new SsrUrlValidator())->ensureAllowed($this->webhook->endpoint);
$client = $http->buildClient($this->webhook->timeout, [
'connect_timeout' => 10,
'allow_redirects' => ['strict' => true],
]);
$response = $client->sendRequest($http->jsonRequest('POST', $this->webhook->endpoint, $this->webhookData));
$statusCode = $response->getStatusCode();
if ($statusCode >= 400) {
$lastError = "Response status from endpoint was {$statusCode}";
Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with status {$statusCode}");
}
} catch (\Exception $error) {
$lastError = $error->getMessage();
Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with error \"{$lastError}\"");
}
$this->webhook->last_called_at = now();
if ($lastError) {
$this->webhook->last_errored_at = now();
$this->webhook->last_error = $lastError;
}
$this->webhook->save();
}
}

View File

@@ -1,11 +1,11 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Models;
use BookStack\Auth\Permissions\JointPermission;
use BookStack\Auth\User;
use BookStack\App\Model;
use BookStack\Entities\Models\Entity;
use BookStack\Model;
use BookStack\Permissions\Models\JointPermission;
use BookStack\Users\Models\User;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
@@ -19,6 +19,8 @@ use Illuminate\Support\Str;
* @property string $entity_type
* @property int $entity_id
* @property int $user_id
* @property Carbon $created_at
* @property Carbon $updated_at
*/
class Activity extends Model
{

View File

@@ -0,0 +1,76 @@
<?php
namespace BookStack\Activity\Models;
use BookStack\App\Model;
use BookStack\Users\Models\HasCreatorAndUpdater;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property int $id
* @property string $text
* @property string $html
* @property int|null $parent_id - Relates to local_id, not id
* @property int $local_id
* @property string $entity_type
* @property int $entity_id
* @property int $created_by
* @property int $updated_by
*/
class Comment extends Model implements Loggable
{
use HasFactory;
use HasCreatorAndUpdater;
protected $fillable = ['text', 'parent_id'];
protected $appends = ['created', 'updated'];
/**
* Get the entity that this comment belongs to.
*/
public function entity(): MorphTo
{
return $this->morphTo('entity');
}
/**
* Get the parent comment this is in reply to (if existing).
*/
public function parent(): BelongsTo
{
return $this->belongsTo(Comment::class, 'parent_id', 'local_id', 'parent')
->where('entity_type', '=', $this->entity_type)
->where('entity_id', '=', $this->entity_id);
}
/**
* Check if a comment has been updated since creation.
*/
public function isUpdated(): bool
{
return $this->updated_at->timestamp > $this->created_at->timestamp;
}
/**
* Get created date as a relative diff.
*/
public function getCreatedAttribute(): string
{
return $this->created_at->diffForHumans();
}
/**
* Get updated date as a relative diff.
*/
public function getUpdatedAttribute(): string
{
return $this->updated_at->diffForHumans();
}
public function logDescriptor(): string
{
return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->entity_type} (ID: {$this->entity_id})";
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Interfaces;
namespace BookStack\Activity\Models;
use Illuminate\Database\Eloquent\Relations\MorphMany;

View File

@@ -1,9 +1,9 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Models;
use BookStack\Auth\Permissions\JointPermission;
use BookStack\Model;
use BookStack\App\Model;
use BookStack\Permissions\Models\JointPermission;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Interfaces;
namespace BookStack\Activity\Models;
interface Loggable
{

View File

@@ -1,9 +1,9 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Models;
use BookStack\Auth\Permissions\JointPermission;
use BookStack\Model;
use BookStack\App\Model;
use BookStack\Permissions\Models\JointPermission;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;

View File

@@ -1,10 +1,9 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Models;
use BookStack\Auth\Permissions\JointPermission;
use BookStack\Interfaces\Viewable;
use BookStack\Model;
use BookStack\App\Model;
use BookStack\Permissions\Models\JointPermission;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
@@ -42,7 +41,7 @@ class View extends Model
public static function incrementFor(Viewable $viewable): int
{
$user = user();
if (is_null($user) || $user->isDefault()) {
if ($user->isGuest()) {
return 0;
}
@@ -55,12 +54,4 @@ class View extends Model
return $view->views;
}
/**
* Clear all views from the system.
*/
public static function clearAll()
{
static::query()->truncate();
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Interfaces;
namespace BookStack\Activity\Models;
use Illuminate\Database\Eloquent\Relations\MorphMany;

View File

@@ -0,0 +1,45 @@
<?php
namespace BookStack\Activity\Models;
use BookStack\Activity\WatchLevels;
use BookStack\Permissions\Models\JointPermission;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property int $id
* @property int $user_id
* @property int $watchable_id
* @property string $watchable_type
* @property int $level
* @property Carbon $created_at
* @property Carbon $updated_at
*/
class Watch extends Model
{
protected $guarded = [];
public function watchable(): MorphTo
{
return $this->morphTo();
}
public function jointPermissions(): HasMany
{
return $this->hasMany(JointPermission::class, 'entity_id', 'watchable_id')
->whereColumn('watches.watchable_type', '=', 'joint_permissions.entity_type');
}
public function getLevelName(): string
{
return WatchLevels::levelValueToName($this->level);
}
public function ignoring(): bool
{
return $this->level === WatchLevels::IGNORE;
}
}

View File

@@ -1,8 +1,8 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Models;
use BookStack\Interfaces\Loggable;
use BookStack\Activity\ActivityType;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

View File

@@ -0,0 +1,42 @@
<?php
namespace BookStack\Activity\Notifications\Handlers;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Notifications\Messages\BaseActivityNotification;
use BookStack\Entities\Models\Entity;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Users\Models\User;
abstract class BaseNotificationHandler implements NotificationHandler
{
/**
* @param class-string<BaseActivityNotification> $notification
* @param int[] $userIds
*/
protected function sendNotificationToUserIds(string $notification, array $userIds, User $initiator, string|Loggable $detail, Entity $relatedModel): void
{
$users = User::query()->whereIn('id', array_unique($userIds))->get();
foreach ($users as $user) {
// Prevent sending to the user that initiated the activity
if ($user->id === $initiator->id) {
continue;
}
// Prevent sending of the user does not have notification permissions
if (!$user->can('receive-notifications')) {
continue;
}
// Prevent sending if the user does not have access to the related content
$permissions = new PermissionApplicator($user);
if (!$permissions->checkOwnableUserAccess($relatedModel, 'view')) {
continue;
}
// Send the notification
$user->notify(new $notification($detail, $initiator));
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace BookStack\Activity\Notifications\Handlers;
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Comment;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Notifications\Messages\CommentCreationNotification;
use BookStack\Activity\Tools\EntityWatchers;
use BookStack\Activity\WatchLevels;
use BookStack\Entities\Models\Page;
use BookStack\Settings\UserNotificationPreferences;
use BookStack\Users\Models\User;
class CommentCreationNotificationHandler extends BaseNotificationHandler
{
public function handle(Activity $activity, Loggable|string $detail, User $user): void
{
if (!($detail instanceof Comment)) {
throw new \InvalidArgumentException("Detail for comment creation notifications must be a comment");
}
// Main watchers
/** @var Page $page */
$page = $detail->entity;
$watchers = new EntityWatchers($page, WatchLevels::COMMENTS);
$watcherIds = $watchers->getWatcherUserIds();
// Page owner if user preferences allow
if (!$watchers->isUserIgnoring($page->owned_by) && $page->ownedBy) {
$userNotificationPrefs = new UserNotificationPreferences($page->ownedBy);
if ($userNotificationPrefs->notifyOnOwnPageComments()) {
$watcherIds[] = $page->owned_by;
}
}
// Parent comment creator if preferences allow
$parentComment = $detail->parent()->first();
if ($parentComment && !$watchers->isUserIgnoring($parentComment->created_by) && $parentComment->createdBy) {
$parentCommenterNotificationsPrefs = new UserNotificationPreferences($parentComment->createdBy);
if ($parentCommenterNotificationsPrefs->notifyOnCommentReplies()) {
$watcherIds[] = $parentComment->created_by;
}
}
$this->sendNotificationToUserIds(CommentCreationNotification::class, $watcherIds, $user, $detail, $page);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace BookStack\Activity\Notifications\Handlers;
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Users\Models\User;
interface NotificationHandler
{
/**
* Run this handler.
* Provides the activity, related activity detail/model
* along with the user that triggered the activity.
*/
public function handle(Activity $activity, string|Loggable $detail, User $user): void;
}

View File

@@ -0,0 +1,24 @@
<?php
namespace BookStack\Activity\Notifications\Handlers;
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Notifications\Messages\PageCreationNotification;
use BookStack\Activity\Tools\EntityWatchers;
use BookStack\Activity\WatchLevels;
use BookStack\Entities\Models\Page;
use BookStack\Users\Models\User;
class PageCreationNotificationHandler extends BaseNotificationHandler
{
public function handle(Activity $activity, Loggable|string $detail, User $user): void
{
if (!($detail instanceof Page)) {
throw new \InvalidArgumentException("Detail for page create notifications must be a page");
}
$watchers = new EntityWatchers($detail, WatchLevels::NEW);
$this->sendNotificationToUserIds(PageCreationNotification::class, $watchers->getWatcherUserIds(), $user, $detail, $detail);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace BookStack\Activity\Notifications\Handlers;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Notifications\Messages\PageUpdateNotification;
use BookStack\Activity\Tools\EntityWatchers;
use BookStack\Activity\WatchLevels;
use BookStack\Entities\Models\Page;
use BookStack\Settings\UserNotificationPreferences;
use BookStack\Users\Models\User;
class PageUpdateNotificationHandler extends BaseNotificationHandler
{
public function handle(Activity $activity, Loggable|string $detail, User $user): void
{
if (!($detail instanceof Page)) {
throw new \InvalidArgumentException("Detail for page update notifications must be a page");
}
// Get last update from activity
$lastUpdate = $detail->activity()
->where('type', '=', ActivityType::PAGE_UPDATE)
->where('id', '!=', $activity->id)
->latest('created_at')
->first();
// Return if the same user has already updated the page in the last 15 mins
if ($lastUpdate && $lastUpdate->user_id === $user->id) {
if ($lastUpdate->created_at->gt(now()->subMinutes(15))) {
return;
}
}
// Get active watchers
$watchers = new EntityWatchers($detail, WatchLevels::UPDATES);
$watcherIds = $watchers->getWatcherUserIds();
// Add page owner if preferences allow
if (!$watchers->isUserIgnoring($detail->owned_by) && $detail->ownedBy) {
$userNotificationPrefs = new UserNotificationPreferences($detail->ownedBy);
if ($userNotificationPrefs->notifyOnOwnPageChanges()) {
$watcherIds[] = $detail->owned_by;
}
}
$this->sendNotificationToUserIds(PageUpdateNotification::class, $watcherIds, $user, $detail, $detail);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace BookStack\Activity\Notifications\MessageParts;
use Illuminate\Contracts\Support\Htmlable;
use Stringable;
/**
* A line of text with linked text included, intended for use
* in MailMessages. The line should have a ':link' placeholder for
* where the link should be inserted within the line.
*/
class LinkedMailMessageLine implements Htmlable, Stringable
{
public function __construct(
protected string $url,
protected string $line,
protected string $linkText,
) {
}
public function toHtml(): string
{
$link = '<a href="' . e($this->url) . '">' . e($this->linkText) . '</a>';
return str_replace(':link', $link, e($this->line));
}
public function __toString(): string
{
$link = "{$this->linkText} ({$this->url})";
return str_replace(':link', $link, $this->line);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace BookStack\Activity\Notifications\MessageParts;
use Illuminate\Contracts\Support\Htmlable;
use Stringable;
/**
* A bullet point list of content, where the keys of the given list array
* are bolded header elements, and the values follow.
*/
class ListMessageLine implements Htmlable, Stringable
{
public function __construct(
protected array $list
) {
}
public function toHtml(): string
{
$list = [];
foreach ($this->list as $header => $content) {
$list[] = '<strong>' . e($header) . '</strong> ' . e($content);
}
return implode("<br>\n", $list);
}
public function __toString(): string
{
$list = [];
foreach ($this->list as $header => $content) {
$list[] = $header . ' ' . $content;
}
return implode("\n", $list);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace BookStack\Activity\Notifications\Messages;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Notifications\MessageParts\LinkedMailMessageLine;
use BookStack\App\MailNotification;
use BookStack\Translation\LocaleDefinition;
use BookStack\Users\Models\User;
use Illuminate\Bus\Queueable;
abstract class BaseActivityNotification extends MailNotification
{
use Queueable;
public function __construct(
protected Loggable|string $detail,
protected User $user,
) {
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'activity_detail' => $this->detail,
'activity_creator' => $this->user,
];
}
/**
* Build the common reason footer line used in mail messages.
*/
protected function buildReasonFooterLine(LocaleDefinition $locale): LinkedMailMessageLine
{
return new LinkedMailMessageLine(
url('/preferences/notifications'),
$locale->trans('notifications.footer_reason'),
$locale->trans('notifications.footer_reason_link'),
);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace BookStack\Activity\Notifications\Messages;
use BookStack\Activity\Models\Comment;
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
use BookStack\Entities\Models\Page;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class CommentCreationNotification extends BaseActivityNotification
{
public function toMail(User $notifiable): MailMessage
{
/** @var Comment $comment */
$comment = $this->detail;
/** @var Page $page */
$page = $comment->entity;
$locale = $notifiable->getLocale();
return $this->newMailMessage($locale)
->subject($locale->trans('notifications.new_comment_subject', ['pageName' => $page->getShortName()]))
->line($locale->trans('notifications.new_comment_intro', ['appName' => setting('app-name')]))
->line(new ListMessageLine([
$locale->trans('notifications.detail_page_name') => $page->name,
$locale->trans('notifications.detail_commenter') => $this->user->name,
$locale->trans('notifications.detail_comment') => strip_tags($comment->html),
]))
->action($locale->trans('notifications.action_view_comment'), $page->getUrl('#comment' . $comment->local_id))
->line($this->buildReasonFooterLine($locale));
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace BookStack\Activity\Notifications\Messages;
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
use BookStack\Entities\Models\Page;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class PageCreationNotification extends BaseActivityNotification
{
public function toMail(User $notifiable): MailMessage
{
/** @var Page $page */
$page = $this->detail;
$locale = $notifiable->getLocale();
return $this->newMailMessage($locale)
->subject($locale->trans('notifications.new_page_subject', ['pageName' => $page->getShortName()]))
->line($locale->trans('notifications.new_page_intro', ['appName' => setting('app-name')], $locale))
->line(new ListMessageLine([
$locale->trans('notifications.detail_page_name') => $page->name,
$locale->trans('notifications.detail_created_by') => $this->user->name,
]))
->action($locale->trans('notifications.action_view_page'), $page->getUrl())
->line($this->buildReasonFooterLine($locale));
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace BookStack\Activity\Notifications\Messages;
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
use BookStack\Entities\Models\Page;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class PageUpdateNotification extends BaseActivityNotification
{
public function toMail(User $notifiable): MailMessage
{
/** @var Page $page */
$page = $this->detail;
$locale = $notifiable->getLocale();
return $this->newMailMessage($locale)
->subject($locale->trans('notifications.updated_page_subject', ['pageName' => $page->getShortName()]))
->line($locale->trans('notifications.updated_page_intro', ['appName' => setting('app-name')]))
->line(new ListMessageLine([
$locale->trans('notifications.detail_page_name') => $page->name,
$locale->trans('notifications.detail_updated_by') => $this->user->name,
]))
->line($locale->trans('notifications.updated_page_debounce'))
->action($locale->trans('notifications.action_view_page'), $page->getUrl())
->line($this->buildReasonFooterLine($locale));
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace BookStack\Activity\Notifications;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Notifications\Handlers\CommentCreationNotificationHandler;
use BookStack\Activity\Notifications\Handlers\NotificationHandler;
use BookStack\Activity\Notifications\Handlers\PageCreationNotificationHandler;
use BookStack\Activity\Notifications\Handlers\PageUpdateNotificationHandler;
use BookStack\Users\Models\User;
class NotificationManager
{
/**
* @var class-string<NotificationHandler>[]
*/
protected array $handlers = [];
public function handle(Activity $activity, string|Loggable $detail, User $user): void
{
$activityType = $activity->type;
$handlersToRun = $this->handlers[$activityType] ?? [];
foreach ($handlersToRun as $handlerClass) {
/** @var NotificationHandler $handler */
$handler = new $handlerClass();
$handler->handle($activity, $detail, $user);
}
}
/**
* @param class-string<NotificationHandler> $handlerClass
*/
public function registerHandler(string $activityType, string $handlerClass): void
{
if (!isset($this->handlers[$activityType])) {
$this->handlers[$activityType] = [];
}
if (!in_array($handlerClass, $this->handlers[$activityType])) {
$this->handlers[$activityType][] = $handlerClass;
}
}
public function loadDefaultHandlers(): void
{
$this->registerHandler(ActivityType::PAGE_CREATE, PageCreationNotificationHandler::class);
$this->registerHandler(ActivityType::PAGE_UPDATE, PageUpdateNotificationHandler::class);
$this->registerHandler(ActivityType::COMMENT_CREATE, CommentCreationNotificationHandler::class);
}
}

View File

@@ -1,8 +1,8 @@
<?php
namespace BookStack\Actions\Queries;
namespace BookStack\Activity\Queries;
use BookStack\Actions\Webhook;
use BookStack\Activity\Models\Webhook;
use BookStack\Util\SimpleListOptions;
use Illuminate\Pagination\LengthAwarePaginator;

View File

@@ -1,9 +1,10 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity;
use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Activity\Models\Tag;
use BookStack\Entities\Models\Entity;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Util\SimpleListOptions;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;

View File

@@ -1,22 +1,30 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Tools;
use BookStack\Activity\DispatchWebhookJob;
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Models\Webhook;
use BookStack\Activity\Notifications\NotificationManager;
use BookStack\Entities\Models\Entity;
use BookStack\Facades\Theme;
use BookStack\Interfaces\Loggable;
use BookStack\Theming\ThemeEvents;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
class ActivityLogger
{
public function __construct(
protected NotificationManager $notifications
) {
$this->notifications->loadDefaultHandlers();
}
/**
* Add a generic activity event to the database.
*
* @param string|Loggable $detail
*/
public function add(string $type, $detail = '')
public function add(string $type, string|Loggable $detail = ''): void
{
$detailToStore = ($detail instanceof Loggable) ? $detail->logDescriptor() : $detail;
@@ -32,6 +40,7 @@ class ActivityLogger
$this->setNotification($type);
$this->dispatchWebhooks($type, $detail);
$this->notifications->handle($activity, $detail, user());
Theme::dispatch(ThemeEvents::ACTIVITY_LOGGED, $type, $detail);
}
@@ -52,7 +61,7 @@ class ActivityLogger
* and instead uses the 'extra' field with the entities name.
* Used when an entity is deleted.
*/
public function removeEntity(Entity $entity)
public function removeEntity(Entity $entity): void
{
$entity->activity()->update([
'detail' => $entity->name,
@@ -73,10 +82,7 @@ class ActivityLogger
}
}
/**
* @param string|Loggable $detail
*/
protected function dispatchWebhooks(string $type, $detail): void
protected function dispatchWebhooks(string $type, string|Loggable $detail): void
{
$webhooks = Webhook::query()
->whereHas('trackedEvents', function (Builder $query) use ($type) {
@@ -95,7 +101,7 @@ class ActivityLogger
* Log out a failed login attempt, Providing the given username
* as part of the message if the '%u' string is used.
*/
public function logFailedLogin(string $username)
public function logFailedLogin(string $username): void
{
$message = config('logging.failed_login.message');
if (!$message) {

View File

@@ -0,0 +1,102 @@
<?php
namespace BookStack\Activity\Tools;
use BookStack\Activity\Models\Comment;
use BookStack\Entities\Models\Page;
class CommentTree
{
/**
* The built nested tree structure array.
* @var array{comment: Comment, depth: int, children: array}[]
*/
protected array $tree;
protected array $comments;
public function __construct(
protected Page $page
) {
$this->comments = $this->loadComments();
$this->tree = $this->createTree($this->comments);
}
public function enabled(): bool
{
return !setting('app-disable-comments');
}
public function empty(): bool
{
return count($this->tree) === 0;
}
public function count(): int
{
return count($this->comments);
}
public function get(): array
{
return $this->tree;
}
/**
* @param Comment[] $comments
*/
protected function createTree(array $comments): array
{
$byId = [];
foreach ($comments as $comment) {
$byId[$comment->local_id] = $comment;
}
$childMap = [];
foreach ($comments as $comment) {
$parent = $comment->parent_id;
if (is_null($parent) || !isset($byId[$parent])) {
$parent = 0;
}
if (!isset($childMap[$parent])) {
$childMap[$parent] = [];
}
$childMap[$parent][] = $comment->local_id;
}
$tree = [];
foreach ($childMap[0] ?? [] as $childId) {
$tree[] = $this->createTreeForId($childId, 0, $byId, $childMap);
}
return $tree;
}
protected function createTreeForId(int $id, int $depth, array &$byId, array &$childMap): array
{
$childIds = $childMap[$id] ?? [];
$children = [];
foreach ($childIds as $childId) {
$children[] = $this->createTreeForId($childId, $depth + 1, $byId, $childMap);
}
return [
'comment' => $byId[$id],
'depth' => $depth,
'children' => $children,
];
}
protected function loadComments(): array
{
if (!$this->enabled()) {
return [];
}
return $this->page->comments()
->with('createdBy')
->get()
->all();
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace BookStack\Activity\Tools;
use BookStack\Activity\Models\Watch;
use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use Illuminate\Database\Eloquent\Builder;
class EntityWatchers
{
/**
* @var int[]
*/
protected array $watchers = [];
/**
* @var int[]
*/
protected array $ignorers = [];
public function __construct(
protected Entity $entity,
protected int $watchLevel,
) {
$this->build();
}
public function getWatcherUserIds(): array
{
return $this->watchers;
}
public function isUserIgnoring(int $userId): bool
{
return in_array($userId, $this->ignorers);
}
protected function build(): void
{
$watches = $this->getRelevantWatches();
// Sort before de-duping, so that the order looped below follows book -> chapter -> page ordering
usort($watches, function (Watch $watchA, Watch $watchB) {
$entityTypeDiff = $watchA->watchable_type <=> $watchB->watchable_type;
return $entityTypeDiff === 0 ? ($watchA->user_id <=> $watchB->user_id) : $entityTypeDiff;
});
// De-dupe by user id to get their most relevant level
$levelByUserId = [];
foreach ($watches as $watch) {
$levelByUserId[$watch->user_id] = $watch->level;
}
// Populate the class arrays
$this->watchers = array_keys(array_filter($levelByUserId, fn(int $level) => $level >= $this->watchLevel));
$this->ignorers = array_keys(array_filter($levelByUserId, fn(int $level) => $level === 0));
}
/**
* @return Watch[]
*/
protected function getRelevantWatches(): array
{
/** @var Entity[] $entitiesInvolved */
$entitiesInvolved = array_filter([
$this->entity,
$this->entity instanceof BookChild ? $this->entity->book : null,
$this->entity instanceof Page ? $this->entity->chapter : null,
]);
$query = Watch::query()->where(function (Builder $query) use ($entitiesInvolved) {
foreach ($entitiesInvolved as $entity) {
$query->orWhere(function (Builder $query) use ($entity) {
$query->where('watchable_type', '=', $entity->getMorphClass())
->where('watchable_id', '=', $entity->id);
});
}
});
return $query->get([
'level', 'watchable_id', 'watchable_type', 'user_id'
])->all();
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace BookStack\Actions;
namespace BookStack\Activity\Tools;
class IpFormatter
{

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