diff --git a/modules/webapp/src/main/elm/Comp/ShareManage.elm b/modules/webapp/src/main/elm/Comp/ShareManage.elm index 19bdce86..80f242be 100644 --- a/modules/webapp/src/main/elm/Comp/ShareManage.elm +++ b/modules/webapp/src/main/elm/Comp/ShareManage.elm @@ -382,7 +382,7 @@ viewForm texts settings flags model = , title = "Submit this form" , icon = "fa fa-save" , label = texts.basics.submit - , disabled = not isOwner + , disabled = not isOwner && not newShare , attrs = [ href "#" ] } , MB.SecondaryButton @@ -427,12 +427,12 @@ viewForm texts settings flags model = text m ] , div - [ classList [ ( "hidden", isOwner ) ] + [ classList [ ( "hidden", isOwner || newShare ) ] , class S.infoMessage ] [ text texts.notOwnerInfo ] - , div [ classList [ ( "hidden", not isOwner ) ] ] + , div [ classList [ ( "hidden", not isOwner && not newShare ) ] ] [ Html.map FormMsg (Comp.ShareForm.view texts.shareForm model.formModel) ] , B.loadingDimmer diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/View.elm b/modules/webapp/src/main/elm/Page/ShareDetail/View.elm index 3ef1f3ea..be394d93 100644 --- a/modules/webapp/src/main/elm/Page/ShareDetail/View.elm +++ b/modules/webapp/src/main/elm/Page/ShareDetail/View.elm @@ -36,7 +36,7 @@ viewSidebar : Texts -> Bool -> Flags -> UiSettings -> String -> String -> Model viewSidebar texts visible flags settings shareId itemId model = div [ id "sidebar" - , classList [ ( "hidden", not visible ) ] + , classList [ ( "hidden", not visible || model.viewMode /= ViewNormal ) ] , class S.sidebar ] [ div [ class "pt-2" ] diff --git a/website/shell.nix b/website/shell.nix index 356643cf..181b4ad1 100644 --- a/website/shell.nix +++ b/website/shell.nix @@ -1,8 +1,7 @@ let nixpkgs = builtins.fetchTarball { #url = "channel:nixos-21.05"; - url = "https://github.com/NixOS/nixpkgs/archive/2d6ab6c6b92f7aaf8bc53baba9754b9bfdce56f2.tar.gz"; - #sha256 = "0l975q132x08qvw73qj391dl6qk9a661my8njcg5sl5rcmna3bmj"; + url = "https://github.com/NixOS/nixpkgs/archive/e6badb26fc0d238fda2432c45b7dd4e782eb8200.tar.gz"; }; pkgs = import nixpkgs { }; in diff --git a/website/site/config.toml b/website/site/config.toml index 6efb0447..dd5decc2 100644 --- a/website/site/config.toml +++ b/website/site/config.toml @@ -4,14 +4,14 @@ base_url = "https://docspell.org" # Whether to automatically compile all Sass files in the sass directory compile_sass = true -[markdown] -# Whether to do syntax highlighting -# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola -highlight_code = true -highlight_theme = "gruvbox-dark" # Whether to build a search index to be used later on by a JavaScript library build_search_index = true +default_language = "en" + +[markdown] +highlight_code = true +highlight_theme = "gruvbox-dark" [link_checker] skip_prefixes = [ diff --git a/website/site/content/docs/api/intro.md b/website/site/content/docs/api/intro.md index cb232f53..4ed589d5 100644 --- a/website/site/content/docs/api/intro.md +++ b/website/site/content/docs/api/intro.md @@ -18,12 +18,13 @@ The "raw" `openapi.yml` specification file can be found The routes can be divided into protected, unprotected routes and admin routes. The unprotected, or open routes are at `/open/*` while the -protected routes are at `/sec/*` and admin routes are at `/admin/*`. -Open routes don't require authenticated access and can be used by any -user. The protected routes require an authenticated user. The admin -routes require a special http header with a value from the config -file. They are disabled by default, you need to specify a secret in -order to enable admin routes. +protected routes are at `/sec/*` and `/share/*`, while admin routes +are at `/admin/*`. Open routes don't require authenticated access and +can be used by any user. The protected routes require either an +authenticated user (for `/sec/*`) or a valid share id and possibly the +password (for `/share/*`). The admin routes require a special http +header with a value from the config file. They are disabled by +default, you need to specify a secret in order to enable admin routes. ## Authentication @@ -44,6 +45,16 @@ must be `docspell_auth` and a custom header must be named The admin route (see below) `/admin/user/resetPassword` can be used to reset a password of a user. +To authenticate for a share, the `/open/share/verify` endpoint must be +used. It expects the share id and possibly a password. If the share +requires a password, but it was not specified in the request, the +response indicates this. The request must then be replayed with the +correct password to retrieve a token. This token can then be used to +all `/share/*` endpoints to identify the share - analog to the +protected `/sec/*` routes. It can be either specfied as a cookie or +via the header `Docspell-Share-Auth`. + + ### OpenID Connect Docspell can be configured to be a relying party for OpenID Connect. diff --git a/website/site/content/docs/features/_index.md b/website/site/content/docs/features/_index.md index 03da2d7d..58e5ab79 100644 --- a/website/site/content/docs/features/_index.md +++ b/website/site/content/docs/features/_index.md @@ -44,6 +44,8 @@ description = "A list of features and limitations." upload files; these urls allow to be configured with metadata like tags, folder etc that are applied to all files uploaded through this url +- [Share](@/docs/webapp/share.md) documents via cryptic public links + (optionally protected by a password) - [Send documents via e-mail](@/docs/webapp/mailitem.md) - [E-Mail notification](@/docs/webapp/notifydueitems.md) for documents with due dates diff --git a/website/site/content/docs/tools/cli.md b/website/site/content/docs/tools/cli.md index 97a694bb..fcf3836c 100644 --- a/website/site/content/docs/tools/cli.md +++ b/website/site/content/docs/tools/cli.md @@ -112,7 +112,7 @@ command doesn't require to be logged in, it can also upload via a A source id can be given in the config file, then there are no additional options required. The simplest form is this: -``` shell +``` ❯ dsc upload *.pdf File already in Docspell: article-velo.pdf Adding to request: test-ocr.pdf @@ -142,7 +142,7 @@ in a separate request, so the `--single-item` option cannot be used. There are options to exclude/include files based on a [glob pattern](https://docs.rs/glob/0.3.0/glob/struct.Pattern.html). -``` shell +``` ❯ dsc upload --traverse . File already in Docspell: article-velo.pdf File already in Docspell: demo/dirc/scan.21-03-12.15-50-54.pdf @@ -175,7 +175,7 @@ It detects file creations and skips a rename within a watched folder. The flag `-r` or `--recursive` is required to recursively watch a directory. -``` shell +``` ❯ dsc watch -r . Watching directory (Recursive): . Press Ctrl-C to quit. @@ -204,7 +204,7 @@ query. It is possible to download them all flat into some directory or directly into a zip file. For example, download all files that are tagged with `todo` into a zip file: -``` shell +``` ❯ dsc download --zip 'tag:todo' Zipping 2 attachments into docspell-files.zip Downloading DOC-20191223-155707.jpg.pdf … @@ -214,7 +214,7 @@ Downloading DOC-20200803-174448.jpg.pdf … It downloads the converted PDF files by default, which can be changed via some options. -``` shell +``` ❯ dsc download --zip --original 'tag:todo' Zipping original files of 2 attachments into docspell-files.zip Downloading DOC-20191223-155707.jpg … @@ -296,7 +296,7 @@ into recursive paths (as shown in the example output above) using the resulting in `Finance-Institute`. Example run (producing output shown above): -``` shell +``` ❯ dsc export --all --date-links --folder-links --folder-delimiter "/" --tag-links --target exports Exported item: test3.zip Exported item: README.md @@ -329,7 +329,7 @@ config file or as an argument. ### Reset user password -``` shell +``` ❯ dsc admin reset-password --account demo ┌─────────┬──────────────┬──────────────────┐ │ success │ new password │ message │ @@ -340,7 +340,7 @@ config file or as an argument. ### Recreate fulltext index -``` shell +``` ❯ dsc admin --admin-secret admin123 recreate-index ┌─────────┬─────────────────────────────────────┐ │ success │ message │ @@ -350,7 +350,7 @@ config file or as an argument. ``` ### Convert all files to PDF -``` shell +``` ❯ dsc admin --admin-secret admin123 convert-all-pdf ┌─────────┬─────────────────────────────────┐ │ success │ message │ @@ -364,7 +364,7 @@ enabling it now. ### Regenerate preview images -``` shell +``` ❯ dsc admin --admin-secret admin123 convert-all-pdf ┌─────────┬───────────────────────────────────────┐ │ success │ message │ @@ -382,7 +382,7 @@ config. The `search` command takes a [query](@/docs/query/_index.md) and prints the results. -``` shell +``` ❯ dsc search 'corr:*' ┌──────────┬────────────────────────────┬───────────┬────────────┬─────┬───────────────────────────┬───────────────┬────────┬─────────────┬────────────┬───────┐ │ id │ name │ state │ date │ due │ correspondent │ concerning │ folder │ tags │ fields │ files │ @@ -399,7 +399,7 @@ prints the results. ``` The same can be formatted as json and, for example, only print the ids: -``` shell +``` ❯ dsc -f json search 'corr:*' | jq '.groups[].items[].id' "HVK7JuCFt4W-qxkcwq1cWCV-dvpGo4DpZzU-Q16Xoujojas" "3odNawKE1Ek-YJrWfPzekAq-47cjt14sexd-GK35JAEAanx" diff --git a/website/site/content/docs/webapp/share-01.png b/website/site/content/docs/webapp/share-01.png new file mode 100644 index 00000000..5c0447f3 Binary files /dev/null and b/website/site/content/docs/webapp/share-01.png differ diff --git a/website/site/content/docs/webapp/share-02.png b/website/site/content/docs/webapp/share-02.png new file mode 100644 index 00000000..3a3ed5fd Binary files /dev/null and b/website/site/content/docs/webapp/share-02.png differ diff --git a/website/site/content/docs/webapp/share-03.png b/website/site/content/docs/webapp/share-03.png new file mode 100644 index 00000000..ceba5e13 Binary files /dev/null and b/website/site/content/docs/webapp/share-03.png differ diff --git a/website/site/content/docs/webapp/share-04.png b/website/site/content/docs/webapp/share-04.png new file mode 100644 index 00000000..31288b53 Binary files /dev/null and b/website/site/content/docs/webapp/share-04.png differ diff --git a/website/site/content/docs/webapp/share-05.png b/website/site/content/docs/webapp/share-05.png new file mode 100644 index 00000000..529600fb Binary files /dev/null and b/website/site/content/docs/webapp/share-05.png differ diff --git a/website/site/content/docs/webapp/share-06.png b/website/site/content/docs/webapp/share-06.png new file mode 100644 index 00000000..443efcd0 Binary files /dev/null and b/website/site/content/docs/webapp/share-06.png differ diff --git a/website/site/content/docs/webapp/share-07.png b/website/site/content/docs/webapp/share-07.png new file mode 100644 index 00000000..200620a9 Binary files /dev/null and b/website/site/content/docs/webapp/share-07.png differ diff --git a/website/site/content/docs/webapp/share-08.png b/website/site/content/docs/webapp/share-08.png new file mode 100644 index 00000000..b995654f Binary files /dev/null and b/website/site/content/docs/webapp/share-08.png differ diff --git a/website/site/content/docs/webapp/share-09.png b/website/site/content/docs/webapp/share-09.png new file mode 100644 index 00000000..84dce974 Binary files /dev/null and b/website/site/content/docs/webapp/share-09.png differ diff --git a/website/site/content/docs/webapp/share-10.png b/website/site/content/docs/webapp/share-10.png new file mode 100644 index 00000000..975f503e Binary files /dev/null and b/website/site/content/docs/webapp/share-10.png differ diff --git a/website/site/content/docs/webapp/share-11.png b/website/site/content/docs/webapp/share-11.png new file mode 100644 index 00000000..135955b2 Binary files /dev/null and b/website/site/content/docs/webapp/share-11.png differ diff --git a/website/site/content/docs/webapp/share-12.png b/website/site/content/docs/webapp/share-12.png new file mode 100644 index 00000000..cc8fd2b6 Binary files /dev/null and b/website/site/content/docs/webapp/share-12.png differ diff --git a/website/site/content/docs/webapp/share.md b/website/site/content/docs/webapp/share.md new file mode 100644 index 00000000..861f5fa4 --- /dev/null +++ b/website/site/content/docs/webapp/share.md @@ -0,0 +1,196 @@ ++++ +title = "Shares" +weight = 120 +[extra] +mktoc = true ++++ + +Docspell has a thought-out share feature that allows you to create +read-only views to a subset of your documents and create a public but +not-guessable link to it. + +# Concept + +A share is a cryptic *share id* that maps to a +[query](@/docs/query/_index.md). A share can be accessed via a public +link that contains the share id. + +{% infobubble(mode="warning", title="Please note") %} + +Everyone who has this link can access all documents resulting from the +query and their metadata. + +{% end %} + +To further protect this link, a password can be specified which should +be distributed via a different channel than the link. If a password is +defined for a share, it is required to access the items. Otherwise, +the share id is all that's needed. + +A share also requires to set a *publication end date* as a protection +for leaving links available forever. Of course, you can explicitely +set it to a very far away date should you really want it. + +The query is executed under the user who created the share. Thus it +returns all the items the user can see. This is important when you +have folders that are only visible to you. If you don't want to share +certain items, you must alter the query accordingly. + +Given the nature of a query, there are two kinds of shares possible: +dynamic and static ones. A dynamic share uses a query that may yield +different results over time, for example `tag:invoice`. A static query +is a query that explicitely selects items by their id. This means the +latter will always result in the selected items (except if one of them +is deleted); whereas the former query could return different results +each time it is executed, because new documents could have been added +in the meantime that now match the criteria (like tagged with +`invoice` in the example). + +A share can be enabled and disabled to quickly make it available or +hidden. + + +## Use Cases + +A useful application for shares is to have a simple view to documents +that are not sensible, like manuals. You could create a share for all +your manuals, for example using tags `tags:manual` and bookmark it. + +Another use case is to share sensible documents with a partner who +needs access to it, for example if you want to share all your tax +documents with the company/person who helps you with doing you tax +submission. + +# Creating shares + +There are the following ways to create a share: + +1. From the search page: enter a query or use the search menu and then + click the *share* button to share the resulting documents. This + usually creates a dynamic share. +2. From selecting items: In the search view, click *Select Mode* and + select a few items. Then click the *share* button to share exactly + these items. This will create a static share. +3. By creating it manually: You can also go to *Collective Profile* + and create a new share using the provided form. + +Once you created the share, you can copy the url or send it via e-mail +(requires to have [e-mail +settings](@/docs/webapp/emailsettings.md#smtp-settings) defined). + +## Creating from search results + +When at the search page, add some criteria until you have the results +you want to publish. In the screenshot below all items with tag +`Manual` are selected. Then click the *Share Button*. + +{{ figure(file="share-01.png") }} + +A form appears that lets you edit the query and set some properties. +The query is taken from the search page and may look a bit strange. It +will use ids rather than names, which makes the query a bit more +robust. For example: the query `tag=manual` also works, but should you +rename the tag, the share won't work anymore. By using ids as in +`tag.id=4AUye…`, the query is immune to renamings. + +A name can be given to make it better distinguishable from other +shares. Then a password and the *Publish Until* date can be set. The +*Publish Until* date is mandatory. You can set it to something very +far away to have shares exist "forever". + +{{ figure(file="share-02.png") }} + +Clicking *Cancel* brings you back to the search results. If you are +satifsied, click *Publish*. The next screen allows you to inspect your +new share and to copy the url and/or send an e-mail. The email form is +prefilled with some template that contains the link, so you don't need +to copy it. + +{{ figure(file="share-03.png") }} + +The new share can now be found in *Collective Profile -> Shares*. +Clicking *Done* brings you back to the search results. + +## Creating from selecting items + +Creating a share for a hand picked set of items is almost the same as +the above. In the search page, go to *Select Mode* and select some +items. + +{{ figure(file="share-04.png") }} + +Then click the *Share* button and follow the same process as described +above. The query selects now exactly the picked items like in +`id~=AhVX…,FG65Xy…`. + + +## Creating manually + +At *Collective Profile -> Shares* there is a *New share* button, which +will present a form where you can create a share. The query must then +be filled manually (there is some syntax help). It is the same query +as in the "power search" bar, as described +[here](@/docs/query/_index.md). + +{{ figure(file="share-12.png") }} + +## Managing Shares + +Go to *Collective Profile -> Shares* to see all the shares of your +collective. You can also look into shares that were created by other +users. + +{{ figure(file="share-06.png") }} + +To not make it too easy to look into private folders, you cannot +change attributes of shares that were created by another user. +However, you can delete all shares. This is for now a compromise, +assuming small groups that still talk to each other: All users of a +collective are equal and should be able to see shares and also delete +them. But since a share of another user could be used to easily look +into folders where you are not a member, editing other shares is not +allowed. + +If you edit your own share, you can change its properties. + +{{ figure(file="share-07.png") }} + +If you are not the owner, the form is hidden: + +{{ figure(file="share-08.png") }} + + +# Accessing a share + +Pasting the share link into a browser shows you the results of the +query: + +{{ figure(file="share-09.png") }} + +The search input allows to do a fulltext search and the search menu to +the left can be used to further constrain the results. The search will +be combined with the stored query, such that the results always remain +within the original results of the share. + +The options in the dropdown menus for correspondent, concerning etc +are taken from the results. So only the data that is shared by the +search results will be available to select. Other data is not leaked. + +Clicking the search icon next to the search input, switches the input +to be the "power search" input: + +{{ figure(file="share-11.png") }} + +There is a link below the input field that opens a new tab with the +[query documentation page](@/docs/query/_index.md). + +The user can click on the tags and other data in the item cards which +will populate the corresponding section in the search menu, just like +the default search view. You can click on an item card to go to the +detail view: + +{{ figure(file="share-10.png") }} + +This link to a single item is also bookmarkable. You can copy it via +the QR code or by clicking the *Copy* button. In the detail view you +can select multiple attachments and download each. diff --git a/website/site/templates/index.html b/website/site/templates/index.html index 34d27d58..dbd2b349 100644 --- a/website/site/templates/index.html +++ b/website/site/templates/index.html @@ -4,10 +4,11 @@ {% include "meta.html" %} Docspell – Simple document organizer - + + + {% include "fathom.html" %} diff --git a/website/site/templates/overview.html b/website/site/templates/overview.html index 8436c4d5..3ebc3874 100644 --- a/website/site/templates/overview.html +++ b/website/site/templates/overview.html @@ -4,7 +4,6 @@ {% include "meta.html" %} {{ section.title }} – Docspell Documentation - {% include "search-head.html" %}
@@ -63,6 +62,7 @@ {% include "footer.html" %} + {% include "search-head.html" %} {% include "search-part.html" %} {% include "fathom.html" %} diff --git a/website/site/templates/page.html b/website/site/templates/page.html index e2d4e037..46e9ef19 100644 --- a/website/site/templates/page.html +++ b/website/site/templates/page.html @@ -4,7 +4,6 @@ {% include "meta.html" %} {{ page.title }} – Docspell Documentation - {% include "search-head.html" %}
@@ -98,6 +97,7 @@
{% include "footer.html" %} + {% include "search-head.html" %} {% include "search-part.html" %} {% include "fathom.html" %} diff --git a/website/site/templates/pages.html b/website/site/templates/pages.html index 88a81569..aad46a6b 100644 --- a/website/site/templates/pages.html +++ b/website/site/templates/pages.html @@ -4,7 +4,6 @@ {% include "meta.html" %} {{ section.title }} – Docspell Documentation - {% include "search-head.html" %}
@@ -71,6 +70,8 @@ {% include "footer.html" %} + + {% include "search-head.html" %} {% include "search-part.html" %} {% include "fathom.html" %} diff --git a/website/site/templates/search-head.html b/website/site/templates/search-head.html index 1d69d4e2..8d3d075d 100644 --- a/website/site/templates/search-head.html +++ b/website/site/templates/search-head.html @@ -1,4 +1,4 @@ - - - - + + + +