mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-25 16:45:05 +00:00
Merge branch 'master' of github.com:eikek/docspell into pr-fix-ocrmypdf
This commit is contained in:
commit
d5f0d47e80
57
Changelog.md
57
Changelog.md
@ -1,5 +1,62 @@
|
||||
# Changelog
|
||||
|
||||
## v0.14.0
|
||||
|
||||
*Soon*
|
||||
|
||||
This release contains many bug fixes, thank you all so much for
|
||||
helping out! There is also a new feature and some more scripts in
|
||||
tools.
|
||||
|
||||
- Edit/delete multiple items at once (#253, #412)
|
||||
- Show/hide side menus via ui settings (#351)
|
||||
- Adds two more scripts to the `tools/` section (thanks to
|
||||
@totti4ever):
|
||||
- one script to import data from paperless (#358, #359), and
|
||||
- a script to check clean a directory from files that are already in
|
||||
docspell (#403)
|
||||
- Extend docker image to use newest ocrmypdf version (#393, thanks
|
||||
@totti4ever)
|
||||
- Fix bug that would stop processing when pdf conversion fails (#392,
|
||||
#387)
|
||||
- Fix bug to have a separate, configurable source identifier for the
|
||||
integration upload endpoint (#389)
|
||||
- Fixes ui bug to not highlight the last viewed item when searching
|
||||
again. (#373)
|
||||
- Fixes bug when saving multiple changes to the ui settings (#368)
|
||||
- Fixes uniqueness check for equipments (#370)
|
||||
- Fixes a bug when doing document classification where user input was
|
||||
not correctly escaped for regexes (#356)
|
||||
- Fixes debian packages to have both (joex + restserver) the same user
|
||||
to make H2 work (#336)
|
||||
- Fixes a bug when searching with multiple tags using MariaDB (#404)
|
||||
|
||||
### REST Api Changes
|
||||
|
||||
- Routes for managing multiple items:
|
||||
- `/sec/items/deleteAll`
|
||||
- `/sec/items/tags`
|
||||
- `/sec/items/tagsremove`
|
||||
- `/sec/items/name`
|
||||
- `/sec/items/folder`
|
||||
- `/sec/items/direction`
|
||||
- `/sec/items/date`
|
||||
- `/sec/items/duedate`
|
||||
- `/sec/items/corrOrg`
|
||||
- `/sec/items/corrPerson`
|
||||
- `/sec/items/concPerson`
|
||||
- `/sec/items/concEquipment`
|
||||
- `/sec/items/confirm`
|
||||
- `/sec/items/unconfirm`
|
||||
- `/sec/items/reprocess`
|
||||
- Adds another parameter to `ItemSearch` structure to enable searching
|
||||
in a subset of items giving their ids.
|
||||
|
||||
### Configuration Changes
|
||||
|
||||
- new setting `….integration-endpoint.source-name` to define the
|
||||
source name for files uploaded through this endpoint
|
||||
|
||||
## v0.13.0
|
||||
|
||||
*Oct 19, 2020*
|
||||
|
@ -46,6 +46,12 @@ trait OItem[F[_]] {
|
||||
collective: Ident
|
||||
): F[UpdateResult]
|
||||
|
||||
def removeTagsMultipleItems(
|
||||
items: NonEmptyList[Ident],
|
||||
tags: List[String],
|
||||
collective: Ident
|
||||
): F[UpdateResult]
|
||||
|
||||
/** Toggles tags of the given item. Tags must exist, but can be IDs or names. */
|
||||
def toggleTags(item: Ident, tags: List[String], collective: Ident): F[UpdateResult]
|
||||
|
||||
@ -225,6 +231,29 @@ object OItem {
|
||||
}
|
||||
}
|
||||
|
||||
def removeTagsMultipleItems(
|
||||
items: NonEmptyList[Ident],
|
||||
tags: List[String],
|
||||
collective: Ident
|
||||
): F[UpdateResult] =
|
||||
tags.distinct match {
|
||||
case Nil => UpdateResult.success.pure[F]
|
||||
case ws =>
|
||||
store.transact {
|
||||
(for {
|
||||
itemIds <- OptionT
|
||||
.liftF(RItem.filterItems(items, collective))
|
||||
.filter(_.nonEmpty)
|
||||
given <- OptionT.liftF(RTag.findAllByNameOrId(ws, collective))
|
||||
_ <- OptionT.liftF(
|
||||
itemIds.traverse(item =>
|
||||
RTagItem.removeAllTags(item, given.map(_.tagId).toList)
|
||||
)
|
||||
)
|
||||
} yield UpdateResult.success).getOrElse(UpdateResult.notFound)
|
||||
}
|
||||
}
|
||||
|
||||
def toggleTags(
|
||||
item: Ident,
|
||||
tags: List[String],
|
||||
|
@ -1945,6 +1945,29 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
/sec/items/tagsremove:
|
||||
post:
|
||||
tags:
|
||||
- Item (Multi Edit)
|
||||
summary: Remove tags from multiple items
|
||||
description: |
|
||||
Remove the given tags from all given items.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ItemsAndRefs"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
put:
|
||||
tags:
|
||||
- Item (Multi Edit)
|
||||
|
@ -73,6 +73,18 @@ object ItemMultiRoutes {
|
||||
resp <- Ok(Conversions.basicResult(res, "Tags added."))
|
||||
} yield resp
|
||||
|
||||
case req @ POST -> Root / "tagsremove" =>
|
||||
for {
|
||||
json <- req.as[ItemsAndRefs]
|
||||
items <- readIds[F](json.items)
|
||||
res <- backend.item.removeTagsMultipleItems(
|
||||
items,
|
||||
json.refs,
|
||||
user.account.collective
|
||||
)
|
||||
resp <- Ok(Conversions.basicResult(res, "Tags removed"))
|
||||
} yield resp
|
||||
|
||||
case req @ PUT -> Root / "name" =>
|
||||
for {
|
||||
json <- req.as[ItemsAndName]
|
||||
|
@ -10,6 +10,7 @@ module Api exposing
|
||||
, changeFolderName
|
||||
, changePassword
|
||||
, checkCalEvent
|
||||
, confirmMultiple
|
||||
, createImapSettings
|
||||
, createMailSettings
|
||||
, createNewFolder
|
||||
@ -74,6 +75,7 @@ module Api exposing
|
||||
, refreshSession
|
||||
, register
|
||||
, removeMember
|
||||
, removeTagsMultiple
|
||||
, sendMail
|
||||
, setAttachmentName
|
||||
, setCollectiveSettings
|
||||
@ -107,6 +109,7 @@ module Api exposing
|
||||
, startReIndex
|
||||
, submitNotifyDueItems
|
||||
, toggleTags
|
||||
, unconfirmMultiple
|
||||
, updateNotifyDueItems
|
||||
, updateScanMailbox
|
||||
, upload
|
||||
@ -1284,6 +1287,34 @@ getJobQueueStateTask flags =
|
||||
--- Item (Mulit Edit)
|
||||
|
||||
|
||||
confirmMultiple :
|
||||
Flags
|
||||
-> Set String
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
confirmMultiple flags ids receive =
|
||||
Http2.authPut
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/items/confirm"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.IdList.encode (IdList (Set.toList ids)))
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
|
||||
unconfirmMultiple :
|
||||
Flags
|
||||
-> Set String
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
unconfirmMultiple flags ids receive =
|
||||
Http2.authPut
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/items/unconfirm"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.IdList.encode (IdList (Set.toList ids)))
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
|
||||
setTagsMultiple :
|
||||
Flags
|
||||
-> ItemsAndRefs
|
||||
@ -1312,6 +1343,20 @@ addTagsMultiple flags data receive =
|
||||
}
|
||||
|
||||
|
||||
removeTagsMultiple :
|
||||
Flags
|
||||
-> ItemsAndRefs
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
removeTagsMultiple flags data receive =
|
||||
Http2.authPost
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/items/tagsremove"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.ItemsAndRefs.encode data)
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
|
||||
setNameMultiple :
|
||||
Flags
|
||||
-> ItemsAndName
|
||||
|
@ -53,6 +53,12 @@ type SaveNameState
|
||||
| SaveFailed
|
||||
|
||||
|
||||
type TagEditMode
|
||||
= AddTags
|
||||
| RemoveTags
|
||||
| ReplaceTags
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ tagModel : Comp.Dropdown.Model Tag
|
||||
, nameModel : String
|
||||
@ -70,6 +76,7 @@ type alias Model =
|
||||
, concPersonModel : Comp.Dropdown.Model IdName
|
||||
, concEquipModel : Comp.Dropdown.Model IdName
|
||||
, modalEdit : Maybe Comp.DetailEdit.Model
|
||||
, tagEditMode : TagEditMode
|
||||
}
|
||||
|
||||
|
||||
@ -81,6 +88,8 @@ type Msg
|
||||
| UpdateThrottle
|
||||
| RemoveDueDate
|
||||
| RemoveDate
|
||||
| ConfirmMsg Bool
|
||||
| ToggleTagEditMode
|
||||
| FolderDropdownMsg (Comp.Dropdown.Msg IdName)
|
||||
| TagDropdownMsg (Comp.Dropdown.Msg Tag)
|
||||
| DirDropdownMsg (Comp.Dropdown.Msg Direction)
|
||||
@ -145,6 +154,7 @@ init =
|
||||
, dueDate = Nothing
|
||||
, dueDatePicker = Comp.DatePicker.emptyModel
|
||||
, modalEdit = Nothing
|
||||
, tagEditMode = AddTags
|
||||
}
|
||||
|
||||
|
||||
@ -201,6 +211,9 @@ resultNone model =
|
||||
update : Flags -> Msg -> Model -> UpdateResult
|
||||
update flags msg model =
|
||||
case msg of
|
||||
ConfirmMsg flag ->
|
||||
resultNoCmd (ConfirmChange flag) model
|
||||
|
||||
TagDropdownMsg m ->
|
||||
let
|
||||
( m2, _ ) =
|
||||
@ -209,19 +222,48 @@ update flags msg model =
|
||||
newModel =
|
||||
{ model | tagModel = m2 }
|
||||
|
||||
mkChange list =
|
||||
case model.tagEditMode of
|
||||
AddTags ->
|
||||
AddTagChange list
|
||||
|
||||
RemoveTags ->
|
||||
RemoveTagChange list
|
||||
|
||||
ReplaceTags ->
|
||||
ReplaceTagChange list
|
||||
|
||||
change =
|
||||
if isDropdownChangeMsg m then
|
||||
Comp.Dropdown.getSelected newModel.tagModel
|
||||
|> Util.List.distinct
|
||||
|> List.map (\t -> IdName t.id t.name)
|
||||
|> ReferenceList
|
||||
|> TagChange
|
||||
|> mkChange
|
||||
|
||||
else
|
||||
NoFormChange
|
||||
in
|
||||
resultNoCmd change newModel
|
||||
|
||||
ToggleTagEditMode ->
|
||||
let
|
||||
( m2, _ ) =
|
||||
Comp.Dropdown.update (Comp.Dropdown.SetSelection []) model.tagModel
|
||||
|
||||
newModel =
|
||||
{ model | tagModel = m2 }
|
||||
in
|
||||
case model.tagEditMode of
|
||||
AddTags ->
|
||||
resultNone { newModel | tagEditMode = RemoveTags }
|
||||
|
||||
RemoveTags ->
|
||||
resultNone { newModel | tagEditMode = ReplaceTags }
|
||||
|
||||
ReplaceTags ->
|
||||
resultNone { newModel | tagEditMode = AddTags }
|
||||
|
||||
GetTagsResp (Ok tags) ->
|
||||
let
|
||||
tagList =
|
||||
@ -550,16 +592,66 @@ renderEditForm cfg settings model =
|
||||
|
||||
else
|
||||
span [ class "invisible hidden" ] []
|
||||
|
||||
tagModeIcon =
|
||||
case model.tagEditMode of
|
||||
AddTags ->
|
||||
i [ class "grey plus link icon" ] []
|
||||
|
||||
RemoveTags ->
|
||||
i [ class "grey eraser link icon" ] []
|
||||
|
||||
ReplaceTags ->
|
||||
i [ class "grey redo alternate link icon" ] []
|
||||
|
||||
tagModeMsg =
|
||||
case model.tagEditMode of
|
||||
AddTags ->
|
||||
"Tags chosen here are *added* to all selected items."
|
||||
|
||||
RemoveTags ->
|
||||
"Tags chosen here are *removed* from all selected items."
|
||||
|
||||
ReplaceTags ->
|
||||
"Tags chosen here *replace* those on selected items."
|
||||
in
|
||||
div [ class cfg.menuClass ]
|
||||
[ div [ class "ui form warning" ]
|
||||
[ optional [ Data.Fields.Tag ] <|
|
||||
[ div [ class "field" ]
|
||||
[ div
|
||||
[ class "ui fluid buttons"
|
||||
]
|
||||
[ button
|
||||
[ class "ui primary button"
|
||||
, onClick (ConfirmMsg True)
|
||||
]
|
||||
[ text "Confirm"
|
||||
]
|
||||
, div [ class "or" ] []
|
||||
, button
|
||||
[ class "ui secondary button"
|
||||
, onClick (ConfirmMsg False)
|
||||
]
|
||||
[ text "Unconfirm"
|
||||
]
|
||||
]
|
||||
]
|
||||
, optional [ Data.Fields.Tag ] <|
|
||||
div [ class "field" ]
|
||||
[ label []
|
||||
[ Icons.tagsIcon "grey"
|
||||
, text "Tags"
|
||||
, a
|
||||
[ class "right-float"
|
||||
, href "#"
|
||||
, title "Change tag edit mode"
|
||||
, onClick ToggleTagEditMode
|
||||
]
|
||||
[ tagModeIcon
|
||||
]
|
||||
]
|
||||
, Html.map TagDropdownMsg (Comp.Dropdown.view settings model.tagModel)
|
||||
, Markdown.toHtml [ class "small-info" ] tagModeMsg
|
||||
]
|
||||
, div [ class " field" ]
|
||||
[ label [] [ text "Name" ]
|
||||
|
@ -20,7 +20,9 @@ import Set exposing (Set)
|
||||
|
||||
type FormChange
|
||||
= NoFormChange
|
||||
| TagChange ReferenceList
|
||||
| AddTagChange ReferenceList
|
||||
| ReplaceTagChange ReferenceList
|
||||
| RemoveTagChange ReferenceList
|
||||
| FolderChange (Maybe IdName)
|
||||
| DirectionChange Direction
|
||||
| OrgChange (Maybe IdName)
|
||||
@ -30,6 +32,7 @@ type FormChange
|
||||
| ItemDateChange (Maybe Int)
|
||||
| DueDateChange (Maybe Int)
|
||||
| NameChange String
|
||||
| ConfirmChange Bool
|
||||
|
||||
|
||||
multiUpdate :
|
||||
@ -44,13 +47,27 @@ multiUpdate flags ids change receive =
|
||||
Set.toList ids
|
||||
in
|
||||
case change of
|
||||
TagChange tags ->
|
||||
ReplaceTagChange tags ->
|
||||
let
|
||||
data =
|
||||
ItemsAndRefs items (List.map .id tags.items)
|
||||
in
|
||||
Api.setTagsMultiple flags data receive
|
||||
|
||||
AddTagChange tags ->
|
||||
let
|
||||
data =
|
||||
ItemsAndRefs items (List.map .id tags.items)
|
||||
in
|
||||
Api.addTagsMultiple flags data receive
|
||||
|
||||
RemoveTagChange tags ->
|
||||
let
|
||||
data =
|
||||
ItemsAndRefs items (List.map .id tags.items)
|
||||
in
|
||||
Api.removeTagsMultiple flags data receive
|
||||
|
||||
NameChange name ->
|
||||
let
|
||||
data =
|
||||
@ -114,5 +131,12 @@ multiUpdate flags ids change receive =
|
||||
in
|
||||
Api.setConcEquipmentMultiple flags data receive
|
||||
|
||||
ConfirmChange flag ->
|
||||
if flag then
|
||||
Api.confirmMultiple flags ids receive
|
||||
|
||||
else
|
||||
Api.unconfirmMultiple flags ids receive
|
||||
|
||||
NoFormChange ->
|
||||
Cmd.none
|
||||
|
@ -59,7 +59,7 @@ view flags settings model =
|
||||
, onClick ToggleSearchMenu
|
||||
, title "Hide menu"
|
||||
]
|
||||
[ i [ class "ui angle left icon" ] []
|
||||
[ i [ class "chevron left icon" ] []
|
||||
]
|
||||
, div [ class "right floated menu" ]
|
||||
[ a
|
||||
@ -303,13 +303,9 @@ viewSearchBar flags model =
|
||||
[ class "search-menu-toggle ui blue icon button"
|
||||
, onClick ToggleSearchMenu
|
||||
, href "#"
|
||||
, if model.searchTypeForm == ContentOnlySearch then
|
||||
title "Search menu disabled"
|
||||
|
||||
else
|
||||
title "Open search menu"
|
||||
, title "Open search menu"
|
||||
]
|
||||
[ i [ class "angle right icon" ] []
|
||||
[ i [ class "filter icon" ] []
|
||||
]
|
||||
, div [ class "item" ]
|
||||
[ div [ class "ui left icon right action input" ]
|
||||
|
@ -14,14 +14,14 @@ object Dependencies {
|
||||
val EmilVersion = "0.6.3"
|
||||
val FastparseVersion = "2.1.3"
|
||||
val FlexmarkVersion = "0.62.2"
|
||||
val FlywayVersion = "7.1.0"
|
||||
val FlywayVersion = "7.1.1"
|
||||
val Fs2Version = "2.4.4"
|
||||
val H2Version = "1.4.200"
|
||||
val Http4sVersion = "0.21.8"
|
||||
val Icu4jVersion = "68.1"
|
||||
val JsoupVersion = "1.13.1"
|
||||
val KindProjectorVersion = "0.10.3"
|
||||
val Log4sVersion = "1.8.2"
|
||||
val Log4sVersion = "1.9.0"
|
||||
val LogbackVersion = "1.2.3"
|
||||
val MariaDbVersion = "2.7.0"
|
||||
val MiniTestVersion = "2.8.2"
|
||||
@ -34,7 +34,7 @@ object Dependencies {
|
||||
val StanfordNlpVersion = "4.0.0"
|
||||
val TikaVersion = "1.24.1"
|
||||
val YamuscaVersion = "0.7.0"
|
||||
val SwaggerUIVersion = "3.36.0"
|
||||
val SwaggerUIVersion = "3.36.1"
|
||||
val SemanticUIVersion = "2.4.1"
|
||||
val TwelveMonkeysVersion = "3.6"
|
||||
val JQueryVersion = "3.5.1"
|
||||
|
179
tools/consumedir-cleaner/consumedir-cleaner.sh
Executable file
179
tools/consumedir-cleaner/consumedir-cleaner.sh
Executable file
@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "##################### START #####################"
|
||||
|
||||
echo " Docspell Consumedir Cleaner - v0.1 beta"
|
||||
echo " by totti4ever" && echo
|
||||
echo " $(date)"
|
||||
echo
|
||||
echo "#################################################"
|
||||
echo && echo
|
||||
|
||||
jq --version > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "please install 'jq'"
|
||||
exit -4
|
||||
fi
|
||||
|
||||
ds_url=${1%/}
|
||||
ds_user_param=$2
|
||||
ds_user=${ds_user_param#*/}
|
||||
ds_collective=${ds_user_param%%/*}
|
||||
ds_password=$3
|
||||
ds_consumedir_path=${4%/}
|
||||
ds_archive_path=$ds_consumedir_path/_archive/$ds_collective
|
||||
|
||||
|
||||
if [ $# -ne 4 ]; then
|
||||
echo "FATAL Exactly four parameters needed"
|
||||
exit -3
|
||||
elif [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then
|
||||
echo "FATAL Parameter missing"
|
||||
echo " ds_url: $ds_url"
|
||||
echo " ds_user: $ds_user"
|
||||
echo " ds_password: $ds_password"
|
||||
echo " ds_consumedir_path: $ds_consumedir_path"
|
||||
exit -2
|
||||
elif [ "$ds_collective" == "_archive" ]; then
|
||||
echo "FATAL collective name '_archive' is not supported by this script"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
|
||||
############# FUNCTIONS
|
||||
function curl_call() {
|
||||
curl_cmd="$1 -H 'X-Docspell-Auth: $ds_token'"
|
||||
curl_result=$(eval $curl_cmd)
|
||||
curl_code=$?
|
||||
|
||||
if [ "$curl_result" == '"Authentication failed."' ] || [ "$curl_result" == 'Response timed out' ]; then
|
||||
printf "\nNew login required ($curl_result)... "
|
||||
login
|
||||
printf "%${#len_resultset}s" " "; printf " .."
|
||||
curl_call $1
|
||||
|
||||
elif [ "$curl_result" == "Bad Gateway" ] || [ "$curl_result" == '404 page not found' ]; then
|
||||
echo "FATAL Connection to server failed"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function login() {
|
||||
curl_call "curl -s -X POST -d '{\"account\": \"$ds_collective/$ds_user\", \"password\": \"$ds_password\"}' ${ds_url}/api/v1/open/auth/login"
|
||||
|
||||
curl_status=$(echo $curl_result | jq -r ".success")
|
||||
|
||||
if [ "$curl_status" == "true" ]; then
|
||||
ds_token=$(echo $curl_result | jq -r ".token")
|
||||
echo "Login successfull ( Token: $ds_token )"
|
||||
|
||||
else
|
||||
echo "FATAL Login not succesfull"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
############# END
|
||||
|
||||
echo "Settings:"
|
||||
if [ "$DS_CC_REMOVE" == "true" ]; then
|
||||
echo " ### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ###"
|
||||
echo " - DELETE files? YES"
|
||||
echo " when already existing in Docspell. This cannot be undone!"
|
||||
echo " ### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ###"
|
||||
else
|
||||
echo " - DELETE files? no"
|
||||
echo " moving already uploaded files to archive"
|
||||
fi
|
||||
echo
|
||||
if [ "$DS_CC_UPLOAD_MISSING" == true ]; then
|
||||
echo " - UPLOAD files? YES"
|
||||
echo " files not existing in Docspell will be uploaded and will be re-checked in the next run."
|
||||
else
|
||||
echo " - UPLOAD files? no"
|
||||
echo " files not existing in Docspell will NOT be uploaded and stay where they are."
|
||||
fi
|
||||
echo && echo
|
||||
echo "Press 'ctrl+c' to cancel"
|
||||
for ((i=9;i>=0;i--)); do
|
||||
printf "\r waiting $i seconds "
|
||||
sleep 1s
|
||||
done
|
||||
echo && echo
|
||||
|
||||
# login, get token
|
||||
login
|
||||
|
||||
echo "Scanning folder for collective '$ds_collective' ($ds_consumedir_path/$ds_collective)"
|
||||
echo && echo
|
||||
|
||||
while read -r line
|
||||
do
|
||||
tmp_filepath=$line
|
||||
|
||||
if [ "$tmp_filepath" == "" ]; then
|
||||
echo "no files found" && echo
|
||||
exit 0 #no results
|
||||
elif [ ! -f "$tmp_filepath" ]; then
|
||||
echo "FATAL no access to file: $tmp_filepath"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
echo "Checking '$tmp_filepath'"
|
||||
printf "%${#len_resultset}s" " "; printf " "
|
||||
|
||||
# check for checksum
|
||||
tmp_checksum=$(sha256sum "$tmp_filepath" | awk '{print $1}')
|
||||
|
||||
curl_call "curl -s -X GET '$ds_url/api/v1/sec/checkfile/$tmp_checksum'"
|
||||
curl_status=$(echo $curl_result | jq -r ".exists")
|
||||
|
||||
if [ $curl_code -ne 0 ]; then
|
||||
# error
|
||||
echo "ERROR $curl_result // $curl_status"
|
||||
|
||||
# file exists in Docspell
|
||||
elif [ "$curl_status" == "true" ]; then
|
||||
item_name=$(echo $curl_result | jq -r ".items[0].name")
|
||||
item_id=$(echo $curl_result | jq -r ".items[0].id")
|
||||
echo "File already exists: '$item_name (ID: $item_id)'"
|
||||
|
||||
printf "%${#len_resultset}s" " "; printf " "
|
||||
if [ "$DS_CC_REMOVE" == "true" ]; then
|
||||
echo "... removing file"
|
||||
rm "$tmp_filepath"
|
||||
else
|
||||
created=$(echo $curl_result | jq -r ".items[0].created")
|
||||
cur_dir="$ds_archive_path/$(date -d @$(echo "($created+500)/1000" | bc) +%Y-%m
|
||||
)"
|
||||
echo "... moving to archive by month added ('$cur_dir')"
|
||||
mkdir -p "$cur_dir"
|
||||
mv "$tmp_filepath" "$cur_dir/"
|
||||
fi
|
||||
|
||||
# file does not exist in Docspell
|
||||
else
|
||||
|
||||
echo "Files does not exist, yet"
|
||||
if [ "$DS_CC_UPLOAD_MISSING" == true ]; then
|
||||
printf "%${#len_resultset}s" " "; printf " "
|
||||
printf "...uploading file.."
|
||||
curl_call "curl -s -X POST '$ds_url/api/v1/sec/upload/item' -H 'Content-Type: multipart/form-data' -F 'file=@$tmp_filepath'"
|
||||
curl_status=$(echo $curl_result | jq -r ".success")
|
||||
if [ "$curl_status" == "true" ]; then
|
||||
echo ". done"
|
||||
else
|
||||
echo -e "\nERROR $curl_result"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
done \
|
||||
<<< $(find $ds_consumedir_path/$ds_collective -type f)
|
||||
|
||||
|
||||
echo ################# DONE #################
|
||||
date
|
@ -87,6 +87,13 @@ The directory contains a file `docspell.conf` that you can
|
||||
]
|
||||
, text "."
|
||||
]
|
||||
, li []
|
||||
[ text "Chat on "
|
||||
, a [ href "https://gitter.im/eikek/docspell" ]
|
||||
[ text "Gitter"
|
||||
]
|
||||
, text " for questions and feedback."
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -173,13 +173,12 @@ footHero model =
|
||||
[ text " • "
|
||||
]
|
||||
, span []
|
||||
[ text "© 2020 "
|
||||
]
|
||||
, a
|
||||
[ href "https://github.com/eikek"
|
||||
, target "_blank"
|
||||
]
|
||||
[ text "@eikek"
|
||||
[ a
|
||||
[ href "https://gitter.im/eikek/docspell"
|
||||
, target "_blank"
|
||||
]
|
||||
[ text "Chat on Gitter"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -104,8 +104,8 @@ view model =
|
||||
[ class "form"
|
||||
, onSubmit SubmitSearch
|
||||
]
|
||||
[ div [ class "dropdown field is-active is-fullwidth" ]
|
||||
[ div [ class "control has-icons-right is-fullwidth" ]
|
||||
[ div [ class "dropdown field is-active is-fullwidth has-addons" ]
|
||||
[ div [ class "control is-fullwidth" ]
|
||||
[ input
|
||||
[ class "input"
|
||||
, type_ "text"
|
||||
@ -114,7 +114,13 @@ view model =
|
||||
, value model.searchInput
|
||||
]
|
||||
[]
|
||||
, span [ class "icon is-right is-small" ]
|
||||
]
|
||||
, div [ class "control" ]
|
||||
[ button
|
||||
[ class "button is-primary"
|
||||
, href "#"
|
||||
, onClick SubmitSearch
|
||||
]
|
||||
[ img [ src "/icons/search-20.svg" ] []
|
||||
]
|
||||
]
|
||||
|
@ -38,6 +38,7 @@ description = "A list of features and limitations."
|
||||
with due dates
|
||||
- [Read your mailboxes](@/docs/webapp/scanmailbox.md) via IMAP to
|
||||
import mails into docspell
|
||||
- [Edit multiple items](@/docs/webapp/multiedit.md) at once
|
||||
- REST server and document processing are separate applications which
|
||||
can be scaled-out independently
|
||||
- Everything stored in a SQL database: PostgreSQL, MariaDB or H2
|
||||
|
52
website/site/content/docs/tools/consumedir-cleaner.md
Normal file
52
website/site/content/docs/tools/consumedir-cleaner.md
Normal file
@ -0,0 +1,52 @@
|
||||
+++
|
||||
title = "Directory Cleaner"
|
||||
description = "Clean directories from files in docspell"
|
||||
weight = 32
|
||||
+++
|
||||
|
||||
# Introduction
|
||||
|
||||
This script is made for cleaning up the consumption directory used for
|
||||
the consumedir service (as it is provided as docker container)which
|
||||
are copied or moved there.
|
||||
|
||||
<https://github.com/eikek/docspell/tree/master/tools/consumedir-cleaner>
|
||||
|
||||
## How it works
|
||||
|
||||
- Checks for every file (in the collective's folder of the given user
|
||||
name) if it already exists in the collective (using Docspell's API).
|
||||
- If so, by default those files are moved to an archive folder just
|
||||
besides the collective's consumption folders named _archive. The
|
||||
archive's files are organized into monthly subfolders by the date
|
||||
they've been added to Docspell
|
||||
- If set, those files can also be deleted instead of being moved to
|
||||
the archive. There is no undo function provided for this, so be
|
||||
careful.
|
||||
- If a file is found which does not exist in the collective, by
|
||||
default nothing happens, so that file would be found in every run
|
||||
and just ignored
|
||||
- If set, those files can also be uploaded to Docspell. Depending on
|
||||
the setting for files already existing these files would either be
|
||||
deleted or moved to the archive in the next run.
|
||||
|
||||
## Usage (parameters / settings)
|
||||
|
||||
Copy the script to your machine and run it with the following
|
||||
parameters:
|
||||
|
||||
1. URL of Docspell, including http(s)
|
||||
2. Username for Docspell, possibly including Collective (if other name
|
||||
as user)
|
||||
3. Password for Docspell
|
||||
4. Path to the directory which files shall be checked against
|
||||
existence in Docspell
|
||||
|
||||
Additionally, environment variables can be used to alter the behavior:
|
||||
|
||||
- `DS_CC_REMOVE`
|
||||
- `true` – delete files which already exist in the collective
|
||||
- `false` (default) - move them to the archive (see above)
|
||||
- `DS_CC_UPLOAD_MISSING`
|
||||
- `true` - uploads files which do not exist in the collective
|
||||
- `false` (default) - ignore them and do nothing
|
43
website/site/content/docs/tools/paperless-import.md
Normal file
43
website/site/content/docs/tools/paperless-import.md
Normal file
@ -0,0 +1,43 @@
|
||||
+++
|
||||
title = "Paperless Import"
|
||||
description = "Import your data from paperless."
|
||||
weight = 35
|
||||
+++
|
||||
|
||||
# Introduction
|
||||
|
||||
Coming from
|
||||
[paperless](https://github.com/the-paperless-project/paperless/), the
|
||||
script in `tools/import-paperless` can be used to get started by
|
||||
importing your data from paperless into docspell.
|
||||
|
||||
<https://github.com/eikek/docspell/tree/master/tools/import-paperless>
|
||||
|
||||
The script imports the files and also tags and correspondents.
|
||||
|
||||
# Usage
|
||||
|
||||
Copy the script to the machine where paperless is running. Run it with
|
||||
the following arguments:
|
||||
|
||||
1. URL of Docspell, including http(s)
|
||||
2. Username for Docspell, possibly including Collective (if other name as user)
|
||||
3. Password for Docspell
|
||||
4. Path to Paperless' database file (`db.sqlite3`). When using Paperless with docker, it is in the mapped directory `/usr/src/paperless/data`
|
||||
5. Path to Paperless' document base directory. When using Paperless with docker, it is the mapped directory `/usr/src/paperless/media/documents/origin/`
|
||||
|
||||
Some settings can be changed inside the script, right at the top:
|
||||
|
||||
* `LIMIT="LIMIT 0"` (default: inactive): For testing purposes, limits
|
||||
the number of tags and correspondents read from Paperless (this will
|
||||
most likely lead to warnings when processing the documents)
|
||||
* `LIMIT_DOC="LIMIT 5"` (default: inactive): For testing purposes,
|
||||
limits the number of documents and document-to-tag relations read
|
||||
from Paperless
|
||||
* `SKIP_EXISTING_DOCS=true` (default: true): Won't touch already
|
||||
existing documents. If set to `false` documents, which exist
|
||||
already, won't be uploaded again, but the tags, correspondent, date
|
||||
and title from Paperless will be applied.
|
||||
|
||||
**Warning** In case you already had set these information in Docspell,
|
||||
they will be overwritten!
|
Binary file not shown.
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 143 KiB |
Binary file not shown.
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 139 KiB |
@ -12,10 +12,7 @@
|
||||
</a>
|
||||
<span class="ml-1 mr-1">•</span>
|
||||
<span>
|
||||
© 2020
|
||||
<a href="https://github.com/eikek" target="_blank">
|
||||
@eikek
|
||||
</a>
|
||||
<a href="https://gitter.im/eikek/docspell">Chat on Gitter</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user