changeset 810:c8afdaeb877a

merge
author Alain Mazy <am@osimis.io>
date Mon, 14 Feb 2022 08:52:12 +0100
parents 23f21a00b03b (current diff) 1c3972c03c2a (diff)
children d7d3117e5186
files Sphinx/source/faq/log.rst
diffstat 94 files changed, 4158 insertions(+), 902 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Feb 14 08:48:23 2022 +0100
+++ b/.hgtags	Mon Feb 14 08:52:12 2022 +0100
@@ -1,3 +1,8 @@
 eaa6cdfa7ba659f5b2eb737fbbe9c9ac29f19a4a Orthanc-1.9.0
 cfeb018b91503eac2847961ac20705951388b78f Orthanc-1.9.1
 b3e75cef601d8621dc51f948d015fc470985235a Orthanc-1.9.2
+17c1ff4e6ae44412d3c53d6ca1087772d956b0d3 Orthanc-1.9.3
+8a247c645ac697386cb663d25215b9dae41fcd85 Orthanc-1.9.4
+e6386c0124387871fb9799acbfc6f5814d8dcaae Orthanc-1.9.5
+97e6a0431c5ee60d5a0b77adde25df8e87185564 Orthanc-1.9.6
+4e9dc26a5cb9c9fe98acb0ab633391ce3d19d574 Orthanc-1.9.7
--- a/AUTHORS	Mon Feb 14 08:48:23 2022 +0100
+++ b/AUTHORS	Mon Feb 14 08:52:12 2022 +0100
@@ -2,6 +2,10 @@
 ==================================================
 
 * Sebastien Jodogne <s.jodogne@orthanc-labs.com>
+
+  UCLouvain, ICTeam
+  Belgium
+
   Department of Medical Physics
   University Hospital of Liege
   Belgium
--- a/OpenAPI/orthanc-openapi.json	Mon Feb 14 08:48:23 2022 +0100
+++ b/OpenAPI/orthanc-openapi.json	Mon Feb 14 08:52:12 2022 +0100
@@ -2,7 +2,7 @@
    "info" : {
       "description" : "This is the full documentation of the [REST API](https://book.orthanc-server.com/users/rest.html) of Orthanc.<p>This reference is automatically generated from the source code of Orthanc. A [shorter cheat sheet](https://book.orthanc-server.com/users/rest-cheatsheet.html) is part of the Orthanc Book.<p>An earlier, manually crafted version from August 2019, is [still available](2019-08-orthanc-openapi.html), but is not up-to-date anymore ([source](https://groups.google.com/g/orthanc-users/c/NUiJTEICSl8/m/xKeqMrbqAAAJ)).",
       "title" : "Orthanc API",
-      "version" : "1.9.2"
+      "version" : "1.9.7"
    },
    "openapi" : "3.0.0",
    "paths" : {
@@ -165,6 +165,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Limit the number of results",
                   "in" : "query",
                   "name" : "limit",
@@ -174,6 +183,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Show only the resources since the provided index",
                   "in" : "query",
                   "name" : "since",
@@ -298,6 +316,24 @@
             "description" : "Get detailed information about the DICOM instance whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the instance of interest",
                   "in" : "path",
                   "name" : "id",
@@ -370,7 +406,7 @@
                               "type" : "boolean"
                            },
                            "Keep" : {
-                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization",
+                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -380,19 +416,23 @@
                               "description" : "Keep the private tags from the DICOM instances (defaults to `false`)",
                               "type" : "boolean"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "PrivateCreator" : {
                               "description" : "The private creator to be used for private tags in `Replace`",
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of additional tags to be removed from the DICOM instances",
+                              "description" : "List of additional tags to be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
                               "type" : "array"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            }
                         }
@@ -455,7 +495,7 @@
             "description" : "Delete an attachment associated with the given DICOM instance. This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -1842,21 +1882,21 @@
             "description" : "Get the DICOM tags in the meta-header of the DICOM instance. By default, the `full` format is used, which combines hexadecimal tags with human-readable description.",
             "parameters" : [
                {
-                  "description" : "If present, report the DICOM tags indexed in hexadecimal format",
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
                   "in" : "query",
                   "name" : "short",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
-                  }
-               },
-               {
-                  "description" : "If present, report the DICOM tags in human-readable format",
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
                   "in" : "query",
                   "name" : "simplify",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
+                     "type" : "boolean"
                   }
                },
                {
@@ -2196,7 +2236,7 @@
             "description" : "Delete some metadata associated with the given DICOM instance. This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -2366,12 +2406,16 @@
                               },
                               "type" : "array"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "PrivateCreator" : {
                               "description" : "The private creator to be used for private tags in `Replace`",
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of tags that must be removed from the DICOM instances",
+                              "description" : "List of tags that must be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -2382,7 +2426,7 @@
                               "type" : "boolean"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Transcode" : {
@@ -2429,6 +2473,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the instance of interest",
                   "in" : "path",
                   "name" : "id",
@@ -2497,6 +2559,24 @@
             "description" : "Get detailed information about the parent patient of the DICOM instance whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the instance of interest",
                   "in" : "path",
                   "name" : "id",
@@ -2766,6 +2846,24 @@
             "description" : "Get detailed information about the parent series of the DICOM instance whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the instance of interest",
                   "in" : "path",
                   "name" : "id",
@@ -2827,7 +2925,7 @@
       "/instances/{id}/simplified-tags" : {
          "get" : {
             "deprecated" : false,
-            "description" : "Get the DICOM tags in human-readable format",
+            "description" : "Get the DICOM tags in human-readable format (same as the `/instances/{id}/tags?simplify` route)",
             "parameters" : [
                {
                   "description" : "Also include the DICOM tags that are provided in this list, even if their associated value is long",
@@ -2961,6 +3059,24 @@
             "description" : "Get detailed information about the parent study of the DICOM instance whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the instance of interest",
                   "in" : "path",
                   "name" : "id",
@@ -3019,21 +3135,33 @@
             "description" : "Get the DICOM tags in the specified format. By default, the `full` format is used, which combines hexadecimal tags with human-readable description.",
             "parameters" : [
                {
-                  "description" : "If present, report the DICOM tags indexed in hexadecimal format",
+                  "description" : "Also include the DICOM tags that are provided in this list, even if their associated value is long",
+                  "in" : "query",
+                  "name" : "ignore-length",
+                  "required" : false,
+                  "schema" : {
+                     "items" : {
+                        "type" : "string"
+                     },
+                     "type" : "array"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
                   "in" : "query",
                   "name" : "short",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
-                  }
-               },
-               {
-                  "description" : "If present, report the DICOM tags in human-readable format (same as the `/instances/{id}/simplified-tags` route)",
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
                   "in" : "query",
                   "name" : "simplify",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
+                     "type" : "boolean"
                   }
                },
                {
@@ -3137,10 +3265,8 @@
                         "schema" : {
                            "description" : "JSON array containing either the jobs identifiers, or detailed information about the reported jobs (if `expand` argument is provided)",
                            "example" : [
-                              "7a09255e-84f9-4ac8-8f63-81a674ad130e",
-                              "7b065aae-0255-4e51-bdfc-d3273d1c5e78",
-                              "e093b29c-8ac1-4d25-9d9b-2fa73bd408cd",
-                              "..."
+                              "0c686aae-1509-4179-a8c5-1824c180bb84",
+                              "f30d99df-3a10-480f-96a9-d0701550f07c"
                            ]
                         }
                      }
@@ -3850,7 +3976,21 @@
                "content" : {
                   "application/json" : {
                      "schema" : {
-                        "description" : "Associative array containing the query on the values of the DICOM tags"
+                        "description" : "",
+                        "properties" : {
+                           "Full" : {
+                              "description" : "If set to `true`, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                              "type" : "boolean"
+                           },
+                           "Query" : {
+                              "description" : "Associative array containing the filter on the values of the DICOM tags",
+                              "type" : "object"
+                           },
+                           "Short" : {
+                              "description" : "If set to `true`, report the DICOM tags in hexadecimal format",
+                              "type" : "boolean"
+                           }
+                        }
                      }
                   }
                }
@@ -3893,10 +4033,6 @@
                      "schema" : {
                         "description" : "",
                         "properties" : {
-                           "Asynchronous" : {
-                              "description" : "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately return, reporting the identifier of a job. Prefer this flavor wherever possible.",
-                              "type" : "boolean"
-                           },
                            "Level" : {
                               "description" : "Level of the query (`Patient`, `Study`, `Series` or `Instance`)",
                               "type" : "string"
@@ -3905,14 +4041,6 @@
                               "description" : "Local AET that is used for this commands, defaults to `DicomAet` configuration option. Ignored if `DicomModalities` already sets `LocalAet` for this modality.",
                               "type" : "string"
                            },
-                           "Permissive" : {
-                              "description" : "If `true`, ignore errors during the individual steps of the job.",
-                              "type" : "boolean"
-                           },
-                           "Priority" : {
-                              "description" : "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.",
-                              "type" : "number"
-                           },
                            "Resources" : {
                               "description" : "List of the Orthanc identifiers of all the DICOM resources to be sent",
                               "items" : {
@@ -3920,10 +4048,6 @@
                               },
                               "type" : "array"
                            },
-                           "Synchronous" : {
-                              "description" : "If `true`, run the job in synchronous mode, which means that the HTTP answer will directly contain the result of the job. This is the default, easy behavior, but it is *not* desirable for long jobs, as it might lead to network timeouts.",
-                              "type" : "boolean"
-                           },
                            "TargetAet" : {
                               "description" : "Target AET that will be used by the remote DICOM modality as a target for its C-STORE SCU commands, defaults to `DicomAet` configuration option in order to do a simple query/retrieve",
                               "type" : "string"
@@ -3939,24 +4063,6 @@
             },
             "responses" : {
                "200" : {
-                  "content" : {
-                     "application/json" : {
-                        "examples" : {},
-                        "schema" : {
-                           "description" : "",
-                           "properties" : {
-                              "ID" : {
-                                 "description" : "In asynchronous mode, identifier of the job",
-                                 "type" : "string"
-                              },
-                              "Path" : {
-                                 "description" : "In asynchronous mode, path to access the job in the REST API",
-                                 "type" : "string"
-                              }
-                           }
-                        }
-                     }
-                  },
                   "description" : ""
                }
             },
@@ -4275,6 +4381,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Limit the number of results",
                   "in" : "query",
                   "name" : "limit",
@@ -4284,6 +4399,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Show only the resources since the provided index",
                   "in" : "query",
                   "name" : "since",
@@ -4341,6 +4465,24 @@
             "description" : "Get detailed information about the DICOM patient whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -4411,7 +4553,7 @@
                               "type" : "boolean"
                            },
                            "Keep" : {
-                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization",
+                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -4421,6 +4563,10 @@
                               "description" : "Keep the private tags from the DICOM instances (defaults to `false`)",
                               "type" : "boolean"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -4434,14 +4580,14 @@
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of additional tags to be removed from the DICOM instances",
+                              "description" : "List of additional tags to be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
                               "type" : "array"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Synchronous" : {
@@ -4541,7 +4687,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Priority" : {
@@ -4549,7 +4695,7 @@
                               "type" : "number"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -4632,7 +4778,7 @@
             "description" : "Delete an attachment associated with the given DICOM patient. This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -5287,6 +5433,24 @@
             "description" : "Get detailed information about the child instances of the DICOM patient whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -5398,6 +5562,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -5636,7 +5818,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Extended" : {
@@ -5648,7 +5830,7 @@
                               "type" : "number"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -5740,7 +5922,7 @@
             "description" : "Delete some metadata associated with the given DICOM patient. This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -5914,6 +6096,10 @@
                               },
                               "type" : "array"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -5927,7 +6113,7 @@
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of tags that must be removed from the DICOM instances",
+                              "description" : "List of tags that must be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -5938,7 +6124,7 @@
                               "type" : "boolean"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Synchronous" : {
@@ -5999,6 +6185,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -6120,6 +6324,24 @@
             "description" : "Get detailed information about the child series of the DICOM patient whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -6168,6 +6390,24 @@
             "description" : "Extract the DICOM tags whose value is constant across all the child instances of the DICOM patient whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -6317,6 +6557,24 @@
             "description" : "Get detailed information about the child studies of the DICOM patient whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the patient of interest",
                   "in" : "path",
                   "name" : "id",
@@ -6751,10 +7009,14 @@
                               "gdcm",
                               "mysql-index",
                               "mysql-storage",
+                              "odbc-index",
+                              "odbc-storage",
                               "postgresql-index",
                               "postgresql-storage",
                               "serve-folders",
+                              "stone-rtviewer",
                               "stone-webviewer",
+                              "tcia",
                               "transfers",
                               "web-viewer",
                               "worklists",
@@ -6818,7 +7080,7 @@
                               "ExtendsOrthancExplorer" : true,
                               "ID" : "dicom-web",
                               "RootUri" : "../dicom-web/app/client/index.html",
-                              "Version" : "1.5"
+                              "Version" : "1.6"
                            }
                         }
                      }
@@ -6921,12 +7183,21 @@
                   }
                },
                {
-                  "description" : "If present and if `expand` is present, format the tags of the answers in human-readable format",
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
                   "in" : "query",
                   "name" : "simplify",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
+                     "type" : "boolean"
                   }
                },
                {
@@ -7003,12 +7274,21 @@
             "description" : "Get the content (DICOM tags) of one answer associated with the query/retrieve operation whose identifier is provided in the URL",
             "parameters" : [
                {
-                  "description" : "If present, format the tags of the answer in human-readable format",
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
                   "in" : "query",
                   "name" : "simplify",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
+                     "type" : "boolean"
                   }
                },
                {
@@ -7291,6 +7571,10 @@
                               "description" : "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately return, reporting the identifier of a job. Prefer this flavor wherever possible.",
                               "type" : "boolean"
                            },
+                           "Full" : {
+                              "description" : "If set to `true`, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -7299,6 +7583,10 @@
                               "description" : "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.",
                               "type" : "number"
                            },
+                           "Simplify" : {
+                              "description" : "If set to `true`, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                              "type" : "boolean"
+                           },
                            "Synchronous" : {
                               "description" : "If `true`, run the job in synchronous mode, which means that the HTTP answer will directly contain the result of the job. This is the default, easy behavior, but it is *not* desirable for long jobs, as it might lead to network timeouts.",
                               "type" : "boolean"
@@ -7418,12 +7706,21 @@
             "description" : "Get the original DICOM filter associated with the query/retrieve operation whose identifier is provided in the URL",
             "parameters" : [
                {
-                  "description" : "If present, format the tags of the DICOM filter in human-readable format",
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
                   "in" : "query",
                   "name" : "simplify",
                   "required" : false,
                   "schema" : {
-                     "type" : "string"
+                     "type" : "boolean"
                   }
                },
                {
@@ -7478,6 +7775,10 @@
                               "description" : "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately return, reporting the identifier of a job. Prefer this flavor wherever possible.",
                               "type" : "boolean"
                            },
+                           "Full" : {
+                              "description" : "If set to `true`, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -7486,6 +7787,10 @@
                               "description" : "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.",
                               "type" : "number"
                            },
+                           "Simplify" : {
+                              "description" : "If set to `true`, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                              "type" : "boolean"
+                           },
                            "Synchronous" : {
                               "description" : "If `true`, run the job in synchronous mode, which means that the HTTP answer will directly contain the result of the job. This is the default, easy behavior, but it is *not* desirable for long jobs, as it might lead to network timeouts.",
                               "type" : "boolean"
@@ -7550,6 +7855,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Limit the number of results",
                   "in" : "query",
                   "name" : "limit",
@@ -7559,6 +7873,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Show only the resources since the provided index",
                   "in" : "query",
                   "name" : "since",
@@ -7616,6 +7939,24 @@
             "description" : "Get detailed information about the DICOM series whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -7695,7 +8036,7 @@
                               "type" : "boolean"
                            },
                            "Keep" : {
-                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization",
+                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -7705,6 +8046,10 @@
                               "description" : "Keep the private tags from the DICOM instances (defaults to `false`)",
                               "type" : "boolean"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -7718,14 +8063,14 @@
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of additional tags to be removed from the DICOM instances",
+                              "description" : "List of additional tags to be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
                               "type" : "array"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Synchronous" : {
@@ -7825,7 +8170,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Priority" : {
@@ -7833,7 +8178,7 @@
                               "type" : "number"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -7916,7 +8261,7 @@
             "description" : "Delete an attachment associated with the given DICOM series. This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -8571,6 +8916,24 @@
             "description" : "Get detailed information about the child instances of the DICOM series whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -8630,6 +8993,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -8755,7 +9136,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Extended" : {
@@ -8767,7 +9148,7 @@
                               "type" : "number"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -8859,7 +9240,7 @@
             "description" : "Delete some metadata associated with the given DICOM series. This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -9033,6 +9414,10 @@
                               },
                               "type" : "array"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -9046,7 +9431,7 @@
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of tags that must be removed from the DICOM instances",
+                              "description" : "List of tags that must be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -9057,7 +9442,7 @@
                               "type" : "boolean"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Synchronous" : {
@@ -9118,6 +9503,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -9373,6 +9776,24 @@
             "description" : "Get detailed information about the parent patient of the DICOM series whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -9441,6 +9862,24 @@
             "description" : "Extract the DICOM tags whose value is constant across all the child instances of the DICOM series whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -9580,6 +10019,24 @@
             "description" : "Get detailed information about the parent study of the DICOM series whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the series of interest",
                   "in" : "path",
                   "name" : "id",
@@ -9795,6 +10252,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Limit the number of results",
                   "in" : "query",
                   "name" : "limit",
@@ -9804,6 +10270,15 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Show only the resources since the provided index",
                   "in" : "query",
                   "name" : "since",
@@ -9861,6 +10336,24 @@
             "description" : "Get detailed information about the DICOM study whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -9953,7 +10446,7 @@
                               "type" : "boolean"
                            },
                            "Keep" : {
-                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization",
+                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -9963,6 +10456,10 @@
                               "description" : "Keep the private tags from the DICOM instances (defaults to `false`)",
                               "type" : "boolean"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -9976,14 +10473,14 @@
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of additional tags to be removed from the DICOM instances",
+                              "description" : "List of additional tags to be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
                               "type" : "array"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Synchronous" : {
@@ -10083,7 +10580,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Priority" : {
@@ -10091,7 +10588,7 @@
                               "type" : "number"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -10174,7 +10671,7 @@
             "description" : "Delete an attachment associated with the given DICOM study. This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the attachment, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -10829,6 +11326,24 @@
             "description" : "Get detailed information about the child instances of the DICOM study whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -10950,6 +11465,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -11188,7 +11721,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Extended" : {
@@ -11200,7 +11733,7 @@
                               "type" : "number"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -11248,7 +11781,7 @@
       "/studies/{id}/merge" : {
          "post" : {
             "deprecated" : false,
-            "description" : "Start a new job so as to move some DICOM series into the DICOM study whose Orthanc identifier is provided in the URL: https://book.orthanc-server.com/users/anonymization.html#merging",
+            "description" : "Start a new job so as to move some DICOM resources into the DICOM study whose Orthanc identifier is provided in the URL: https://book.orthanc-server.com/users/anonymization.html#merging",
             "parameters" : [
                {
                   "description" : "Orthanc identifier of the study of interest",
@@ -11283,7 +11816,7 @@
                               "type" : "number"
                            },
                            "Resources" : {
-                              "description" : "The list of DICOM resources (patients, studies, series, and/or instances) to be merged into the study of interest (mandatory option)",
+                              "description" : "The list of DICOM resources (studies, series, and/or instances) to be merged into the study of interest (mandatory option)",
                               "items" : {
                                  "type" : "string"
                               },
@@ -11372,7 +11905,7 @@
             "description" : "Delete some metadata associated with the given DICOM study. This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).",
             "parameters" : [
                {
-                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevision` option is `true`.",
+                  "description" : "Revision of the metadata, to check if its content has not changed and can be deleted. This header is mandatory if `CheckRevisions` option is `true`.",
                   "in" : "header",
                   "name" : "If-Match",
                   "required" : false,
@@ -11546,6 +12079,10 @@
                               },
                               "type" : "array"
                            },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
                            "Permissive" : {
                               "description" : "If `true`, ignore errors during the individual steps of the job.",
                               "type" : "boolean"
@@ -11559,7 +12096,7 @@
                               "type" : "string"
                            },
                            "Remove" : {
-                              "description" : "List of tags that must be removed from the DICOM instances",
+                              "description" : "List of tags that must be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "items" : {
                                  "type" : "string"
                               },
@@ -11570,7 +12107,7 @@
                               "type" : "boolean"
                            },
                            "Replace" : {
-                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances",
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
                               "type" : "object"
                            },
                            "Synchronous" : {
@@ -11631,6 +12168,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -11757,6 +12312,24 @@
                   }
                },
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -11810,6 +12383,24 @@
             "description" : "Get detailed information about the parent patient of the DICOM study whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -11878,6 +12469,24 @@
             "description" : "Get detailed information about the child series of the DICOM study whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                  "in" : "query",
+                  "name" : "full",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -11987,6 +12596,24 @@
             "description" : "Extract the DICOM tags whose value is constant across all the child instances of the DICOM study whose Orthanc identifier is provided in the URL",
             "parameters" : [
                {
+                  "description" : "If present, report the DICOM tags in hexadecimal format",
+                  "in" : "query",
+                  "name" : "short",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
+                  "description" : "If present, report the DICOM tags in human-readable format (using the symbolic name of the tags)",
+                  "in" : "query",
+                  "name" : "simplify",
+                  "required" : false,
+                  "schema" : {
+                     "type" : "boolean"
+                  }
+               },
+               {
                   "description" : "Orthanc identifier of the study of interest",
                   "in" : "path",
                   "name" : "id",
@@ -12043,7 +12670,7 @@
       "/studies/{id}/split" : {
          "post" : {
             "deprecated" : false,
-            "description" : "Start a new job so as to split the DICOM study whose Orthanc identifier is provided in the URL, by taking some of its children series out of it and putting them into a brand new study (this new study is created by setting the `StudyInstanceUID` tag to a random identifier): https://book.orthanc-server.com/users/anonymization.html#splitting",
+            "description" : "Start a new job so as to split the DICOM study whose Orthanc identifier is provided in the URL, by taking some of its children series or instances out of it and putting them into a brand new study (this new study is created by setting the `StudyInstanceUID` tag to a random identifier): https://book.orthanc-server.com/users/anonymization.html#splitting",
             "parameters" : [
                {
                   "description" : "Orthanc identifier of the study of interest",
@@ -12065,8 +12692,15 @@
                               "description" : "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately return, reporting the identifier of a job. Prefer this flavor wherever possible.",
                               "type" : "boolean"
                            },
+                           "Instances" : {
+                              "description" : "The list of instances to be separated from the parent study. These instances must all be children of the same source study, that is specified in the URI.",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
                            "KeepSource" : {
-                              "description" : "If set to `true`, instructs Orthanc to keep a copy of the original series in the source study. By default, the original series are deleted from Orthanc.",
+                              "description" : "If set to `true`, instructs Orthanc to keep a copy of the original series/instances in the source study. By default, the original series/instances are deleted from Orthanc.",
                               "type" : "boolean"
                            },
                            "Permissive" : {
@@ -12089,7 +12723,7 @@
                               "type" : "object"
                            },
                            "Series" : {
-                              "description" : "The list of series to be separated from the parent study (mandatory option). These series must all be children of the same source study, that is specified in the URI.",
+                              "description" : "The list of series to be separated from the parent study. These series must all be children of the same source study, that is specified in the URI.",
                               "items" : {
                                  "type" : "string"
                               },
@@ -12228,7 +12862,7 @@
                         "schema" : {
                            "description" : "",
                            "example" : {
-                              "ApiVersion" : 12,
+                              "ApiVersion" : 14,
                               "CheckRevisions" : false,
                               "DatabaseBackendPlugin" : null,
                               "DatabaseVersion" : 6,
@@ -12239,7 +12873,7 @@
                               "Name" : "Orthanc Demo",
                               "PluginsEnabled" : true,
                               "StorageAreaPlugin" : null,
-                              "Version" : "1.9.2"
+                              "Version" : "1.9.6"
                            },
                            "properties" : {
                               "ApiVersion" : {
@@ -12314,6 +12948,10 @@
                            "description" : "List of the available operations",
                            "example" : [
                               "accepted-transfer-syntaxes",
+                              "bulk-anonymize",
+                              "bulk-content",
+                              "bulk-delete",
+                              "bulk-modify",
                               "create-archive",
                               "create-dicom",
                               "create-media",
@@ -12409,6 +13047,298 @@
             "tags" : [ "System" ]
          }
       },
+      "/tools/bulk-anonymize" : {
+         "post" : {
+            "deprecated" : false,
+            "description" : "Start a job that will anonymize all the DICOM patients, studies, series or instances whose identifiers are provided in the `Resources` field.",
+            "parameters" : [],
+            "requestBody" : {
+               "content" : {
+                  "application/json" : {
+                     "schema" : {
+                        "description" : "",
+                        "properties" : {
+                           "Asynchronous" : {
+                              "description" : "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately return, reporting the identifier of a job. Prefer this flavor wherever possible.",
+                              "type" : "boolean"
+                           },
+                           "DicomVersion" : {
+                              "description" : "Version of the DICOM standard to be used for anonymization. Check out configuration option `DeidentifyLogsDicomVersion` for possible values.",
+                              "type" : "string"
+                           },
+                           "Force" : {
+                              "description" : "Allow the modification of tags related to DICOM identifiers, at the risk of breaking the DICOM model of the real world",
+                              "type" : "boolean"
+                           },
+                           "Keep" : {
+                              "description" : "List of DICOM tags whose value must not be destroyed by the anonymization. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "KeepPrivateTags" : {
+                              "description" : "Keep the private tags from the DICOM instances (defaults to `false`)",
+                              "type" : "boolean"
+                           },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
+                           "Permissive" : {
+                              "description" : "If `true`, ignore errors during the individual steps of the job.",
+                              "type" : "boolean"
+                           },
+                           "Priority" : {
+                              "description" : "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.",
+                              "type" : "number"
+                           },
+                           "PrivateCreator" : {
+                              "description" : "The private creator to be used for private tags in `Replace`",
+                              "type" : "string"
+                           },
+                           "Remove" : {
+                              "description" : "List of additional tags to be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "Replace" : {
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
+                              "type" : "object"
+                           },
+                           "Resources" : {
+                              "description" : "List of the Orthanc identifiers of the patients/studies/series/instances of interest.",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "Synchronous" : {
+                              "description" : "If `true`, run the job in synchronous mode, which means that the HTTP answer will directly contain the result of the job. This is the default, easy behavior, but it is *not* desirable for long jobs, as it might lead to network timeouts.",
+                              "type" : "boolean"
+                           }
+                        }
+                     }
+                  }
+               }
+            },
+            "responses" : {
+               "200" : {
+                  "content" : {
+                     "application/json" : {
+                        "examples" : {},
+                        "schema" : {
+                           "description" : "The list of all the resources that have been created by this anonymization",
+                           "properties" : {
+                              "ID" : {
+                                 "description" : "In asynchronous mode, identifier of the job",
+                                 "type" : "string"
+                              },
+                              "Path" : {
+                                 "description" : "In asynchronous mode, path to access the job in the REST API",
+                                 "type" : "string"
+                              }
+                           }
+                        }
+                     }
+                  },
+                  "description" : ""
+               }
+            },
+            "summary" : "Anonymize a set of resources",
+            "tags" : [ "System" ]
+         }
+      },
+      "/tools/bulk-content" : {
+         "post" : {
+            "deprecated" : false,
+            "description" : "Get the content all the DICOM patients, studies, series or instances whose identifiers are provided in the `Resources` field, in one single call.",
+            "parameters" : [],
+            "requestBody" : {
+               "content" : {
+                  "application/json" : {
+                     "schema" : {
+                        "description" : "",
+                        "properties" : {
+                           "Full" : {
+                              "description" : "If set to `true`, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                              "type" : "boolean"
+                           },
+                           "Level" : {
+                              "description" : "This optional argument specifies the level of interest (can be `Patient`, `Study`, `Series` or `Instance`). Orthanc will loop over the items inside `Resources`, and explorer upward or downward in the DICOM hierarchy in order to find the level of interest.",
+                              "type" : "string"
+                           },
+                           "Metadata" : {
+                              "description" : "If set to `true` (default value), the metadata associated with the resources will also be retrieved.",
+                              "type" : "boolean"
+                           },
+                           "Resources" : {
+                              "description" : "List of the Orthanc identifiers of the patients/studies/series/instances of interest.",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "Short" : {
+                              "description" : "If set to `true`, report the DICOM tags in hexadecimal format",
+                              "type" : "boolean"
+                           }
+                        }
+                     }
+                  }
+               }
+            },
+            "responses" : {
+               "200" : {
+                  "description" : ""
+               }
+            },
+            "summary" : "Describe a set of instances",
+            "tags" : [ "System" ]
+         }
+      },
+      "/tools/bulk-delete" : {
+         "post" : {
+            "deprecated" : false,
+            "description" : "Delete all the DICOM patients, studies, series or instances whose identifiers are provided in the `Resources` field.",
+            "parameters" : [],
+            "requestBody" : {
+               "content" : {
+                  "application/json" : {
+                     "schema" : {
+                        "description" : "",
+                        "properties" : {
+                           "Resources" : {
+                              "description" : "List of the Orthanc identifiers of the patients/studies/series/instances of interest.",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           }
+                        }
+                     }
+                  }
+               }
+            },
+            "responses" : {
+               "200" : {
+                  "description" : ""
+               }
+            },
+            "summary" : "Delete a set of instances",
+            "tags" : [ "System" ]
+         }
+      },
+      "/tools/bulk-modify" : {
+         "post" : {
+            "deprecated" : false,
+            "description" : "Start a job that will modify all the DICOM patients, studies, series or instances whose identifiers are provided in the `Resources` field.",
+            "parameters" : [],
+            "requestBody" : {
+               "content" : {
+                  "application/json" : {
+                     "schema" : {
+                        "description" : "",
+                        "properties" : {
+                           "Asynchronous" : {
+                              "description" : "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately return, reporting the identifier of a job. Prefer this flavor wherever possible.",
+                              "type" : "boolean"
+                           },
+                           "Force" : {
+                              "description" : "Allow the modification of tags related to DICOM identifiers, at the risk of breaking the DICOM model of the real world",
+                              "type" : "boolean"
+                           },
+                           "Keep" : {
+                              "description" : "Keep the original value of the specified tags, to be chosen among the `StudyInstanceUID`, `SeriesInstanceUID` and `SOPInstanceUID` tags. Avoid this feature as much as possible, as this breaks the DICOM model of the real world.",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "KeepSource" : {
+                              "description" : "If set to `false`, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.",
+                              "type" : "boolean"
+                           },
+                           "Level" : {
+                              "description" : "Level of the modification (`Patient`, `Study`, `Series` or `Instance`). If absent, the level defaults to `Instance`, but is set to `Patient` if `PatientID` is modified, to `Study` if `StudyInstanceUID` is modified, or to `Series` if `SeriesInstancesUID` is modified. (new in Orthanc 1.9.7)",
+                              "type" : "string"
+                           },
+                           "Permissive" : {
+                              "description" : "If `true`, ignore errors during the individual steps of the job.",
+                              "type" : "boolean"
+                           },
+                           "Priority" : {
+                              "description" : "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.",
+                              "type" : "number"
+                           },
+                           "PrivateCreator" : {
+                              "description" : "The private creator to be used for private tags in `Replace`",
+                              "type" : "string"
+                           },
+                           "Remove" : {
+                              "description" : "List of tags that must be removed from the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "RemovePrivateTags" : {
+                              "description" : "Remove the private tags from the DICOM instances (defaults to `false`)",
+                              "type" : "boolean"
+                           },
+                           "Replace" : {
+                              "description" : "Associative array to change the value of some DICOM tags in the DICOM instances. Starting with Orthanc 1.9.4, paths to subsequences can be provided using the same syntax as the `dcmodify` command-line tool (wildcards are supported as well).",
+                              "type" : "object"
+                           },
+                           "Resources" : {
+                              "description" : "List of the Orthanc identifiers of the patients/studies/series/instances of interest.",
+                              "items" : {
+                                 "type" : "string"
+                              },
+                              "type" : "array"
+                           },
+                           "Synchronous" : {
+                              "description" : "If `true`, run the job in synchronous mode, which means that the HTTP answer will directly contain the result of the job. This is the default, easy behavior, but it is *not* desirable for long jobs, as it might lead to network timeouts.",
+                              "type" : "boolean"
+                           },
+                           "Transcode" : {
+                              "description" : "Transcode the DICOM instances to the provided DICOM transfer syntax: https://book.orthanc-server.com/faq/transcoding.html",
+                              "type" : "string"
+                           }
+                        }
+                     }
+                  }
+               }
+            },
+            "responses" : {
+               "200" : {
+                  "content" : {
+                     "application/json" : {
+                        "examples" : {},
+                        "schema" : {
+                           "description" : "The list of all the resources that have been altered by this modification",
+                           "properties" : {
+                              "ID" : {
+                                 "description" : "In asynchronous mode, identifier of the job",
+                                 "type" : "string"
+                              },
+                              "Path" : {
+                                 "description" : "In asynchronous mode, path to access the job in the REST API",
+                                 "type" : "string"
+                              }
+                           }
+                        }
+                     }
+                  },
+                  "description" : ""
+               }
+            },
+            "summary" : "Modify a set of resources",
+            "tags" : [ "System" ]
+         }
+      },
       "/tools/create-archive" : {
          "post" : {
             "deprecated" : false,
@@ -12421,7 +13351,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Priority" : {
@@ -12436,7 +13366,7 @@
                               "type" : "array"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -12560,7 +13490,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Extended" : {
@@ -12579,7 +13509,7 @@
                               "type" : "array"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -12636,7 +13566,7 @@
                         "description" : "",
                         "properties" : {
                            "Asynchronous" : {
-                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background. Prefer this flavor wherever possible.",
+                              "description" : "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create the archive in background.",
                               "type" : "boolean"
                            },
                            "Extended" : {
@@ -12655,7 +13585,7 @@
                               "type" : "array"
                            },
                            "Synchronous" : {
-                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive large amount of data, as it might lead to network timeouts.",
+                              "description" : "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly contain the ZIP file. This is the default, easy behavior. However, if global configuration option \"SynchronousZipStream\" is set to \"false\", asynchronous transfers should be preferred for large amount of data, as the creation of the temporary file might lead to network timeouts.",
                               "type" : "boolean"
                            },
                            "Transcode" : {
@@ -12868,6 +13798,10 @@
                               "description" : "Also retrieve the content of the matching resources, not only their Orthanc identifiers",
                               "type" : "boolean"
                            },
+                           "Full" : {
+                              "description" : "If set to `true`, report the DICOM tags in full format (tags indexed by their hexadecimal format, associated with their symbolic name and their value)",
+                              "type" : "boolean"
+                           },
                            "Level" : {
                               "description" : "Level of the query (`Patient`, `Study`, `Series` or `Instance`)",
                               "type" : "string"
@@ -12880,6 +13814,10 @@
                               "description" : "Associative array containing the filter on the values of the DICOM tags",
                               "type" : "object"
                            },
+                           "Short" : {
+                              "description" : "If set to `true`, report the DICOM tags in hexadecimal format",
+                              "type" : "boolean"
+                           },
                            "Since" : {
                               "description" : "Show only the resources since the provided index (in conjunction with `Limit`)",
                               "type" : "number"
@@ -13379,7 +14317,7 @@
                "200" : {
                   "content" : {
                      "text/plain" : {
-                        "example" : "orthanc_count_instances 2552 1619185717552\northanc_count_patients 8 1619185717552\northanc_count_series 26 1619185717552\northanc_count_studies 8 1619185717552\northanc_dicom_cache_count 58 1619172290641\northanc_dicom_cache_size 50.601326 1619172290641\northanc_disk_size_mb 1018.65021 1619185717552\northanc_jobs_completed 4 1619185717552\northanc_jobs_failed 0 1619185717552\northanc_jobs_pending 0 1619185717552\northanc_jobs_running 0 1619185717552\northanc_jobs_success 4 1619185717552\northanc_rest_api_active_requests 1 1619185717552\northanc_rest_api_duration_ms 4519 1619185710153\northanc_storage_read_duration_ms 0 1619185717053\northanc_uncompressed_size_mb 1018.65021 1619185717552\n"
+                        "example" : "orthanc_count_instances 2552 1630401803782\northanc_count_patients 8 1630401803782\northanc_count_series 26 1630401803782\northanc_count_studies 8 1630401803782\northanc_dicom_cache_count 213 1630393626028\northanc_dicom_cache_size 125.938728 1630393626028\northanc_disk_size_mb 1018.65021 1630401803782\northanc_jobs_completed 2 1630401803782\northanc_jobs_failed 0 1630401803782\northanc_jobs_pending 0 1630401803782\northanc_jobs_running 0 1630401803782\northanc_jobs_success 2 1630401803782\northanc_rest_api_active_requests 1 1630401803781\northanc_rest_api_duration_ms 353 1630401802149\northanc_storage_read_duration_ms 0 1630401797848\northanc_uncompressed_size_mb 1018.65021 1630401803782\n"
                      }
                   },
                   "description" : ""
@@ -13398,7 +14336,7 @@
                "200" : {
                   "content" : {
                      "text/plain" : {
-                        "example" : "20210423T134837",
+                        "example" : "20210831T092323",
                         "schema" : {
                            "description" : "The UTC time"
                         }
@@ -13420,7 +14358,7 @@
                "200" : {
                   "content" : {
                      "text/plain" : {
-                        "example" : "20210423T134837",
+                        "example" : "20210831T092323",
                         "schema" : {
                            "description" : "The local time"
                         }
--- a/Sphinx/source/_templates/layout.html	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/_templates/layout.html	Mon Feb 14 08:52:12 2022 +0100
@@ -17,7 +17,8 @@
     </p>
     <p>
 
-      &copy; Copyright 2015-2021, University Hospital of Liège and Osimis S.A., Belgium, and the Orthanc community<br/>
+      &copy; Copyright 2015-2021, University Hospital of Liège, Osimis
+      S.A. (Belgium), UCLouvain ICTEAM, and the Orthanc community<br/>
       The Orthanc Book is licensed under
       <a href="http://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Creative 
         Commons CC-BY-SA 4.0</a>.<br/>
--- a/Sphinx/source/contributing.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/contributing.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -18,10 +18,11 @@
           * **Orthanc Core**:
 
             - :ref:`Improved Web user interface <improving-interface>`
-          
+            - Support of DICOM C-GET SCU
+   
           * **Stone Web viewer**:
-     
-            - Annotations/measurements
+
+            - Save/load annotations
             - Internationalization/translations
             - MPR volume rendering
             - Viewer dedicated to nuclear medicine and radiotherapy
@@ -29,8 +30,7 @@
 
           * **Plugins**:
 
-            - Support of MSSQL databases
-            - :ref:`Worklist plugin <worklist>` to interface with REST API, HL7 or Myrth
+            - :ref:`Worklist plugin <worklist>` to interface with REST API, HL7 or Mirth
             
           Please `get in touch with Osimis <info@osimis.io>`__ if you
           want to sponsor these developments.
@@ -79,7 +79,12 @@
     GNU/Linux distributions (e.g. Ubuntu PPA, RHEL/`EPEL
     <https://fedoraproject.org/wiki/EPEL>`__, CentOS, openSUSE...).
   - Take ownership of the now-orphaned `Fedora package
-    <https://src.fedoraproject.org/rpms/orthanc>`__.
+    <https://src.fedoraproject.org/rpms/orthanc>`__. Check out the
+    related `issue 1677806
+    <https://bugzilla.redhat.com/show_bug.cgi?id=1677806>`__ and
+    `issue 1843127
+    <https://bugzilla.redhat.com/show_bug.cgi?id=1843127>`__.
+  - Take care of :ref:`Debian/Ubuntu backporting <debian-packages>`.
   - Share your maintenance scripts or sample code inside the "`Orthanc Contributed
     <https://github.com/jodogne/OrthancContributed>`_" public GitHub
     repository, via pull requests.
@@ -93,6 +98,8 @@
 
 * **Coding tasks**:
       
+  - Have a look at the TODO file containing our `official roadmap
+    <https://hg.orthanc-server.com/orthanc/file/default/TODO>`__.
   - The Orthanc project will happily accept patches in the core of
     Orthanc and in its associated official plugins. Please read the
     :ref:`dedicated FAQ entry <cla>`.
@@ -133,9 +140,20 @@
       <https://twitter.com/ZeClint/status/1192086039160086529?s=20>`__
       to be handled by the PACS, not by the RIS). Reference: Events
       ``Axx`` of Chapter 3 ("Patient Administration") in the HL7 v2.9
-      specification.
-    + Have a look at the TODO file containing our `official roadmap
-      <https://hg.orthanc-server.com/orthanc/file/default/TODO>`__.
+      specification. ``ADT`` messages have also been `discussed in the
+      past on the Orthanc forum
+      <https://groups.google.com/g/orthanc-users/c/Spjtcj9vSPo/m/ktUArWxUDQAJ>`__.
+    + Create a `DICOM proxy
+      <https://groups.google.com/g/orthanc-users/c/15dYEm4Tguw/m/PoldpTOQAQAJ>`__
+      (to share a single connection on a PACS by several DICOM
+      clients/viewers), or a `DICOMweb proxy
+      <https://groups.google.com/g/orthanc-users/c/AQ6qs0TgO6I/m/WxdOVEeKBAAJ>`__
+      (to turn a DICOM-only PACS into a DICOMweb server). This could
+      be done as a :ref:`Python plugin <python-plugin>` by wrapping
+      the C-FIND and C-MOVE callbacks in the Python API.
+    + Get involved in the call for ideas by Salim Kanoun about a
+      `DICOM router built on the top of Orthanc
+      <https://groups.google.com/g/orthanc-users/c/tx7E1RQuKIY/m/_GsrRZljBgAJ>`__.
   
   - Always remember that he **recommended way of contributing to the
     source code of Orthanc is by creating C/C++/Python plugins, or by
@@ -146,10 +164,13 @@
     <https://groups.google.com/forum/#!forum/orthanc-users>`_.
 
 
-* **Financial support**:
+* **Financial support**: Buying professional services is the best way
+  to make the Orthanc project sustainable in the long term.
 
   - Osimis provides `support packs and professional development
-    services <https://www.osimis.io/en/services.html>`__ around the
+    services <https://osimis.io/en/orthanc-support-contract>`__ around the
     Orthanc ecosystem and, more generally, around medical
-    imaging. Buying such professional services is the best way to make
-    the Orthanc project sustainable in the long term.
+    imaging. 
+
+  - Check out the :ref:`professional services provided by our
+    community <support-freelancers>`.
--- a/Sphinx/source/developers/creating-plugins.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/developers/creating-plugins.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -12,16 +12,29 @@
 <contributing>` consists in extending it by creating new :ref:`plugins
 <plugins>`.
 
-Orthanc plugins must use the `plugin SDK
-<https://sdk.orthanc-server.com/>`__ and must be written in C or
-C++. They must also fullfil the terms of the `GPLv3 license
-<http://www.gnu.org/licenses/quick-guide-gplv3.en.html>`__ that is
-used by the core of Orthanc.
+Native Orthanc plugins must use the `plugin SDK
+<https://sdk.orthanc-server.com/>`__ whose interface is available as a
+`C header
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__.
+As a consequence, an Orthanc plugin will typically be written using C
+or C++, although it is also possible to create native plugins using
+languages that feature compatibility with C headers and with `FFI of
+the C language
+<https://en.wikipedia.org/wiki/Foreign_function_interface>`__ (such as
+Rust or Objective-C).
 
 For developers who are more familiar with Python, it is also possible
 to create plugins using this simpler language. Check out the
 :ref:`dedicated Python plugin <python-plugin>`.
 
+Because the C header providing the Orthanc SDK interface is licensed
+using the GPLv3 license, any Orthanc plugin must be licensed either
+under the `GPLv3 license
+<http://www.gnu.org/licenses/quick-guide-gplv3.en.html>`__ that is
+used by the :ref:`core of Orthanc <licensing>`, or under a more
+restrictive license that is compatible with the GPL (typically the
+AGPL).
+
 Here are some resources about creating C/C++ plugins:
 
 * Sample code for plugins can be found `in the official Orthanc
@@ -56,7 +69,7 @@
 
 A plugin takes the form of a shared library (``.DLL`` under Windows,
 ``.so`` under GNU/Linux, ``.dylib`` under Apple OS X...) that uses the
-`ABI of the C language
+`FFI of the C language
 <https://en.wikipedia.org/wiki/Application_binary_interface>`__ to
 declare 4 public functions/symbols:
 
@@ -83,7 +96,7 @@
 that is part of the Orthanc source distribution:
 
 * `Plugins/Include/orthanc/OrthancCPlugin.h
-  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__
+  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__
 
 `Online documentation <https://sdk.orthanc-server.com/>`__ for this C
 header is available, as generated by `Doxygen
@@ -97,8 +110,8 @@
 ``HAS_ORTHANC_EXCEPTION`` is set to ``0``:
 
 * `Plugins/Samples/Common/OrthanPluginCppWrapper.h
-  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h>`__
+  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h>`__
 * `Plugins/Samples/Common/OrthanPluginCppWrapper.cpp
-  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp>`__
+  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp>`__
 * `Plugins/Samples/Common/OrthanPluginException.h
-  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Plugins/Samples/Common/OrthancPluginException.h>`__
+  <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Samples/Common/OrthancPluginException.h>`__
--- a/Sphinx/source/developers/db-versioning.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/developers/db-versioning.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -26,7 +26,7 @@
 Version                           DB v2   DB v3   DB v4   DB v5   DB v6
 ===============================   =====   =====   =====   =====   =====
 Mainline                                  u       u       u       x
-Orthanc 0.9.5 - Orthanc 1.9.2             u       u       u       x
+Orthanc 0.9.5 - Orthanc 1.9.7             u       u       u       x
 Orthanc 0.8.5 - Orthanc 0.9.4             u       u       x
 Orthanc 0.7.3 - Orthanc 0.8.4             u       x
 Orthanc 0.4.0 - Orthanc 0.7.2             x
--- a/Sphinx/source/developers/repositories.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/developers/repositories.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -84,7 +84,7 @@
 configuration. Add the following lines to your ``~/.hgrc`` file::
 
   [hostfingerprints]
-  hg.orthanc-server.com = 72:F4:32:09:0F:F1:67:03:83:44:BF:0B:44:34:9D:B6:C6:0C:6A:32
+  hg.orthanc-server.com = 06:AE:A1:EE:61:74:77:26:F3:D5:5C:AB:91:0E:39:B1:95:7F:00:25
   
 .. highlight:: bash
 
@@ -117,7 +117,7 @@
 and add the following lines::
 
   [hostsecurity]
-  hg.orthanc-server.com:fingerprints=sha256:74:24:16:D3:C8:22:45:7E:6B:00:6C:63:86:37:9F:A1:B1:04:A7:F4:3C:7E:5A:1A:68:0A:7A:AF:3B:1A:14:52
+  hg.orthanc-server.com:fingerprints=sha256:13:CA:46:BB:84:96:BD:D4:F7:09:94:60:0F:C2:3B:BA:87:E2:33:50:75:ED:0A:44:81:DF:80:EB:44:6B:C5:40
   
 .. highlight:: bash
 
--- a/Sphinx/source/dicom-guide.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/dicom-guide.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -370,7 +370,7 @@
    displayed by :ref:`Orthanc Explorer <orthanc-explorer>` (possibly
    its AET).
 2. The AET of the remote server.
-3. Its IP address or its hostname.
+3. Its IP address.
 4. Its DICOM port (most probably 104, or 4242 if the remote server is
    another instance of Orthanc).
 
@@ -378,6 +378,14 @@
 software must be restarted to take the new parameters into account.
 
 
+**Remark:** The IP address in the ``DicomModalities`` configuration
+option can possibly be replaced by the hostname of the remote modality
+(obtained by DNS). This will work if Orthanc acts as an SCU to this
+remote modality. However, if the remote modality acts as a SCU that
+can contact Orthanc SCP and if the option ``DicomCheckModalityHost``
+is set to ``true``, you are obliged to use an IP address.
+
+
 .. _dicom-echo:
 
 C-Echo: Testing connectivity
@@ -657,7 +665,7 @@
 
     $ getscu -v localhost 4242 -aec ORTHANC -k "0008,0052=STUDY" -k "0020,000d=1.2.840.113543.6.6.4.7.64067529866380271256212683512383713111129"
 
-*Note:* As of Orthanc 1.9.2, Orthanc only implements C-Get as a
+*Note:* As of Orthanc 1.9.7, Orthanc only implements C-Get as a
 service class provider (SCP). Using C-Get as a service class user
 (SCU) is not currently supported in Orthanc.
 
--- a/Sphinx/source/faq.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -68,3 +68,4 @@
    faq/matlab.rst
    faq/series-completion.rst
    faq/dcmtk-tricks.rst
+   faq/debugging-encodings.rst
--- a/Sphinx/source/faq/authentication.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/authentication.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -14,6 +14,9 @@
 * In ``RegisteredUsers``, assign a username and a password to all your
   users.
 
+**Important:** Make sure to read the FAQ about :ref:`how to secure
+Orthanc <security>`.
+  
 Once a user has logged in, she will have full access, in read-write
 mode, to all the features offered by the REST API of Orthanc. This
 built-in mechanism might be of limited usefulness in enterprise
@@ -35,8 +38,10 @@
   `access control lists
   <https://en.wikipedia.org/wiki/Access_control_list>`__.
 * Develop a :ref:`C/C++ plugin <creating-plugins>` that uses the
-  ``OrthancPluginRegisterIncomingHttpRequestFilter()``.  This solution
-  is potentially useful if you wish to integrate with an LDAP server.
+  ``OrthancPluginRegisterIncomingHttpRequestFilter()``, or a
+  :ref:`Python plugin <python_authorization>` that uses
+  ``orthanc.RegisterIncomingHttpRequestFilter()``. This solution is
+  potentially useful if you wish to integrate with an LDAP server.
 * Use Orthanc as a reverse proxy (e.g. behind :ref:`nginx <nginx>`,
   :ref:`Apache <apache>`, or :ref:`Microsoft IIS <iis>`), and use the
   authentication mechanisms of the main Web server.
--- a/Sphinx/source/faq/crash.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/crash.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -64,13 +64,13 @@
 above <segfault-plugin>` is available as the ``crash.cpp`` file, here
 is a sample debug session::
 
-  $ wget https://lsb.orthanc-server.com/orthanc/debug/1.9.2/Orthanc
+  $ wget https://lsb.orthanc-server.com/orthanc/debug/1.9.7/Orthanc
   $ chmod +x ./Orthanc
   $ gcc -fPIC -shared ./crash.cpp -I ~/orthanc/Plugins/Include -o crash.so
   $ ulimit -c unlimited
   $ echo '{ "Plugins" : ["crash.so"] }' > Configuration.json
   $ rm -f core ; ./Orthanc Configuration.json
-  W0427 15:43:24.215783 main.cpp:1436] Orthanc version: 1.9.2
+  W0427 15:43:24.215783 main.cpp:1436] Orthanc version: 1.9.7
   W0427 15:43:24.215910 main.cpp:1279] Performance warning: Non-release build, runtime debug assertions are turned on
   W0427 15:43:24.217585 OrthancConfiguration.cpp:61] Reading the configuration from: "Configuration.json"
   W0427 15:43:24.254733 main.cpp:700] Loading plugin(s) from: crash.so
--- a/Sphinx/source/faq/debian-daemon.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/debian-daemon.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -44,12 +44,14 @@
 ----------------
 
 A sample `systemd daemon <https://en.wikipedia.org/wiki/Systemd>`__
-for Orthanc can be found in the official `Fedora package
-<https://src.fedoraproject.org/rpms/orthanc>`__ (now orphaned - please
-consider :ref:`contributing by adopting this package <contributing>`):
+for Orthanc can be found in the official `Debian package
+<https://tracker.debian.org/pkg/orthanc>`_ and in the official `Fedora
+package <https://src.fedoraproject.org/rpms/orthanc/tree/f32>`__ (now
+orphaned - please consider :ref:`contributing by adopting this package
+<contributing>`):
 
 1. Download the `systemd script
-   <https://src.fedoraproject.org/rpms/orthanc/blob/f32/f/orthanc.service>`__,
+   <https://salsa.debian.org/med-team/orthanc/raw/master/debian/orthanc.service>`__,
 2. Adapt some of its variables to reflect the configuration of your
    system,
 3. Copy it as ``/etc/systemd/system/orthanc.service``,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/faq/debugging-encodings.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,34 @@
+.. _debugging_encodings:
+
+Debugging encoding issues (SpecificCharacterSet)
+================================================
+
+.. contents::
+
+.. highlight:: bash
+
+Orthanc does not display the PatientName correctly
+--------------------------------------------------
+
+If your DICOM files are valid, Orthanc should display all strings correctly both
+in the UI and in the Rest API in which all strings are converted to UTF-8.
+
+However, it might still be useful to understand what's wrong with your files
+such that you can possibly fix your files once they have been stored in Orthanc
+or configure your modality correctly.
+
+**Example 1**: a DICOM file is sent to Orthanc with ``SpecificCharacterSet`` set to ``ISO_IR 100``
+(Latin1).  The PatientName is expected to be ``ccžšd^CCŽŠÐ`` but Orthanc displays ``ccžšd^CCŽŠÐ``.
+If you open the DICOM file in an Hex editor and search for the PatientName, you'll find this sequence
+of bytes: ``63 63 9e 9a 64 5e 43 43 8e 8a d0``.  By checking the `Latin1 code page 
+<https://en.wikipedia.org/wiki/ISO/IEC_8859-1>`__, you realise that the ``9e`` and ``9a`` characters
+are not valid Latin1 characters and are therefore replaced by ``ž`` in Orthanc UI.  
+
+In this case, they have most likely been generated on a Windows system by using the default `Windows 1252 
+<https://en.wikipedia.org/wiki/Windows-1252>`__ encoding in which ``9e`` is ``ž``.
+
+How to solve it ?  It is highly recommended to fix it before Orthanc: in your RIS, worklist server or modality.
+However, if you can not fix it there, you may still try to fix it once the file has been stored in Orthanc.
+You can get inspiration from this `lua script <https://bitbucket.org/osimis/orthanc-setup-samples/src/master/lua-samples/sanitizeInvalidUtf8TagValues.lua>`__ 
+that is fixing invalid UTF-8 characters
+
--- a/Sphinx/source/faq/debugging.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/debugging.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -45,13 +45,13 @@
 to debug Orthanc without compiling from sources. Here is a sample
 debug session::
 
-  $ wget https://lsb.orthanc-server.com/orthanc/debug/1.9.2/Orthanc
+  $ wget https://lsb.orthanc-server.com/orthanc/debug/1.9.7/Orthanc
   $ chmod +x ./Orthanc
   $ gdb ./Orthanc Configuration.json
   (gdb) catch throw
   Catchpoint 1 (throw)
   (gdb) run
-  W0513 15:24:42.374349 main.cpp:1436] Orthanc version: 1.9.2
+  W0513 15:24:42.374349 main.cpp:1436] Orthanc version: 1.9.7
   ---> Reproduce your error case <---
   Thread 15 "Orthanc" hit Catchpoint 1 (exception thrown), 0x00007ffff6de68bd in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
   (gdb) backtrace
--- a/Sphinx/source/faq/dicom-tls.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/dicom-tls.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -29,7 +29,7 @@
   $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
             -keyout orthanc.key -out orthanc.crt -subj "/C=BE/CN=localhost"
 
-Obviously, you have adapt the arguments to your setup (notably the
+Obviously, you must adapt the arguments to your setup (notably the
 ``subj`` argument that generates a certificate for Belgium for the
 server whose DNS address is ``localhost``). This command line will
 generate two files using the `PEM file format
@@ -82,9 +82,24 @@
 **Remark 2:** Orthanc SCU and Orthanc SCP share the same set of
 trusted certificates.
 
+**Important:** `DCMTK 3.6.4 seems to have an issue with DICOM TLS
+<https://forum.dcmtk.org/viewtopic.php?t=5073>`__, which produces the
+errors ``DUL secure transport layer: no suitable signature algorithm``
+(in the DICOM SCP) and ``DUL secure transport layer: sslv3 alert
+handshake failure`` (in the DICOM SCU). This problem is not specific
+to Orthanc, as it also occurs between two command-line tools of the
+DCMTK 3.6.4 suite. Make sure to use either DCMTK 3.6.2 or DCMTK
+3.6.6. In particular, Debian Buster (10) uses DCMTK 3.6.4 and should
+be avoided in non-static builds of Orthanc, or if using the DCMTK
+command-line tools.
 
-Example using DCMTK
--------------------
+
+
+Examples
+--------
+
+Using DCMTK
+^^^^^^^^^^^
 
 .. highlight:: bash
 
@@ -115,9 +130,9 @@
     }  
   }
 
-.. highlight:: txt
+.. highlight:: text
 
-It is then possible to trigger a secure C-GET SCU request from DCMTK
+It is then possible to trigger a secure C-ECHO SCU request from DCMTK
 to Orthanc as follows::
 
   $ echoscu -v -aet DCMTK localhost 4242 +tls dcmtk.key dcmtk.crt +cf orthanc.crt 
@@ -126,3 +141,90 @@
   I: Sending Echo Request (MsgID 1)
   I: Received Echo Response (Success)
   I: Releasing Association
+
+
+Using dcm4che
+^^^^^^^^^^^^^
+
+.. highlight:: bash
+
+To use the dcm4che command-line tools instead of DCMTK, the two
+certificates must first be converted from `X.509
+<https://en.wikipedia.org/wiki/X.509>`__ to `PKCS #12
+<https://en.wikipedia.org/wiki/PKCS_12>`__::
+
+  $ openssl pkcs12 -export -out orthanc.p12 -in orthanc.crt -inkey orthanc.key
+  $ openssl pkcs12 -export -out dcm4che.p12 -in dcmtk.crt -inkey dcmtk.key
+
+For this example, you can let the ``Export Password`` as an empty
+string in the two calls above. Then, here is how to trigger a secure
+C-STORE SCU request to send the ``sample.dcm`` file from dcm4che to
+Orthanc::
+
+  $ ~/Downloads/dcm4che-5.23.3/bin/storescu -c ORTHANC@localhost:4242 --tls \
+   --trust-store ./orthanc.p12 --key-store ./dcm4che.p12 --trust-store-pass "" --key-store-pass "" sample.dcm
+
+**Remarks:**
+
+* The empty strings provided to the ``--trust-store-pass`` and
+  ``--key-store-pass`` options correspond to the empty strings
+  provided to ``Export Password``.
+
+* Disclaimer: In this setup, ``orthanc.p12`` contains the private key
+  of the Orthanc server. It is unclear how to remove this private key
+  that should be unknown to the DICOM client for security reasons.
+   
+
+Secure TLS connections without certificate
+------------------------------------------
+
+In Orthanc <= 1.9.2, the remote DICOM modalities are required to
+provide a valide DICOM TLS certificate (which corresponds to the
+default ``--require-peer-cert`` option of the DCMTK command-line
+tools).
+
+Starting from Orthanc 1.9.3, it is possible to allow connections
+to/from remote DICOM modalities that do not provide a DICOM TLS
+certificate (which corresponds to the ``--verify-peer-cert`` option of
+DCMTK). This requires setting the :ref:`configuration option
+<configuration>` ``DicomTlsRemoteCertificateRequired`` of Orthanc to
+``false``.
+
+.. highlight:: bash
+
+As an example, let us generate one single certificate that is
+dedicated to Orthanc::
+
+  $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+            -keyout orthanc.key -out orthanc.crt -subj "/C=BE/CN=localhost"
+
+
+.. highlight:: json
+
+Let us start Orthanc using the following minimal configuration file::
+
+  {
+    "DicomTlsEnabled" : true,
+    "DicomTlsCertificate" : "orthanc.crt",
+    "DicomTlsPrivateKey" : "orthanc.key",
+    "DicomTlsTrustedCertificates" : "orthanc.crt",
+    "DicomTlsRemoteCertificateRequired" : false
+  }
+
+.. highlight:: text
+
+Note that the ``DicomTlsTrustedCertificates`` is set to a dummy value,
+because this option must always be present. It is then possible to
+connect to Orthanc without SCU certificate as follows::
+
+  $ echoscu -v localhost 4242 --anonymous-tls +cf /tmp/k/orthanc.crt 
+  I: Requesting Association
+  I: Association Accepted (Max Send PDV: 16372)
+  I: Sending Echo Request (MsgID 1)
+  I: Received Echo Response (Success)
+  I: Releasing Association
+
+
+**Remark:** Importantly, if the remote DICOM modality provides an
+invalid DICOM TLS certificate, Orthanc will never accept the
+connection.
--- a/Sphinx/source/faq/dicom.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/dicom.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -48,6 +48,8 @@
   the "Query/Retrieve" page).
 * If the two modalities succeed with C-Echo, but query/retrieve does not
   succeed, please carefully read the :ref:`dicom-move` section.
+* Check out the :ref:`more generic troubleshooting guide
+  <troubleshooting>`.
 
 As a last resort, please contact the `mailing list
 <https://groups.google.com/forum/#!forum/orthanc-users>`_ by sending a
--- a/Sphinx/source/faq/donations.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/donations.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -3,18 +3,15 @@
 How can I donate money to the project?
 ======================================
 
-The Orthanc project **does not accept donations for now**. We think
-it's a poor business model for running an open source project as it
-suggests the developers are working for charity.
+Since February 2022, you are invited to fund the Orthanc project
+through its `Open Collective <https://opencollective.com/orthanc>`__
+page.  The funds collected there will help us maintain Orthanc,
+release new features and answer questions on the Orthanc Users Group.
 
 If you are a **commercial company** that builds upon the Orthanc
-ecosystem for your own products, please contribute by buying the
-`corporate services provided by Osimis
-<https://www.osimis.io/en/services.html>`__.
-
-If you want to **help the project financially as an end-user**, you
-can approach some of the companies offering commercial support for
-Orthanc and engage their services. Doing so creates a healthy
+ecosystem for your own products, you may also contribute by buying the
+:ref:`professional services offered by our community
+<support-freelancers>`. Doing so creates a healthy
 ecosystem around the project where companies are encouraged to
 contribute their resources to improve the project to create a larger
 pool of potential customers wanting their services. A :ref:`list of
--- a/Sphinx/source/faq/features.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/features.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -111,6 +111,7 @@
 resources stored inside Orthanc. Once one DICOM instance leaves the
 Orthanc ecosystem, its associated metadata and attachments are lost.
 
+.. _metadata-core:
 
 Core metadata
 ^^^^^^^^^^^^^
@@ -121,8 +122,8 @@
   Orthanc. Similarly, ``LastUpdate`` records, for each
   patient/study/series, the last time a DICOM instance was added to
   this resource.
-* ``RemoteAet`` records the AET of the modality that has sent some
-  DICOM instance to Orthanc.
+* ``RemoteAET`` records the AET of the modality that has sent some
+  DICOM instance to Orthanc using the DICOM protocol.
 * ``ModifiedFrom`` and ``AnonymizedFrom`` hold from which original
   resource, a resource was modified or anonymized. The presence of
   this metadata indicates that the resource is the result of a
@@ -133,11 +134,24 @@
 * ``IndexInSeries`` records the expected index of a DICOM instance
   inside its parent series. Conversely, ``ExpectedNumberOfInstances``
   associates to each series, the number of DICOM instances this series
-  is expected to contain.
+  is expected to contain. This information is :ref:`not always
+  available <series-completion>`.
 * Starting with Orthanc 1.2.0, ``TransferSyntax`` and ``SopClassUid``
-  respectively stores the transfer syntax UID and the SOP class UID of
-  DICOM instances, in order to speed up the access to this
+  respectively stores the `transfer syntax UID
+  <http://dicom.nema.org/medical/dicom/current/output/html/part05.html#chapter_10>`__
+  and the `SOP class UID
+  <http://dicom.nema.org/medical/dicom/current/output/chtml/part02/sect_A.1.html>`__
+  of DICOM instances, in order to speed up the access to this
   information.
+* ``RemoteIP`` (new in Orthanc 1.4.0): The IP address of the remote
+  SCU (for REST API and DICOM protocol).
+* ``CalledAET`` (new in Orthanc 1.4.0): The AET that was called by the
+  SCU, which normally matches the AET of Orthanc (for DICOM protocol).
+* ``HttpUsername`` (new in Orthanc 1.4.0): The username that created
+  the instance (for REST API).
+* ``PixelDataOffset`` (new in Orthanc 1.9.1): Offset (in bytes) of the
+  Pixel Data DICOM tag in the DICOM file, if available.
+  
 
 Metadata listed above are set privately by the Orthanc core. They are
 **read-only** from the perspective of the end user, as Orthanc
@@ -221,7 +235,45 @@
 
   $ curl http://localhost:8042/instances/cb855110-5f4da420-ec9dc9cb-2af6a9bb-dcbd180e/attachments/samplePdf -X PUT --data-binary @sample.pdf
   $ curl http://localhost:8042/instances/cb855110-5f4da420-ec9dc9cb-2af6a9bb-dcbd180e/attachments/sampleRaw -X PUT -d 'raw data'
-  
+
+
+DICOM-as-JSON attachments
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the version of Orthanc <= 1.9.0, whenever Orthanc receives a DICOM
+file, it pre-computes a JSON summary of its DICOM tags, and caches
+this JSON file as an attachment to the DICOM instance (accessible at
+the ``/instances/{...}/attachments/dicom-as-json/`` URI). This
+attachment is used as a cache to seep up future accesses to
+``/instances/.../tags``, lookups using ``/tools/find`` or C-FIND
+queries.
+
+This caching might cause issues if the dictionary of DICOM tags is
+subsequently modified, which implies that the cached JSON file does
+not perfectly match the new dictionary.
+
+.. highlight:: bash
+
+Since Orthanc 1.2.0, you can force the re-generation of the cached
+JSON file by DELETE-ing it, for instance::
+
+  $ curl -X DELETE http://localhost:8042/instances/301896f2-1416807b-3e05dcce-ff4ce9bb-a6138832/attachments/dicom-as-json
+
+.. highlight:: text
+
+The next time you open this particular instance with Orthanc Explorer,
+you will see messages in the Orthanc logs (in verbose mode) stating
+that the Orthanc server has reconstructed the JSON summary, which will
+match the new content of the dictionary::
+
+  I0222 08:56:00.923070 FilesystemStorage.cpp:155] Reading attachment "2309c47b-1cbd-4601-89b5-1be1ad80382c" of "DICOM" content type
+  I0222 08:56:00.923394 ServerContext.cpp:401] Reconstructing the missing DICOM-as-JSON summary for instance: 301896f2-1416807b-3e05dcce-ff4ce9bb-a6138832
+  I0222 08:56:00.929117 ServerContext.cpp:540] Adding attachment dicom-as-json to resource 301896f2-1416807b-3e05dcce-ff4ce9bb-a6138832
+  I0222 08:56:00.929425 FilesystemStorage.cpp:118] Creating attachment "3c830b66-8a00-42f0-aa3a-5e37b4a8b5a4" of "JSON summary of DICOM" type (size: 1MB)
+
+These DICOM-as-JSON attachments are not automatically generated
+anymore starting with Orthanc 1.9.1.
+
 
 .. _registry:
 
@@ -320,7 +372,7 @@
 
 The revision mechanism is optional, was introduced in **Orthanc
 1.9.2** and must be enabled by setting :ref:`configuration option
-<configuration>` ``CheckRevision`` to ``true``. It is strongly
+<configuration>` ``CheckRevisions`` to ``true``. It is strongly
 inspired by the `CouchDB API
 <https://docs.couchdb.org/en/stable/api/document/common.html>`__.
 
@@ -368,5 +420,38 @@
 of the REST API of Orthanc for more information.
 
 **Warning:** The database index back-end must support revisions. As of
-writing, only the **PostgreSQL plugin** in versions above 4.0
-implement support for revisions.
+writing, only the **PostgreSQL plugins** in versions above 4.0 and the
+**ODBC plugins** implement support for revisions.
+
+
+Synchronous vs. asynchronous C-MOVE SCP
+---------------------------------------
+
+The :ref:`C-MOVE SCP <dicom-move>` of Orthanc (i.e. the component of
+the Orthanc server that is responsible for routing DICOM instances
+from Orthanc to other modalities) can be configured to run either in
+synchronous or in asynchronous mode, depending on the value of the
+``SynchronousCMove`` :ref:`configuration option <configuration>`:
+
+* In **synchronous mode** (if ``SynchronousCMove`` is ``true``),
+  Orthanc will interleave its C-STORE SCU commands with the C-MOVE
+  instructions received from the remote modality. In other words,
+  Orthanc immediately sends the DICOM instances while it handles the
+  C-MOVE command from the remote modality. This mode is for
+  compatibility with simple DICOM client software that considers that
+  when its C-MOVE SCU is over, it should have received all the
+  instructed DICOM instances. This is the default behavior of Orthanc.
+
+* In **asynchronous mode** (if ``SynchronousCMove`` is ``false``),
+  Orthanc will queue the C-MOVE instructions and :ref:`creates a job
+  <jobs-synchronicity>` that will issue the C-STORE SCU commands
+  afterward. This behavior is typically encountered in hospital-wide
+  PACS systems, but requires the client software to be more complex as
+  it must be handle the delay between its C-MOVE queries and the
+  actual reception of the DICOM instances through C-STORE.
+
+As a consequence, by setting ``SynchronousCMove`` to ``true``, Orthanc
+can be used as a buffer that enables communications between a simple
+C-MOVE client and a hospital-wide PACS. This can be interesting to
+introduce compatibility with specialized image processing
+applications.
--- a/Sphinx/source/faq/https.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/https.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -74,9 +74,7 @@
   {
     "SslEnabled" : true,
     "SslCertificate" : "/tmp/certificate.pem"
-  }
-  
-        
+  }      
 
 
 Querying Orthanc using HTTPS
@@ -177,10 +175,10 @@
   }
 
 
-Securing Orthanc with mutual TLS authentication
-...............................................
+Securing Orthanc peers with mutual TLS authentication
+.....................................................
         
-.. highlight:: bash
+.. highlight:: json
                
 Once HTTPS is enabled, Orthanc can also be configured to accept incoming
 connections based on a certificate provided by the client.
@@ -213,4 +211,48 @@
 :ref:`DICOMweb client <dicomweb-client>`.
 
 An example of such a setup with instructions to generate the
-certificates is available `here <https://bitbucket.org/osimis/orthanc-setup-samples/src/master/docker/tls-mutual-auth/>`__ .
\ No newline at end of file
+certificates is available `here
+<https://bitbucket.org/osimis/orthanc-setup-samples/src/master/docker/tls-mutual-auth/>`__.
+
+
+.. _client-certificate-web-browser:
+
+Securing Orthanc with a client certificate and access it using a Web browser
+............................................................................
+
+.. highlight:: bash
+
+Firstly, create a PEM certificate for the Orthanc HTTPS server, and another
+PKCS12 certificate for the client::
+
+  $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+            -keyout server.key -out server.crt -subj "/C=BE/CN=localhost"
+  $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+            -keyout client.key -out client.crt -subj "/C=BE/CN=localhost"
+  $ cat server.key server.crt > server.pem
+  $ openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12
+
+In the last step, you'll have to provide a password (that can be
+empty).
+  
+.. highlight:: bash
+
+Secondly, start Orthanc using the following configuration file for Orthanc::
+
+  {
+    "SslEnabled" : true,
+    "SslCertificate" : "server.pem",
+    "SslVerifyPeers": true,
+    "SslTrustedClientCertificates": "client.crt"
+  }
+
+Thirdly, install the PKCS12 client-side certificate ``client.p12`` in
+your Web browser. For instance, check out `these instructions for
+Mozilla Firefox
+<https://security.stackexchange.com/questions/163199/firefox-certificate-can-t-be-installed>`__.
+
+You are then able to access Orthanc using HTTPS encryption, with
+cryptographic identification of a client Web browser. Note that
+because the certificate is self-signed, the Web browser will warn
+about a potential security risk.
+
--- a/Sphinx/source/faq/improving-interface.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/improving-interface.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -28,8 +28,10 @@
 language translations, sorting resources, access control lists,
 tagging images, beautiful layout, tunable anonymization, modification
 of instances, paging if many patients, handling of timeouts,
-login/logout, Web diffusion to patients/physicians...). If you need
-such a more advanced `user experience
+login/logout, Web diffusion to patients/physicians, `calendar to chose
+dates
+<https://groups.google.com/g/orthanc-users/c/LftvnfB2bY0/m/YUB-OPZyAgAJ>`__...). If
+you need such a more advanced `user experience
 <https://en.wikipedia.org/wiki/User_experience>`__ so that Orthanc
 better fits your clinical workflow, you will have to develop a
 separate, custom Web interface on the top of the :ref:`rest`, maybe as
@@ -78,9 +80,18 @@
   <https://groups.google.com/g/orthanc-users/c/Kkxqx6ZW2yw/m/dFbTuHZHCQAJ>`__
   on the discussion group.
 
+* `Menba <https://github.com/fidelio33b/menba>`__ is a Web interface
+  built on the top of the REST API of Orthanc. It is written using
+  `Django <https://www.djangoproject.com/>`__ and `Bootstrap
+  <https://getbootstrap.com/>`__, and takes advantage of `Celery
+  <https://docs.celeryproject.org/en/stable/getting-started/introduction.html>`__
+  and `AMQP
+  <https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol>`__
+  to handle the asynchronous tasks.
+
 * Last but not least, as written above, please consider buying the
   `professional development services by Osimis
-  <https://www.osimis.io/en/services.html>`__. Osimis can help medical
+  <https://osimis.io/en/orthanc-support-contract>`__. Osimis can help medical
   and hospital audience with the deployment of a **certified,
   integrated clinical environment** around Orthanc. In turn, the money
   you pay will contribute to make the Orthanc project sustainable in
--- a/Sphinx/source/faq/orthanc-storage.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/orthanc-storage.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -3,6 +3,8 @@
 How does Orthanc store its database?
 ====================================
 
+.. _orthanc-storage-area:
+
 Storage area
 ------------
 
@@ -57,9 +59,9 @@
 database schema is kept as simple as possible, and can be found in the
 following two files of the source code of Orthanc:
 `PrepareDatabase.sql
-<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Sources/Database/PrepareDatabase.sql>`__
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Sources/Database/PrepareDatabase.sql>`__
 and `InstallTrackAttachmentsSize.sql
-<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Sources/Database/InstallTrackAttachmentsSize.sql>`__.
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Sources/Database/InstallTrackAttachmentsSize.sql>`__.
 
 
 Direct access
@@ -83,7 +85,9 @@
   that are `explained in the SQLite FAQ
   <https://www.sqlite.org/faq.html#q5>`__. Orthanc will stop if it
   receives the ``SQLITE_BUSY`` status.
-
+* The internal structure of the databases might evolve across
+  successive versions of Orthanc or of the database plugins.
+  
 As a consequence, it is **HIGHLY recommended NOT to directly access**
 the ``OrthancStorage`` folder and the SQLite/MySQL/PostgreSQL
 database. Use the :ref:`REST API <rest>` instead, which contains
--- a/Sphinx/source/faq/proprietary.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/proprietary.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -46,5 +46,5 @@
 derived version of Orthanc**.
 
 Finally, if you need to complement the services of your vendor,
-consider buying the `Orthanc support packs provided by Osimis
-<https://www.osimis.io/en/services.html>`__.
+consider buying :ref:`professional services from our community
+<support-freelancers>`.
--- a/Sphinx/source/faq/scalability.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/scalability.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -54,8 +54,8 @@
 Here is a generic setup that should provide best performance in the
 presence of large databases:
 
-* Make sure to use the latest release of Orthanc (1.9.2 at the time of
-  writing).
+* Make sure to use the latest release of Orthanc (1.9.7 at the time of
+  writing) running on a GNU/Linux distribution.
 
 * We suggest to use the latest release of the :ref:`PostgreSQL plugin
   <postgresql>` to store the database index (4.0 at the time of
@@ -87,8 +87,8 @@
   * ``LimitFindInstances = 100``
   * ``KeepAlive = true``
   * ``TcpNoDelay = true``
-  * ``SaveJobs = false``
   * ``StorageAccessOnFind = Never``
+  * Consider adding ``SaveJobs = false``
 
 * Since Orthanc 1.9.2 and PostgreSQL plugins 4.0: By default, the
   PostgreSQL index plugin uses 1 single connection to the PostgreSQL
@@ -127,10 +127,21 @@
   `Btrfs <https://en.wikipedia.org/wiki/Btrfs>`__ on GNU/Linux
   distributions.
 
-* On GNU/Linux distributions, `LVM (Logical Volume Manager)
-  <https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)>`__
-  can be used to dynamically and easily grow the storage area as more
-  space becomes needed.
+* If you need to grow the storage area as more space becomes needed,
+  you can consider the following solutions:
+
+  - Move the storage area to another disk partition, and update the
+    ``StorageDirectory`` :ref:`configuration option <configuration>`
+    accordingly.
+  - :ref:`Replicate <replication>` your current instance of Orthanc
+    onto another instance of Orthanc with a larger storage area.
+  - On GNU/Linux distributions, check out `LVM (Logical Volume Manager)
+    <https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)>`__.
+  - On Microsoft Windows, check out the so-called "`Storage Spaces
+    <https://docs.microsoft.com/en-us/windows-server/storage/storage-spaces/overview>`__".
+  - Another approach is to use `MinIO <https://docs.min.io/>`__ in
+    distributed mode in conjunction with the :ref:`AWS S3 plugin
+    <minio>` for Orthanc.
 
 * If using the :ref:`DICOMweb server plugin <dicomweb-server-config>`,
   consider setting configuration option ``StudiesMetadata`` to
@@ -299,7 +310,36 @@
   a :ref:`revision mechanism <revisions>` to prevent concurrent
   updates.
 
+* Thanks to this support of concurrent accesses, it is possible to put
+  a **load balancer** on the top of the REST API of Orthanc. All the
+  DICOM resources (patients, studies, series and instances) are indeed
+  shared by all the instances of Orthanc connected to the same
+  underlying database. As an application, this might be of great help
+  if multiple viewers must connect to Orthanc. In `Kubernetes
+  <https://kubernetes.io/>`__, concurrent accesses also make it
+  possible to manage a set of replicas of Orthanc (e.g. as `deployment
+  <https://kubernetes.io/docs/concepts/workloads/controllers/deployment/>`__).
 
+  There are however some caveats if using a load balancer or
+  Kubernetes replicas, notably:
+    
+  - Each Orthanc instance maintains its own list of jobs. Therefore,
+    the ``/jobs`` route will return only the jobs of the responding
+    Orthanc.
+
+  - The ``/modalities`` or the ``/peers`` are also private to each
+    instance of Orthanc in the cluster, as soon as the respective
+    options ``DicomModalitiesInDatabase`` and
+    ``OrthancPeersInDatabase`` are set to ``true``.
+
+  If you need to use such primitives in your application, you have
+  three possibilities: (1) Introduce a distinguished Orthanc server
+  that is responsible to take care of all the jobs (including
+  modalities and peers), (2) create an :ref:`Orthanc plugin <plugins>`
+  (e.g. using :ref:`Python <python-plugin>`) that queries all the
+  Orthanc in the cluster and that aggregates all of their answers,
+  or (3) do the same using a higher-level framework (such as Node.js).
+    
 
 Latency
 ^^^^^^^
@@ -330,15 +370,19 @@
 Slow deletions
 ^^^^^^^^^^^^^^
 
-Deleting large studies can take some time, because removing a large
+Deleting large studies can take much time, because removing a large
 number of files from a filesystem can be an expensive operation (which
-might sound counterintuitive).
+might sound counter-intuitive). This is especially true with HDD
+drives, that can be much slower than SSD (`an user has reported
+<https://groups.google.com/g/orthanc-users/c/1lga0oFCHN4/m/jF1inrc4AgAJ>`__
+a 20 times speedup by switching from HDD to SSD).
 
-It is possible to create an :ref:`storage area plugin
-<creating-plugins>` that delays the actual deletion from the
-filesystem. The plugin would maintain a queue (e.g. as a SQLite
-database) of files to be removed. The actual deletion from the
-filesystem would be done asynchronously in a separate thread.
+If switching from HDD to SDD is not applicable, it is possible to
+create an :ref:`storage area plugin <creating-plugins>` that delays
+the actual deletion from the filesystem. The plugin would maintain a
+queue (e.g. as a SQLite database) of files to be removed. The actual
+deletion from the filesystem would be done asynchronously in a
+separate thread.
 
 We are looking for funding from the industry to implement such a
 plugin.
--- a/Sphinx/source/faq/security.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/security.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -163,7 +163,9 @@
   to disallow C-STORE commands from unknown modalities.
 
 * Set the ``DicomCheckModalityHost`` configuration option to ``true``
-  to validate the IP and hostname address of the remote modalities.
+  to validate the IP address of the remote modalities (note that
+  hostnames cannot be used in ``DicomModalities`` when this option is
+  enabled: The ``Host`` values should only contain IP addresses).
 
 * For each modality that is defined in ``DicomModalities``,
   selectively specify what DICOM commands are allowed to be issued by
--- a/Sphinx/source/faq/series-completion.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/series-completion.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -1,16 +1,26 @@
-What do the various status displayed in a series header mean?
-=============================================================
+.. _series-completion:
+
+What does the series completion status mean?
+============================================
 
-A series can be assigned a status by Orthanc. I can list as *unknown*, *missing*, *inconsistent* or *complete*.
+Each DICOM series is assigned a completion status by Orthanc. This
+completion status can list as ``Unknown``, ``Missing``,
+``Inconsistent`` or ``Complete``.
 
-The status is meant to convey the completeness of the series, as known by Orthanc.
+The status is meant to convey the completeness of the series, as known
+by Orthanc.
 
-As a general rule, since there is no generic DICOM tag that contains the number of DICOM instances that
-are contained in a given series, and since there is no generic tag that contains the index of a DICOM
-instance inside its parent series, it is in general impossible to know if a series is complete in terms
-of its instances. This is the meaning of the *Unknown* status displayed by Orthanc.
+As a general rule, since there is no generic DICOM tag that contains
+the number of DICOM instances that are contained in a given series,
+and since there is no generic tag that contains the index of a DICOM
+instance inside its parent series, it is in general impossible to know
+if a series is complete in terms of its instances. This is the meaning
+of the ``Unknown`` status displayed by Orthanc.
 
-However, for some types of images (such as cardiac MRI), the DICOM modules might specify a tag that contains
-this information. If such a tag is available for a series, Orthanc will either report a *Complete* status
-(if all the instances have been received), *Incomplete* (if some instances are [still] missing) or *Inconsistent*
-if there is an error inside the numbering of the instances.
+However, for some types of images (such as cardiac MRI), the DICOM
+modules might specify a tag that contains this information. If such a
+tag is available for a series, Orthanc will either report a
+``Complete`` status (if all the instances have been received),
+``Incomplete`` (if some instances are [still] missing) or
+``Inconsistent`` if there is an error inside the numbering of the
+instances.
--- a/Sphinx/source/faq/supported-images.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/supported-images.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -10,13 +10,19 @@
 <configuration>` can be set to `true` if interfacing with modalities
 that are producing non-standard SOP classes.
 
-Orthanc Explorer can **display the raw DICOM tags** of any such DICOM
-file (including notably DICOM-SR structured reports).
+Orthanc Explorer can also **display the raw DICOM tags** of any such
+DICOM file.
 
-However, the core engine of Orthanc is not able to **render all of the
-DICOM files as PNG images**. An image that Orthanc cannot decode is
-displayed as "Unsupported" by clicking on the "Preview" buttons of
-Orthanc Explorer. Currently, the core engine of Orthanc can decode:
+For instance, **DICOM-SR and DICOM-RT** instances are supported by
+Orthanc, i.e. such file can be received/stored/retransmitted with
+Orthanc. However, if you need to analyze/create such files, you will
+have to resort on another specialized tool.
+
+As far as pixel data is concerned, the core engine of Orthanc is **not
+able to render all of the DICOM instances as PNG images**. An image
+that Orthanc cannot decode is displayed as "Unsupported" by clicking
+on the "Preview" buttons of Orthanc Explorer. Currently, the core
+engine of Orthanc can decode:
 
 * uncompressed (raw) DICOM files,
 * JPEG DICOM files, and
@@ -25,7 +31,8 @@
 The supported photometric interpretations are:
 
 * RGB,
-* Grayscale2.
+* Grayscale2,
+* YUV if dealing with JPEG derivatives.
 
 The Orthanc core supports from 8bpp to 16bpp depth, with integer
 values.  Multiframe (notably cine), uncompressed DICOM instances can
--- a/Sphinx/source/faq/troubleshooting.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/troubleshooting.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -15,6 +15,7 @@
 
 Startup
 -------
+
 * If **Orthanc fails to start** with the error "**The TCP port of the DICOM 
   server is privileged or already in use**", this means another software is
   already using the port Orthanc is trying to use.  Usually, this means
@@ -32,6 +33,42 @@
   (it also checks for UDP socket using the same port) and Orthanc 1.3.0 might 
   display error messages that where not displayed by previous versions.
 
+* If Orthanc **does not start anymore after a hard shutdown** and if
+  you use the :ref:`Orthanc Web viewer plugin <webviewer>`, this might
+  reflect a corruption in the cache of the Web viewer. In such a case,
+  it is safe to remove the folder that contains the cache. By default,
+  this folder is called ``OrthancStorage/WebViewerCache/``
+  (cf. :ref:`configuration option <configuration>` ``CachePath`` in
+  the ``WebViewer`` section). Of course, don't remove the folder
+  ``OrthancStorage/``, as it contains the DICOM files.
+  
+  
+Validating DICOM files
+----------------------
+
+* Invalid DICOM files are often encountered in practice. Such files
+  can cause failures in Orthanc, or can prevent DICOM network
+  transfers. You can validate DICOM files by using the ``dciodvfy``
+  command-line tool (cf. `its documentation
+  <http://dclunie.com/dicom3tools/dciodvfy.html>`__) from the
+  `dicom3tools <https://www.dclunie.com/dicom3tools.html>`__ project
+  by David Clunie.
+
+  The core team of Orthanc will **only provide support for DICOM files
+  that are reported as valid** by ``dciodvfy``.
+
+* Side-note: The default transfer syntax of DICOM is Little Endian
+  Implicit (``1.2.840.10008.1.2``). For DICOM files that include
+  private tags, **we recommend using Little Endian Explicit**
+  (``1.2.840.10008.1.2.1``) instead Little Endian Implicit whenever
+  possible. Instead, in Little Endian Explicit, each DICOM tag has an
+  explicit declaration of its value representation (type), which
+  contrasts with Little Endian Implicit that necessitates to configure
+  the dictionary of private tags to be properly handled in some
+  operations (cf. the ``Dictionary`` :ref:`configuration option
+  <configuration>`).
+  
+  
 Orthanc Explorer
 ----------------
 
@@ -108,21 +145,35 @@
   CMake
   <https://hg.orthanc-server.com/orthanc/file/default/LinuxCompilation.txt>`__.
 
-Checking DICOM file integrity
------------------------------
-Orthanc stores, in its database, an `MD5 hash <https://en.wikipedia.org/wiki/MD5>`_ of the DICOM file contents.
+  
+Checking integrity of the storage area
+--------------------------------------
+
+.. highlight:: bash
 
-This MD5 corresponds to the hash of the DICOM file in memory, before it is written to the disk by Orthanc. This information is safely stored inside the database for any incoming DICOM file (provided that the ``StoreMD5ForAttachments`` configuration option is set to ``true``).
-
-It ispossible to ask Orthanc to check by itself whether the DICOM file was corrupted (i.e. to check whether the MD5 hash stored in the database corresponds to the hash of the file on the disk):
+Orthanc stores, in its database, an `MD5 hash
+<https://en.wikipedia.org/wiki/MD5>`_ of the files stored in its
+:ref:`storage area <orthanc-storage>` (which notably includes the
+DICOM files), provided that the ``StoreMD5ForAttachments``
+configuration option is set to ``true``.
 
-``curl -X POST http://localhost:8042/instances/f257b066-f3992cc4-ca6a5e5f-3f8dcf3a-d4958939/attachments/dicom/verify-md5 -d ''``
+This MD5 corresponds to the hash of the files in memory, before they
+are written to the disk by Orthanc. This information is safely stored
+inside the database for any incoming file attachment.
 
-This MD5 may be different if errors occurred while the DICOM file was initially written to the storage, or if the file contents were tampered with afterwards.
+It is possible to ask Orthanc to check by itself whether some attachment
+file was corrupted (i.e. to check whether the MD5 hash stored in the
+database corresponds to the hash of the file on the disk)::
 
-You can retrieve the stored MD5 hash of a DICOM instance as follows:
+  $ curl -X POST http://localhost:8042/instances/f257b066-f3992cc4-ca6a5e5f-3f8dcf3a-d4958939/attachments/dicom/verify-md5 -d ''
 
-``curl http://localhost:8042/instances/f257b066-f3992cc4-ca6a5e5f-3f8dcf3a-d4958939/attachments/dicom/md5``
+This MD5 may be different if errors occurred while the DICOM file was
+initially written to the storage, or if the file contents were
+tampered with afterwards.
+
+You can retrieve the stored MD5 hash of a DICOM instance as follows::
+
+  $ curl http://localhost:8042/instances/f257b066-f3992cc4-ca6a5e5f-3f8dcf3a-d4958939/attachments/dicom/md5
 
 Windows-specific issues
 -----------------------
@@ -142,3 +193,6 @@
   precompiled command-line versions of Orthanc for Windows 64bit are
   available courtesy of `Osimis
   <https://www.osimis.io/en/download.html>`__.
+
+* Avoid installing Orthanc, its database or its storage area in
+  folders whose names contain **spaces or special characters**.
--- a/Sphinx/source/faq/video.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/video.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -1,3 +1,5 @@
+.. _videos:
+
 Does Orthanc support videos?
 ============================
 
@@ -20,6 +22,10 @@
 distinction is also discussed in :ref:`another FAQ entry
 <supported-images>`.
 
+It is easy to **extract and download** the raw video embedded in the
+DICOM instance using the :ref:`REST API of Orthanc
+<download-pdf-videos>`.
+
 If you also want to **play** the videos, the :ref:`Osimis Web Viewer
 plugin <osimis_webviewer>` is able to play H.264 (MPEG4) videos and
 2D+t (cine) sequences but not MPEG2 videos that currently can not be
--- a/Sphinx/source/faq/worklist.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/faq/worklist.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -31,3 +31,10 @@
 Orthanc plugin SDK
 <https://sdk.orthanc-server.com/group__DicomCallbacks.html#ga23080c4e871b8428ede61d9841c10d76>`__
 that is related to the management of worklists.
+
+Custom MWL plugin can also be implemented using :ref:`Python plugins
+<python_worklists>`.  This is especially useful to easily create
+bridges between Orthanc, HL7 messages, RIS systems and `FHIR
+<https://www.hl7.org/fhir/overview.html>`__ stores. Indeed, Python
+provides many tools to handle HL7 or FHIR such as `python-hl7 library
+<https://python-hl7.readthedocs.io/en/latest/>`__.
Binary file Sphinx/source/images/2021-12-12-Cytomine.png has changed
Binary file Sphinx/source/images/Cytomine.png has changed
Binary file Sphinx/source/images/CytomineKeys.png has changed
Binary file Sphinx/source/images/odbc-windows-system-locale.png has changed
Binary file Sphinx/source/images/tcia-interface.png has changed
Binary file Sphinx/source/images/tcia-nbia-export.png has changed
--- a/Sphinx/source/plugins.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -66,9 +66,21 @@
    plugins/google-cloud-platform.rst    
    plugins/python.rst 
    plugins/object-storage.rst   
+   plugins/odbc.rst
    plugins/osimis-webviewer.rst
    plugins/authorization.rst
 
+.. _plugins-uclouvain:
+
+From UCLouvain
+^^^^^^^^^^^^^^
+
+.. toctree::
+   :maxdepth: 1
+
+   plugins/tcia.rst
+   plugins/indexer.rst
+
 .. _plugins-contributed:
 
 Index of the contributed plugins
@@ -118,9 +130,10 @@
   devices. This topic is further discussed on the `Orthanc Users forum
   <https://groups.google.com/d/msg/orthanc-users/NO7MnWzKsAc/5hEVxymWBQAJ>`__.
 
-* Stephen Douglas Scotti maintains a `Python plugin
-  <https://github.com/sscotti/OrthancBrowser>`__ to implement
-  pagination on one Orthanc server.
+* Stephen Douglas Scotti maintains an `integrated Docker package
+  <https://github.com/sscotti/PACS_Integration>`__, that provides a
+  portal application using Laravel (which demonstrates pagination and
+  some other features), and some MPPS and MWL features.
 
 * Walco van Loon maintains `Orthanc Server Extensions
   <https://github.com/walkIT-nl/orthanc-server-extensions>`__ as *"A
--- a/Sphinx/source/plugins/authorization.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/authorization.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -28,7 +28,7 @@
 
 The procedure to compile this plugin is similar of that for the
 :ref:`core of Orthanc <binaries>`. The following commands should work
-for every UNIX-like distribution (including GNU/Linux)::
+for most UNIX-like distribution (including GNU/Linux)::
 
   $ mkdir Build
   $ cd Build
--- a/Sphinx/source/plugins/dicomweb.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/dicomweb.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -29,7 +29,7 @@
 
 The procedure to compile this plugin is similar of that for the
 :ref:`core of Orthanc <compiling>`. The following commands should work
-for every UNIX-like distribution (including GNU/Linux)::
+for most UNIX-like distribution (including GNU/Linux)::
 
   $ mkdir Build
   $ cd Build
@@ -350,8 +350,28 @@
     }
   }
 
-Finally, it is also possible to use client authentication with
-hardware security modules and smart cards through `PKCS#11
+
+The definition of a DICOMweb server can also specify the HTTP headers
+to be provided during each request to the remote DICOMweb server. This
+can for instance be useful to set authorization tokens::
+
+  {
+    [...]
+    "DicomWeb" : {
+      "Servers" : {
+        "sample" : {
+          "Url" : "http://localhost:8042/dicom-web/",
+          "HttpHeaders": {
+            "Authorization" : "Bearer HelloWorldToken"
+          }
+        }
+      }
+    }
+  }
+
+
+Finally, it is possible to use client authentication with hardware
+security modules and smart cards through `PKCS#11
 <https://en.wikipedia.org/wiki/PKCS_11>`__ (this feature is only
 available is the core of Orthanc was compiled with the
 ``-DENABLE_PKCS11=ON`` option in CMake, and if the Orthanc
@@ -492,6 +512,9 @@
 DICOMweb server. Please check out `the full reference of the DICOMweb
 API <https://www.dicomstandard.org/dicomweb/>`__ for more information.
 
+Also, check out the :ref:`section about additional samples
+<dicomweb-additional-samples>` that notably provides example of
+STOW-RS clients in JavaScript and Python.
 
 
 .. _dicomweb-client:
--- a/Sphinx/source/plugins/gdcm.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/gdcm.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -49,7 +49,7 @@
 
 The procedure to compile this plugin is similar of that for the
 :ref:`core of Orthanc <binaries>`. The following commands should work
-for every UNIX-like distribution (including GNU/Linux)::
+for most UNIX-like distribution (including GNU/Linux)::
 
   $ mkdir Build
   $ cd Build
--- a/Sphinx/source/plugins/google-cloud-platform.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/google-cloud-platform.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -49,7 +49,7 @@
 Docker, Windows, Linux or OS X. These pre-compiled binaries do exist,
 but are reserved to the companies who have subscribed to a
 `professional support contract
-<https://www.osimis.io/en/services.html#cloud-plugins>`__ by
+<https://osimis.io/en/orthanc-support-contract>`__ by
 Osimis. Although you are obviously free to compile this plugin by
 yourself (instructions are given below), purchasing such support
 contracts makes the Orthanc project sustainable in the long term, to
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/indexer.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,93 @@
+.. _indexer:
+
+
+Folder Indexer plugin
+=====================
+
+.. contents::
+
+This **official** plugin by the `ICTEAM institute of UCLouvain
+<https://uclouvain.be/en/research-institutes/icteam>`__ uses Orthanc
+to publish filesystems containing medical images as a DICOM modality.
+
+The plugin continuously synchronizes the content of an Orthanc server
+with the content of a filesystem, which can then be accessed, through
+Orthanc, based on the :ref:`DICOM model of the real world <model-world>`.
+The indexed DICOM resources are immediately available in a Web
+interface and in a Web viewer, and can be queried/retrieved by DICOM
+clients. The DICOM files are **not** copied, so this solution has a
+very small footprint in terms of storage requirements.
+
+
+Compilation
+-----------
+
+.. highlight:: bash
+
+Official releases can be `downloaded from the Orthanc homepage
+<https://www.orthanc-server.com/browse.php?path=/plugin-indexer>`__. As
+an alternative, the `repository containing the source code
+<https://hg.orthanc-server.com/orthanc-indexer/>`__ can be accessed using
+Mercurial.
+
+The procedure to compile this plugin is similar of that for the
+:ref:`core of Orthanc <binaries>`. The following commands should work
+for most UNIX-like distribution (including GNU/Linux)::
+
+  $ mkdir Build
+  $ cd Build
+  $ cmake .. -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release
+  $ make
+
+The compilation will produce a shared library ``OrthancIndexer``
+that contains the folder indexer plugin for Orthanc.
+
+Pre-compiled Linux Standard Base (LSB) binaries `can be downloaded
+<https://lsb.orthanc-server.com/plugin-indexer/>`__.
+
+Pre-compiled binaries for Microsoft Windows and macOS `are also
+available
+<https://www.orthanc-server.com/browse.php?path=/plugin-indexer>`__.
+
+Furthermore, the :ref:`Docker images <docker>`
+``jodogne/orthanc-plugins`` and ``osimis/orthanc`` also contain the
+plugin.
+
+
+Usage
+-----
+
+.. highlight:: json
+
+Here is a minimal sample :ref:`configuration file <configuration>` to
+use this plugin::
+
+  {
+    "Plugins" : [
+      "/home/user/OrthancIndexer/Build/libOrthancIndexer.so"
+    ],
+    "Indexer" : {
+      "Enable" : true,
+      "Folders" : [ "/home/user/DICOM" ],   // List of folders to synchronize
+      "Interval" : 10                       // Delay between two synchronizations
+    }
+  }
+
+Orthanc must of course be restarted after the modification of its
+configuration file.
+
+Once Orthanc is started, the folders are transparently synchronized
+without any further interaction. You can start Orthanc with the
+``--verbose-plugins`` command-line option in order to monitor the
+synchronization process.
+
+Some remarks:
+
+* This plugin cannot be used together with other custom storage area
+  plugins (such as :ref:`cloud object storage <object-storage>`).
+
+* Even if the folder indexer plugin is in use, you can still add other
+  DICOM files using the :ref:`REST API <rest>` or the :ref:`DICOM
+  network protocol <dicom-protocol>`. Such files would be stored in
+  the ``OrthancStorage`` :ref:`usual folder <orthanc-storage-area>`.
+
--- a/Sphinx/source/plugins/mysql.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/mysql.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -16,6 +16,12 @@
 For information about scalability, make sure to read the section about
 :ref:`multiple writers in large-scale deployments <multiple-writers>`.
 
+The source code of the MySQL/MariaDB plugins can be found in the
+``orthanc-databases`` `Mercurial repository
+<https://hg.orthanc-server.com/orthanc-databases/>`__, next to the
+source code of the :ref:`ODBC <odbc>` and :ref:`PostgreSQL
+<postgresql>` plugins.
+
 **Warning:** According to `this thread on our discussion group
 <https://groups.google.com/d/msg/orthanc-users/yV3LSTh_TjI/Fb4ShaYMBAAJ>`__,
 the MySQL/MariaDB plugins require MySQL 8.x if running on Microsoft
@@ -49,7 +55,7 @@
 Microsoft Windows
 ^^^^^^^^^^^^^^^^^
 
-Pre-compiled binaries for Microsoft Windows `are also available
+Pre-compiled binaries for Microsoft Windows 32bit `are also available
 <https://www.orthanc-server.com/browse.php?path=/plugin-mysql>`__.
 
 
@@ -220,5 +226,14 @@
 Scalability
 ^^^^^^^^^^^
 
-When configuring your MySQL plugin, ensure you've read the :ref:`scalability section 
-<scalability>`
+When configuring your MySQL plugin, ensure you've read the
+:ref:`scalability section <scalability>`
+
+
+Backup
+------
+
+The MySQL plugin uses stored routines (i.e. functions/procedures) that
+are not archived by default by the ``mysqldump`` tool. As a
+consequence, make sure to add the ``--routines`` command-line flag to
+also archive such routines in your backup.
--- a/Sphinx/source/plugins/object-storage.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/object-storage.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -37,7 +37,7 @@
 Docker, Windows, Linux or OS X. These pre-compiled binaries do exist,
 but are reserved to the companies who have subscribed to a
 `professional support contract
-<https://www.osimis.io/en/services.html#cloud-plugins>`__ by
+<https://osimis.io/en/orthanc-support-contract>`__ by
 Osimis. Although you are obviously free to compile these plugins by
 yourself (instructions are given below), purchasing such support
 contracts makes the Orthanc project sustainable in the long term, to
@@ -134,17 +134,19 @@
 Sample configuration::
 
   "AwsS3Storage" : {
-  	"BucketName": "test-orthanc-s3-plugin",
+    "BucketName": "test-orthanc-s3-plugin",
     "Region" : "eu-central-1",
-    "AccessKey" : "AKXXX",
-    "SecretKey" : "RhYYYY",
-    "Endpoint": "",                           // custom endpoint
-    "ConnectionTimeout": 30,                  // connection timeout in seconds
-    "RequestTimeout": 1200,                   // request timeout in seconds (max time to upload/download a file)
-    "RootPath": "",                           // see below
-    "MigrationFromFileSystemEnabled": false,  // see below
-    "StorageStructure": "flat",               // see below
-    "VirtualAddressing": true                 // see the section related to MinIO
+    "AccessKey" : "AKXXX",                    // optional: if not specified, the plugin will use the default credentials manager (available from version 1.3.0)
+    "SecretKey" : "RhYYYY",                   // optional: if not specified, the plugin will use the default credentials manager (available from version 1.3.0)
+    "Endpoint": "",                           // optional: custom endpoint
+    "ConnectionTimeout": 30,                  // optional: connection timeout in seconds
+    "RequestTimeout": 1200,                   // optional: request timeout in seconds (max time to upload/download a file)
+    "RootPath": "",                           // optional: see below
+    "MigrationFromFileSystemEnabled": false,  // optional: see below
+    "StorageStructure": "flat",               // optional: see below
+    "EnableLegacyUnknownFiles": true,         // optional: see below
+    "VirtualAddressing": true,                // optional: see the section related to MinIO
+    "StorageEncryption" : {}                  // optional: see the section related to encryption
   }
 
 The **EndPoint** configuration is used when accessing an S3 compatible cloud provider.  I.e. here is a configuration to store data on Scaleway::
@@ -158,6 +160,8 @@
   }
 
 
+.. _minio:
+  
 Emulation of AWS S3 using MinIO
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -221,7 +225,8 @@
     "CreateContainerIfNotExists": true,       // available from version 1.2.0
     "RootPath": "",                           // see below
     "MigrationFromFileSystemEnabled": false,  // see below
-    "StorageStructure": "flat"                // see below
+    "StorageStructure": "flat",               // see below
+    "EnableLegacyUnknownFiles": true          // optional: see below
   }
 
 
@@ -235,7 +240,8 @@
     "BucketName": "test-orthanc-storage-plugin",
     "RootPath": "",                           // see below
     "MigrationFromFileSystemEnabled": false,  // see below
-    "StorageStructure": "flat"                // see below
+    "StorageStructure": "flat",               // see below
+    "EnableLegacyUnknownFiles": true          // optional: see below
   }
 
 
@@ -259,7 +265,7 @@
 
 Note that you can not change these configurations once you've uploaded the first files in Orthanc.
 
-The **MigrationFromFileSystemEnabled** configuration has been for Orthanc to continue working
+The **MigrationFromFileSystemEnabled** configuration has been introduced for Orthanc to continue working
 while you're migrating your data from the file system to the object storage.  While this option is enabled,
 Orthanc will store all new files into the object storage but will try to read/delete files
 from both the file system and the object storage.
@@ -270,6 +276,11 @@
 
 A migration script from File System to Azure Blob Storage is available courtesy of `Steve Hawes <https://github.com/jodogne/OrthancContributed/blob/master/Scripts/Migration/2020-09-08-TransferToAzure.sh>`__ .
 
+The **EnableLegacyUnknownFiles** configuration has been introduced to allow recent version of the plugins (from 1.3.3)
+continue working with data that was saved with Orthanc version around 1.9.3 and plugins version around 1.2.0 (e.g. osimis/orthanc:21.5.1 docker images).
+With these specific versions, some ``.unk`` files were generated instead of ``.dcm.head`` files.  With this configuration option enabled,
+when reading files, the plugin will try both file extensions.
+If you have ``.unk`` files in your storage, you must enable this configuration.
 
 Sample setups
 -------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/odbc.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,504 @@
+.. _odbc:
+
+
+ODBC plugins
+============
+
+.. contents::
+
+The Orthanc project provides two **official** plugins to replace the
+default storage area (on the filesystem) and the default SQLite index
+by a `ODBC database
+<https://en.wikipedia.org/wiki/Open_Database_Connectivity>`__. This
+can notably be used to connect Orthanc to Microsoft SQL Server.
+
+The source code of the ODBC plugins can be found in the
+``orthanc-databases`` `Mercurial repository
+<https://hg.orthanc-server.com/orthanc-databases/>`__, next to the
+source code of the :ref:`PostgreSQL <postgresql>` and
+:ref:`MySQL/MariaDB <mysql>` plugins.
+
+     
+When to use ODBC?
+-----------------
+
+In general, you'll always get better performance by using native C/C++
+plugins dedicated to one single database engine, instead of the ODBC
+plugins that can connect to any database driver. This is the price of
+genericity: Some specific optimisations can only be done if focusing
+on one single database.
+
+That being said, there are multiple use cases for the ODBC plugins:
+
+* Connection to Microsoft SQL Server (MSSQL), including Microsoft
+  Azure SQL, is only possible with ODBC. Note that the ODBC plugins
+  were only validated against MSSQL 2017 and MSSQL 2019, under
+  GNU/Linux.
+
+* Contrarily to the built-in SQLite engine and to the MySQL/MariaDB
+  index plugin, the ODBC index plugin supports the :ref:`revision
+  mechanism <revisions>` to protect metadata and attachments from
+  concurrent modifications.
+
+* Because of its genericity, the ODBC storage area plugin does not
+  implement the :ref:`read-range primitive <registry>`. As a
+  consequence, using it will write two attachments for each stored
+  DICOM instance (namely, ``Attachment 1`` that corresponds to the
+  DICOM instance itself, and ``Attachment 3`` that corresponds to the
+  tags before the pixel data).
+
+* The Linux Standard Base (LSB) `pre-compiled binaries
+  <https://lsb.orthanc-server.com/plugin-odbc/>`__ of the ODBC plugins
+  are not compatible with the ``libsqliteodbc`` `Debian/Ubuntu package
+  <http://www.ch-werner.de/sqliteodbc/>`__ because the latter package
+  was compiled with the ``HAVE_SQLITE3LOADEXTENSION`` flag.
+  
+* Finally, make sure to read the :ref:`recommended setup for best
+  scalability <scalability>`.
+  
+Very importantly, pay attention to the fact that the ODBC plugins use
+a different database schema than the built-in SQLite driver, and than
+the PostgreSQL/MariaDB/MySQL plugins. As a consequence, it is **not
+possible to switch back and forth** between ODBC and the native
+drivers without running a :ref:`full replication procedure
+<replication>`. As a consequence, pay attention to choose the right
+plugin from the beginning, as you will need to stick to it.
+
+Summarizing, here are two tables containing our recommendations about
+when to use the ODBC plugins:
+
++------------------------------+--------------------------------------------------------+
+| Database management system   | Recommended index plugin                               |
++==============================+========================================================+
+| Microsoft SQL server (MSSQL) | ODBC plugin                                            |
+| or Microsoft Azure SQL       |                                                        |
++------------------------------+--------------------------------------------------------+
+| MySQL (with revisions)       | ODBC plugin                                            |
++------------------------------+--------------------------------------------------------+
+| MySQL (without revisions)    | :ref:`MySQL plugin <mysql>`                            |
++------------------------------+--------------------------------------------------------+
+| PostgreSQL                   | :ref:`PostgreSQL plugin <postgresql>`                  |
++------------------------------+--------------------------------------------------------+
+| SQLite (with revisions)      | ODBC plugin                                            |
++------------------------------+--------------------------------------------------------+
+| SQLite (without revisions)   | No plugin needed                                       |
++------------------------------+--------------------------------------------------------+
+| Other                        | Create a :ref:`dedicated plugin <creating-plugins>`    |
+|                              | or implement a new dialect in the ODBC plugins         |
++------------------------------+--------------------------------------------------------+
+
+
++------------------------------+--------------------------------------------------------+
+| Type of storage area         | Recommended storage plugin                             |
++==============================+========================================================+
+| Filesystem                   | No plugin needed                                       |
++------------------------------+--------------------------------------------------------+
+| Microsoft SQL server (MSSQL) | ODBC plugin                                            |
+| or Microsoft Azure SQL       |                                                        |
++------------------------------+--------------------------------------------------------+
+| MySQL                        | :ref:`MySQL plugin <mysql>`                            |
++------------------------------+--------------------------------------------------------+
+| PostgreSQL                   | :ref:`PostgreSQL plugin <postgresql>`                  |
++------------------------------+--------------------------------------------------------+
+| SQLite                       | ODBC plugin                                            |
++------------------------------+--------------------------------------------------------+
+| Google Cloud Storage, Azure  | :ref:`Cloud object storage plugins <object-storage>`   |
+| blob storage, AWS S3         |                                                        |
++------------------------------+--------------------------------------------------------+
+| Other                        | Create a :ref:`dedicated plugin <creating-plugins>`,   |
+|                              | implement a new dialect in the ODBC plugins,           |
+|                              | or prototype using :ref:`Python <python_storage_area>` |
++------------------------------+--------------------------------------------------------+
+
+
+Compilation
+-----------
+
+Static linking
+^^^^^^^^^^^^^^
+
+.. highlight:: text
+
+The procedure to compile these plugins is similar to that for the
+:ref:`core of Orthanc <compiling>`. The following commands should work
+for most UNIX-like distribution (including GNU/Linux)::
+
+  $ mkdir BuildOdbc
+  $ cd BuildOdbc
+  $ cmake ../Odbc -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release
+  $ make
+
+The compilation will produce 2 shared libraries, each containing one plugin for Orthanc:
+
+* ``OrthancOdbcIndex`` replaces the default SQLite index of Orthanc by ODBC. 
+* ``OrthancOdbcStorage`` makes Orthanc store the DICOM files it receives into ODBC. 
+
+  
+Microsoft Windows and Apple OS X
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Pre-compiled binaries for Microsoft Windows 32bit `are also available
+<https://www.orthanc-server.com/browse.php?path=/plugin-odbc>`__. A
+package for `Apple's Mac OS X
+<https://www.osimis.io/en/download.html>`__ is available courtesy of
+`Osimis <https://www.osimis.io/>`__.
+
+
+.. _odbc-ubuntu1604:
+
+Dynamic linking on Ubuntu 16.04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. highlight:: text
+
+If static linking is not desired, here are build instructions for
+Ubuntu 16.04 (provided build dependencies for the :ref:`core of
+Orthanc <compiling>` have already been installed)::
+
+  $ sudo apt-get install libodbc1 unixodbc unixodbc-dev
+  $ mkdir BuildOdbc
+  $ cd BuildOdbc
+  $ cmake ../Odbc -DCMAKE_BUILD_TYPE=Release \
+                  -DALLOW_DOWNLOADS=ON \
+                  -DUSE_SYSTEM_GOOGLE_TEST=OFF \
+                  -DUSE_SYSTEM_ORTHANC_SDK=OFF
+  $ make
+
+
+  
+Usage
+-----
+
+You of course first have to :ref:`install Orthanc <binaries>`, with a
+version above 0.9.5. You then have to **configure an ODBC data
+source** dedicated to Orthanc. The procedure depends upon your
+operating system:
+
+* Many UNIX-like platforms (including Debian and Ubuntu) use `unixODBC
+  <https://en.wikipedia.org/wiki/UnixODBC>`__. You first have to
+  install at least one ODBC driver (e.g. on Debian, installing the
+  packages ``libsqliteodbc`` and ``odbc-postgresql`` will respectively
+  install the driver for SQLite and for PostgreSQL). Secondly, you
+  have to edit your ``~/.odbc.ini`` to define the data sources
+  (i.e. the actual databases).
+
+* On Microsoft Windows, the configuration tool ``odbcad32.exe`` ("ODBC
+  Data Source Administrator") allows to define the data sources.  You
+  also have to install at least one ODBC driver. For instance, the
+  `SQLite ODBC Driver <http://www.ch-werner.de/sqliteodbc/>`__ can be
+  used to access SQLite.
+
+* If you are interested in interfacing Orthanc with Microsoft SQL
+  Server, the corresponding ODBC drivers can be `downloaded from
+  Microsoft
+  <https://docs.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server>`__.
+
+.. highlight:: json
+
+Once Orthanc is installed and the data sources have been defined, you
+must add a section in the :ref:`configuration file <configuration>`
+that specifies the **data source(s) to be used**. You also have to
+tell Orthanc in which path it can find the plugins: This is done by
+properly modifying the ``Plugins`` option. You could for instance
+adapt the following configuration file::
+
+  {
+    "Name" : "MyOrthanc",
+    "Odbc" : {
+      "EnableIndex" : true,
+      "EnableStorage" : true,
+      "IndexConnectionString" : "DSN=index",
+      "StorageConnectionString" : "DSN=storage",
+      "MaximumConnectionRetries" : 10,
+      "ConnectionRetryInterval" : 5,
+      "IndexConnectionsCount" : 1
+    },
+    "Plugins" : [
+      "/home/user/orthanc-databases/BuildOdbc/libOrthancOdbcIndex.so",
+      "/home/user/orthanc-databases/BuildOdbc/libOrthancOdbcStorage.so"
+    ]
+  }
+
+The values of ``IndexConnectionString`` and
+``StorageConnectionString`` are known as `ODBC connection strings
+<https://www.connectionstrings.com/>`__, and define how to connect to
+the ODBC data source. These connection strings are specific to the
+different types of ODBC drivers. In the following sections, we'll
+review connection strings for SQLite, PostgreSQL, MySQL and Microsoft
+SQL Server.
+  
+**Important:** The ``EnableIndex`` and ``EnableStorage`` options must
+be explicitly set to ``true``, otherwise Orthanc will continue to use
+its default SQLite back-end and the filesystem storage area.
+
+**Remark 1:** When using the ODBC storage area plugin, the DICOM files
+are stored as large objects in the database.  This might actually
+consume more space than the DICOM file itself.
+
+**Remark 2:** A typical usage of the ODBC plugins is to enable only
+the index plugin, and to use the default filesystem storage for DICOM
+files (on a NAS with proper disaster recovery strategies).
+
+Orthanc must of course be **restarted** after the modification of its
+configuration file.
+
+
+Supported ODBC drivers
+----------------------
+
+The ODBC plugins for Orthanc are universal, in the sense that they can
+connect to any ODBC driver. However, there are some minor variations
+in the SQL language, that are known as "dialects" in the `source code
+of the plugins <https://hg.orthanc-server.com/orthanc-databases/>`__.
+
+As of ODBC plugins 1.0, the supported dialects are Microsoft SQL
+Server, PostgreSQL, MySQL and SQLite. Orthanc auto-detects the dialect
+to be used. Adapting the ODBC plugins to support more dialects should
+be fairly easy by adding new values to the
+``OrthancDatabases::Dialect`` enumeration in the C++ source code.
+
+Also, note that the database for the index and the database for the
+storage area can mix different type of ODBC drivers.
+
+We now review sample `connection strings
+<https://www.connectionstrings.com/>`__ for the supported ODBC drivers
+under Ubuntu 18.04.
+
+
+Microsoft SQL Server
+^^^^^^^^^^^^^^^^^^^^
+
+.. highlight:: bash
+
+1. Install the `ODBC driver for SQL server
+   <https://docs.microsoft.com/fr-fr/sql/connect/odbc/download-odbc-driver-for-sql-server>`__
+   (version 2017).
+               
+2. A **non-persistent** developer instance of MSSQL 2019 can be
+   started using the `Docker image provided by Microsoft
+   <https://hub.docker.com/_/microsoft-mssql-server>`__ as follows::
+
+     $ docker run --name mssql --rm -t -i -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=MyStrOngPa55word!' \
+       -e MSSQL_MEMORY_LIMIT_MB=512 -p 1433:1433 mcr.microsoft.com/mssql/server:2019-latest
+
+3. Create a database dedicated to Orthanc in MSSQL::
+
+     $ /opt/mssql-tools/bin/sqlcmd -S 192.168.0.17 -U sa -P 'MyStrOngPa55word!' -Q 'CREATE DATABASE orthanctest'
+
+.. highlight:: text
+
+4. Create the following sample `unixODBC
+   <https://en.wikipedia.org/wiki/UnixODBC>`__ configuration file::
+    
+     $ cat ~/.odbc.ini
+     [orthanctest]
+     Driver = ODBC Driver 17 for SQL Server  
+     Server = tcp:localhost,1433
+     Database = orthanctest
+
+   Note that there exist `many more configuration options
+   <https://docs.microsoft.com/en-us/sql/relational-databases/native-client/applications/using-connection-string-keywords-with-sql-server-native-client>`__
+   for Microsoft SQL Server. In particular, ``Encrypt`` and
+   ``TrustServerCertificate`` and ``Connect Timeout`` can be
+   interesting in the case of a connection to Microsoft Azure SQL.
+     
+.. highlight:: json
+
+5. Start Orthanc using the following :ref:`configuration file
+   <configuration>` for ODBC::
+     
+     {
+       "Odbc" : {
+         "EnableIndex" : true,
+         "EnableStorage" : true,
+         "IndexConnectionString" : "DSN=orthanctest;Uid=sa;Pwd=MyStrOngPa55word!",
+         "StorageConnectionString" : "DSN=orthanctest;Uid=sa;Pwd=MyStrOngPa55word!"
+       }
+     }
+
+   In the connection strings:
+     
+   * ``DSN`` corresponds to the name of the entry in ``~/.odbc.ini``.
+   * ``Uid`` is the user name in MSSQL (by default, the Docker image uses ``sa``).
+   * ``Pwd`` is the password that has been provided in the ``SA_PASSWORD``
+     environment variable when starting Docker.
+   * For security reasons, the ``Uid`` and ``Pwd`` parameters cannot
+     be set in ``~/.odbc.ini``.
+
+**Remark:** It is actually not necessary to create an entry in
+``~/.odbc.ini``.  All the parameters can indeed be provided directly
+in the connection strings, for instance::
+   
+  {
+    "Odbc" : {
+      "EnableIndex" : true,
+      "EnableStorage" : true,
+      "IndexConnectionString" : "Driver={ODBC Driver 17 for SQL Server};Server=tcp:localhost,1433;Database=orthanctest;Uid=sa;Pwd=MyStrOngPa55word!",
+      "StorageConnectionString" : "Driver={ODBC Driver 17 for SQL Server};Server=tcp:localhost,1433;Database=orthanctest;Uid=sa;Pwd=MyStrOngPa55word!"
+    }
+  }
+
+**Remark:** On Windows systems, we have noticed that the ODBC drivers character encoding 
+seems to depend on a system level configuration.  This configuration needs to enforce UTF-8.
+Therefore, it is advised to configure the system locale as follow:
+
+.. image:: ../images/odbc-windows-system-locale.png
+           :align: center
+           :width: 600px
+
+|
+
+PostgreSQL
+^^^^^^^^^^
+
+1. Install the ``odbc-postgresql`` package.
+
+.. highlight:: text
+
+2. Create the following sample `unixODBC
+   <https://en.wikipedia.org/wiki/UnixODBC>`__ configuration file::
+    
+     $ cat ~/.odbc.ini
+     [orthanctest]
+     Driver      = PostgreSQL Unicode
+     Servername  = localhost
+     Database    = orthanctest
+     UserName    = postgres
+     Password    = postgres
+     Port        = 5432
+
+.. highlight:: json
+
+3. Start Orthanc using the following :ref:`configuration file
+   <configuration>` for ODBC::
+     
+     {
+       "Odbc" : {
+         "EnableIndex" : true,
+         "EnableStorage" : true,
+         "IndexConnectionString" : "DSN=orthanctest",
+         "StorageConnectionString" : "DSN=orthanctest"
+       }
+     }
+   
+
+MySQL
+^^^^^
+
+1. Install the official `Connect/ODBC package
+   <https://dev.mysql.com/downloads/connector/odbc/>`__ (it is not
+   packaged for Ubuntu 18.04).
+
+.. highlight:: text
+
+2. Create the following sample `unixODBC
+   <https://en.wikipedia.org/wiki/UnixODBC>`__ configuration file::
+    
+     $ cat ~/.odbc.ini
+     [orthanctest]
+     Driver      = MySQL ODBC 8.0 Unicode Driver
+     Servername  = localhost
+     Database    = orthanctest
+     UID         = root
+     PWD         = root
+     Port        = 3306
+
+.. highlight:: json
+
+3. Start Orthanc using the following :ref:`configuration file
+   <configuration>` for ODBC::
+     
+     {
+       "Odbc" : {
+         "EnableIndex" : true,
+         "EnableStorage" : true,
+         "IndexConnectionString" : "DSN=orthanctest;charset=utf8",
+         "StorageConnectionString" : "DSN=orthanctest;charset=utf8"
+       }
+     }
+
+   The ``charset=utf8`` option is necessary if using MySQL 8.x.
+
+
+SQLite
+^^^^^^
+
+1. Install the ``libsqliteodbc`` package.
+
+.. highlight:: text
+
+2. Create the following sample `unixODBC
+   <https://en.wikipedia.org/wiki/UnixODBC>`__ configuration file::
+    
+     $ cat ~/.odbc.ini
+     [index]
+     Driver=SQLite3
+     Database=/tmp/test-odbc-index.sqlite
+
+     [storage]
+     Driver=SQLite3
+     Database=/tmp/test-odbc-storage.sqlite
+
+   Note that we define two different data sources, one for the index
+   and another for the storage area, because a SQLite database can
+   only be opened by one client at once.
+     
+.. highlight:: json
+
+3. Start Orthanc using the following :ref:`configuration file
+   <configuration>` for ODBC::
+     
+     {
+       "Odbc" : {
+         "EnableIndex" : true,
+         "EnableStorage" : true,
+         "IndexConnectionString" : "DSN=index",
+         "StorageConnectionString" : "DSN=storage",
+         "IndexConnectionsCount" : 1
+       }
+     }
+
+   **Remark 1:** As written just above, one SQLite database should
+   only be opened by one client at a time. This implies that the
+   ``IndexConnectionsCount`` must be set to ``1``, and that the index
+   and storage area must never have connection strings corresponding
+   to the same SQLite database.
+
+   **Remark 2:** As written above, the ODBC plugin supports the
+   :ref:`revision mechanism <revisions>`. This contrasts with the
+   built-in SQLite database of Orthanc. So, it might be interesting to
+   use the ODBC index plugin instead of the built-in SQLite database
+   of Orthanc, if you are a developer who wants to test revisions
+   before a :ref:`large-scale deployment <scalability>`.
+
+
+Advanced options
+----------------
+
+Several advanced options are available as well to fine-tune the
+configuration of the ODBC plugins. They are documented below.
+
+
+.. _odbc-multiple-writers:
+
+Multiple writers or connections
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Starting with Orthanc 1.9.2, it is possible to use :ref:`multiple
+writers or connections in large-scale deployments
+<multiple-writers>`. Here is the list of configuration that control
+this behaviour:
+
+* ``MaximumConnectionRetries`` governs how many times Orthanc tries to
+  connect to the database, as well as how many times Orthanc replays
+  transactions to deal with collisions between multiple writers.
+
+* ``IndexConnectionsCount`` controls the number of connections from
+  the index plugin to the ODBC database. It is set to ``1`` by
+  default, which corresponds to the old behaviour of Orthanc <= 1.9.1.
+
+* ``ConnectionRetryInterval`` is only used when opening one database
+  connection to ODBC.
+
+* These options cannot be used in the case of SQLite databases, that
+  only support one client at once.
--- a/Sphinx/source/plugins/postgresql.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/postgresql.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -16,6 +16,11 @@
 For information about scalability, make sure to read the section about
 :ref:`multiple writers in large-scale deployments <multiple-writers>`.
 
+The source code of the PostgreSQL plugins can be found in the
+``orthanc-databases`` `Mercurial repository
+<https://hg.orthanc-server.com/orthanc-databases/>`__, next to the
+source code of the :ref:`ODBC <odbc>` and
+:ref:`MySQL/MariaDB <mysql>` plugins.
 
 
 Compilation
@@ -44,7 +49,7 @@
 Microsoft Windows and Apple OS X
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Pre-compiled binaries for Microsoft Windows `are also available
+Pre-compiled binaries for Microsoft Windows 32bit `are also available
 <https://www.orthanc-server.com/browse.php?path=/plugin-postgresql>`__.
 A package for `Apple's Mac OS X
 <https://www.osimis.io/en/download.html>`__
--- a/Sphinx/source/plugins/python.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/python.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -16,15 +16,15 @@
 instead of the more complex C/C++ programming languages.
 
 Python plugins have access to more features and a more consistent SDK
-than :ref:`Lua scripts <lua>`. The Python API is automatically
-generated from the `Orthanc plugin SDK in C
-<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__
+than :ref:`Lua scripts <lua>`. The largest part of the Python API is
+automatically generated from the `Orthanc plugin SDK in C
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__
 using the `Clang <https://en.wikipedia.org/wiki/Clang>`__ compiler
 front-end.
 
-As of release 2.0 of the plugin, the coverage of the C SDK is about
-75% (119 functions are automatically wrapped in Python out of a total
-of 157 functions in the Orthanc SDK 1.7.2).
+As of release 3.2 of the plugin, the coverage of the C SDK is about
+87% (138 functions are automatically wrapped in Python out of a total
+of 158 functions from the Orthanc SDK 1.8.1).
 
 
 Source code
@@ -66,7 +66,7 @@
 Docker
 ......
 
-.. highlight:: json
+.. highlight:: python
 
 The most direct way of starting Orthanc together with the Python
 plugin is through :ref:`Docker <docker>`. Let's create the file
@@ -111,9 +111,30 @@
 `Here <https://bitbucket.org/osimis/orthanc-setup-samples/src/master/docker/python/>`__ is a full example
 of a more complex setup using the :ref:`osimis/orthanc <docker-osimis>` images.
 
+
+Microsoft Windows
+.................
+
+Pre-compiled binaries for Microsoft Windows `are also available
+<https://www.orthanc-server.com/browse.php?path=/plugin-python>`__.
+
+Beware that one version of the Python plugin can only be run against
+one version of the Python interpreter. This version is clearly
+indicated in the filename of the precompiled binaries.  
+
+Pay also attention to pick the right 32/64 bits version.  If you are
+running Orthanc 64bits, install Python in 64bits and select the 64bits
+Python plugin too.
+
+When you install Python on your Windows machine, make sure to install
+Python for ``All Users`` and select the ``Add Python to Path`` option.
+
 Compiling from source
 .....................
 
+For GNU/Linux
+^^^^^^^^^^^^^
+
 .. highlight:: bash
 
 The procedure to compile this plugin from source is similar to that
@@ -148,15 +169,8 @@
           -DPYTHON_INCLUDE_DIR=/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/include/python3.8/
   
   
-Microsoft Windows
-.................
-
-Pre-compiled binaries for Microsoft Windows `are also available
-<https://www.orthanc-server.com/browse.php?path=/plugin-python>`__.
-
-Beware that one version of the Python plugin can only be run against
-one version of the Python interpreter. This version is clearly
-indicated in the filename of the precompiled binaries.
+For Microsoft Windows
+^^^^^^^^^^^^^^^^^^^^^
 
 .. highlight:: text
 
@@ -164,7 +178,7 @@
 to explicitly specify the path to your Python installation while
 invoking CMake. For instance::
 
-  C:\orthanc-python\Build> cmake .. -DPYTHON_VERSION=2.7 -DPYTHON_WINDOWS_ROOT=C:/Python27 \
+  C:\orthanc-python\Build> cmake .. -DPYTHON_VERSION=3.8 -DPYTHON_WINDOWS_ROOT=C:/Python38 \
                                     -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 15 2017"
 
 **Note about debug builds**: Usually, building Python modules such as the Python 
@@ -185,7 +199,7 @@
 that is ``ON`` by default, to ``OFF``. The previous build example would then be,
 should you require a full debug build::
 
-  C:\orthanc-python\Build> cmake .. -DPYTHON_VERSION=2.7 -DPYTHON_WINDOWS_ROOT=C:/Python27 \
+  C:\orthanc-python\Build> cmake .. -DPYTHON_VERSION=3.8 -DPYTHON_WINDOWS_ROOT=C:/Python38 \
                                     -DSTATIC_BUILD=ON -DPYTHON_WINDOWS_USE_RELEASE_LIBS=OFF \
                                     -DCMAKE_BUILD_TYPE=Debug -G "Visual Studio 15 2017"
 
@@ -215,21 +229,11 @@
 Extending the REST API
 ......................
 
-.. highlight:: python
-
 Here is a basic Python script that registers two new routes in the
-REST API::
-
-  import orthanc
-  import pprint
+REST API:
 
-  def OnRest(output, uri, **request):
-      pprint.pprint(request)
-      print('Accessing uri: %s' % uri)
-      output.AnswerBuffer('ok\n', 'text/plain')
-    
-  orthanc.RegisterRestCallback('/(to)(t)o', OnRest)
-  orthanc.RegisterRestCallback('/tata', OnRest)
+.. literalinclude:: python/extending-rest-api.py
+                    :language: python
 
 .. highlight:: json
 
@@ -255,27 +259,10 @@
 Listening to changes
 ....................
 
-.. highlight:: python
-
-This sample uploads a DICOM file as soon as Orthanc is started::
-
-   import orthanc
-
-   def OnChange(changeType, level, resource):
-       if changeType == orthanc.ChangeType.ORTHANC_STARTED:
-           print('Started')
+This sample uploads a DICOM file as soon as Orthanc is started:
 
-           with open('/tmp/sample.dcm', 'rb') as f:
-               orthanc.RestApiPost('/instances', f.read())
-        
-        elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
-            print('Stopped')
-
-        elif changeType == orthanc.ChangeType.NEW_INSTANCE:
-            print('A new instance was uploaded: %s' % resource)
-
-    orthanc.RegisterOnChangeCallback(OnChange)
-
+.. literalinclude:: python/listening-changes.py
+                    :language: python
 
 
 .. warning::
@@ -289,22 +276,10 @@
 these calls in a separate thread, passing the pending events to be
 processed through a message queue. Here is the template of a possible
 solution to postpone such deadlocks as much as possible by relying on
-the multithreading primitives of Python::
-
-  import orthanc
-  import threading
+the multithreading primitives of Python:
 
-  def OnChange(changeType, level, resource):
-      # One can safely invoke the "orthanc" module in this function
-      orthanc.LogWarning("Hello world")
-  
-  def _OnChange(changeType, level, resource):
-      # Invoke the actual "OnChange()" function in a separate thread
-      t = threading.Timer(0, function = OnChange, args = (changeType, level, resource))
-      t.start()
-
-  orthanc.RegisterOnChangeCallback(_OnChange)
-
+.. literalinclude:: python/changes-deadlock-3.0.py
+                    :language: python
 
 Beware that **this workaround is imperfect** and deadlocks have been
 observed even if using it! Make sure to upgrade your plugin to solve
@@ -316,31 +291,8 @@
 Accessing the content of a new instance
 .......................................
 
-.. highlight:: python
-
-::
-   
-  import orthanc
-  import json
-  import pprint
-
-  def OnStoredInstance(dicom, instanceId):
-      print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % (
-          instanceId, dicom.GetInstanceSize(),
-          dicom.GetInstanceMetadata('TransferSyntax'),
-          dicom.GetInstanceMetadata('SopClassUid')))
-
-      # Print the origin information
-      if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL:
-          print('This instance was received through the DICOM protocol')
-      elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API:
-          print('This instance was received through the REST API')
-
-      # Print the DICOM tags
-      pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson()))
-
-  orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance)
-
+.. literalinclude:: python/accessing-new-instance.py
+                    :language: python
 
 .. warning::
    Your callback function will be called synchronously with
@@ -357,34 +309,16 @@
 Calling pydicom
 ...............
 
-.. highlight:: python
-
 Here is a sample Python plugin that registers a REST callback to dump
 the content of the dataset of one given DICOM instance stored in
-Orthanc, using `pydicom <https://pydicom.github.io/>`__::
-  
-  import io
-  import orthanc
-  import pydicom
+Orthanc, using `pydicom <https://pydicom.github.io/>`__:
 
-  def DecodeInstance(output, uri, **request):
-      if request['method'] == 'GET':
-          # Retrieve the instance ID from the regular expression (*)
-          instanceId = request['groups'][0]
-          # Get the content of the DICOM file
-          f = orthanc.GetDicomForInstance(instanceId)
-          # Parse it using pydicom
-          dicom = pydicom.dcmread(io.BytesIO(f))
-          # Return a string representation the dataset to the caller
-          output.AnswerBuffer(str(dicom), 'text/plain')
-      else:
-          output.SendMethodNotAllowed('GET')
-
-  orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance)  # (*)
+.. literalinclude:: python/pydicom.py
+                    :language: python
 
 .. highlight:: bash
 
-This can be called as follows::
+This callback can be called as follows::
   
   $ curl http://localhost:8042/pydicom/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5
   
@@ -392,68 +326,25 @@
 Auto-routing studies
 ....................
 
-.. highlight:: python
-
 Here is a sample Python plugin that routes any :ref:`stable study
 <stable-resources>` to a modality named ``samples`` (as declared in the
-``DicomModalities`` configuration option)::
+``DicomModalities`` configuration option):
   
-  import orthanc
-
-  def OnChange(changeType, level, resourceId):
-      if changeType == orthanc.ChangeType.STABLE_STUDY:
-          print('Stable study: %s' % resourceId)
-          orthanc.RestApiPost('/modalities/sample/store', resourceId)
-
-  orthanc.RegisterOnChangeCallback(OnChange)
-
+.. literalinclude:: python/autorouting-1.py
+                    :language: python
 
 Note that, if you want to use an orthanc plugin to transfer the study,
-you should use the ``RestApiPostAfterPlugins()`` method::
-
-  import orthanc
+you should use the ``RestApiPostAfterPlugins()`` method:
 
-  def OnChange(changeType, level, resourceId):
-      if changeType == orthanc.ChangeType.STABLE_STUDY:
-          print('Stable study: %s' % resourceId)
-          orthanc.RestApiPostAfterPlugins('/dicom-web/servers/sample/store', resourceId)
-
-  orthanc.RegisterOnChangeCallback(OnChange)
-
+.. literalinclude:: python/autorouting-2.py
+                    :language: python
+                               
 
 Rendering a thumbnail using PIL/Pillow
 ......................................
 
-.. highlight:: python
-
-::
-   
-  from PIL import Image
-  import io
-  import orthanc
-
-  def DecodeInstance(output, uri, **request):
-      if request['method'] == 'GET':
-          # Retrieve the instance ID from the regular expression (*)
-          instanceId = request['groups'][0]
-
-          # Render the instance, then open it in Python using PIL/Pillow
-          png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId)
-          image = Image.open(io.BytesIO(png))
-
-          # Downsize the image as a 64x64 thumbnail
-          image.thumbnail((64, 64), Image.ANTIALIAS)
-
-          # Save the thumbnail as JPEG, then send the buffer to the caller
-          jpeg = io.BytesIO()
-          image.save(jpeg, format = "JPEG", quality = 80)
-          jpeg.seek(0)
-          output.AnswerBuffer(jpeg.read(), 'text/plain')
-
-      else:
-          output.SendMethodNotAllowed('GET')
-
-  orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance)  # (*)
+.. literalinclude:: python/pil.py
+                    :language: python
 
 
 .. _python-introspection:
@@ -461,68 +352,24 @@
 Inspecting the available API
 ............................
 
-.. highlight:: python
-
 Thanks to Python's introspection primitives, it is possible to inspect
 the API of the ``orthanc`` module in order to dump all the available
-features::
-
-  import inspect
-  import numbers
-  import orthanc
-
-  # Loop over the members of the "orthanc" module
-  for (name, obj) in inspect.getmembers(orthanc):
-      if inspect.isroutine(obj):
-          print('Function %s():\n  Documentation: %s\n' % (name, inspect.getdoc(obj)))
+features:
 
-      elif inspect.isclass(obj):
-          print('Class %s:\n  Documentation: %s' % (name, inspect.getdoc(obj)))
+.. literalinclude:: python/inspect-api.py
+                    :language: python
 
-          # Loop over the members of the class
-          for (subname, subobj) in inspect.getmembers(obj):
-              if isinstance(subobj, numbers.Number):
-                  print('  - Enumeration value %s: %s' % (subname, subobj))
-              elif (not subname.startswith('_') and
-                    inspect.ismethoddescriptor(subobj)):
-                  print('  - Method %s(): %s' % (subname, inspect.getdoc(subobj)))
-          print('')
-
-
+                               
 .. _python-scheduler:
 
 Scheduling a task for periodic execution
 ........................................
 
-.. highlight:: python
-
 The following Python script will periodically (every second) run the
-function ``Hello()`` thanks to the ``threading`` module::
-
-  import orthanc
-  import threading
-
-  TIMER = None
+function ``Hello()`` thanks to the ``threading`` module:
 
-  def Hello():
-      global TIMER
-      TIMER = None
-      orthanc.LogWarning("In Hello()")
-      # Do stuff...
-      TIMER = threading.Timer(1, Hello)  # Re-schedule after 1 second
-      TIMER.start()
-
-  def OnChange(changeType, level, resource):
-      if changeType == orthanc.ChangeType.ORTHANC_STARTED:
-          orthanc.LogWarning("Starting the scheduler")
-          Hello()
-
-      elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
-          if TIMER != None:
-              orthanc.LogWarning("Stopping the scheduler")
-              TIMER.cancel()
-
-  orthanc.RegisterOnChangeCallback(OnChange)
+.. literalinclude:: python/periodic-execution.py
+                    :language: python
 
 
 .. _python-metadata:
@@ -542,83 +389,11 @@
 tags. Filtering metadata requires a linear search over all the
 matching resources, which induces a cost in the performance.
 
-.. highlight:: python
-
 Nevertheless, here is a full sample Python script that overwrites the
-``/tools/find`` route in order to give access to metadata::
-
-  import json
-  import orthanc
-  import re
-
-  # Get the path in the REST API to the given resource that was returned
-  # by a call to "/tools/find"
-  def GetPath(resource):
-      if resource['Type'] == 'Patient':
-          return '/patients/%s' % resource['ID']
-      elif resource['Type'] == 'Study':
-          return '/studies/%s' % resource['ID']
-      elif resource['Type'] == 'Series':
-          return '/series/%s' % resource['ID']
-      elif resource['Type'] == 'Instance':
-          return '/instances/%s' % resource['ID']
-      else:
-          raise Exception('Unknown resource level')
-
-  def FindWithMetadata(output, uri, **request):
-      # The "/tools/find" route expects a POST method
-      if request['method'] != 'POST':
-          output.SendMethodNotAllowed('POST')
-      else:
-          # Parse the query provided by the user, and backup the "Expand" field
-          query = json.loads(request['body'])       
-
-          if 'Expand' in query:
-              originalExpand = query['Expand']
-          else:
-              originalExpand = False
+``/tools/find`` route in order to give access to metadata:
 
-          # Call the core "/tools/find" route
-          query['Expand'] = True
-          answers = orthanc.RestApiPost('/tools/find', json.dumps(query))
-
-          # Loop over the matching resources
-          filteredAnswers = []
-          for answer in json.loads(answers):
-              try:
-                  # Read the metadata that is associated with the resource
-                  metadata = json.loads(orthanc.RestApiGet('%s/metadata?expand' % GetPath(answer)))
-
-                  # Check whether the metadata matches the regular expressions
-                  # that were provided in the "Metadata" field of the user request
-                  isMetadataMatch = True
-                  if 'Metadata' in query:
-                      for (name, pattern) in query['Metadata'].items():
-                          if name in metadata:
-                              value = metadata[name]
-                          else:
-                              value = ''
-
-                          if re.match(pattern, value) == None:
-                              isMetadataMatch = False
-                              break
-
-                  # If all the metadata matches the provided regular
-                  # expressions, add the resource to the filtered answers
-                  if isMetadataMatch:
-                      if originalExpand:
-                          answer['Metadata'] = metadata
-                          filteredAnswers.append(answer)
-                      else:
-                          filteredAnswers.append(answer['ID'])
-              except:
-                  # The resource was deleted since the call to "/tools/find"
-                  pass
-
-          # Return the filtered answers in the JSON format
-          output.AnswerBuffer(json.dumps(filteredAnswers, indent = 3), 'application/json')
-
-  orthanc.RegisterRestCallback('/tools/find', FindWithMetadata)
+.. literalinclude:: python/filtering-metadata.py
+                    :language: python
 
 
 **Warning:** In the sample above, the filtering of the metadata is
@@ -641,54 +416,15 @@
 Implementing basic paging
 .........................
 
-.. highlight:: python
-
 As explained in the FAQ, the :ref:`Orthanc Explorer interface is
 low-level <improving-interface>`, and is not adapted for
 end-users. One common need is to implement paging of studies, which
 calls for server-side sorting of studies. This can be done using the
 following sample Python plugin that registers a new route
-``/sort-studies`` in the REST API of Orthanc::
-
- import json
- import orthanc
-
- def GetStudyDate(study):
-     if 'StudyDate' in study['MainDicomTags']:
-         return study['MainDicomTags']['StudyDate']
-     else:
-         return ''
-
- def SortStudiesByDate(output, uri, **request):
-     if request['method'] == 'GET':
-         # Retrieve all the studies
-         studies = json.loads(orthanc.RestApiGet('/studies?expand'))
-
-         # Sort the studies according to the "StudyDate" DICOM tag
-         studies = sorted(studies, key = GetStudyDate)
+``/sort-studies`` in the REST API of Orthanc:
 
-         # Read the limit/offset arguments provided by the user
-         offset = 0
-         if 'offset' in request['get']:
-             offset = int(request['get']['offset'])
-
-         limit = 0
-         if 'limit' in request['get']:
-             limit = int(request['get']['limit'])
-
-         # Truncate the list of studies
-         if limit == 0:
-             studies = studies[offset : ]
-         else:
-             studies = studies[offset : offset + limit]
-
-         # Return the truncated list of studies
-         output.AnswerBuffer(json.dumps(studies), 'application/json')
-     else:
-         output.SendMethodNotAllowed('GET')
-
- orthanc.RegisterRestCallback('/sort-studies', SortStudiesByDate)
-
+.. literalinclude:: python/paging.py
+                    :language: python
 
 .. highlight:: bash
 
@@ -716,41 +452,12 @@
 Creating a Microsoft Excel report
 .................................
 
-.. highlight:: python
-
 As Orthanc plugins have access to any installed Python module, it is
 very easy to implement a server-side plugin that generates a report in
-the Microsoft Excel ``.xls`` format. Here is a working example::
+the Microsoft Excel ``.xls`` format. Here is a working example:
 
- import StringIO
- import json
- import orthanc
- import xlwt 
- 
- def CreateExcelReport(output, uri, **request):
-     if request['method'] != 'GET' :
-         output.SendMethodNotAllowed('GET')
-     else:
-         # Create an Excel writer
-         excel = xlwt.Workbook()
-         sheet = excel.add_sheet('Studies')
- 
-         # Loop over the studies stored in Orthanc
-         row = 0
-         studies = orthanc.RestApiGet('/studies?expand')
-         for study in json.loads(studies):
-             sheet.write(row, 0, study['PatientMainDicomTags'].get('PatientID'))
-             sheet.write(row, 1, study['PatientMainDicomTags'].get('PatientName'))
-             sheet.write(row, 2, study['MainDicomTags'].get('StudyDescription'))
-             row += 1
- 
-         # Serialize the Excel workbook to a string, and return it to the caller
-         # https://stackoverflow.com/a/15649139/881731
-         b = StringIO.StringIO()
-         excel.save(b)       
-         output.AnswerBuffer(b.getvalue(), 'application/vnd.ms-excel')
-
- orthanc.RegisterRestCallback('/report.xls', CreateExcelReport)
+.. literalinclude:: python/excel.py
+                    :language: python
 
 If opening the ``http://localhost:8042/report.xls`` URI, this Python
 will generate a workbook with one sheet that contains the list of
@@ -760,23 +467,15 @@
 
 .. _python_authorization:
 
-Forbid or allow access to REST resources (authorization)
-........................................................
-
-.. highlight:: python
+Forbid or allow access to REST resources (authorization, new in 3.0)
+....................................................................
 
 The following Python script installs a callback that is triggered
-whenever the HTTP server of Orthanc is accessed::
-
-  import orthanc
-  import pprint
+whenever the HTTP server of Orthanc is accessed:
 
-  def Filter(uri, **request):
-      print('User trying to access URI: %s' % uri)
-      pprint.pprint(request)
-      return True  # False to forbid access
+.. literalinclude:: python/authorization-1.py
+                    :language: python
 
-  orthanc.RegisterIncomingHttpRequestFilter(Filter)
 
 If access is not granted, the ``Filter`` callback must return
 ``False``. As a consequence, the HTTP status code would be set to
@@ -789,49 +488,301 @@
 callback that is available in :ref:`Lua scripts <lua-filter-rest>`.
 
 Thanks to Python, it is extremely easy to call remote Web services for
-authorization. Here is an example using the ``requests`` library::
-
-  import json
-  import orthanc
-  import requests
+authorization. Here is an example using the ``requests`` library:
 
-  def Filter(uri, **request):
-      body = {
-          'uri' : uri,
-          'headers' : request['headers']
-      }
-      r = requests.post('http://localhost:8000/authorize',
-                        data = json.dumps(body))
-      return r.json() ['granted']  # Must be a Boolean
-
-  orthanc.RegisterIncomingHttpRequestFilter(Filter)
-
-.. highlight:: javascript
+.. literalinclude:: python/authorization-2.py
+                    :language: python
 
 This filter could be used together with the following Web service
 implemented using `Node.js
-<https://en.wikipedia.org/wiki/Node.js>`__::
+<https://en.wikipedia.org/wiki/Node.js>`__:
+
+.. literalinclude:: python/authorization-node-service.js
+                    :language: javascript
+
+  
+.. _python_lookup_dictionary:
+
+Lookup DICOM dictionary (new in 3.2)
+....................................
+
+Python plugins can access the dictionary of the DICOM tags that are
+handled by Orthanc:
+
+.. literalinclude:: python/lookup-dictionary.py
+                    :language: python
+
+.. highlight:: text
+
+Note how Python introspection is used in order to map the values in
+enumeration ``orthanc.ValueRepresentation`` to a string description of
+the value representation. If started, the plugin above would output
+the following information in the Orthanc logs::
 
-  const http = require('http');
+  W0611 14:04:08.563957 PluginsManager.cpp:168] Entry in the dictionary: {
+      "Element": 32, 
+      "Group": 16, 
+      "MaxMultiplicity": 1, 
+      "MinMultiplicity": 1, 
+      "ValueRepresentation": 11
+  }
+  W0611 14:04:08.563975 PluginsManager.cpp:168] Name of the value representation: LO
+
+
+.. _python_create_dicom:
+
+Creating DICOM instances (new in 3.2)
+.....................................
+
+The following sample Python script will write on the disk a new DICOM
+instance including the traditional Lena sample image, and will decode
+the single frame of this DICOM instance:
+
+.. literalinclude:: python/create-dicom.py
+                    :language: python
+
+
+.. _python_pil_conversions:
+
+Conversions between Orthanc and Python images (new in 3.2)
+..........................................................
+
+The Python method ``orthanc.Image.GetImageBuffer()`` returns a copy of
+the memory buffer of an image that is handled Orthanc. Conversely, the
+Python function ``orthanc.CreateImageFromBuffer()`` can be used to
+create an Orthanc image from a Python buffer. Taken together, these
+two functions can be used to do bidirectional conversions between
+Orthanc images and Python images.
 
-  const requestListener = function(req, res) {
-    let body = '';
-      req.on('data', function(chunk) {
-      body += chunk;
-    });
-    req.on('end', function() {
-      console.log(JSON.parse(body));
-      var answer = {
-        'granted' : false  // Forbid access
-      };
-      res.writeHead(200);
-      res.end(JSON.stringify(answer));
-    });
+Here is a full working example using PIL/Pillow that shows how to
+decode one frame of a DICOM instance using Orthanc, then to modify
+this image using PIL, and finally to upload the modified image as a
+new DICOM instance:
+
+.. literalinclude:: python/pil-conversions.py
+                    :language: python
+
+
+.. _python_dicom_scp:
+
+Handling DICOM SCP requests (new in 3.2)
+........................................
+
+Starting with release 3.2 of the Python plugin, it is possible to
+replace the C-FIND SCP and C-MOVE SCP of Orthanc by a Python
+script. This feature can notably be used to create a custom DICOM
+proxy. Here is a minimal example:
+
+.. literalinclude:: python/dicom-find-move-scp.py
+                    :language: python
+
+
+.. highlight:: text
+  
+In this sample, the C-FIND SCP will send one single answer that
+reproduces the values provided by the SCU::
+
+  $ findscu localhost 4242 -S -k QueryRetrieveLevel=STUDY -k PatientName=TEST -k SeriesDescription=
+  I: ---------------------------
+  I: Find Response: 1 (Pending)
+  I: 
+  I: # Dicom-Data-Set
+  I: # Used TransferSyntax: Little Endian Explicit
+  I: (0008,0005) CS [ISO_IR 100]                             #  10, 1 SpecificCharacterSet
+  I: (0008,0052) CS [HELLO0-STUDY]                           #  12, 1 QueryRetrieveLevel
+  I: (0008,103e) LO [HELLO1- ]                               #   8, 1 SeriesDescription
+  I: (0010,0010) PN [HELLO2-TEST ]                           #  12, 1 PatientName
+  I: 
+
+A more realistic Python script could for instance call the route
+``/modalities/{...}/query`` in the :ref:`REST API <rest-find-scu>` of
+Orthanc using ``orthanc.RestApiPost()``, in order to query the content
+a remote modality through a second C-FIND SCU request (this time
+issued by Orthanc as a SCU).
+  
+The C-MOVE SCP can be invoked as follows::
+  
+  $ movescu localhost 4242 -aem TARGET -aec SOURCE -aet MOVESCU -S -k QueryRetrieveLevel=IMAGE -k StudyInstanceUID=1.2.3.4
+
+The C-MOVE request above would print the following information in the
+Orthanc logs::
+
+  W0610 18:30:36.840865 PluginsManager.cpp:168] C-MOVE request to be handled in Python: {
+      "AccessionNumber": "", 
+      "Level": "INSTANCE", 
+      "OriginatorAET": "MOVESCU", 
+      "OriginatorID": 1, 
+      "PatientID": "", 
+      "SOPInstanceUID": "", 
+      "SeriesInstanceUID": "", 
+      "SourceAET": "SOURCE", 
+      "StudyInstanceUID": "1.2.3.4", 
+      "TargetAET": "TARGET"
   }
 
-  http.createServer(requestListener).listen(8000);
+It is now up to your Python callback to process the C-MOVE SCU request,
+for instance by calling the route ``/modalities/{...}/store`` in the
+:ref:`REST API <rest-store-scu>` of Orthanc using
+``orthanc.RestApiPost()``. It is highly advised to create a Python
+thread to handle the request, in order to avoid blocking Orthanc as
+much as possible.
+
+
+.. _python_worklists:
+
+Handling worklist SCP requests (new in 3.2)
+...........................................
+
+Starting with release 3.2 of the Python plugin, it is possible to
+answer :ref:`worklist queries <worklist>` using a Python script. This
+is especially useful to easily create a bridge between Orthanc,
+HL7/FHIR messages and RIS systems. Indeed, Python provides many tools
+to handle HL7 such as `python-hl7 library
+<https://python-hl7.readthedocs.io/en/latest/>`__.
+
+The following Python script reproduces features similar to the
+:ref:`sample modality worklists plugin <worklists-plugin>`:
+
+.. literalinclude:: python/worklist.py
+                    :language: python
+
+.. highlight:: text
+  
+Here is the result of this plugin on a sample call::
+
+  $ findscu -W -k "ScheduledProcedureStepSequence[0].Modality=MR" 127.0.0.1 4242
+  I: ---------------------------
+  I: Find Response: 1 (Pending)
+  I: 
+  I: # Dicom-Data-Set
+  I: # Used TransferSyntax: Little Endian Explicit
+  I: (0008,0005) CS [ISO_IR 100]                             #  10, 1 SpecificCharacterSet
+  I: (0040,0100) SQ (Sequence with explicit length #=1)      #  18, 1 ScheduledProcedureStepSequence
+  I:   (fffe,e000) na (Item with explicit length #=1)          #  10, 1 Item
+  I:     (0008,0060) CS [MR]                                     #   2, 1 Modality
+  I:   (fffe,e00d) na (ItemDelimitationItem for re-encoding)   #   0, 0 ItemDelimitationItem
+  I: (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) #   0, 0 SequenceDelimitationItem
+  I: 
+  I: ---------------------------
+  I: Find Response: 2 (Pending)
+  I: 
+  I: # Dicom-Data-Set
+  I: # Used TransferSyntax: Little Endian Explicit
+  I: (0008,0005) CS [ISO_IR 100]                             #  10, 1 SpecificCharacterSet
+  I: (0040,0100) SQ (Sequence with explicit length #=1)      #  18, 1 ScheduledProcedureStepSequence
+  I:   (fffe,e000) na (Item with explicit length #=1)          #  10, 1 Item
+  I:     (0008,0060) CS [MR]                                     #   2, 1 Modality
+  I:   (fffe,e00d) na (ItemDelimitationItem for re-encoding)   #   0, 0 ItemDelimitationItem
+  I: (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) #   0, 0 SequenceDelimitationItem
+  I:
+
+
+.. _pynetdicom:
+
+Replacing DICOM SCP of Orthanc by pynetdicom
+............................................
+
+.. highlight:: json
+
+Thanks to Python plugins, it is also possible to replace the built-in
+DICOM SCP of Orthanc by `pynetdicom
+<https://pydicom.github.io/pynetdicom/stable/examples/storage.html>`__
+so as to customize how the DICOM protocol is handled. Firstly, in the
+configuration file, make sure to disable the Orthanc SCP by setting
+``DicomServerEnabled`` to ``false``::
+
+  {
+    "Plugins" : [ "." ],
+    "PythonScript" : "pynetdicom.py",
+    "DicomServerEnabled" : false
+  }
+
+Secondly, here a basic plugin illustrating how to start and stop the
+pynetdicom server, and handle incoming C-STORE requests:
 
-  
+.. literalinclude:: python/pynetdicom.py
+                    :language: python
+
+As can be seen in this listing, whenever the pynetdicom receives an
+incoming C-STORE request, it makes a POST call to the URI
+``/instances`` in the REST API of Orthanc in order to store the
+embedded DICOM dataset into Orthanc. Obviously, one can build more
+complex DICOM servers by handling more messages than C-STORE alone.
+
+
+.. _python_exception:
+
+Catching exceptions
+...................
+
+Starting with release 3.3 of the Python plugin, the plugin generates a
+Python exception derived from class ``orthanc.OrthancException`` if an
+error is encountered. This exception contains a tuple that provides
+the error code and its textual description.
+
+In releases <= 3.2, the Python plugin raised the `built-in exception
+<https://docs.python.org/3/library/exceptions.html>`__ ``ValueError``.
+
+Here is an example showing how to catch exceptions:
+
+.. literalinclude:: python/exception.py
+                    :language: python
+
+
+.. _python_storage_area:
+
+Implementing a custom storage area (new in 3.3)
+...............................................
+
+Starting with release 3.3 of the Python plugin, it is possible to
+replace the built-in storage area of Orthanc (that writes
+:ref:`attachments <metadata>` onto the filesystem in the
+``OrthancStorage`` folder by default), by providing 3 Python callbacks
+to the ``orthanc.RegisterStorageArea()`` function:
+
+* The first callback indicates how to **create** an attachment into
+  the storage area.
+
+* The second callback indicates how to **read** an attachment from the
+  storage area.
+
+* The third callback indicates how to **remove** an attachment out of
+  the storage area.
+
+This feature can be used to quickly and easily interface Orthanc with
+any `object-based storage
+<https://en.wikipedia.org/wiki/Object_storage>`__ technology available
+in Python (such as `Ceph
+<https://en.wikipedia.org/wiki/Ceph_(software)>`__ or AWS S3-like
+tools). The performance will not be as good as a C/C++ native plugin
+(cf. the :ref:`cloud storage <object-storage>`, the :ref:`PostgreSQL
+<postgresql>` and the :ref:`MySQL <mysql>` plugins), but it can be
+used for prototyping or for basic setups.
+
+Here is a full, self-explaining sample:
+
+.. literalinclude:: python/storage-area.py
+                    :language: python
+
+The ``contentType`` can be used to apply a special treatment to some
+types of attachments (typically, DICOM instances). This parameter
+takes its values from the ``orthanc.ContentType`` enumeration.
+
+
+.. _python_received_instance:
+
+Modifying received instances (new in 3.5 - not released yet)
+............................................................
+
+Starting with release 3.5 of the Python plugin, it is possible to
+modify instances received by Orthanc before they are stored in
+the storage.  This is usually easier to perform modification at this
+stage compared to using the ``/modify`` route once the instances
+has been stored.
+
+.. literalinclude:: python/received-instance-callback.py
+                    :language: python
+
 
 
 Performance and concurrency
@@ -845,31 +796,11 @@
 Using slave processes
 .....................
 
-.. highlight:: python
-
 Let us consider the following sample Python script that makes a
-CPU-intensive computation on a REST callback::
-
-  import math
-  import orthanc
-  import time
+CPU-intensive computation on a REST callback:
 
-  # CPU-intensive computation taking about 4 seconds
-  def SlowComputation():
-      start = time.time()
-      for i in range(1000):
-          for j in range(30000):
-              math.sqrt(float(j))
-      end = time.time()
-      duration = (end - start)
-      return 'computation done in %.03f seconds\n' % duration
-
-  def OnRest(output, uri, **request):
-      answer = SlowComputation()
-      output.AnswerBuffer(answer, 'text/plain')
-
-  orthanc.RegisterRestCallback('/computation', OnRest)
-
+.. literalinclude:: python/multiprocessing-1.py
+                    :language: python
 
 .. highlight:: text
 
@@ -901,8 +832,6 @@
 the GIL only applies to the Python script: The baseline REST API of
 Orthanc is not affected by the GIL.
 
-.. highlight:: python
-
 The solution is to use the `multiprocessing primitives
 <https://docs.python.org/3/library/multiprocessing.html>`__ of Python.
 The "master" Python interpreter that is initially started by the
@@ -911,39 +840,10 @@
 processes running a separate Python interpreter. This allows to
 offload intensive computations from the "master" Python interpreter of
 Orthanc onto those "slave" interpreters. The ``multiprocessing``
-library is actually quite straightforward to use::
-
-  import math
-  import multiprocessing
-  import orthanc
-  import signal
-  import time
+library is actually quite straightforward to use:
 
-  # CPU-intensive computation taking about 4 seconds
-  # (same code as above)
-  def SlowComputation():
-      start = time.time()
-      for i in range(1000):
-          for j in range(30000):
-              math.sqrt(float(j))
-      end = time.time()
-      duration = (end - start)
-      return 'computation done in %.03f seconds\n' % duration
-
-  # Ignore CTRL+C in the slave processes
-  def Initializer():
-      signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-  # Create a pool of 4 slave Python interpreters
-  POOL = multiprocessing.Pool(4, initializer = Initializer)
-
-  def OnRest(output, uri, **request):
-      # Offload the call to "SlowComputation" onto one slave process.
-      # The GIL is unlocked until the slave sends its answer back.
-      answer = POOL.apply(SlowComputation)
-      output.AnswerBuffer(answer, 'text/plain')
-
-  orthanc.RegisterRestCallback('/computation', OnRest)
+.. literalinclude:: python/multiprocessing-2.py
+                    :language: python
 
 .. highlight:: text
 
@@ -975,8 +875,6 @@
 Slave processes and the "orthanc" module
 ........................................
 
-.. highlight:: python
-
 Very importantly, pay attention to the fact that **only the "master"
 Python interpreter has access to the Orthanc SDK**. The "slave"
 processes have no access to the ``orthanc`` module.
@@ -984,22 +882,11 @@
 You must write your Python plugin so as that all the calls to
 ``orthanc`` are moved from the slaves process to the master
 process. For instance, here is how you would parse a DICOM file in a
-slave process::
-
-  import pydicom
-  import io
+slave process:
 
-  def OffloadedDicomParsing(dicom):
-      # No access to the "orthanc" library here, as we are in the slave process
-      dataset = pydicom.dcmread(io.BytesIO(dicom))
-      return str(dataset)
+.. literalinclude:: python/multiprocessing-3.py
+                    :language: python
 
-  def OnRest(output, uri, **request):
-      # The call to "orthanc.RestApiGet()" is only possible in the master process
-      dicom = orthanc.RestApiGet('/instances/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5/file')
-      answer = POOL.apply(OffloadedDicomParsing, args = (dicom, ))
-      output.AnswerBuffer(answer, 'text/plain')
-      
 Communication primitives such as ``multiprocessing.Queue`` are
 available to exchange messages from the "slave" Python interpreters to
 the "master" Python interpreter for more advanced scenarios.
@@ -1011,28 +898,7 @@
 the REST API of Orthanc (without have to set credentials in your
 plugin). Any HTTP client library for Python, such as `requests
 <https://requests.readthedocs.io/en/master/>`__, can then be used to
-access the REST API of Orthanc. Here is a minimal example::
+access the REST API of Orthanc. Here is a minimal example:
 
-  import json
-  import multiprocessing
-  import orthanc
-  import requests
-  import signal
-  
-  TOKEN = orthanc.GenerateRestApiAuthorizationToken()
-  
-  def SlaveProcess():
-      r = requests.get('http://localhost:8042/instances',
-                       headers = { 'Authorization' : TOKEN })
-      return json.dumps(r.json())
-  
-  def Initializer():
-      signal.signal(signal.SIGINT, signal.SIG_IGN)
-  
-  POOL = multiprocessing.Pool(4, initializer = Initializer)
-  
-  def OnRest(output, uri, **request):
-      answer = POOL.apply(SlaveProcess)
-      output.AnswerBuffer(answer, 'text/plain')
-  
-  orthanc.RegisterRestCallback('/computation', OnRest)
+.. literalinclude:: python/multiprocessing-4.py
+                    :language: python
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/accessing-new-instance.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,20 @@
+import orthanc
+import json
+import pprint
+
+def OnStoredInstance(dicom, instanceId):
+    print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % (
+        instanceId, dicom.GetInstanceSize(),
+        dicom.GetInstanceMetadata('TransferSyntax'),
+        dicom.GetInstanceMetadata('SopClassUid')))
+
+    # Print the origin information
+    if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL:
+        print('This instance was received through the DICOM protocol')
+    elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API:
+        print('This instance was received through the REST API')
+
+    # Print the DICOM tags
+    pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson()))
+
+orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/authorization-1.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,9 @@
+import orthanc
+import pprint
+
+def Filter(uri, **request):
+    print('User trying to access URI: %s' % uri)
+    pprint.pprint(request)
+    return True  # False to forbid access
+
+orthanc.RegisterIncomingHttpRequestFilter(Filter)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/authorization-2.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,14 @@
+import json
+import orthanc
+import requests
+
+def Filter(uri, **request):
+    body = {
+        'uri' : uri,
+        'headers' : request['headers']
+    }
+    r = requests.post('http://localhost:8000/authorize',
+                      data = json.dumps(body))
+    return r.json() ['granted']  # Must be a Boolean
+
+orthanc.RegisterIncomingHttpRequestFilter(Filter)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/authorization-node-service.js	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,18 @@
+const http = require('http');
+
+const requestListener = function(req, res) {
+  let body = '';
+  req.on('data', function(chunk) {
+    body += chunk;
+  });
+  req.on('end', function() {
+    console.log(JSON.parse(body));
+    var answer = {
+      'granted' : false  // Forbid access
+    };
+    res.writeHead(200);
+    res.end(JSON.stringify(answer));
+  });
+}
+
+http.createServer(requestListener).listen(8000);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/autorouting-1.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,8 @@
+import orthanc
+
+def OnChange(changeType, level, resourceId):
+    if changeType == orthanc.ChangeType.STABLE_STUDY:
+        print('Stable study: %s' % resourceId)
+        orthanc.RestApiPost('/modalities/sample/store', resourceId)
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/autorouting-2.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,8 @@
+import orthanc
+
+def OnChange(changeType, level, resourceId):
+    if changeType == orthanc.ChangeType.STABLE_STUDY:
+        print('Stable study: %s' % resourceId)
+        orthanc.RestApiPostAfterPlugins('/dicom-web/servers/sample/store', resourceId)
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/changes-deadlock-3.0.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,13 @@
+import orthanc
+import threading
+
+def OnChange(changeType, level, resource):
+    # One can safely invoke the "orthanc" module in this function
+    orthanc.LogWarning("Hello world")
+
+def _OnChange(changeType, level, resource):
+    # Invoke the actual "OnChange()" function in a separate thread
+    t = threading.Timer(0, function = OnChange, args = (changeType, level, resource))
+    t.start()
+
+orthanc.RegisterOnChangeCallback(_OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/create-dicom.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,24 @@
+import json
+import orthanc
+
+def OnChange(changeType, level, resource):
+    if changeType == orthanc.ChangeType.ORTHANC_STARTED:
+        tags = {
+            'SOPClassUID' : '1.2.840.10008.5.1.4.1.1.1',
+            'PatientID' : 'HELLO',
+            'PatientName' : 'WORLD',
+        }
+
+        with open('Lena.png', 'rb') as f:
+            img = orthanc.UncompressImage(f.read(), orthanc.ImageFormat.PNG)
+
+        s = orthanc.CreateDicom(json.dumps(tags), img, orthanc.CreateDicomFlags.GENERATE_IDENTIFIERS)
+
+        with open('/tmp/sample.dcm', 'wb') as f:
+            f.write(s)
+
+        dicom = orthanc.CreateDicomInstance(s)
+        frame = dicom.GetInstanceDecodedFrame(0)
+        print('Size of the frame: %dx%d' % (frame.GetImageWidth(), frame.GetImageHeight()))
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/dicom-find-move-scp.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,27 @@
+import json
+import orthanc
+import pprint
+
+def OnFind(answers, query, issuerAet, calledAet):
+    print('Received incoming C-FIND request from %s:' % issuerAet)
+
+    answer = {}
+    for i in range(query.GetFindQuerySize()):
+        print('  %s (%04x,%04x) = [%s]' % (query.GetFindQueryTagName(i),
+                                           query.GetFindQueryTagGroup(i),
+                                           query.GetFindQueryTagElement(i),
+                                           query.GetFindQueryValue(i)))
+        answer[query.GetFindQueryTagName(i)] = ('HELLO%d-%s' % (i, query.GetFindQueryValue(i)))
+
+    answers.FindAddAnswer(orthanc.CreateDicom(
+        json.dumps(answer), None, orthanc.CreateDicomFlags.NONE))
+
+def OnMove(**request):
+    orthanc.LogWarning('C-MOVE request to be handled in Python: %s' %
+                       json.dumps(request, indent = 4, sort_keys = True))
+
+    # To indicate a failure in the processing, one can raise an exception:
+    #   raise Exception('Cannot handle C-MOVE')
+
+orthanc.RegisterFindCallback(OnFind)
+orthanc.RegisterMoveCallback(OnMove)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/excel.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,29 @@
+import StringIO
+import json
+import orthanc
+import xlwt
+
+def CreateExcelReport(output, uri, **request):
+    if request['method'] != 'GET' :
+        output.SendMethodNotAllowed('GET')
+    else:
+        # Create an Excel writer
+        excel = xlwt.Workbook()
+        sheet = excel.add_sheet('Studies')
+
+        # Loop over the studies stored in Orthanc
+        row = 0
+        studies = orthanc.RestApiGet('/studies?expand')
+        for study in json.loads(studies):
+            sheet.write(row, 0, study['PatientMainDicomTags'].get('PatientID'))
+            sheet.write(row, 1, study['PatientMainDicomTags'].get('PatientName'))
+            sheet.write(row, 2, study['MainDicomTags'].get('StudyDescription'))
+            row += 1
+
+        # Serialize the Excel workbook to a string, and return it to the caller
+        # https://stackoverflow.com/a/15649139/881731
+        b = StringIO.StringIO()
+        excel.save(b)
+        output.AnswerBuffer(b.getvalue(), 'application/vnd.ms-excel')
+
+orthanc.RegisterRestCallback('/report.xls', CreateExcelReport)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/exception.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,17 @@
+import orthanc
+
+def OnChange(changeType, level, resource):
+    if changeType == orthanc.ChangeType.ORTHANC_STARTED:
+        try:
+            print(orthanc.RestApiGet('/nope'))
+        except ValueError as e:
+            # Raised in releases <= 3.2 of the plugin (doesn't occur in releases >= 3.3)
+            print(e)
+        except orthanc.OrthancException as e:
+            # Raised in releases >= 3.3 of the plugin (fails with releases <= 3.2)
+            print(e)
+            print(e.args[0])  # Error code of Orthanc (cf. "orthanc.ErrorCode" enumeration)
+            print(e.args[1])  # Description of the error
+            print(e.args[0] == orthanc.ErrorCode.UNKNOWN_RESOURCE)  # Returns "True"
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/extending-rest-api.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,10 @@
+import orthanc
+import pprint
+
+def OnRest(output, uri, **request):
+    pprint.pprint(request)
+    print('Accessing uri: %s' % uri)
+    output.AnswerBuffer('ok\n', 'text/plain')
+
+orthanc.RegisterRestCallback('/(to)(t)o', OnRest)
+orthanc.RegisterRestCallback('/tata', OnRest)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/filtering-metadata.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,72 @@
+import json
+import orthanc
+import re
+
+# Get the path in the REST API to the given resource that was returned
+# by a call to "/tools/find"
+def GetPath(resource):
+    if resource['Type'] == 'Patient':
+        return '/patients/%s' % resource['ID']
+    elif resource['Type'] == 'Study':
+        return '/studies/%s' % resource['ID']
+    elif resource['Type'] == 'Series':
+        return '/series/%s' % resource['ID']
+    elif resource['Type'] == 'Instance':
+        return '/instances/%s' % resource['ID']
+    else:
+        raise Exception('Unknown resource level')
+
+def FindWithMetadata(output, uri, **request):
+    # The "/tools/find" route expects a POST method
+    if request['method'] != 'POST':
+        output.SendMethodNotAllowed('POST')
+    else:
+        # Parse the query provided by the user, and backup the "Expand" field
+        query = json.loads(request['body'])
+
+        if 'Expand' in query:
+            originalExpand = query['Expand']
+        else:
+            originalExpand = False
+
+        # Call the core "/tools/find" route
+        query['Expand'] = True
+        answers = orthanc.RestApiPost('/tools/find', json.dumps(query))
+
+        # Loop over the matching resources
+        filteredAnswers = []
+        for answer in json.loads(answers):
+            try:
+                # Read the metadata that is associated with the resource
+                metadata = json.loads(orthanc.RestApiGet('%s/metadata?expand' % GetPath(answer)))
+
+                # Check whether the metadata matches the regular expressions
+                # that were provided in the "Metadata" field of the user request
+                isMetadataMatch = True
+                if 'Metadata' in query:
+                    for (name, pattern) in query['Metadata'].items():
+                        if name in metadata:
+                            value = metadata[name]
+                        else:
+                            value = ''
+
+                        if re.match(pattern, value) == None:
+                            isMetadataMatch = False
+                            break
+
+                # If all the metadata matches the provided regular
+                # expressions, add the resource to the filtered answers
+                if isMetadataMatch:
+                    if originalExpand:
+                        answer['Metadata'] = metadata
+                        filteredAnswers.append(answer)
+                    else:
+                        filteredAnswers.append(answer['ID'])
+            except:
+                # The resource was deleted since the call to "/tools/find"
+                pass
+
+        # Return the filtered answers in the JSON format
+        output.AnswerBuffer(json.dumps(filteredAnswers, indent = 3), 'application/json')
+
+orthanc.RegisterRestCallback('/tools/find', FindWithMetadata)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/inspect-api.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,20 @@
+import inspect
+import numbers
+import orthanc
+
+# Loop over the members of the "orthanc" module
+for (name, obj) in inspect.getmembers(orthanc):
+    if inspect.isroutine(obj):
+        print('Function %s():\n  Documentation: %s\n' % (name, inspect.getdoc(obj)))
+
+    elif inspect.isclass(obj):
+        print('Class %s:\n  Documentation: %s' % (name, inspect.getdoc(obj)))
+
+        # Loop over the members of the class
+        for (subname, subobj) in inspect.getmembers(obj):
+            if isinstance(subobj, numbers.Number):
+                print('  - Enumeration value %s: %s' % (subname, subobj))
+            elif (not subname.startswith('_') and
+                  inspect.ismethoddescriptor(subobj)):
+                print('  - Method %s(): %s' % (subname, inspect.getdoc(subobj)))
+        print('')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/listening-changes.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,16 @@
+import orthanc
+
+def OnChange(changeType, level, resource):
+    if changeType == orthanc.ChangeType.ORTHANC_STARTED:
+        print('Started')
+
+        with open('/tmp/sample.dcm', 'rb') as f:
+            orthanc.RestApiPost('/instances', f.read())
+
+    elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
+        print('Stopped')
+
+    elif changeType == orthanc.ChangeType.NEW_INSTANCE:
+        print('A new instance was uploaded: %s' % resource)
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/lookup-dictionary.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,18 @@
+import json
+import orthanc
+
+# Create a dictionary mapping the numeric values in enumeration
+# "orthanc.ValueRepresentation" to the name of the corresponding VR
+VR_NAMES = {}
+for name in dir(orthanc.ValueRepresentation):
+    if not name.startswith('_'):
+        value = getattr(orthanc.ValueRepresentation, name)
+        VR_NAMES[value] = name
+
+entry = orthanc.LookupDictionary('PatientID')
+
+orthanc.LogWarning('Entry in the dictionary: %s' %
+                   json.dumps(entry, indent = 4, sort_keys = True))
+
+orthanc.LogWarning('Name of the value representation: %s' %
+                   VR_NAMES[entry['ValueRepresentation']])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/multiprocessing-1.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,19 @@
+import math
+import orthanc
+import time
+
+# CPU-intensive computation taking about 4 seconds
+def SlowComputation():
+    start = time.time()
+    for i in range(1000):
+        for j in range(30000):
+            math.sqrt(float(j))
+    end = time.time()
+    duration = (end - start)
+    return 'computation done in %.03f seconds\n' % duration
+
+def OnRest(output, uri, **request):
+    answer = SlowComputation()
+    output.AnswerBuffer(answer, 'text/plain')
+
+orthanc.RegisterRestCallback('/computation', OnRest)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/multiprocessing-2.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,31 @@
+import math
+import multiprocessing
+import orthanc
+import signal
+import time
+
+# CPU-intensive computation taking about 4 seconds
+# (same code as above)
+def SlowComputation():
+    start = time.time()
+    for i in range(1000):
+        for j in range(30000):
+            math.sqrt(float(j))
+    end = time.time()
+    duration = (end - start)
+    return 'computation done in %.03f seconds\n' % duration
+
+# Ignore CTRL+C in the slave processes
+def Initializer():
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+# Create a pool of 4 slave Python interpreters
+POOL = multiprocessing.Pool(4, initializer = Initializer)
+
+def OnRest(output, uri, **request):
+    # Offload the call to "SlowComputation" onto one slave process.
+    # The GIL is unlocked until the slave sends its answer back.
+    answer = POOL.apply(SlowComputation)
+    output.AnswerBuffer(answer, 'text/plain')
+
+orthanc.RegisterRestCallback('/computation', OnRest)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/multiprocessing-3.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,13 @@
+import pydicom
+import io
+
+def OffloadedDicomParsing(dicom):
+    # No access to the "orthanc" library here, as we are in the slave process
+    dataset = pydicom.dcmread(io.BytesIO(dicom))
+    return str(dataset)
+
+def OnRest(output, uri, **request):
+    # The call to "orthanc.RestApiGet()" is only possible in the master process
+    dicom = orthanc.RestApiGet('/instances/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5/file')
+    answer = POOL.apply(OffloadedDicomParsing, args = (dicom, ))
+    output.AnswerBuffer(answer, 'text/plain')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/multiprocessing-4.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,23 @@
+import json
+import multiprocessing
+import orthanc
+import requests
+import signal
+
+TOKEN = orthanc.GenerateRestApiAuthorizationToken()
+
+def SlaveProcess():
+    r = requests.get('http://localhost:8042/instances',
+                     headers = { 'Authorization' : TOKEN })
+    return json.dumps(r.json())
+
+def Initializer():
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+POOL = multiprocessing.Pool(4, initializer = Initializer)
+
+def OnRest(output, uri, **request):
+    answer = POOL.apply(SlaveProcess)
+    output.AnswerBuffer(answer, 'text/plain')
+
+orthanc.RegisterRestCallback('/computation', OnRest)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/paging.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,38 @@
+import json
+import orthanc
+
+def GetStudyDate(study):
+    if 'StudyDate' in study['MainDicomTags']:
+        return study['MainDicomTags']['StudyDate']
+    else:
+        return ''
+
+def SortStudiesByDate(output, uri, **request):
+    if request['method'] == 'GET':
+        # Retrieve all the studies
+        studies = json.loads(orthanc.RestApiGet('/studies?expand'))
+
+        # Sort the studies according to the "StudyDate" DICOM tag
+        studies = sorted(studies, key = GetStudyDate)
+
+        # Read the limit/offset arguments provided by the user
+        offset = 0
+        if 'offset' in request['get']:
+            offset = int(request['get']['offset'])
+
+        limit = 0
+        if 'limit' in request['get']:
+            limit = int(request['get']['limit'])
+
+        # Truncate the list of studies
+        if limit == 0:
+            studies = studies[offset : ]
+        else:
+            studies = studies[offset : offset + limit]
+
+        # Return the truncated list of studies
+        output.AnswerBuffer(json.dumps(studies), 'application/json')
+    else:
+        output.SendMethodNotAllowed('GET')
+
+orthanc.RegisterRestCallback('/sort-studies', SortStudiesByDate)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/periodic-execution.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,24 @@
+import orthanc
+import threading
+
+TIMER = None
+
+def Hello():
+    global TIMER
+    TIMER = None
+    orthanc.LogWarning("In Hello()")
+    # Do stuff...
+    TIMER = threading.Timer(1, Hello)  # Re-schedule after 1 second
+    TIMER.start()
+
+def OnChange(changeType, level, resource):
+    if changeType == orthanc.ChangeType.ORTHANC_STARTED:
+        orthanc.LogWarning("Starting the scheduler")
+        Hello()
+
+    elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
+        if TIMER != None:
+            orthanc.LogWarning("Stopping the scheduler")
+            TIMER.cancel()
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/pil-conversions.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,52 @@
+import json
+import PIL.Image
+import PIL.ImageDraw
+import orthanc
+
+URL = 'http://hg.orthanc-server.com/orthanc-tests/raw-file/Orthanc-1.9.7/Database/LenaTwiceWithFragments.dcm'
+USERNAME = ''
+PASSWORD = ''
+
+def OnChange(changeType, level, resource):
+    if changeType == orthanc.ChangeType.ORTHANC_STARTED:
+
+        # (1) Download a sample DICOM instance and decode it
+        orthanc.LogWarning('Downloading: %s' % URL)
+        lena = orthanc.HttpGet(URL, USERNAME, PASSWORD)
+
+        dicom = orthanc.CreateDicomInstance(lena)
+        orthanc.LogWarning('Number of frames: %d' % dicom.GetInstanceFramesCount())
+
+        # (2) Access the first frame of the instance as a PIL image
+        frame = dicom.GetInstanceDecodedFrame(0)
+        size = (frame.GetImageWidth(), frame.GetImageHeight())
+
+        if frame.GetImagePixelFormat() == orthanc.PixelFormat.RGB24:
+            mode = 'RGB'
+        else:
+            raise Exception('Unsupported pixel format')
+
+        image = PIL.Image.frombuffer(mode, size, frame.GetImageBuffer(), 'raw', mode, 0, 1)
+
+        # (3) Draw a red cross over the PIL image
+        draw = PIL.ImageDraw.Draw(image)
+        draw.line((0, 0) + image.size, fill=(255,0,0), width=10)
+        draw.line((0, image.size[1], image.size[0], 0), fill=(255,0,0), width=10)
+
+        # (4) Convert back the modified PIL image to an Orthanc image
+        buf = image.tobytes()
+        a = orthanc.CreateImageFromBuffer(frame.GetImagePixelFormat(), image.size[0], image.size[1],
+                                          len(buf) / image.size[1], buf)
+
+        # (5) Create and upload a new DICOM instance with the modified frame
+        tags = {
+            'SOPClassUID' : '1.2.840.10008.5.1.4.1.1.1',
+            'PatientID' : 'HELLO',
+            'PatientName' : 'WORLD',
+        }
+
+        s = orthanc.CreateDicom(json.dumps(tags), a, orthanc.CreateDicomFlags.GENERATE_IDENTIFIERS)
+        orthanc.RestApiPost('/instances', s)
+        orthanc.LogWarning('Image successfully modified and uploaded!')
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/pil.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,26 @@
+from PIL import Image
+import io
+import orthanc
+
+def DecodeInstance(output, uri, **request):
+    if request['method'] == 'GET':
+        # Retrieve the instance ID from the regular expression (*)
+        instanceId = request['groups'][0]
+
+        # Render the instance, then open it in Python using PIL/Pillow
+        png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId)
+        image = Image.open(io.BytesIO(png))
+
+        # Downsize the image as a 64x64 thumbnail
+        image.thumbnail((64, 64), Image.ANTIALIAS)
+
+        # Save the thumbnail as JPEG, then send the buffer to the caller
+        jpeg = io.BytesIO()
+        image.save(jpeg, format = "JPEG", quality = 80)
+        jpeg.seek(0)
+        output.AnswerBuffer(jpeg.read(), 'text/plain')
+
+    else:
+        output.SendMethodNotAllowed('GET')
+
+orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance)  # (*)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/pydicom.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,18 @@
+import io
+import orthanc
+import pydicom
+
+def DecodeInstance(output, uri, **request):
+    if request['method'] == 'GET':
+        # Retrieve the instance ID from the regular expression (*)
+        instanceId = request['groups'][0]
+        # Get the content of the DICOM file
+        f = orthanc.GetDicomForInstance(instanceId)
+        # Parse it using pydicom
+        dicom = pydicom.dcmread(io.BytesIO(f))
+        # Return a string representation the dataset to the caller
+        output.AnswerBuffer(str(dicom), 'text/plain')
+    else:
+        output.SendMethodNotAllowed('GET')
+
+orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance)  # (*)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/pynetdicom.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,31 @@
+import json
+import orthanc
+import pynetdicom
+
+def HandleStore(event):
+    orthanc.LogWarning('Handling C-STORE using pynetdicom')
+    orthanc.RestApiPost('/instances', event.request.DataSet.getvalue())
+    return 0x0000
+
+ae = pynetdicom.AE()
+ae.supported_contexts = pynetdicom.AllStoragePresentationContexts
+
+SCP = None
+
+def OnChange(changeType, level, resource):
+    global SCP
+    
+    if changeType == orthanc.ChangeType.ORTHANC_STARTED:
+        port = json.loads(orthanc.GetConfiguration()).get('DicomPort', 4242)
+        
+        SCP = ae.start_server(('', port), block = False, evt_handlers = [
+            (pynetdicom.evt.EVT_C_STORE, HandleStore),
+        ])
+        
+        orthanc.LogWarning('DICOM server using pynetdicom has started')
+
+    elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
+        orthanc.LogWarning('Stopping pynetdicom')
+        SCP.shutdown()
+
+orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/received-instance-callback.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,33 @@
+from io import BytesIO
+
+from pydicom import dcmread, dcmwrite
+from pydicom.filebase import DicomFileLike
+
+
+# from https://pydicom.github.io/pydicom/stable/auto_examples/memory_dataset.html
+def write_dataset_to_bytes(dataset):
+    with BytesIO() as buffer:
+        memory_dataset = DicomFileLike(buffer)
+        dcmwrite(memory_dataset, dataset)
+        memory_dataset.seek(0)
+
+        return memory_dataset.read()
+
+
+def ReceivedInstanceCallback(receivedDicom):
+
+    dataset = dcmread(BytesIO(receivedDicom))
+
+    if dataset.PatientID.startswith('001-'):
+        return orthanc.ReceivedInstanceCallbackResult.DISCARD, None
+
+    if dataset.PatientID.startswith('002-'):
+        return orthanc.ReceivedInstanceCallbackResult.KEEP_AS_IS, None
+
+    dataset.PatientName = str(dataset.PatientName).upper()
+    dataset.PatientID = '002-' + dataset.PatientID
+    dataset.InstitutionName = "MY INSTITUTION"
+
+    return orthanc.ReceivedInstanceCallbackResult.MODIFIED, write_dataset_to_bytes(dataset)
+
+orthanc.RegisterReceivedInstanceCallback(ReceivedInstanceCallback)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/storage-area.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,19 @@
+import orthanc
+import os
+
+def GetPath(uuid, contentType):
+    # Returns the path where to store the given attachment
+    return 'attachment-%d-%s' % (contentType, uuid)
+
+def OnCreate(uuid, contentType, data):
+    with open(GetPath(uuid, contentType), 'wb') as f:
+        f.write(data)
+
+def OnRead(uuid, contentType):
+    with open(GetPath(uuid, contentType), 'rb') as f:
+        return f.read()
+
+def OnRemove(uuid, contentType):
+    os.remove(GetPath(uuid, contentType))
+
+orthanc.RegisterStorageArea(OnCreate, OnRead, OnRemove)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/python/worklist.py	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,33 @@
+import json
+import orthanc
+import os
+
+# Path to the directory containing the DICOM worklists
+# https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Plugins/Samples/ModalityWorklists/WorklistsDatabase
+WORKLIST_DIR = '/tmp/WorklistsDatabase'
+
+def OnWorklist(answers, query, issuerAet, calledAet):
+    print('Received incoming C-FIND worklist request from %s:' % issuerAet)
+
+    # Get a memory buffer containing the DICOM instance
+    dicom = query.WorklistGetDicomQuery()
+
+    # Get the DICOM tags in the JSON format from the binary buffer
+    jsonTags = json.loads(orthanc.DicomBufferToJson(
+        dicom, orthanc.DicomToJsonFormat.SHORT, orthanc.DicomToJsonFlags.NONE, 0))
+
+    orthanc.LogWarning('C-FIND worklist request to be handled in Python: %s' %
+                       json.dumps(jsonTags, indent = 4, sort_keys = True))
+
+    # Loop over the available DICOM worklists
+    for path in os.listdir(WORKLIST_DIR):
+        if os.path.splitext(path) [1] == '.wl':
+            with open(os.path.join(WORKLIST_DIR, path), 'rb') as f:
+                content = f.read()
+                
+                # Test whether the query matches the current worklist
+                if query.WorklistIsMatch(content):
+                    orthanc.LogWarning('Matching worklist: %s' % path)
+                    answers.WorklistAddAnswer(query, content)
+
+orthanc.RegisterWorklistCallback(OnWorklist)
--- a/Sphinx/source/plugins/stone-webviewer.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/stone-webviewer.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -32,7 +32,7 @@
 The compilation process is quite complex since it requires using the
 `Emscripten <https://emscripten.org/>`__ compiler toolchain. The full
 build instructions are available in the `source code
-<https://hg.orthanc-server.com/orthanc-stone/file/default/Applications/StoneWebViewer/WebAssembly/NOTES.txt>`__.
+<https://hg.orthanc-server.com/orthanc-stone/file/StoneWebViewer-2.2/Applications/StoneWebViewer/WebAssembly/NOTES.txt>`__.
 
 
 Usage
@@ -48,7 +48,7 @@
 suited to devops need, as they allow to start a minimal Docker
 environment as follows::
 
-  $ docker run -p 4242:4242 -p 8042:8042 -e STONE_WEB_VIEWER_PLUGIN_ENABLED=true -e DICOM_WEB_PLUGIN_ENABLED=true --rm osimis/orthanc:20.12.0
+  $ docker run -p 4242:4242 -p 8042:8042 -e STONE_WEB_VIEWER_PLUGIN_ENABLED=true -e DICOM_WEB_PLUGIN_ENABLED=true --rm osimis/orthanc:21.6.2
 
 
 .. highlight:: json
@@ -93,7 +93,7 @@
 
 The configuration of the Web viewer can be fine-tuned by adapting some
 options in the `configuration file
-<https://hg.orthanc-server.com/orthanc-stone/file/default/Applications/StoneWebViewer/WebApplication/configuration.json>`__.
+<https://hg.orthanc-server.com/orthanc-stone/file/StoneWebViewer-2.2/Applications/StoneWebViewer/WebApplication/configuration.json>`__.
 
 FAQ
 ---
@@ -148,14 +148,14 @@
 - **How can I migrate from Osimis Web viewer to Stone Web viewer?**
 
   Full instructions are provided in the `source distribution
-  <https://hg.orthanc-server.com/orthanc-stone/file/default/Applications/StoneWebViewer/NOTES.txt>`__.
+  <https://hg.orthanc-server.com/orthanc-stone/file/StoneWebViewer-2.2/Applications/StoneWebViewer/NOTES.txt>`__.
 
 - **What are the future plans?**
 
   The internal use of :ref:`Stone of Orthanc library <stone>` gives us
   a lot of flexibility to implement new advanced features, such as 3D
   rendering (MPR, reslicing, image fusion...), DICOM-RT support,
-  creation of annotations/measurements, viewer for mobile devices,
+  storage of annotations/measurements, viewer for mobile devices,
   internationalization (translation)...
 
   We are looking for :ref:`industrial sponsors <contributing>` to
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/plugins/tcia.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -0,0 +1,129 @@
+.. _tcia:
+
+
+TCIA plugin
+===========
+
+.. contents::
+
+This **official** plugin by the `ICTEAM institute of UCLouvain
+<https://uclouvain.be/en/research-institutes/icteam>`__ extends
+Orthanc with a Web interface that can be used to import open-data
+medical images from `The Cancer Imaging Archive (TCIA)
+<https://www.cancerimagingarchive.net/>`__, and serve them immediately
+using Orthanc.
+
+The plugin can be used to import so-called "cart spreadsheet"
+generated by the `NBIA Search Client
+<https://nbia.cancerimagingarchive.net/nbia-search/>`__, or to browse
+the image collections of TCIA thanks to its `REST API
+<https://wiki.cancerimagingarchive.net/display/Public/TCIA+REST+API+Guide>`__.
+
+
+Compilation
+-----------
+
+.. highlight:: bash
+
+Official releases can be `downloaded from the Orthanc homepage
+<https://www.orthanc-server.com/browse.php?path=/plugin-tcia>`__. As
+an alternative, the `repository containing the source code
+<https://hg.orthanc-server.com/orthanc-tcia/>`__ can be accessed using
+Mercurial.
+
+The procedure to compile this plugin is similar of that for the
+:ref:`core of Orthanc <binaries>`. The following commands should work
+for most UNIX-like distribution (including GNU/Linux)::
+
+  $ mkdir Build
+  $ cd Build
+  $ cmake .. -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release
+  $ make
+
+The compilation will produce a shared library ``OrthancTcia``
+that contains the TCIA plugin for Orthanc.
+
+Pre-compiled Linux Standard Base (LSB) binaries `can be downloaded
+<https://lsb.orthanc-server.com/plugin-tcia/>`__.
+
+Pre-compiled binaries for Microsoft Windows and macOS `are also
+available
+<https://www.orthanc-server.com/browse.php?path=/plugin-tcia>`__.
+
+Furthermore, the :ref:`Docker images <docker>`
+``jodogne/orthanc-plugins`` and ``osimis/orthanc`` also contain the
+plugin.
+
+
+Configuration
+-------------
+
+You of course first have to :ref:`install Orthanc <compiling>`. Once
+Orthanc is installed, you must change the :ref:`configuration file
+<configuration>` to tell Orthanc where it can find the plugin: This is
+done by properly modifying the ``Plugins`` option.
+
+You then have to set the two following options:
+
+* The setting ``Enable`` in section ``Tcia`` must be set to ``true``
+  to enable the TCIA plugin.
+
+* The setting ``HttpsCACertificates`` must contain a path to a file
+  containing a list of `trusted Certificate Authorities (CA)
+  <https://curl.haxx.se/docs/sslcerts.html>`__. Depending on your
+  operating system, this file can be found as follows:
+
+  - On Debian-based system, the standard file
+    ``/etc/ssl/certs/ca-certificates.crt`` can be used.
+  - On other systems (including Microsoft Windows), the cURL project
+    provides `CA certificates
+    <https://curl.haxx.se/docs/caextract.html>`__ that are extracted
+    from Mozilla.
+
+.. highlight:: json
+
+On Ubuntu, you could for instance use the following minimalist
+configuration file::
+
+  {
+    "HttpsCACertificates" : "/etc/ssl/certs/ca-certificates.crt",
+    "Plugins" : [
+      "/home/user/OrthancTcia/Build/libOrthancTcia.so"
+    ],
+    "Tcia" : {
+      "Enable" : true
+    }
+  }
+
+.. highlight:: text
+
+Orthanc must of course be restarted after the modification of its
+configuration file.
+
+
+Usage
+-----
+
+Once Orthanc Explorer is opened, a button entitled "The Cancer Imaging
+Archive" will show up at the bottom of the page. Clicking on this
+button will open the TCIA interface for Orthanc:
+
+.. image:: ../images/tcia-interface.png
+           :align: center
+           :width: 640
+
+As can be seen, this Web application allows to both import a cart from
+the NBIA Search Client, and to browse the various collections of
+medical images from TCIA.
+
+The following screenshot illustrates how to export a cart using the
+NBIA Search Client, that can then be imported using this plugin:
+
+.. image:: ../images/tcia-nbia-export.png
+           :align: center
+           :width: 640
+
+The TCIA plugin creates a job to do the import. This implies that an
+import can be monitored, paused or stopped using the :ref:`REST API
+for Orthanc jobs <jobs>`.
+
--- a/Sphinx/source/plugins/webviewer.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/webviewer.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -19,7 +19,7 @@
 
 The procedure to compile this plugin is similar of that for the
 :ref:`core of Orthanc <binaries>`. The following commands should work
-for every UNIX-like distribution (including GNU/Linux)::
+for most UNIX-like distribution (including GNU/Linux)::
 
   $ mkdir Build
   $ cd Build
--- a/Sphinx/source/plugins/worklists-plugin.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/worklists-plugin.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -29,6 +29,10 @@
 <https://hg.orthanc-server.com/orthanc/file/default/OrthancServer/Plugins/Samples/ModalityWorklists/>`__
 (GPLv3+ license).
 
+Note that it is possible to reproduce the features of this sample
+using :ref:`Python plugins <python_worklists>`.
+
+
 
 Basic configuration
 -------------------
--- a/Sphinx/source/plugins/wsi.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/plugins/wsi.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -31,7 +31,7 @@
 
 The procedure to compile the WSI framework is similar of that for the
 :ref:`core of Orthanc <binaries>`. The following commands should work
-for every UNIX-like distribution (including GNU/Linux)::
+for most UNIX-like distribution (including GNU/Linux)::
 
   # Firstly, compile the command-line tools
   $ mkdir Applications/Build
@@ -86,7 +86,7 @@
 Usage of the plugin
 -------------------
 
-.. highlight:: json
+.. highlight:: text
 
 You of course first have to :ref:`install Orthanc <compiling>`. Once
 Orthanc is installed, you must change the :ref:`configuration file
@@ -294,6 +294,57 @@
 parameters of your Orthanc server.
 
 
+Interface with Cytomine
+-----------------------
+
+`Cytomine <https://cytomine.be/>`__ is an "*open-source rich internet
+application for collaborative analysis of multi-gigapixel images.*"
+Starting with release 1.1 of the whole-slide imaging framework for
+Orthanc, it is possible to exchange digital pathology images back and
+forth between Orthanc and Cytomine according to the following
+workflow:
+
+.. image:: ../images/2021-12-12-Cytomine.png
+           :align: center
+           :width: 500px
+
+|
+
+As can be seen, ``OrthancWSIDicomizer`` imports the source image from
+Cytomine using its REST API, then puts the converted DICOM instances
+onto Orthanc using its REST API. Here is a minimalist sample call to
+the ``OrthancWSIDicomizer`` command-line tool to convert an image from
+a Cytomine server onto an Orthanc server listening on
+``localhost:8042`` with default parameters ::
+
+  $ ./OrthancWSIDicomizer --cytomine-url=http://XXX --cytomine-image=325 \
+                          --cytomine-public-key=YYY --cytomine-private-key=ZZZ \
+                          --threads=4 --pyramid=1 --username=orthanc --password=orthanc --verbose 
+
+The ``--cytomine-image`` parameter corresponds to the ID of the `Image
+Instance <https://doc.uliege.cytomine.org/dev-guide/api/reference>`__
+of interest. This ID can easily be retrieved from the Web interface of
+Cytomine:
+
+.. image:: ../images/Cytomine.png
+           :align: center
+           :width: 600px
+
+|
+
+The ``--cytomine-public-key`` and ``--cytomine-private-key``
+parameters grant access to the REST API of Cytomine, and can be found
+in the parameters of your account using the Web interface of Cytomine:
+
+.. image:: ../images/CytomineKeys.png
+           :align: center
+           :width: 600px
+
+|
+
+
+
+
 REST API
 --------
 
--- a/Sphinx/source/unanswered-forum.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/unanswered-forum.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -31,11 +31,8 @@
 -----------------
 
 * **2020-04-24**: `Auto routing with OnStableStudy <https://groups.google.com/g/orthanc-users/c/GuFqiZtkwtg/m/PGu2fM5LCAAJ>`__
-* **2020-04-13**: `Advanced authorization plugin vs. Remote access <https://groups.google.com/g/orthanc-users/c/m2VM3AhhWok/m/EjVy5_ZFCAAJ>`__
 * **2020-03-28**: `REST Query Remote Modality using DICOM Sequence <https://groups.google.com/g/orthanc-users/c/7o0RNFEtVuA/m/KmpalFxTAwAJ>`__
 * **2020-03-04**: `Bulk Store SCU question <https://groups.google.com/g/orthanc-users/c/upftCWzl7qc/m/2FFEmXqkAQAJ>`__
-* **2020-02-10**: `Authorisation web service and the AC_AUTHENTICATION_ENABLED variable <https://groups.google.com/g/orthanc-users/c/liOW6BQMbdQ/m/yqfm2B0vFgAJ>`__
-* **2020-02-05**: `TotalDiskSize Disagrees With filesystem du; any way to stop storing json <https://groups.google.com/g/orthanc-users/c/gA-ixbFCjrI/m/NzH3FDS9AQAJ>`__
 * **2019-11-14**: `instance anonymization with consistent PatientID <https://groups.google.com/g/orthanc-users/c/9rIpNHxA4d8/m/dDABlu4LAgAJ>`__  
 * **2018-05-11**: `Web vs API query/retrieve with Orthanc Docker <https://groups.google.com/d/msg/orthanc-users/3g7V7kqr3g0/3i83GIfxBwAJ>`__
 
@@ -43,9 +40,7 @@
 Questions requiring a development
 ---------------------------------
 
-* **2020-08-06**: `Advanced authorization plugin dicomweb-plugin issue <https://groups.google.com/g/orthanc-users/c/gwgGJsLvQUk/m/CkiaojrCAgAJ>`__
 * **2019-10-25**: `Expediting stability of a DICOM study - new API endpoint? <https://groups.google.com/g/orthanc-users/c/kADj2eoELK4/m/xFrc0wfIBgAJ>`__
-* **2019-10-07**: `Timeout too short when connecting to MySQL server resulting in crash <https://groups.google.com/g/orthanc-users/c/rYA4eJzEp7Q/m/teFOHBI6BwAJ>`__
 * **2019-05-14**: `Jobs Engine: Study Details <https://groups.google.com/g/orthanc-users/c/9GCV88GLEzw/m/A8r4cb_UAgAJ>`__
 
 
--- a/Sphinx/source/users/advanced-rest.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/advanced-rest.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -26,11 +26,13 @@
 ``JobsHistorySize`` option).
 
 By default, Orthanc saves the jobs into its database (check out the
-``SaveJobs`` option). If Orthanc is stopped then relaunched, the jobs
+``SaveJobs`` option).  Jobs are saved every 10 seconds and when
+Orthanc stops. If Orthanc is stopped then relaunched, the jobs
 whose processing was not finished are automatically put into the queue
-of pending tasks. The command-line option ``--no-jobs`` can also be
-used to prevent the loading of jobs from the database upon the launch
-of Orthanc.
+of pending tasks or resumed if they were being processed when Orthanc
+stopped. The command-line option ``--no-jobs`` can also be used to 
+prevent the loading of jobs from the database upon the launch of 
+Orthanc.
 
 Note that the queue of pending jobs has a maximum size (check out the
 ``LimitJobs`` option). When this limit is reached, the addition of new
@@ -38,6 +40,8 @@
 
 
 
+.. _jobs-synchronicity:
+
 Synchronous vs. asynchronous calls
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -75,9 +79,11 @@
 
 Despite being more complex to handle, the asynchronous mode is highly
 recommended for jobs whose execution time can last over a dozen of
-seconds (typically, the creation of an archive or a network transfer).
-Indeed, synchronous calls can be affected by timeouts in the HTTP
-protocol if they last too long.
+seconds (typically, the creation of an archive if
+``SynchronousZipStream`` :ref:`configuration option <configuration>`
+is set to ``false``, or a network transfer).  Indeed, synchronous
+calls can be affected by timeouts in the HTTP protocol if they last
+too long.
 
 
 .. _jobs-monitoring:
@@ -128,7 +134,7 @@
   ``ErrorCode`` and ``ErrorDescription`` fields for more information.
 * ``Paused``: The job has been paused.
 * ``Retry``: The job has failed internally, and has been scheduled for
-  re-submission after a delay. As of Orthanc 1.9.2, this feature is not
+  re-submission after a delay. As of Orthanc 1.9.7, this feature is not
   used by any type of job.
 
 In order to wait for the end of an asynchronous call, the caller will
@@ -136,6 +142,11 @@
 calls), waiting for the ``State`` field to become ``Success`` or
 ``Failure``.
 
+Note that the `integration tests of Orthanc
+<https://hg.orthanc-server.com/orthanc-tests/file/Orthanc-1.9.7/Tests/Toolbox.py>`__
+give an example about how to monitor a job in Python using the REST
+API (cf. function ``MonitorJob()``).
+
 
 .. _jobs-controlling:
 
@@ -193,7 +204,7 @@
 Note how we retrieve the content of the archive by accessing the
 ``archive`` output of the job (check out the virtual method
 ``IJob::GetOutput()`` from the `source code
-<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp>`__
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp>`__
 of Orthanc).
 
 Here is the corresponding sequence of commands to generate a DICOMDIR
@@ -202,7 +213,7 @@
   $ curl http://localhost:8042/studies/27f7126f-4f66fb14-03f4081b-f9341db2-53925988/media -d '{"Asynchronous":true}'
   $ curl http://localhost:8042/jobs/6332be8a-0052-44fb-8cc2-ac959aeccad9/archive > a.zip
 
-As of Orthanc 1.9.2, only the creation of a ZIP or a DICOMDIR archive
+As of Orthanc 1.9.7, only the creation of a ZIP or a DICOMDIR archive
 produces such "outputs".
 
   
@@ -293,7 +304,12 @@
     }
   }
 
+Rob Oakes provides more a `detailed explanation about how to use
+private tags with Orthanc
+<https://oak-tree.tech/blog/soandor-orthanc-private-headers>`__ on
+Oak-Tree's homepage.
 
+  
 .. _prometheus:
 
 Instrumentation with Prometheus
--- a/Sphinx/source/users/anonymization.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/anonymization.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -16,9 +16,11 @@
 ----------------------------------
 
 Orthanc allows to anonymize a single DICOM instance and to download
-the resulting anonymized DICOM file. Anonymization consists in erasing
-all the tags that are specified in Table E.1-1 from PS 3.15 of the
-DICOM standard 2008 or 2017c (default). Example::
+the resulting anonymized DICOM file (without storing the anonymized
+DICOM instance into Orthanc). Anonymization consists in erasing all
+the tags that are specified in `Table E.1-1 from PS 3.15
+<http://dicom.nema.org/medical/dicom/current/output/chtml/part15/chapter_E.html#table_E.1-1>`__
+of the DICOM standard 2008, 2017c or 2021b (default). Example::
 
     $ curl http://localhost:8042/instances/6e67da51-d119d6ae-c5667437-87b9a8a5-0f07c49f/anonymize -X POST -d '{}' > Anonymized.dcm
 
@@ -39,7 +41,7 @@
                 "DicomVersion" : "2017c"
               }' > Anonymized.dcm
 
-Explanations:
+**Explanations:**
 
 * New UUIDs are automatically generated for the study, the series and
   the instance.
@@ -58,18 +60,35 @@
   the anonymization process. The default behavior consists in removing
   the private tags, as such tags can contain patient-specific
   information.
-* ``DicomVersion`` specifies which version of the DICOM standard shall be used
-  for anonymization.  Allowed values are ``2008`` and ``2017c`` (default value 
-  if the parameter is absent).  This parameter has been introduced in Orthanc 
-  1.3.0.  In earlier version, the ``2008`` standard was used.
+* ``DicomVersion`` specifies which version of the DICOM standard shall
+  be used for anonymization. Allowed values are ``2008``, ``2017c``,
+  or ``2021b`` (new in Orthanc 1.9.4). This parameter has been
+  introduced in Orthanc 1.3.0. In earlier version, the ``2008``
+  standard was used. If the parameter is absent, the highest version
+  that is supported by Orthanc is used.
+* ``Remove`` can also be used to provide a list of tags to be manually
+  deleted.
+
+**Important:** Starting with Orthanc 1.9.4, the ``Replace``, ``Keep``
+and ``Remove`` fields can also specify sequences, using the same
+syntax as the ``dcmodify`` command-line tool (wildcards are supported
+as well). Earlier versions were limited to top-level tags in the DICOM
+dataset. Check out the integration test ``test_modify_subsequences``
+for `examples
+<https://hg.orthanc-server.com/orthanc-tests/file/default/Tests/Tests.py>`__.
+
+**Implementation:** Internally, the setup of the anonymization
+profiles can be found in the methods ``SetupAnonymizationXXX()`` of
+the class ``Orthanc::DicomModification`` (cf. `source code
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancFramework/Sources/DicomParsing/DicomModification.cpp>`__).
 
 
 Modification of a Single Instance
 ---------------------------------
 
 Orthanc allows to modify a set of specified tags in a single DICOM
-instance and to download the resulting anonymized DICOM
-file. Example::
+instance and to download the resulting modified DICOM file (without
+storing the modified DICOM instance into Orthanc). Example::
 
     $ curl -X POST http://localhost:8042/instances/6e67da51-d119d6ae-c5667437-87b9a8a5-0f07c49f/modify \
       --data '{
@@ -85,7 +104,7 @@
                 "Transcode": "1.2.840.10008.1.2.4.70"
               }' > Modified.dcm
 
-Remarks:
+**Remarks:**
 
 * The ``Remove`` array specifies the list of the tags to remove.
 * The ``Replace`` associative array specifies the substitions to be applied (cf. anonymization).
@@ -100,7 +119,7 @@
   modification to one of the ``PatientID``, ``StudyInstanceUID``,
   ``SeriesInstanceUID``, and ``SOPInstanceUID`` requires ``Force`` to
   be set to ``true``, in order to prevent any unwanted side effect.     
-
+  
 .. highlight:: json
 
 * To replace a sequence of tags, you may use this syntax:: 
@@ -131,6 +150,16 @@
       }
     }
 
+
+**Important:** Similarly to anonymization, starting with Orthanc
+1.9.4, the ``Replace``, ``Keep`` and ``Remove`` fields can also
+specify sequences, using the same syntax as the ``dcmodify``
+command-line tool (wildcards are supported as well). Earlier versions
+were limited to top-level tags in the DICOM dataset. Check out the
+integration test ``test_modify_subsequences`` for `examples
+<https://hg.orthanc-server.com/orthanc-tests/file/default/Tests/Tests.py>`__.
+
+
 .. _study-modification:
 
 Modification of Studies or Series
@@ -172,6 +201,19 @@
       "Path" : "/studies/1c3f7bf4-85b4aa20-236e6315-5d450dcc-3c1bcf28"
     }
 
+Pay attention to the fact that Orthanc implements safety checks to
+preserve the :ref:`DICOM model of the real world <model-world>`. These
+checks prevent the modification of some tags that are known to belong
+to a level in the patient/study/series/instance hierarchy that is
+higher than the level that corresponds to the REST API call. For
+instance, the tag ``PatientID`` cannot be modified if using the
+``/studies/{id}/modify`` route (in the latter case, the
+``/patients/{id}/modify`` route must be used, cf. next section). You
+also have to set the ``Force`` argument to ``true`` if modifying one
+of the :ref:`DICOM identifiers tags <orthanc-ids>`
+(i.e. ``PatientID``, ``StudyInstanceUID``, ``SeriesInstanceUID`` and
+``SOPInstanceUID``).
+
 
 Modification of Patients
 ------------------------
@@ -216,6 +258,71 @@
 a JSON body.
 
 
+.. _bulk-modification:
+
+Bulk modification or anonymization
+----------------------------------
+
+Starting with Orthanc 1.9.4, it is possible to use the new routes
+``/tools/bulk-modify`` and ``/tools/bulk-anonymize`` to respectively
+modify or anonymize a set of multiple DICOM resources that are not
+related (i.e. that don't share any parent DICOM resource). A typical
+use case is to modify/anonymize a list of DICOM instances that don't
+belong to the same parent patient/study/series.
+
+.. highlight:: bash
+
+These two routes accept the same arguments as described above, but
+must also be provided with an additional argument ``Resources`` that
+lists the :ref:`Orthanc identifiers <orthanc-ids>` of the resources of
+interest (that may indifferently correspond to patients, studies,
+series or instances). Here are two sample calls::
+
+  $ curl http://localhost:8042/tools/bulk-modify -d '{"Replace":{"SeriesDescription":"HELLO"},"Resources":["b6da0b16-a25ae9e7-1a80fc33-20df01a9-a6f7a1b0","d6634d97-24379e4a-1e68d3af-e6d0451f-e7bcd3d1"]}'
+  $ curl http://localhost:8042/tools/bulk-anonymize -d '{"Resources":["b6da0b16-a25ae9e7-1a80fc33-20df01a9-a6f7a1b0","d6634d97-24379e4a-1e68d3af-e6d0451f-e7bcd3d1"]}'
+
+.. highlight:: json
+
+The output of the modification/anonymization lists all the resources
+that have been altered by the call (including their parents). Here is
+the output of the second sample above::
+
+  {
+    "Description" : "REST API",
+    "FailedInstancesCount" : 0,
+    "InstancesCount" : 2,
+    "IsAnonymization" : true,
+    "Resources" : [
+      {
+         "ID" : "04c04806-27b01a5a-08ea66cb-cb36c8b9-ebe62fe3",
+         "Path" : "/instances/04c04806-27b01a5a-08ea66cb-cb36c8b9-ebe62fe3",
+         "Type" : "Instance"
+      },
+      {
+         "ID" : "4e37fce9-6b33b8ba-7bb378e1-abc7e2c4-fca4ade3",
+         "Path" : "/instances/4e37fce9-6b33b8ba-7bb378e1-abc7e2c4-fca4ade3",
+         "Type" : "Instance"
+      },
+      {
+         "ID" : "6438ee62-b58a4788-517931b3-e10321eb-d1ab2613",
+         "Path" : "/series/6438ee62-b58a4788-517931b3-e10321eb-d1ab2613",
+         "Type" : "Series"
+      },
+      {
+         "ID" : "660494fd-1ddd661b-4358d996-ba600e5a-066d94cc",
+         "Path" : "/studies/660494fd-1ddd661b-4358d996-ba600e5a-066d94cc",
+         "Type" : "Study"
+      },
+      {
+         "ID" : "5faa0bf8-8a45520b-3a07e536-fc24f241-f59ae3e1",
+         "Path" : "/patients/5faa0bf8-8a45520b-3a07e536-fc24f241-f59ae3e1",
+         "Type" : "Patient"
+      }
+    ]
+  }
+
+  
+
 .. _split-merge: 
 
 Split/merge of DICOM studies
@@ -259,7 +366,7 @@
 * ``KeepSource`` (Boolean value), if set to ``true``, instructs
   Orthanc to keep a copy of the original series in the source study.
   By default, the original series are deleted from Orthanc.
-
+  
 .. _merge:
 
 Merging
--- a/Sphinx/source/users/configuration.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/configuration.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -1,13 +1,14 @@
 .. _configuration:
-.. highlight:: bash
 
 Configuration of Orthanc
 ========================
 
+.. highlight:: bash
+
 Configuring Orthanc simply consists in providing a configuration file.
 Orthanc has numerous configuration that are documented in the `default
 configuration file
-<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.2/OrthancServer/Resources/Configuration.json>`_. This
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Resources/Configuration.json>`_. This
 file is in the `JSON <https://en.wikipedia.org/wiki/JSON>`_ file
 format. You can generate this file file with the following call::
 
@@ -20,6 +21,8 @@
 * Create a HTTP server for the REST API that listens on the port 8042.
 * Store the Orthanc database in a folder called ``OrthancStorage``.
 
+.. highlight:: json
+
 However, we recommend that you start from an empty configuration file
 and only specify the options for which you don't wan't to use
 the default value.  In example, a simple configuration file would be::
@@ -31,10 +34,13 @@
         "DicomPort": 104
     }
 
-It's also a very good practice to split your configuration files per topic.
-In example, have a ``dicom.json`` for everything that is related to DICOM,
-a ``http.json`` for all HTTP related configurations, one file per plugin...  
-This is how the configuration files are provided with the Windows Installer.
+It's also a very good practice to split your configuration files per
+topic.  In example, have a ``dicom.json`` for everything that is
+related to DICOM, a ``http.json`` for all HTTP related configurations,
+one file per plugin.  This is how the configuration files are provided
+with the Windows Installer.
+
+.. highlight:: bash
 
 Once your configuration file is ready, start Orthanc by giving it the path to the 
 configuration file path as a command-line argument.  If you use multiple configuration
@@ -45,21 +51,84 @@
     $ Orthanc ./config/
 
 
-*Remark:* When specifying paths under Microsoft Windows, backslashes
-(i.e. ``\``) should be either escaped by doubling them (as in ``\\``),
-or replaced by forward slashes (as in ``/``).
-*Remark:* When installing Orthanc with the Windows Installer, you won't be
-able to edit your files unless you start your editor with ``Run as administrator``.
-We recommend to edit your configuration file with an editor such as `Notepad++ <https://notepad-plus-plus.org/>`_.  
-It shall warn you that this file can be edited only by an admin, and will suggest you 
-to restart Notepad++ as an admin such that you'll be able to save it.
+**Remark 1:** When specifying paths under Microsoft Windows,
+backslashes (i.e. ``\``) should be either escaped by doubling them (as
+in ``\\``), or replaced by forward slashes (as in ``/``).
+
+**Remark 2:** When installing Orthanc using the Windows installer by
+Osimis, you won't be able to edit your files unless you start your
+editor with ``Run as administrator``. We recommend to edit your
+configuration file with an editor such as `Notepad++
+<https://notepad-plus-plus.org/>`_.  It shall warn you that this file
+can be edited only by an admin, and will suggest you to restart
+Notepad++ as an admin such that you'll be able to save it.
 
+**Remark 3:** The Windows installers by Osimis provide a `Windows
+service <https://en.wikipedia.org/wiki/Windows_service>`__ that
+automatically starts Orthanc during the startup of Microsoft
+Windows. You can control the parameters of the service by typing
+``services.msc`` at a command-line prompt. The Windows service of
+Orthanc will do its best to cleanly stop Orthanc at the shutdown of
+Windows, but `there are some caveats
+<https://bugs.orthanc-server.com/show_bug.cgi?id=48>`__.
  
-To obtain more diagnostic, you can use the ``--verbose`` or the
-``--trace`` options::
+**Remark 4:** To obtain more diagnostic, you can use the ``--verbose``
+or the ``--trace`` options::
 
     $ Orthanc ./Configuration.json --verbose
     $ Orthanc ./Configuration.json --trace
 
 To learn more about the Orthanc logs, :ref:`check out the dedicated
 page <log>`.
+
+
+.. _orthanc-environment-variables:
+
+Environment variables
+---------------------
+
+.. highlight:: json
+
+Starting with Orthanc 1.5.0, the configuration file can include the
+value of environment variables. Consider the following configuration::
+
+  {
+    "Name" : "${ORTHANC_NAME}"
+  }
+
+
+.. highlight:: bash
+
+In this case, once Orthanc starts, the configuration option ``Name``
+will be read from the value of the environment variable
+``ORTHANC_NAME``. For instance::
+
+  $ ORTHANC_NAME=Hello ./Orthanc Configuration.json
+  $ curl http://localhost:8042/system
+  {
+    "Name" : "Hello",
+    [...]
+  }
+
+
+.. highlight:: json
+
+It is also possible to set a default value if the environment variable
+is not set. Here is the syntax in the configuration file::
+
+  {
+    "Name" : "${ORTHANC_NAME:-DefaultName}"
+  }
+
+
+.. highlight:: bash
+
+If the environment variable ``ORTHANC_NAME`` is not set, here is the
+result::
+
+  $ ./Orthanc Configuration2.json
+  $ curl http://localhost:8042/system
+  {
+    "Name" : "DefaultName",
+    [...]
+  }
--- a/Sphinx/source/users/cookbook.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/cookbook.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -18,18 +18,48 @@
 * `Download pre-compiled packages <https://www.orthanc-server.com/download.php>`__.
 * :ref:`Use Docker <docker>`.
 * On GNU/Linux, use precompiled packages for :ref:`Debian/Ubuntu
-  <debian-packages>`, or for `openSUSE
-  <https://software.opensuse.org/search?q=orthanc>`__ (courtesy of
-  Axel Braun).
+  <debian-packages>` (courtesy of DebianMed and Sébastien Jodogne), or
+  for `openSUSE <https://software.opensuse.org/search?q=orthanc>`__
+  (courtesy of Axel Braun).
 * On GNU/Linux, use our `LSB binaries
   <https://lsb.orthanc-server.com/>`__ (Linux Standard Base), that
   should easily and immediately run on most distributions. Those
   binaries are statically linked together with all their third-party
-  dependencies.
+  dependencies. Don't forget to execute ``chmod +x ./Orthanc`` in
+  order to be able to run the main Orthanc executable.
 * :ref:`Compile Orthanc by yourself <compiling>`.
 * External contributors are also maintaining `Vagrant VM for Orthanc
   <https://github.com/jodogne/OrthancContributed/blob/master/Links.md#user-content-vagrant>`__.
 
+.. highlight:: bash
+
+Furthermore, if you are running Debian 9 (stretch), Debian 10
+(buster), Debian 11 (bullseye), Ubuntu 18.04 LTS (bionic) or Ubuntu
+20.04 LTS (focal) on an **AMD64 architecture**, Sébastien Jodogne
+maintains a **standalone Debian repository** that provides the latest
+versions of the LSB binaries. For instance, here is how to install the
+:ref:`Stone Web viewer <stone_webviewer>` on a barebone Docker setup::
+
+  # docker run --rm -t -i -p 8042:8042 -p 4242:4242 debian:9
+
+  $ apt update
+  $ DEBIAN_FRONTEND=noninteractive apt install -y software-properties-common wget curl nano gnupg apt-transport-https
+
+  $ apt install --upgrade ca-certificates
+  $ wget -qO - https://debian.orthanc-labs.com/archive.key | apt-key add -
+  $ apt-add-repository "deb https://debian.orthanc-labs.com/ `grep VERSION_CODENAME /etc/os-release | cut -d'=' -f 2` main"
+
+  $ apt clean && apt update
+  $ apt install orthanc-stone-webviewer
+  $ /etc/init.d/orthanc start
+
+Note that this standalone Debian repository **does not** contain the
+:ref:`Python plugin <python-plugin>`, as this plugin must be
+dynamically linked against the system-wide version of your Python
+runtime. You should install the ``orthanc-python`` package from your
+native Debian/Ubuntu distribution if available, or compile the plugin
+from sources.
+
 
 .. _orthanc-explorer:
 
--- a/Sphinx/source/users/debian-packages.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/debian-packages.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -12,11 +12,12 @@
 Introduction
 ------------
 
-Orthanc is available as an offical `Debian package
+Orthanc is available as an official `Debian package
 <https://packages.debian.org/search?keywords=orthanc&searchon=names&exact=1&suite=all&section=all>`__
-that is continuously updated by the Orthanc core developers. This
-implies that Orthanc is also available in the `Debian derivative
-distributions
+that is continuously updated by the Orthanc core developers in the
+`Debian Sid/unstable distribution
+<https://wiki.debian.org/DebianUnstable>`__. This implies that Orthanc
+is also available in the `Debian derivative distributions
 <https://en.wikipedia.org/wiki/List_of_Linux_distributions#Debian-based>`__,
 most notably in `Ubuntu
 <https://packages.ubuntu.com/search?keywords=orthanc&searchon=names&suite=all&section=all>`__. Most
@@ -38,6 +39,15 @@
 
 * Advanced users: :ref:`replace the binaries from the package by the
   LSB binaries <lsb-replace-debian-binaries>`.
+
+
+**Note about backporting:** Bringing a new version of Orthanc to an
+older Ubuntu/Debian release (typically, a LTS release) is known as
+"backporting". The process for initiating a backport in `Ubuntu
+<https://wiki.ubuntu.com/UbuntuBackports>`__ or in `Debian
+<https://backports.debian.org/>`__ is publicly available, but the core
+developers of Orthanc will not do this packaging task by themselves
+because of a limited bandwidth: You are kindly invited to contribute!
   
 
 Installation
@@ -96,12 +106,12 @@
 This can be done with this sequence of commands::
 
   $ sudo service orthanc stop
-  $ sudo wget https://lsb.orthanc-server.com/orthanc/1.9.2/Orthanc --output-document /usr/sbin/Orthanc
+  $ sudo wget https://lsb.orthanc-server.com/orthanc/1.9.7/Orthanc --output-document /usr/sbin/Orthanc
   $ sudo rm -f /usr/share/orthanc/plugins/*.so
-  $ sudo wget https://lsb.orthanc-server.com/orthanc/1.9.2/libServeFolders.so --output-document /usr/share/orthanc/plugins/libServeFolders.so
-  $ sudo wget https://lsb.orthanc-server.com/orthanc/1.9.2/libModalityWorklists.so --output-document /usr/share/orthanc/plugins/libModalityWorklists.so
+  $ sudo wget https://lsb.orthanc-server.com/orthanc/1.9.7/libServeFolders.so --output-document /usr/share/orthanc/plugins/libServeFolders.so
+  $ sudo wget https://lsb.orthanc-server.com/orthanc/1.9.7/libModalityWorklists.so --output-document /usr/share/orthanc/plugins/libModalityWorklists.so
   $
-  $ sudo wget https://lsb.orthanc-server.com/plugin-dicom-web/1.3/libOrthancDicomWeb.so --output-document /usr/share/orthanc/plugins/libOrthancDicomWeb.so
+  $ sudo wget https://lsb.orthanc-server.com/plugin-dicom-web/1.6/libOrthancDicomWeb.so --output-document /usr/share/orthanc/plugins/libOrthancDicomWeb.so
   $ ...
   $ sudo service orthanc restart
 
--- a/Sphinx/source/users/docker-osimis.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/docker-osimis.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -94,10 +94,12 @@
 - ``VERBOSE_ENABLED=true`` will start Orthanc with the ``--verbose`` option
 - ``TRACE_ENABLED=true`` will start Orthanc with the ``--trace`` option
 - ``NO_JOBS=true`` will start Orthanc with the ``--no-jobs`` option
-- ``UNLOCK=true`` will start Orthanc with the ``--unlock`` option
+- ``LOGDIR=/logs`` will start Orthanc with the ``--logdir=/logs`` option (introduced in 21.9.1)
+- ``LOGFILE=/logs`` will start Orthanc with the ``--logfile=/logs/orthanc.log`` option (introduced in 21.9.1)
 - ``MALLOC_ARENA_MAX=10`` will :ref:`control memory usage <scalability-memory>`
 - ``ORTHANC_JSON`` can be used to pass a JSON "root" configuration (see below).
-
+- ``BEFORE_ORTHANC_STARTUP_SCRIPT`` can be used to `run a custom script <https://groups.google.com/g/orthanc-users/c/EXjTq2ZU1vw/m/02CwW1jzAQAJ>`__ before starting Orthanc.
+  
 Configuration files
 -------------------
 
@@ -241,6 +243,13 @@
 you POST to ``/tools/reset``.  Note that it declares an ``IncomingHttpRequestFilter`` 
 callback that might conflict with your scripts.
 
+Healthcheck probe
+-----------------
+
+In version 21.10.0, the `/probes/test-aliveness.py <https://bitbucket.org/osimis/orthanc-builder/src/master/docker/orthanc/test-aliveness.py>`__ 
+script has been added in order to perform healthchecks.  Check the doc in the script itself for more details.
+A sample configuration is also available in `this sample <https://bitbucket.org/osimis/orthanc-setup-samples/src/8016d140a237a892db703aac4782307c46732847/docker/tls-mutual-auth/docker-compose.yml#lines-51>`__
+
 
 Plugins
 -------
@@ -287,7 +296,7 @@
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
 | **OrthancWebViewer**                             | ``ORTHANC_WEB_VIEWER_PLUGIN_ENABLED``            |                                                                                                    |
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
-| **StoneWebViewer**                               | ``ORTHANC_STONE_VIEWER_PLUGIN_ENABLED``          |                                                                                                    |
+| **StoneWebViewer**                               | ``STONE_WEB_VIEWER_PLUGIN_ENABLED``              |                                                                                                    |
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
 | **OsimisWebViewerBasic**                         | ``OSIMIS_WEB_VIEWER1_PLUGIN_ENABLED``            |                                                                                                    |
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
@@ -339,9 +348,34 @@
 |                                                  |                                                  |     }                                                                                              |
 |                                                  |                                                  |   }                                                                                                |
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
-| **Whole-slide imaging**                          | ``WSI_PLUGIN_ENABLED``                           |                                                                                                    |
+| **Wsi**                                          | ``WSI_PLUGIN_ENABLED``                           |                                                                                                    |
++--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
+| **Odbc**                                         | ``ODBC_PLUGIN_ENABLED``                          | .. code-block:: json                                                                               |
+|                                                  |                                                  |                                                                                                    |
+|                                                  |                                                  |   {                                                                                                |
+|                                                  |                                                  |     "Odbc": {                                                                                      |
+|                                                  |                                                  |       "EnableIndex": true,                                                                         |
+|                                                  |                                                  |       "EnableStorage": false,                                                                      |
+|                                                  |                                                  |       "IndexConnectionString": "MUST BE DEFINED",                                                  |
+|                                                  |                                                  |       "StorageConnectionString": "MUST BE DEFINED"                                                 |
+|                                                  |                                                  |     }                                                                                              |
+|                                                  |                                                  |   }                                                                                                |
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
-| **Osimis cloud** (synchronization plugin)        | ``OSIMIS_CLOUD_PLUGIN_ENABLED``                  |                                                                                                    |
+| **Tcia**                                         | ``TCIA_PLUGIN_ENABLED``                          | .. code-block:: json                                                                               |
+|                                                  |                                                  |                                                                                                    |
+|                                                  |                                                  |   {                                                                                                |
+|                                                  |                                                  |     "Tcia": {                                                                                      |
+|                                                  |                                                  |       "Enable": true                                                                               |
+|                                                  |                                                  |     }                                                                                              |
+|                                                  |                                                  |   }                                                                                                |
++--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
+| **Indexer**                                      | ``INDEXER_PLUGIN_ENABLED``                       | .. code-block:: json                                                                               |
+|                                                  |                                                  |                                                                                                    |
+|                                                  |                                                  |   {                                                                                                |
+|                                                  |                                                  |     "Indexer": {                                                                                   |
+|                                                  |                                                  |       "Enable": true                                                                               |
+|                                                  |                                                  |     }                                                                                              |
+|                                                  |                                                  |   }                                                                                                |
 +--------------------------------------------------+--------------------------------------------------+----------------------------------------------------------------------------------------------------+
 
 Under the hood
--- a/Sphinx/source/users/docker.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/docker.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -30,21 +30,23 @@
   images that are described on this page are always kept in sync with
   the latest releases of the Orthanc project, with a basic
   configuration system that is inherited from the Debian packages
-  (i.e. manual edition of the configuration files).
+  (i.e. manual edition of the configuration files). These images are
+  most useful to **software developers and researchers**.
 
 * Our commercial partner `Osimis <https://www.osimis.io>`__ also
   `publishes separated Docker images
-  <https://hub.docker.com/r/osimis/orthanc>`__.
-  These ``osimis/orthanc`` images are used by the technical team of
-  Osimis in order to provide professional support to their customers,
-  with a configuration system that can be tuned through environment
-  variables (which is very handy if using ``docker-compose``). These
-  images are not necessarily always in sync with the Orthanc project,
-  but they also include the :ref:`plugins edited by Osimis
-  <plugins-osimis>`, notably the Osimis Web viewer (that is much more
-  advanced than the Orthanc Web viewer) and the advanced authorization
-  plugin. A :ref:`specific page <docker-osimis>` is available to describe 
-  how these images should be used.
+  <https://hub.docker.com/r/osimis/orthanc>`__.  These
+  ``osimis/orthanc`` images are used by the technical team of Osimis
+  in order to provide professional support to their customers, with a
+  configuration system that can be tuned through **environment
+  variables** (which is very handy if using ``docker-compose`` or
+  Kubernetes). These images are not necessarily always in sync with
+  the Orthanc project, but they also include the :ref:`plugins edited
+  by Osimis <plugins-osimis>`, notably the Osimis Web viewer (that is
+  much more advanced than the Orthanc Web viewer) and the advanced
+  authorization plugin. A :ref:`specific page <docker-osimis>` is
+  available to describe how these images should be used. These images
+  are targeted at **ops teams**.
 
 **Note for CentOS users:** The Docker environment might be difficult to
 configure on your platform. Hints are available on the `Orthanc Users
@@ -73,7 +75,7 @@
 If more stability is required, you can select the official release of
 Orthanc to be run::
 
-  $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc:1.9.2
+  $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc:1.9.7
 
 Passing additional command-line options (e.g. to make Orthanc verbose)
 can be done as follows (note the ``/etc/orthanc`` option that is
@@ -95,7 +97,7 @@
 
 Or you can also start a specific version of Orthanc for more stability::
 
-  $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc-plugins:1.9.2
+  $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc-plugins:1.9.7
 
 If you have an interest in the :ref:`Python plugin <python-plugin>`,
 you can use the ``orthanc-python`` image. The latter image is a
@@ -103,7 +105,7 @@
 Python 3.7 interpreter. Here is how to start this image::
 
   $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc-python
-  $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc-python:1.9.2
+  $ docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc-python:1.9.7
   
 
 Fine-tuning the configuration
@@ -147,7 +149,7 @@
 unfortunately it is only available to `Docker Swarm
 <https://github.com/docker/compose/issues/5110>`__).
 
-.. highlight:: yml
+.. highlight:: yaml
 
 First create the ``docker-compose.yml`` file as follows (this one uses
 the `YAML file format <https://en.wikipedia.org/wiki/YAML>`__)::
@@ -155,13 +157,15 @@
   version: '3.1'  # Secrets are only available since this version of Docker Compose
   services:
     orthanc:
-      image: jodogne/orthanc-plugins:1.9.2
+      image: jodogne/orthanc-plugins:1.9.7
       command: /run/secrets/  # Path to the configuration files (stored as secrets)
       ports:
         - 4242:4242
         - 8042:8042
       secrets:
-        - orthanc.json      
+        - orthanc.json
+      environment:
+        - ORTHANC_NAME=HelloWorld
   secrets:
     orthanc.json:
       file: orthanc.json
@@ -172,7 +176,7 @@
 ``docker-compose.yml`` file. Here is a minimalist ``orthanc.json``::
 
   {
-    "Name" : "Orthanc in Docker",
+    "Name" : "${ORTHANC_NAME} in Docker Compose",
     "RemoteAccessAllowed" : true
   }
 
@@ -184,7 +188,15 @@
 
   $ docker-compose up
                
+Note how the `environment variable
+<https://docs.docker.com/compose/environment-variables/>`__
+``ORTHANC_NAME`` has been used in order to easily adapt the
+configuration of Orthanc. This results from the fact that Orthanc
+injects :ref:`environment variables <orthanc-environment-variables>`
+once reading the content of its configuration files (since Orthanc
+1.5.0).
 
+  
 Making the Orthanc database persistent
 --------------------------------------
 
@@ -194,7 +206,7 @@
 container to some path in the filesystem of your Linux host, e.g.::
 
   $ mkdir /tmp/orthanc-db
-  $ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/orthanc-db/:/var/lib/orthanc/db/ jodogne/orthanc:1.9.2 
+  $ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/orthanc-db/:/var/lib/orthanc/db/ jodogne/orthanc:1.9.7 
 
 
 Whole-slide imaging support
@@ -261,7 +273,7 @@
   $ docker inspect --format '{{ .NetworkSettings.Ports }}' some-postgres
   $ docker run --rm --entrypoint=cat jodogne/orthanc-plugins /etc/orthanc/orthanc.json > /tmp/orthanc.json
 
-.. highlight:: json
+.. highlight:: text
 
 Add the following section to ``/tmp/orthanc.json`` (adapting the
 values Host and Port to what docker inspect said above)::
@@ -315,4 +327,4 @@
 * The build artifacts can be found in folder ``/root/orthanc/Build``.
 
 * This command launches the mainline version. To start a released version,
-  use e.g. ``jodogne/orthanc-debug:1.9.2``.
+  use e.g. ``jodogne/orthanc-debug:1.9.7``.
--- a/Sphinx/source/users/lua.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/lua.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -196,6 +196,28 @@
   RestApiPost('/instances/5af318ac-78fb-47ff-b0b0-0df18b0588e0/anonymize', '{}')
 
 
+Instance modification/routing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The Lua engine offers the following special functions to modify and
+route DICOM instances:
+
+* ``ModifyInstance(instanceId, replacements, removals, removePrivateTags)``
+  modifies an instance.
+* ``SendToModality(instanceId, modality)`` performs a C-Store to the 
+  target modality.
+* ``SendToPeer(instanceId, peer)`` sends the instance to a remote Orthanc peer.
+* ``Delete(instanceId)`` deletes the instance.
+
+:ref:`See this section <lua-auto-routing>` for examples. As can be
+seen in those examples, these special functions can be chained
+together, although they return no explicit value.
+
+Note that these special functions should only be used for basic use
+cases: Calls to the REST API :ref:`should always be favored for
+auto-routing <lua-auto-routing-better>`.
+
+
 General-purpose functions
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -439,6 +461,8 @@
  end
 
 
+.. _lua-auto-routing-better:
+
 Important remarks about auto-routing
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -566,3 +590,24 @@
 
     return query
   end
+
+
+.. _lua-external-modules:
+
+Using external modules
+----------------------
+
+Starting with Orthanc 1.3.2, it is possible to use external Lua
+modules if Orthanc was compiled with the ``-DENABLE_LUA_MODULES=ON``
+while invoking CMake.
+
+Importantly, the modules and the Orthanc server must use the same
+version of Lua for external modules to be properly loaded.
+
+Check out the Orthanc Users forum for old discussions about this
+topic: `reference 1
+<https://groups.google.com/g/orthanc-users/c/BXfmwU786B0/m/M47slt5GFwAJ>`__,
+`reference 2
+<https://groups.google.com/g/orthanc-users/c/BXfmwU786B0/m/qpVe8UvGAwAJ>`__,
+`reference 3
+<https://groups.google.com/g/orthanc-users/c/LDAN5jA0X8M/m/4zrk0_AaBAAJ>`__.
--- a/Sphinx/source/users/man-orthanc.txt	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/man-orthanc.txt	Mon Feb 14 08:52:12 2022 +0100
@@ -128,4 +128,4 @@
        This  is  free  software:  you  are free to change and redistribute it.
        There is NO WARRANTY, to the extent permitted by law.
 
-Orthanc 1.9.2			  April 2021			    ORTHANC(1)
+Orthanc 1.9.7			  August 2021			    ORTHANC(1)
--- a/Sphinx/source/users/rest-cheatsheet.csv	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/rest-cheatsheet.csv	Mon Feb 14 08:52:12 2022 +0100
@@ -187,6 +187,10 @@
 ``/system``,`GET <https://api.orthanc-server.com/index.html#tag/System/paths/~1system/get>`__,,,,Get system information
 ``/tools``,`GET <https://api.orthanc-server.com/index.html#tag/Other/paths/~1tools/get>`__,,,,List operations
 ``/tools/accepted-transfer-syntaxes``,`GET <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1accepted-transfer-syntaxes/get>`__,,,`PUT <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1accepted-transfer-syntaxes/put>`__,Get accepted transfer syntaxes
+``/tools/bulk-anonymize``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1bulk-anonymize/post>`__,,,Anonymize a set of resources
+``/tools/bulk-content``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1bulk-content/post>`__,,,Describe a set of instances
+``/tools/bulk-delete``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1bulk-delete/post>`__,,,Delete a set of instances
+``/tools/bulk-modify``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1bulk-modify/post>`__,,,Modify a set of resources
 ``/tools/create-archive``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1create-archive/post>`__,,,Create ZIP archive
 ``/tools/create-dicom``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1create-dicom/post>`__,,,Create one DICOM instance
 ``/tools/create-media``,,`POST <https://api.orthanc-server.com/index.html#tag/System/paths/~1tools~1create-media/post>`__,,,Create DICOMDIR media
--- a/Sphinx/source/users/rest.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/rest.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -25,6 +25,43 @@
 JSON.
 
 
+.. _curl-windows:
+
+Warning about using cURL from the Windows prompt
+------------------------------------------------
+
+The examples on this page assume that the user is running a bash shell
+on some GNU/Linux distribution. Such a shell has the major advantage
+of having the possibility to use either single-quote or double-quotes
+characters in order to group a set of characters (including spaces) as
+a whole string.
+
+.. highlight:: bash
+
+Unfortunately, the default command-line prompt of Microsoft Windows
+**doesn't support single-quote characters**. If you copy/paste a cURL
+command-line from this page that mixes single-quote and double-quotes,
+it won't work as such, and you'll have to replace single-quotes by
+double-quotes, and prefix the double-quotes by a backslash
+character. For instance, consider the following command line that
+works fine on GNU/Linux::
+
+  $ curl -v -X PUT http://localhost:8042/modalities/sample \
+         -d '{"AET" : "ORTHANCC", "Host": "127.0.0.1", "Port": 2002}'
+
+This call will **not** work on the Microsoft Windows prompt as it
+contains single-quotes. You should adapt this command line as follows
+to run it on Windows::
+
+  $ curl -v -X PUT http://localhost:8042/modalities/sample \
+         -d "{\"AET\" : \"ORTHANCC\", \"Host\": \"127.0.0.1\", \"Port\": 2002}"
+
+As an alternative, consider using a different Windows shell, for
+instance `Windows PowerShell
+<https://fr.wikipedia.org/wiki/Windows_PowerShell>`__ (some examples
+of PowerShell can be found below on this page).
+
+
 .. _sending-dicom-images:
 
 Sending DICOM images
@@ -57,12 +94,20 @@
     $ curl -X POST -H "Expect:" http://localhost:8042/instances --data-binary @CT.X.1.2.276.0.7230010.dcm
 
 The code distribution of Orthanc contains a `sample Python script
-<https://hg.orthanc-server.com/orthanc/file/default/OrthancServer/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py>`__
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py>`__
 that recursively upload the content of some folder into Orthanc using
 the REST API::
 
     $ python ImportDicomFiles.py localhost 8042 ~/DICOM/
 
+Starting with Orthanc 1.8.1, the source distribution of Orthanc
+includes another Python script named ``OrthancImport.py`` that
+provides more features than ``ImportDicomFiles.py``. It can notably
+import the content of ``.zip``, ``.tar.gz`` or ``.tar.bz2`` archives
+without having to uncompress them first. It also provides more
+comprehensive command-line options. `Check this script out
+<https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.7/OrthancServer/Resources/Samples/ImportDicomFiles/OrthancImport.py>`__.
+    
 
 .. highlight:: perl
 
@@ -397,6 +442,33 @@
   $ curl http://localhost:8042/studies/6b9e19d9-62094390-5f9ddb01-4a191ae7-9766b715/media > Study.zip
 
 
+.. _download-pdf-videos:
+
+Downloading PDF or videos
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. highlight:: bash
+
+Given a DICOM instance that embeds a PDF file (typically, one instance
+whose SOP Class UID is ``1.2.840.10008.5.1.4.1.1.104.1`` -
+Encapsulated PDF Storage), the PDF content can be downloaded as
+follows::
+
+  $ curl http://localhost:8042/instances/1915e0cc-c2c1a0fc-12cdd7f5-3ba32114-a97c2c9b/content/0042,0011 > sample.pdf
+
+This corresponds to downloading the raw DICOM tag "Encapsulated
+Document" (0042,0011). Beware that the last byte of the downloaded
+file might correspond to one padding byte, if the source PDF had an
+odd number of bytes.
+
+Similarly, if you know that a DICOM instance :ref:`embeds a video
+<videos>` (which can be tested by checking the :ref:`value of the
+metadata <metadata-core>` corresponding to its transfer syntax UID),
+the raw video can be downloaded as follows::
+
+  $ curl http://localhost:8042/instances/e465dd27-83c96343-96848735-7035a133-1facf1a0/frames/0/raw > sample.mp4
+
+
 .. _peering:
 
 Sending resources to remote Orthanc over HTTP/HTTPS (through Orthanc peering)
@@ -433,21 +505,24 @@
 .. highlight:: bash
 
 Such a configuration would enable Orthanc to connect to two other
-Orthanc instances that listens on the
-localhost on the port 8043 & 8044. The peers that are known to Orthanc
-can be queried::
+Orthanc instances that listens on the localhost on the ports 8043
+and 8044. The peers that are known to Orthanc can be queried::
 
     $ curl http://localhost:8042/peers?expand
 
-The peers can then be updated through the API too::
+Instead of using the configuration file, peers can be created or
+updated through the REST API using the ``PUT`` method of HTTP::
 
     $ curl -v -X PUT http://localhost:8042/peers/sample -d '{"Url" : "http://127.0.0.1:8043"}'
 
+One peer can also be removed using the ``DELETE`` method as follows::
+    
+    $ curl -v -X DELETE http://localhost:8042/peers/sample
 
-Note that, by default, peers are stored in Orthanc configuration files
-and are updated in Orthanc memory only.  If you want your modifications
-to be persistent, you should configure Orthanc to store its peers
-in the database.  This is done through this configuration::
+Note that, by default, peers are read from the Orthanc configuration
+files and are updated in Orthanc memory only. If you want your
+modifications to be persistent, you should configure Orthanc to store
+its peers in the database.  This is done through this configuration::
 
     ...
     "OrthancPeersInDatabase" : true,
@@ -562,15 +637,20 @@
 
     $ curl http://localhost:8042/modalities?expand
 
-The modalities can then be updated through the API too::
+Instead of using the configuration file, modalities can be created or
+updated through the REST API using the ``PUT`` method of HTTP::
 
     $ curl -v -X PUT http://localhost:8042/modalities/sample -d '{"AET" : "ORTHANCC", "Host": "127.0.0.1", "Port": 2002}'
 
+One modality can also be removed using the ``DELETE`` method as follows::
+    
+    $ curl -v -X DELETE http://localhost:8042/modalities/sample
 
-Note that, by default, modalities are stored in Orthanc configuration files
-and are updated in Orthanc memory only.  If you want your modifications
-to be persistent, you should configure Orthanc to store its modalities
-in the database.  This is done through this configuration::
+Note that, by default, modalities are read from the Orthanc
+configuration files and are updated in Orthanc memory only. If you
+want your modifications to be persistent, you should configure Orthanc
+to store the modalities in the database.  This is done through this
+configuration::
 
     ...
     "DicomModalitiesInDatabase" : true,
@@ -703,6 +783,7 @@
 within the :ref:`configuration file <configuration>` (See
 Configuration section under Sending resources to remote modalities).
 
+.. _rest-find-scu:
 
 Performing Queries on Modalities
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -845,8 +926,8 @@
   $ curl --request POST --url http://localhost:8042/queries/5af318ac-78fb-47ff-b0b0-0df18b0588e0/answers/0/retrieve --data Orthanc
 
 If C-Moves take too long (for example, performing a C-Move of a big
-study), you may run the request in asynchronous fashion, which will
-create a job in Orthanc::
+study), you may run the request in :ref:`asynchronous mode <jobs>`,
+which will create a job in Orthanc::
 
   $ curl --request POST --url http://localhost:8042/queries/5af318ac-78fb-47ff-b0b0-0df18b0588e0/retrieve \
     --data '{"TargetAet":"Orthanc","Synchronous":false}'
@@ -854,7 +935,9 @@
 
 .. highlight:: bash
 
-The answer of this POST request is the job ID taking care of the C-Move::
+The answer of this POST request is the job ID taking care of the
+C-Move command, :ref:`whose status can be monitored <jobs-monitoring>`
+in order to detect failure or completion::
 
   {
       "ID" : "11541b16-e368-41cf-a8e9-3acf4061d238",
@@ -869,12 +952,13 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 .. highlight:: bash
 
-Performing a find within Orthanc is very similar to using Queries
-against DICOM modalities and the additional options listed above work
-with find also.  When performing a find, you will receive the Orthanc
-ID's of all the matched items within your find. For example if you
-perform a study level find and 5 Studies match you will receive 5
-study level Orthanc ID's in JSON format as a response::
+Performing a find within the local database of Orthanc is very similar
+to using Queries against DICOM modalities and the additional options
+listed above work with find also.  When performing a find, you will
+receive the Orthanc ID's of all the matched items within your
+find. For example if you perform a study level find and 5 Studies
+match you will receive 5 study level Orthanc ID's in JSON format as a
+response::
 
   $ curl --request POST --url http://localhost:8042/tools/find \
     --data '{
@@ -925,6 +1009,18 @@
     }
   ]
 
+Here is a sample REST API call to find the Orthanc identifiers of all
+the DICOM series generated by an imaging modality whose "Device Serial
+Number (0018,1000)" DICOM tag is equal to "123"::
+
+  $ curl -X POST http://localhost:8042/tools/find -d '{"Level":"Series","Query":{"DeviceSerialNumber":"123"},"Expand":true}'
+
+If you are interested by a **list of several items** (in this case, in
+a list of serial numbers), just separate them with backslashes as
+would do with DICOM C-FIND::
+
+  $ curl -X POST http://localhost:8042/tools/find -d '{"Level":"Series","Query":{"DeviceSerialNumber":"123\\abc"},"Expand":true}'
+
   
   
 Additional Options
@@ -1058,6 +1154,20 @@
     $ curl -X DELETE http://localhost:8042/instances/8e289db9-0e1437e1-3ecf395f-d8aae463-f4bb49fe
 
 
+Starting with Orthanc 1.9.4, it is also possible to ``POST`` on the
+new route ``/tools/bulk-delete`` to delete at once a set of multiple
+DICOM resources that are not related (i.e. that don't share any parent
+DICOM resource). A typical use case is to delete a list of DICOM
+instances that don't belong to the same parent patient/study/series.
+The list of the :ref:`Orthanc identifiers <orthanc-ids>` of the
+resources to be deleted (that may indifferently correspond to
+patients, studies, series or instances) must be provided in an
+argument ``Resources`` in the body of the request. Here is a sample
+call::
+
+  $ curl http://localhost:8042/tools/bulk-delete -d '{"Resources":["b6da0b16-a25ae9e7-1a80fc33-20df01a9-a6f7a1b0","d6634d97-24379e4a-1e68d3af-e6d0451f-e7bcd3d1"]}'
+
+    
 Clearing log of changes
 ^^^^^^^^^^^^^^^^^^^^^^^
 
--- a/Sphinx/source/users/support.rst	Mon Feb 14 08:48:23 2022 +0100
+++ b/Sphinx/source/users/support.rst	Mon Feb 14 08:52:12 2022 +0100
@@ -48,7 +48,7 @@
 
 Importantly, for all the features that are pending in the ``TODO``
 file, if you are a company, please consider `buying professional
-services <https://www.osimis.io/en/services.html>`__ in order to get
+services <https://osimis.io/en/orthanc-support-contract>`__ in order to get
 the feature implemented faster.
    
    
@@ -78,13 +78,29 @@
   software does not help <proprietary>`), and that are preferably
   calls the :ref:`REST API of Orthanc <rest>`. The most useful
   commands are `cURL <https://en.wikipedia.org/wiki/CURL>`__, `DCMTK
-  <https://dicom.offis.de/dcmtk.php.en>`__, `dicom3tools
-  <https://www.dclunie.com/dicom3tools.html>`__, `dcm4che command-line
-  tools <https://www.dcm4che.org/>`__, or Python scripts.
+  <https://dicom.offis.de/dcmtk.php.en>`__ (notably ``storescu``),
+  `dicom3tools <https://www.dclunie.com/dicom3tools.html>`__ (notably
+  ``dciodvfy``), `dcm4che command-line tools
+  <https://www.dcm4che.org/>`__ (notably ``storescu``), `GDCM
+  <http://gdcm.sourceforge.net/>`__ (notably ``gdcmscu``), or Python
+  scripts (notably using ``pydicom``).
 * In the case of DICOM networking problems, the logs from the remote
   modality.
 * If applicable, a screenshot is worth a thousand words.
 * If you report a crash, if applicable, a :ref:`core file <crash>`.
+* The `OHIF viewer <https://ohif.org/>`__ can `connect to Orthanc
+  <https://docs.ohif.org/history/v1/connecting-to-image-archives/orthanc-with-docker.html>`__
+  using the DICOMweb plugin of Orthanc, but is a fully separate
+  project. As a consequence, questions regarding OHIF must be asked on
+  the `dedicated discussion group
+  <https://groups.google.com/g/cornerstone-platform>`__ or on the
+  `dedicated bug tracker
+  <https://github.com/OHIF/Viewers/issues>`__. The core developers of
+  Orthanc will happily fix the :ref:`DICOMweb plugin <dicomweb>`, but
+  it is necessary for the reporter to identify the discrepancy wrt.
+  DICOMweb standard by providing a minimal working example as
+  explained above.
+
 
 All this information is mandatory, as it allows other members of the
 Orthanc community to **reproduce your problem independently of your
@@ -106,7 +122,9 @@
 consider directly introducing a `bug report on our issue tracker
 <https://bugs.orthanc-server.com/enter_bug.cgi>`__. Beware however
 that your issue might be closed if too vague or if not reproducible.
-As a consequence, it is advised to first use the discussion forum.
+As a consequence, it is strongly advised to use the `discussion forum
+<https://groups.google.com/forum/#!forum/orthanc-users>`__ in the
+first place.
 
 
 .. _support-freelancers:
@@ -122,23 +140,25 @@
    open-source tools, use the `Orthanc Users discussion forum
    <https://groups.google.com/forum/#!forum/orthanc-users>`__.
 
-2. If you need an **additional feature or a dedicated development**,
-   and if you are ready to pay, `get in touch with Osimis
+2. If you need an **additional feature implemented in Orthanc**,
+   and if you are ready to pay, get in touch with `Osimis
    <mailto:orthanc-support@osimis.io>`__, the commercial partner of
-   the Orthanc project.
+   the Orthanc project or with `Alain Mazy <https://mazy.be/orthanc>`__.
 
 3. If you are not able to reproduce an issue by yourself, if you are
    looking for personalized help related to deployments/training/...,
    or if you need **proximity support in your language/timezone**,
-   here is a list of freelancers:
+   here is a list of freelancers/companies:
 
    * **Worldwide**:
    
-     * `Osimis <https://www.osimis.io/en/services.html>`__
+     * `Osimis <https://osimis.io/en/orthanc-support-contract>`__ (French/English, Belgium)
+     * `Alain Mazy <https://mazy.be/orthanc>`__ (French/English, Belgium)
 
    * **Europe**:
 
      * `Adrian Schiopu <sc.callisto.srl@gmail.com>`__ (Romania)
+     * `Benoit Crickboom <orthanc@sleig.be>`__ (French/English, Belgium)
      * `Krzysztof Turkiewicz <http://www.deeveeloop.pl/>`__ (Polish/English, Poland)
      * `Salim Kanoun <https://github.com/salimkanoun>`__ (French/English/Arabic, France)
 
@@ -147,12 +167,14 @@
      * `Gabriel Couture <https://github.com/gacou54/>`__ (French/English, Canada)
      * `Mohannad Hussain <https://www.linkedin.com/in/mohannadhussain/>`__ (English, Canada)
      * `Oliver Tsai <mailto:oliver@futurepacs.com>`__ (English/Spanish/French, Toronto)
-
+     * `Stephen D. Scotti <mailto:sscotti@sias.dev>`__ (English, USA)
+     * `Yi Lu <https://www.linkedin.com/in/digihunch/>`__ (English, Canada)
 
    * **South America**:
        
      * `Claudio Arenas <mailto:dentista.arenas@gmail.com>`__ (Spanish/English, Chile)
      * `Gustavo Fernandez <https://www.linkedin.com/in/gfernandezguirland/>`__ (Spanish/Portuguese, Uruguay)
+     * `Iván Kuschevatzky <mailto:ivankuche@gmail.com>`__ (English/Spanish, Argentina)
      * `Luiz Eduardo Guida Valmont <https://www.linkedin.com/in/luizvalmont/>`__ (English/Portuguese, Brazil)
      * `William Sanchez Luis <mailto:williamsanchezluis@gmail.com>`__ (English/Spanish, Venezuela)