diff --git a/docker-compose.yml b/docker-compose.yml
index 3c84b91a0dc4cf66f2876a9ae1d3125ece18c89f..a782e9574a3cf5f94faef80b20d95b4be487f15d 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -73,7 +73,7 @@ services:
     command: npm install && node_modules/.bin/grunt watch
 
   autoconfig:
-    image: ubleipzig/vufind-php:7.1-8-debug
+    image: ubleipzig/vufind-php:7.2-8-debug
     volumes:
       - ./:/usr/local/vufind:z
     environment:
diff --git a/fid/config/vufind/fid.ini b/fid/config/vufind/fid.ini
index 607d62d8f4ff16c449a9cf065627c12781c47e43..f9615bf43bb767c9f23c6697454d531717b00993 100644
--- a/fid/config/vufind/fid.ini
+++ b/fid/config/vufind/fid.ini
@@ -2,16 +2,16 @@
 baseUrl = http://172.18.113.133/bbi-alpha.3
 
 [Security]
+access_levels[] = basic_access
 access_levels[] = limited_access
 access_levels[] = full_access
 
 [UserProfile]
 role_display[] = "full_access"
 role_display[] = "limited_access"
-role_display[] = "read_user_list"
-role_display[] = "edit_user"
-role_display_priority[] = "full_access>limited_access"
-role_display_order = "full_access>limited_access>read_user_list>edit_user"
+role_display[] = "basic_access"
+role_display_priority[] = "full_access>limited_access>basic_access"
+role_display_order = "full_access>limited_access>basic_access"
 
 [Admin]
 ; Whitelist of all fields that admins shall be able
@@ -27,7 +27,6 @@ overview_fields[] = 'Lastname'
 overview_fields[] = 'Permissions'
 
 ; List of all available user permissions
-permission_options[] = 'edit_user'
-permission_options[] = 'read_user_list'
+permission_options[] = 'basic_access'
 permission_options[] = 'limited_access'
 permission_options[] = 'full_access'
\ No newline at end of file
diff --git a/local/languages/de.ini b/local/languages/de.ini
index 8234c3a9c6b17a969df39f13fab0d1c95fe192eb..f38013bed2541dbf7d759bbbbe8cf26644a69810 100644
--- a/local/languages/de.ini
+++ b/local/languages/de.ini
@@ -1852,6 +1852,9 @@ non_rda_original_title = "Originaltitel"
 ;#8828
 German Prints Index Number = "VD-Nummer"
 
+# 16016
+Dissertation Note = Hochschulschriftenvermerk
+
 ; LIDO RecordDriver relevant
 expression creation = "Entstehungsvermerk"
 publication event = "Ausstellung"
@@ -1940,4 +1943,11 @@ collection_hierarchy_tree_tab = "Sammlungskontext"
 Collection Items = "Enthaltene Objekte"
 
 ; VF5.1 Offcanvas-Toggler
-offcanvas-toggler-record-view = "Eintrag weiter verarbeiten"
\ No newline at end of file
+offcanvas-toggler-record-view = "Eintrag weiter verarbeiten"
+offcanvas-toggler-myresearch = "Seitenleiste einblenden"
+
+; format_finc
+DigitalCollection  = "Digitale Kollektion"
+Digital Collection = "Digitale Kollektion"
+Visual Media = "Bildmaterial"
+ReliefPrint = Druckgraphik
\ No newline at end of file
diff --git a/local/languages/en.ini b/local/languages/en.ini
index 5d77351298b74c26c0b9fbce74563f3fa757ad70..d4dc4d5a51c424cb8e020122594fa16ccc8359e0 100644
--- a/local/languages/en.ini
+++ b/local/languages/en.ini
@@ -2042,8 +2042,11 @@ hierarchyTreeSelect = "Parent items:"
 
 collection_hierarchy_tree_tab = "Collection Context"
 
-DigitalCollection  = Digital Collection
-Digital Collection = Digital Collection
-
 ; VF5.1 Offcanvas-Toggler
-offcanvas-toggler-record-view = "Further processing options"
\ No newline at end of file
+offcanvas-toggler-record-view = "Further processing options"
+offcanvas-toggler-myresearch = "Show sidebar"
+
+# format_finc
+DigitalCollection  = "Digital Collection"
+Digital Collection = "Digital Collection"
+ReliefPrint = "Printmaking"
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/ILS/Driver/DAIA.php b/module/VuFind/src/VuFind/ILS/Driver/DAIA.php
index 387986a68e6f5af76cc6656f2bc04f991bccc9b1..52b5d4d9ea2fea939f411139c343872ea814667c 100644
--- a/module/VuFind/src/VuFind/ILS/Driver/DAIA.php
+++ b/module/VuFind/src/VuFind/ILS/Driver/DAIA.php
@@ -493,7 +493,7 @@ class DAIA extends AbstractBase implements
                 list($responseMediaType) = array_pad(
                     explode(
                         ';',
-                        $result->getHeaders()->get('ContentType')->getFieldValue(),
+                        $result->getHeaders()->get('Content-type')->getFieldValue(),
                         2
                     ),
                     2,
diff --git a/module/fid/config/address-collection.php b/module/fid/config/address-collection.php
index 09b669c9236b13721745ef27ee42355cc499a623..f94da88e9273a485df4c20b83a5601a7c7b57959 100644
--- a/module/fid/config/address-collection.php
+++ b/module/fid/config/address-collection.php
@@ -19,68 +19,191 @@
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-use Zend\Form\Element\Collection;
+use fid\InputFilter\RootAwareBaseInputFilter;
+use Zend\Filter\StringTrim;
 use Zend\Form\Element\Hidden;
 use Zend\Form\Element\Text;
 use Zend\Form\Fieldset;
+use Zend\Form\InputFilterProviderFieldset;
+use Zend\Validator\NotEmpty;
+use Zend\Validator\StringLength;
 
 return [
     'spec' => [
-        'name'    => 'addresses',
-        'type'    => Collection::class,
-        'options' => [
-            'count'          => 0,
-            'target_element' => [
-                'type'     => Fieldset::class,
-                'elements' => [
-                    'id' => [
-                        'spec' => [
-                            'name' => 'id',
-                            'type' => Hidden::class,
+        'name'     => 'addresses',
+        'type'     => Fieldset::class,
+        'elements' => [
+            'address_0' => [
+                'spec' => [
+                    'name'     => 0,
+                    'type'     => InputFilterProviderFieldset::class,
+                    'elements' => [
+                        'id'      => [
+                            'spec' => [
+                                'name' => 'id',
+                                'type' => Hidden::class,
+                            ],
                         ],
-                    ],
-                    'line1'   => [
-                        'spec' => [
-                            'name'    => 'line1',
-                            'type'    => Text::class,
-                            'options' => [
-                                'label' => 'label_address_line_1',
-                            ]
+                        'line1'   => [
+                            'spec' => [
+                                'name'    => 'line1',
+                                'type'    => Text::class,
+                                'options' => [
+                                    'label' => 'label_address_0_line_1',
+                                ],
+                            ],
                         ],
-                    ],
-                    'line2'   => [
-                        'spec' => [
-                            'name'    => 'line2',
-                            'type'    => Text::class,
-                            'options' => [
-                                'label' => 'label_address_line_2',
-                            ]
+                        'line2'   => [
+                            'spec' => [
+                                'name'    => 'line2',
+                                'type'    => Text::class,
+                                'options' => [
+                                    'label' => 'label_address_0_line_2',
+                                ]
+                            ],
                         ],
-                    ],
-                    'zip'     => [
-                        'spec' => [
-                            'name'    => 'zip',
-                            'type'    => Text::class,
-                            'options' => [
-                                'label' => 'label_address_zip',
-                            ]
+                        'zip'     => [
+                            'spec' => [
+                                'name'    => 'zip',
+                                'type'    => Text::class,
+                                'options' => [
+                                    'label' => 'label_address_0_zip',
+                                ],
+                            ],
                         ],
-                    ],
-                    'city'    => [
-                        'spec' => [
-                            'name'    => 'city',
-                            'type'    => Text::class,
-                            'options' => [
-                                'label' => 'label_address_city',
-                            ]
+                        'city'    => [
+                            'spec' => [
+                                'name'    => 'city',
+                                'type'    => Text::class,
+                                'options' => [
+                                    'label' => 'label_address_0_city',
+                                ],
+                            ],
+                        ],
+                        'country' => [
+                            'spec' => [
+                                'name'    => 'country',
+                                'type'    => Text::class,
+                                'options' => [
+                                    'label' => 'label_address_0_country',
+                                ],
+                            ],
                         ],
                     ],
-                    'country' => [
-                        'spec' => [
-                            'name'    => 'country',
-                            'type'    => Text::class,
-                            'options' => [
-                                'label' => 'label_address_country',
+                    'options'  => [
+                        'template'          => 'address-fieldset.phtml',
+                        'label'             => 'label_address_0',
+                        'input_filter_spec' => [
+                            'type' => RootAwareBaseInputFilter::class,
+                            'line1'   => [
+                                'name'       => 'line1',
+                                'filters'    => [
+                                    StringTrim::class => [
+                                        'name' => StringTrim::class,
+                                    ],
+                                ],
+                                'validators' => [
+                                    NotEmpty::class         => [
+                                        'name'    => NotEmpty::class,
+                                        'options' => [
+                                            'type' => NotEmpty::NULL,
+                                        ],
+                                    ],
+                                    StringLength::class     => [
+                                        'name'    => StringLength::class,
+                                        'options' => [
+                                            'max' => 255
+                                        ]
+                                    ],
+                                ],
+                            ],
+                            'line2'   => [
+                                'name'       => 'line2',
+                                'filters'    => [
+                                    StringTrim::class => [
+                                        'name' => StringTrim::class,
+                                    ],
+                                ],
+                                'validators' => [
+                                    NotEmpty::class     => [
+                                        'name'    => NotEmpty::class,
+                                        'options' => [
+                                            'type' => NotEmpty::NULL,
+                                        ],
+                                    ],
+                                    StringLength::class => [
+                                        'name'    => StringLength::class,
+                                        'options' => [
+                                            'max' => 255
+                                        ]
+                                    ],
+                                ],
+                            ],
+                            'zip'     => [
+                                'name'       => 'zip',
+                                'filters'    => [
+                                    StringTrim::class => [
+                                        'name' => StringTrim::class,
+                                    ],
+                                ],
+                                'validators' => [
+                                    StringLength::class     => [
+                                        'name'    => StringLength::class,
+                                        'options' => [
+                                            'max' => 255
+                                        ]
+                                    ],
+                                    NotEmpty::class         => [
+                                        'name'    => NotEmpty::class,
+                                        'options' => [
+                                            'type' => NotEmpty::NULL,
+                                        ],
+                                    ],
+                                ],
+                            ],
+                            'city'    => [
+                                'name'       => 'city',
+                                'filters'    => [
+                                    StringTrim::class => [
+                                        'name' => StringTrim::class,
+                                    ],
+                                ],
+                                'validators' => [
+                                    StringLength::class     => [
+                                        'name'    => StringLength::class,
+                                        'options' => [
+                                            'max' => 255
+                                        ]
+                                    ],
+                                    NotEmpty::class         => [
+                                        'name'    => NotEmpty::class,
+                                        'options' => [
+                                            'type' => NotEmpty::NULL,
+                                        ],
+                                    ],
+                                ],
+                            ],
+                            'country' => [
+                                'name'       => 'country',
+                                'filters'    => [
+                                    StringTrim::class => [
+                                        'name' => StringTrim::class,
+                                    ],
+                                ],
+                                'validators' => [
+                                    StringLength::class     => [
+                                        'name'    => StringLength::class,
+                                        'options' => [
+                                            'max' => 255
+                                        ]
+                                    ],
+                                    NotEmpty::class         => [
+                                        'name'    => NotEmpty::class,
+                                        'options' => [
+                                            'type' => NotEmpty::NULL,
+                                        ],
+                                    ],
+                                ],
                             ],
                         ],
                     ],
diff --git a/module/fid/config/address-input-filter.php b/module/fid/config/address-input-filter.php
deleted file mode 100644
index 79fa72a8d8fa66ec837c8fc54cdaeb3553508da3..0000000000000000000000000000000000000000
--- a/module/fid/config/address-input-filter.php
+++ /dev/null
@@ -1,179 +0,0 @@
-<?php
-/**
- * Copyright (C) 2019 Leipzig University Library
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * @author  Sebastian Kehr <kehr@ub.uni-leipzig.de>
- * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
- */
-
-use fid\FormModel\AddressValidator;
-use Zend\Filter\StringTrim;
-use Zend\InputFilter\CollectionInputFilter;
-use Zend\Validator\Callback;
-use Zend\Validator\NotEmpty;
-use Zend\Validator\StringLength;
-
-return [
-    'name' => 'addresses',
-    'type'         => CollectionInputFilter::class,
-    'input_filter' => [
-        'line1'   => [
-            'name'       => 'line1',
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                NotEmpty::class     => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class     => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class
-                            . '::validate',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'line2'   => [
-            'name'       => 'line2',
-            'required' => false,
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'zip'     => [
-            'name'       => 'zip',
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                NotEmpty::class     => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class     => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class
-                            . '::validate',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'city'    => [
-            'name'       => 'city',
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                NotEmpty::class     => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class     => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class
-                            . '::validate',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'country' => [
-            'name'       => 'country',
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                NotEmpty::class     => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class     => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class
-                            . '::validate',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-    ],
-];
diff --git a/module/fid/config/admin-edit-form.php b/module/fid/config/admin-edit-form.php
new file mode 100644
index 0000000000000000000000000000000000000000..81322e19f9306857c29d156410b139a279e0a6c4
--- /dev/null
+++ b/module/fid/config/admin-edit-form.php
@@ -0,0 +1,262 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author  Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+use fid\Hydrator\UserHydrator;
+use fid\InputFilter\AdminEditFormInputFilter;
+use fid\InputFilter\RootAwareBaseInputFilter;
+use Zend\Filter\StringTrim;
+use Zend\Form\Element\Collection;
+use Zend\Form\Element\Hidden;
+use Zend\Form\Element\Radio;
+use Zend\Form\Element\Select;
+use Zend\Form\Element\Submit;
+use Zend\Form\Element\Text;
+use Zend\Validator\Regex;
+use Zend\Validator\StringLength;
+
+return [
+    'name'     => 'admin-edit-form',
+    'hydrator' => UserHydrator::class,
+    'elements' => [
+        'id'             => [
+            'spec' => [
+                'name' => 'id',
+                'type' => Hidden::class,
+            ],
+        ],
+        'salutation'     => [
+            'spec' => [
+                'name'    => 'salutation',
+                'type'    => Select::class,
+                'options' => [
+                    'label'        => 'label_salutation',
+                    'options'      => [
+                        'mr'  => [
+                            'value' => 'mr',
+                            'label' => 'label_salutation_mr',
+                        ],
+                        'mrs' => [
+                            'value' => 'mrs',
+                            'label' => 'label_salutation_mrs',
+                        ]
+                    ],
+                    'empty_option' => '',
+                ],
+            ],
+        ],
+        'academic_title' => [
+            'spec' => [
+                'name'    => 'academic_title',
+                'type'    => Text::class,
+                'options' => [
+                    'label' => 'label_academic_title',
+                ],
+            ],
+        ],
+        'firstname'      => [
+            'spec' => [
+                'name'       => 'firstname',
+                'type'       => Text::class,
+                'options'    => [
+                    'label' => 'label_firstname',
+                ],
+                'attributes' => [
+                    'required' => true,
+                ],
+            ],
+        ],
+        'lastname'       => [
+            'spec' => [
+                'name'       => 'lastname',
+                'type'       => Text::class,
+                'options'    => [
+                    'label' => 'label_lastname',
+                ],
+                'attributes' => [
+                    'required' => true,
+                ],
+            ],
+        ],
+        'home_library'   => [
+            'spec' => [
+                'name'       => 'home_library',
+                'type'       => Select::class,
+                'options'    => [
+                    'label'              => 'label_home_library',
+                    'use_hidden_element' => true,
+                ],
+                'attributes' => [
+                    'required' => true,
+                ],
+            ],
+        ],
+        'college'        => [
+            'spec' => [
+                'name'    => 'college',
+                'type'    => Text::class,
+                'options' => [
+                    'label' => 'label_college',
+                ]
+            ],
+        ],
+        'job_title'      => [
+            'spec' => [
+                'name'       => 'job_title',
+                'type'       => Radio::class,
+                'options'    => [
+                    'label'         => 'label_job_title',
+                    'value_options' => [
+                        'job_title_0',
+                        'job_title_1'
+                    ],
+                ],
+                'attributes' => [
+                    'required' => true,
+                ],
+            ],
+        ],
+        'permissions'    => [
+            'spec' => [
+                'name'    => 'permissions',
+                'type'    => Collection::class,
+                'options' => [
+                    'label'          => 'label_permissions',
+                    'target_element' => [
+                        'name'    => 'permission',
+                        'type'    => Select::class,
+                        'options' => [
+                            'value_options' => [
+                                'denied'    => 'permission_status_denied',
+                                'requested' => 'permission_status_requested',
+                                'granted'   => 'permission_status_granted',
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+        ],
+        'submit'         => [
+    'spec' => [
+        'name'       => 'submit',
+        'type'       => Submit::class,
+        'attributes' => [
+            'value' => 'label_update_submit',
+        ],
+    ],
+],
+    ],
+    'input_filter' => [
+    'type'           => RootAwareBaseInputFilter::class,
+    'id'             => [
+        'name'     => 'id',
+        'required' => true,
+    ],
+    'salutation'     => [
+        'name'     => 'salutation',
+        'required' => false,
+        'filters'  => [
+            StringTrim::class => [
+                'name' => StringTrim::class,
+            ],
+        ],
+    ],
+    'academic_title' => [
+        'name'     => 'academic_title',
+        'required' => false,
+        'filters'  => [
+            StringTrim::class => [
+                'name' => StringTrim::class,
+            ],
+        ],
+    ],
+    'firstname'      => [
+        'name'       => 'firstname',
+        'required'   => false,
+        'filters'    => [
+            StringTrim::class => [
+                'name' => StringTrim::class,
+            ],
+        ],
+        'validators' => [
+            StringLength::class => [
+                'name'    => StringLength::class,
+                'options' => [
+                    'max' => 255
+                ],
+            ],
+            Regex::class        => [
+                'name'    => Regex::class,
+                'options' => [
+                    'pattern' => '/^\D*$/',
+                ],
+            ],
+        ],
+    ],
+    'lastname'       => [
+        'name'       => 'lastname',
+        'required'   => false,
+        'filters'    => [
+            StringTrim::class => [
+                'name' => StringTrim::class,
+            ],
+        ],
+        'validators' => [
+            StringLength::class => [
+                'name'    => StringLength::class,
+                'options' => [
+                    'max' => 255
+                ]
+            ],
+            Regex::class        => [
+                'name'    => Regex::class,
+                'options' => [
+                    'pattern' => '/^\D*$/',
+                ],
+            ],
+        ],
+    ],
+    'home_library'   => [
+        'name'    => 'home_library',
+        'filters' => [
+            StringTrim::class => [
+                'name' => StringTrim::class,
+            ],
+        ],
+    ],
+    'college'        => [
+        'name'     => 'college',
+        'required' => false,
+        'filters'  => [
+            StringTrim::class => [
+                'name' => StringTrim::class,
+            ],
+        ],
+    ],
+    'job_title'      => [
+        'name'     => 'job_title',
+        'required' => true,
+    ],
+    'submit'         => [
+        'name'     => 'submit',
+        'required' => true,
+    ],
+],
+];
\ No newline at end of file
diff --git a/module/fid/config/module.config.php b/module/fid/config/module.config.php
index 77b6b0c368be6037f68d6265dde8c9d80300d0ce..f80bb7d8be1cee5d85d05782ab9a2090fd90debf 100644
--- a/module/fid/config/module.config.php
+++ b/module/fid/config/module.config.php
@@ -21,17 +21,19 @@
 
 use fid\Controller\UserController;
 use fid\Controller\UserControllerFactory;
-use fid\FormModel\UsernameChangeModel;
+use fid\Filter\PermissionsFilter;
+use fid\Filter\PermissionsFilterFactory;
 use fid\FormModel\PasswordChangeModel;
 use fid\FormModel\PasswordResetModel;
-use fid\FormModel\UserCreateModel;
-use fid\FormModel\UserInitModel;
-use fid\FormModel\UserUpdateModel;
+use fid\FormModel\UsernameChangeModel;
 use fid\Helper\FormLabel;
 use fid\Helper\TranslatorDelegator;
+use fid\Hydrator\UserHydrator;
+use fid\Hydrator\UserHydratorDelegatorFactory;
 use fid\Listener\ErrorListener;
 use fid\Listener\ErrorListenerFactory;
 use fid\Listener\LocaleListener;
+use fid\Serializer\OrderCreationRequestUserNormalizer;
 use fid\Service\Client;
 use fid\Service\ClientFactory;
 use fid\VuFind\Auth\Authenticator;
@@ -44,6 +46,7 @@ use fid\VuFind\ILS\Fid;
 use fid\VuFind\ILS\FidFactory;
 use fid\VuFind\Resolver\Driver\Ezb;
 use fid\VuFind\Resolver\Driver\EzbDelegatorFactory;
+use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
 use VuFind\Auth\ILSAuthenticator as BaseILSAuthenticator;
 use VuFind\Db\Row\User as BaseUser;
 use VuFind\Db\Row\UserFactory;
@@ -54,12 +57,28 @@ use Zend\ServiceManager\Factory\InvokableFactory;
 
 return [
     'forms'              => [
-        UserInitModel::class       => require_once 'user-init-form.php',
-        UserCreateModel::class     => require_once 'user-create-form.php',
-        UserUpdateModel::class     => require_once 'user-update-form.php',
-        PasswordResetModel::class  => require_once 'password-reset-form.php',
-        PasswordChangeModel::class => require_once 'password-change-form.php',
-        UsernameChangeModel::class => require_once 'username-change-form.php',
+        'user-init-form'           => require 'user-init-form.php',
+        'user-create-form'         => require 'user-create-form.php',
+        'user-update-form'         => require 'user-update-form.php',
+        'admin-edit-form'          => require 'admin-edit-form.php',
+        PasswordResetModel::class  => require 'password-reset-form.php',
+        PasswordChangeModel::class => require 'password-change-form.php',
+        UsernameChangeModel::class => require 'username-change-form.php',
+    ],
+    'filters'            => [
+        'factories' => [
+            PermissionsFilter::class => PermissionsFilterFactory::class,
+        ]
+    ],
+    'hydrators'          => [
+        'factories'  => [
+            UserHydrator::class => InvokableFactory::class,
+        ],
+        'delegators' => [
+            UserHydrator::class => [
+                UserHydratorDelegatorFactory::class,
+            ],
+        ],
     ],
     'controllers'        => [
         'factories' => [
@@ -70,7 +89,7 @@ return [
         ErrorListener::class,
         LocaleListener::class,
     ],
-    // TODO: add alias to vufind core
+    // TODO: issue PR to add alias to vufind core
     'controller_plugins' => [
         'aliases' => [
             'flashmessenger' => FlashMessenger::class,
@@ -96,8 +115,8 @@ return [
             LocaleListener::class   => InvokableFactory::class,
         ],
     ],
-    'view_helpers' => [
-        'aliases' => [
+    'view_helpers'       => [
+        'aliases'   => [
             'formLabel' => FormLabel::class,
         ],
         'factories' => [
@@ -106,7 +125,7 @@ return [
     ],
     'vufind'             => [
         'plugin_managers' => [
-            'auth'       => [
+            'auth'            => [
                 'aliases'   => [
                     'fid' => Authenticator::class,
                 ],
@@ -114,7 +133,7 @@ return [
                     Authenticator::class => AuthenticatorFactory::class,
                 ],
             ],
-            'db_row'     => [
+            'db_row'          => [
                 'aliases'    => [
                     BaseUser::class => User::class,
                 ],
@@ -127,7 +146,7 @@ return [
                     User::class => UserFactory::class,
                 ],
             ],
-            'ils_driver' => [
+            'ils_driver'      => [
                 'aliases'   => [
                     'fid' => Fid::class,
                 ],
@@ -135,13 +154,13 @@ return [
                     Fid::class => FidFactory::class,
                 ],
             ],
-            'recordtab' => [
+            'recordtab'       => [
                 'invokables' => [
                     'worldcat' => 'fid\RecordTab\Worldcat',
                 ],
             ],
             'resolver_driver' => [
-                'factories' => [
+                'factories'  => [
                     Ezb::class =>
                         DriverWithHttpClientFactory::class,
                 ],
@@ -150,20 +169,20 @@ return [
                         EzbDelegatorFactory::class,
                     ],
                 ],
-                'aliases' => [
+                'aliases'    => [
                     'VuFind\Resolver\Driver\Ezb' => Ezb::class,
                 ],
             ],
         ],
     ],
     // Authorization configuration:
-    'zfc_rbac' => [
+    'zfc_rbac'           => [
         'vufind_permission_provider_manager' => [
             'factories' => [
                 'fid\Role\PermissionProvider\FidApiPermission' =>
                     'fid\Role\PermissionProvider\Factory::getFidApiPermission'
             ],
-            'aliases' => [
+            'aliases'   => [
                 'FidApiPermission' => 'fid\Role\PermissionProvider\FidApiPermission'
             ]
         ]
@@ -177,7 +196,7 @@ return [
                     'route' => '/fid',
                 ],
                 'child_routes'  => [
-                    'user' => [
+                    'user'  => [
                         'may_terminate' => false,
                         'type'          => 'literal',
                         'options'       => [
@@ -274,17 +293,27 @@ return [
                                         'action'     => 'updateUsername',
                                     ],
                                 ],
-                            ]
+                            ],
+                            'orders'  => [
+                                'type'    => 'literal',
+                                'options' => [
+                                    'route'    => '/orders',
+                                    'defaults' => [
+                                        'controller' => UserController::class,
+                                        'action'     => 'orders',
+                                    ],
+                                ],
+                            ],
                         ],
                     ],
-                    'admin'          => [
+                    'admin' => [
                         'may_terminate' => false,
                         'type'          => 'literal',
                         'options'       => [
                             'route' => '/admin'
                         ],
                         'child_routes'  => [
-                            'list'          => [
+                            'list' => [
                                 'type'    => 'literal',
                                 'options' => [
                                     'route'    => '/list',
@@ -294,7 +323,7 @@ return [
                                     ],
                                 ],
                             ],
-                            'edit'          => [
+                            'edit' => [
                                 'type'    => 'Zend\Router\Http\Segment',
                                 'options' => [
                                     'route'    => '/edit/[:userid]',
@@ -327,4 +356,11 @@ return [
             ]
         ],
     ],
+    'symfony_serializer' => [
+        'normalizers' => [
+            // TODO: add DateTimeNormalizer to finc/symfony-zend-serializer-bridge
+            DateTimeNormalizer::class                 => 2000,
+            OrderCreationRequestUserNormalizer::class => 1000,
+        ],
+    ],
 ];
diff --git a/module/fid/config/user-create-form.php b/module/fid/config/user-create-form.php
index 0e398fe9c80578edf43232c4f99532aec508c2fd..3d4969eae965f83a3a66fd148890e89cb4587887 100644
--- a/module/fid/config/user-create-form.php
+++ b/module/fid/config/user-create-form.php
@@ -19,20 +19,17 @@
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-use fid\FormModel\AddressValidator;
+use fid\Hydrator\UserHydrator;
+use fid\InputFilter\RootAwareBaseInputFilter;
 use Zend\Filter\Boolean;
 use Zend\Filter\StringTrim;
-use Zend\Filter\ToInt;
-use Zend\Filter\ToNull;
 use Zend\Form\Element\Checkbox;
-use Zend\Form\Element\Number;
 use Zend\Form\Element\Password;
 use Zend\Form\Element\Radio;
 use Zend\Form\Element\Select;
 use Zend\Form\Element\Submit;
 use Zend\Form\Element\Text;
-use Zend\Hydrator\ClassMethods;
-use Zend\Validator\Callback;
+use Zend\Form\InputFilterProviderFieldset;
 use Zend\Validator\EmailAddress;
 use Zend\Validator\Identical;
 use Zend\Validator\NotEmpty;
@@ -40,9 +37,10 @@ use Zend\Validator\Regex;
 use Zend\Validator\StringLength;
 
 return [
-    'hydrator'     => ClassMethods::class,
+    'name'         => 'user-create-form',
+    'hydrator'     => UserHydrator::class,
     'elements'     => [
-        'username'             => [
+        'username'              => [
             'spec' => [
                 'name'       => 'username',
                 'type'       => Text::class,
@@ -50,11 +48,11 @@ return [
                     'label' => 'label_username',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'password'             => [
+        'password'              => [
             'spec' => [
                 'name'       => 'password',
                 'type'       => Password::class,
@@ -62,23 +60,23 @@ return [
                     'label' => 'label_password',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'passwordConfirmation' => [
+        'password_confirmation' => [
             'spec' => [
-                'name'       => 'passwordConfirmation',
+                'name'       => 'password_confirmation',
                 'type'       => Password::class,
                 'options'    => [
                     'label' => 'label_password_confirmation',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'salutation'           => [
+        'salutation'            => [
             'spec' => [
                 'name'    => 'salutation',
                 'type'    => Select::class,
@@ -92,22 +90,22 @@ return [
                         'mrs' => [
                             'value' => 'mrs',
                             'label' => 'label_salutation_mrs',
-                        ]
+                        ],
                     ],
                     'empty_option' => '',
                 ],
             ],
         ],
-        'academicTitle'        => [
+        'academic_title'        => [
             'spec' => [
-                'name'    => 'academicTitle',
+                'name'    => 'academic_title',
                 'type'    => Text::class,
                 'options' => [
                     'label' => 'label_academic_title',
                 ],
             ],
         ],
-        'firstname'            => [
+        'firstname'             => [
             'spec' => [
                 'name'       => 'firstname',
                 'type'       => Text::class,
@@ -115,11 +113,11 @@ return [
                     'label' => 'label_firstname',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'lastname'             => [
+        'lastname'              => [
             'spec' => [
                 'name'       => 'lastname',
                 'type'       => Text::class,
@@ -127,121 +125,65 @@ return [
                     'label' => 'label_lastname',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'homeLibrary'          => [
+        'home_library'          => [
             'spec' => [
-                'name'       => 'homeLibrary',
+                'name'       => 'home_library',
                 'type'       => Select::class,
                 'options'    => [
                     'label'        => 'label_home_library',
                     'empty_option' => '',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'accessLevel'          => [
+        'job_title'             => [
             'spec' => [
-                'name'       => 'accessLevel',
+                'name'       => 'job_title',
                 'type'       => Radio::class,
                 'options'    => [
-                    'label' => 'label_access_level',
+                    'label'         => 'label_job_title',
+                    'value_options' => [
+                        'job_title_0',
+                        'job_title_1'
+                    ],
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'yearOfBirth'          => [
-            'spec' => [
-                'name'       => 'yearOfBirth',
-                'type'       => Number::class,
-                'options'    => [
-                    'label' => 'label_year_of_birth',
-                ],
-                'attributes' => [
-                    'min' => 1900,
-                    'max' => 2018,
-                ],
-            ]
-        ],
-        'jobTitle'             => [
-            'spec' => [
-                'name'    => 'jobTitle',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_job_title',
-                ],
-            ],
-        ],
-        'college'              => [
+        'college'               => [
             'spec' => [
                 'name'    => 'college',
                 'type'    => Text::class,
                 'options' => [
                     'label' => 'label_college',
-                ]
-            ],
-        ],
-        'addressLine1'         => [
-            'spec' => [
-                'name'    => 'addressLine1',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_address_line_1',
-                ]
-            ],
-        ],
-        'addressLine2'         => [
-            'spec' => [
-                'name'    => 'addressLine2',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_address_line_2',
-                ]
-            ],
-        ],
-        'addressZip'         => [
-            'spec' => [
-                'name'    => 'addressZip',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_address_zip',
-                ]
-            ],
-        ],
-        'addressCity'         => [
-            'spec' => [
-                'name'    => 'addressCity',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_address_city',
-                ]
+                ],
             ],
         ],
-        'addressCountry'         => [
+        'addresses'             => require 'address-collection.php',
+        'data'                  => [
             'spec' => [
-                'name'    => 'addressCountry',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_address_country',
-                ]
+                'name' => 'data',
+                'type' => InputFilterProviderFieldset::class,
             ],
         ],
-        'eulaAccepted'         => [
+        'eula_accepted'         => [
             'spec' => [
-                'name'       => 'eulaAccepted',
+                'name'       => 'eula_accepted',
                 'type'       => Checkbox::class,
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'submit'               => [
+        'submit'                => [
             'spec' => [
                 'name'       => 'submit',
                 'type'       => Submit::class,
@@ -252,9 +194,9 @@ return [
         ],
     ],
     'input_filter' => [
-        'username'             => [
+        'type'                  => RootAwareBaseInputFilter::class,
+        'username'              => [
             'name'       => 'username',
-            'required'   => true,
             'filters'    => [
                 StringTrim::class => [
                     'name' => StringTrim::class,
@@ -272,9 +214,8 @@ return [
                 ],
             ],
         ],
-        'password'             => [
+        'password'              => [
             'name'       => 'password',
-            'required'   => true,
             'filters'    => [
                 StringTrim::class => [
                     'name' => StringTrim::class,
@@ -301,11 +242,19 @@ return [
                         ],
                     ],
                 ],
-                Identical::class    => [
+            ],
+        ],
+        'password_confirmation' => [
+            'name'       => 'password_confirmation',
+            'validators' => [
+                NotEmpty::class  => [
+                    'name' => NotEmpty::class,
+                ],
+                Identical::class => [
                     'name'    => Identical::class,
                     'options' => [
                         'strict'   => false,
-                        'token'    => 'passwordConfirmation',
+                        'token'    => 'password',
                         'messages' => [
                             Identical::NOT_SAME => 'error_password_confirmation',
                         ],
@@ -313,16 +262,7 @@ return [
                 ],
             ],
         ],
-        'passwordConfirmation' => [
-            'name'       => 'passwordConfirmation',
-            'required'   => true,
-            'validators' => [
-                NotEmpty::class => [
-                    'name' => NotEmpty::class,
-                ],
-            ],
-        ],
-        'salutation'           => [
+        'salutation'            => [
             'name'     => 'salutation',
             'required' => false,
             'filters'  => [
@@ -331,10 +271,10 @@ return [
                 ],
             ],
         ],
-        'academicTitle'        => [
-            'name'     => 'academicTitle',
-            'required' => false,
-            'filters'  => [
+        'academic_title'        => [
+            'name'       => 'academic_title',
+            'required'   => false,
+            'filters'    => [
                 StringTrim::class => [
                     'name' => StringTrim::class,
                 ],
@@ -344,11 +284,11 @@ return [
                     'name'    => StringLength::class,
                     'options' => [
                         'max' => 255
-                    ]
+                    ],
                 ],
             ],
         ],
-        'firstname'            => [
+        'firstname'             => [
             'name'       => 'firstname',
             'required'   => true,
             'filters'    => [
@@ -371,7 +311,7 @@ return [
                 ],
             ],
         ],
-        'lastname'             => [
+        'lastname'              => [
             'name'       => 'lastname',
             'required'   => true,
             'filters'    => [
@@ -384,7 +324,7 @@ return [
                     'name'    => StringLength::class,
                     'options' => [
                         'max' => 255
-                    ]
+                    ],
                 ],
                 Regex::class        => [
                     'name'    => Regex::class,
@@ -394,128 +334,29 @@ return [
                 ],
             ],
         ],
-        'homeLibrary'          => [
-            'name'     => 'homeLibrary',
-            'required' => true,
-            'filters'  => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'accessLevel'          => [
-            'name'     => 'accessLevel',
-            'required' => true,
-            'filters'  => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-        ],
-        'yearOfBirth'          => [
-            'name'     => 'yearOfBirth',
-            'required' => false,
-            'filters'  => [
-                ToNull::class => [
-                    'name' => ToNull::class,
-                ],
-                ToInt::class  => [
-                    'name' => ToInt::class,
-                ],
-            ],
-        ],
-        'jobTitle'             => [
-            'name'     => 'jobTitle',
-            'required' => false,
-            'filters'  => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'college'              => [
-            'name'     => 'college',
-            'required' => false,
-            'filters'  => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'addressLine1'         => [
-            'name'       => 'addressLine1',
+        'home_library'          => [
+            'name'       => 'home_library',
+            'required'   => true,
             'filters'    => [
                 StringTrim::class => [
                     'name' => StringTrim::class,
                 ],
             ],
             'validators' => [
-                NotEmpty::class => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class.'::init',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
                 StringLength::class => [
                     'name'    => StringLength::class,
                     'options' => [
                         'max' => 255
-                    ]
+                    ],
                 ],
             ],
         ],
-        'addressLine2' => [
-            'name'       => 'addressLine2',
-            'required'   => false,
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
+        'job_title'             => [
+            'name'     => 'job_title',
+            'required' => true,
         ],
-        'addressZip' => [
-            'name'       => 'addressZip',
+        'college'               => [
+            'name'       => 'college',
             'required'   => false,
             'filters'    => [
                 StringTrim::class => [
@@ -523,93 +364,16 @@ return [
                 ],
             ],
             'validators' => [
-                NotEmpty::class => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class.'::init',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'addressCity' => [
-            'name'       => 'addressCity',
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                NotEmpty::class => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class.'::init',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
-                    ],
-                ],
                 StringLength::class => [
                     'name'    => StringLength::class,
                     'options' => [
                         'max' => 255
-                    ]
-                ],
-            ],
-        ],
-        'addressCountry' => [
-            'name'       => 'addressCountry',
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-            'validators' => [
-                NotEmpty::class => [
-                    'name'    => NotEmpty::class,
-                    'options' => [
-                        'type' => NotEmpty::NULL,
-                    ],
-                ],
-                Callback::class => [
-                    'name'    => Callback::class,
-                    'options' => [
-                        'callback' => AddressValidator::class.'::init',
-                        'messages' => [
-                            Callback::INVALID_VALUE => 'error_empty_address_value',
-                        ],
                     ],
                 ],
-                StringLength::class => [
-                    'name'    => StringLength::class,
-                    'options' => [
-                        'max' => 255
-                    ]
-                ],
             ],
         ],
-        'eulaAccepted'         => [
-            'name'       => 'eulaAccepted',
+        'eula_accepted'         => [
+            'name'       => 'eula_accepted',
             'required'   => true,
             'filters'    => [
                 Boolean::class => [
@@ -627,7 +391,7 @@ return [
                 ],
             ],
         ],
-        'submit'               => [
+        'submit'                => [
             'name'     => 'submit',
             'required' => true,
         ],
diff --git a/module/fid/config/user-init-form.php b/module/fid/config/user-init-form.php
index 24a7a49a809cc36169ac9363aa6ce2763a62cc62..0718f0fa86d70aa22a2a36ef93f88a05be361d7c 100644
--- a/module/fid/config/user-init-form.php
+++ b/module/fid/config/user-init-form.php
@@ -19,13 +19,13 @@
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
+use fid\Hydrator\UserHydrator;
 use Zend\Filter\Boolean;
 use Zend\Filter\StringTrim;
 use Zend\Form\Element\Checkbox;
 use Zend\Form\Element\Email;
 use Zend\Form\Element\Submit;
 use Zend\Form\Element\Text;
-use Zend\Hydrator\ClassMethods;
 use Zend\Validator\EmailAddress;
 use Zend\Validator\Identical;
 use Zend\Validator\NotEmpty;
@@ -33,7 +33,8 @@ use Zend\Validator\Regex;
 use Zend\Validator\StringLength;
 
 return [
-    'hydrator'     => ClassMethods::class,
+    'name'         => 'user-init-form',
+    'hydrator'     => UserHydrator::class,
     'elements'     => [
         'username'             => [
             'spec' => [
@@ -43,19 +44,19 @@ return [
                     'label' => 'label_username',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'usernameConfirmation' => [
+        'username_confirmation' => [
             'spec' => [
-                'name'       => 'usernameConfirmation',
+                'name'       => 'username_confirmation',
                 'type'       => Text::class,
                 'options'    => [
                     'label' => 'label_username_confirmation',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
@@ -67,7 +68,7 @@ return [
                     'label' => 'label_firstname',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
@@ -79,16 +80,16 @@ return [
                     'label' => 'label_lastname',
                 ],
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
-        'eulaAccepted'         => [
+        'eula_accepted'         => [
             'spec' => [
-                'name'       => 'eulaAccepted',
+                'name'       => 'eula_accepted',
                 'type'       => Checkbox::class,
                 'attributes' => [
-                    'required' => 'required',
+                    'required' => true,
                 ],
             ],
         ],
@@ -123,8 +124,8 @@ return [
                 ],
             ],
         ],
-        'usernameConfirmation' => [
-            'name'       => 'usernameConfirmation',
+        'username_confirmation' => [
+            'name'       => 'username_confirmation',
             'required'   => true,
             'validators' => [
                 Identical::class => [
@@ -185,8 +186,8 @@ return [
                 ],
             ],
         ],
-        'eulaAccepted'         => [
-            'name'       => 'eulaAccepted',
+        'eula_accepted'         => [
+            'name'       => 'eula_accepted',
             'required'   => true,
             'filters'    => [
                 Boolean::class => [
@@ -205,8 +206,8 @@ return [
             ],
         ],
         'submit'               => [
-            'name'       => 'submit',
-            'required'   => true,
+            'name'     => 'submit',
+            'required' => true,
         ],
     ],
 ];
\ No newline at end of file
diff --git a/module/fid/config/user-update-form.php b/module/fid/config/user-update-form.php
index b7c70903baa2a2cbb51401bff7cb9379ca02b866..067e4686eb64d314d61c8eee0e8fd5a546053b16 100644
--- a/module/fid/config/user-update-form.php
+++ b/module/fid/config/user-update-form.php
@@ -19,33 +19,28 @@
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-use Zend\Filter\Callback;
+use fid\Hydrator\UserHydrator;
+use fid\InputFilter\RootAwareBaseInputFilter;
 use Zend\Filter\StringTrim;
-use Zend\Filter\ToInt;
-use Zend\Filter\ToNull;
-use Zend\Form\Element\Collection;
 use Zend\Form\Element\Hidden;
-use Zend\Form\Element\MultiCheckbox;
-use Zend\Form\Element\Number;
 use Zend\Form\Element\Select;
 use Zend\Form\Element\Submit;
 use Zend\Form\Element\Text;
-use Zend\Hydrator\ClassMethods;
-use Zend\Validator\Explode;
-use Zend\Validator\InArray;
+use Zend\Form\InputFilterProviderFieldset;
 use Zend\Validator\Regex;
 use Zend\Validator\StringLength;
 
 return [
-    'hydrator'     => ClassMethods::class,
+    'name'         => 'user-update-form',
+    'hydrator'     => UserHydrator::class,
     'elements'     => [
-        'userId'   => [
+        'id'             => [
             'spec' => [
-                'name'       => 'userId',
-                'type'       => Hidden::class,
+                'name' => 'id',
+                'type' => Hidden::class,
             ],
         ],
-        'salutation'    => [
+        'salutation'     => [
             'spec' => [
                 'name'    => 'salutation',
                 'type'    => Select::class,
@@ -65,66 +60,52 @@ return [
                 ],
             ],
         ],
-        'academicTitle' => [
+        'academic_title' => [
             'spec' => [
-                'name'    => 'academicTitle',
+                'name'    => 'academic_title',
                 'type'    => Text::class,
                 'options' => [
                     'label' => 'label_academic_title',
                 ],
             ],
         ],
-        'firstname'     => [
+        'firstname'      => [
             'spec' => [
                 'name'       => 'firstname',
                 'type'       => Text::class,
                 'options'    => [
                     'label' => 'label_firstname',
                 ],
+                'attributes' => [
+                    'required' => true,
+                ],
             ],
         ],
-        'lastname'      => [
+        'lastname'       => [
             'spec' => [
                 'name'       => 'lastname',
                 'type'       => Text::class,
                 'options'    => [
                     'label' => 'label_lastname',
                 ],
-            ],
-        ],
-        'homeLibrary'   => [
-            'spec' => [
-                'name'       => 'homeLibrary',
-                'type'       => Select::class,
-                'options'    => [
-                    'label'        => 'label_home_library',
-                    'empty_option' => '',
+                'attributes' => [
+                    'required' => true,
                 ],
             ],
         ],
-        'yearOfBirth'   => [
+        'home_library'   => [
             'spec' => [
-                'name'       => 'yearOfBirth',
-                'type'       => Number::class,
+                'name'       => 'home_library',
+                'type'       => Select::class,
                 'options'    => [
-                    'label' => 'label_year_of_birth',
+                    'label' => 'label_home_library',
                 ],
                 'attributes' => [
-                    'min' => 1900,
-                    'max' => 2018,
-                ],
-            ]
-        ],
-        'jobTitle'      => [
-            'spec' => [
-                'name'    => 'jobTitle',
-                'type'    => Text::class,
-                'options' => [
-                    'label' => 'label_job_title',
+                    'required' => true,
                 ],
             ],
         ],
-        'college'       => [
+        'college'        => [
             'spec' => [
                 'name'    => 'college',
                 'type'    => Text::class,
@@ -133,33 +114,30 @@ return [
                 ]
             ],
         ],
-        'permissions'   => [
+        'addresses'      => require 'address-collection.php',
+        'data'           => [
             'spec' => [
-                'name'       => 'permissions',
-                'type'       => MultiCheckbox::class,
-                'options'    => [
-                    'label'        => 'label_permissions',
-                    'checked_value' => 'granted',
-                    'unchecked_value' => 'denied',
-                ],
+                'name' => 'data',
+                'type' => InputFilterProviderFieldset::class,
             ],
         ],
-        'submit'        => [
+        'submit'         => [
             'spec' => [
                 'name'       => 'submit',
                 'type'       => Submit::class,
                 'attributes' => [
-                    'value' => 'label_submit',
+                    'value' => 'label_update_submit',
                 ],
             ],
         ],
     ],
-    'fieldsets' => [
-        'addresses'     => require_once 'address-collection.php',
-    ],
     'input_filter' => [
-        'addresses' => require_once 'address-input-filter.php',
-        'salutation'    => [
+        'type'           => RootAwareBaseInputFilter::class,
+        'id'             => [
+            'name'     => 'id',
+            'required' => true,
+        ],
+        'salutation'     => [
             'name'     => 'salutation',
             'required' => false,
             'filters'  => [
@@ -168,8 +146,8 @@ return [
                 ],
             ],
         ],
-        'academicTitle' => [
-            'name'     => 'academicTitle',
+        'academic_title' => [
+            'name'     => 'academic_title',
             'required' => false,
             'filters'  => [
                 StringTrim::class => [
@@ -177,7 +155,7 @@ return [
                 ],
             ],
         ],
-        'firstname'     => [
+        'firstname'      => [
             'name'       => 'firstname',
             'required'   => false,
             'filters'    => [
@@ -200,7 +178,7 @@ return [
                 ],
             ],
         ],
-        'lastname'      => [
+        'lastname'       => [
             'name'       => 'lastname',
             'required'   => false,
             'filters'    => [
@@ -223,37 +201,16 @@ return [
                 ],
             ],
         ],
-        'homeLibrary'   => [
-            'name'       => 'homeLibrary',
-            'required'   => false,
-            'filters'    => [
-                StringTrim::class => [
-                    'name' => StringTrim::class,
-                ],
-            ],
-        ],
-        'yearOfBirth'   => [
-            'name'     => 'yearOfBirth',
-            'required' => false,
-            'filters'  => [
-                ToNull::class => [
-                    'name' => ToNull::class,
-                ],
-                ToInt::class  => [
-                    'name' => ToInt::class,
-                ],
-            ],
-        ],
-        'jobTitle'      => [
-            'name'     => 'jobTitle',
-            'required' => false,
+        'home_library'   => [
+            'name'     => 'home_library',
+            'required' => true,
             'filters'  => [
                 StringTrim::class => [
                     'name' => StringTrim::class,
                 ],
             ],
         ],
-        'college'       => [
+        'college'        => [
             'name'     => 'college',
             'required' => false,
             'filters'  => [
@@ -262,38 +219,9 @@ return [
                 ],
             ],
         ],
-        'permissions'   => [
-            'name'      => 'permissions',
-            'required'  => false,
-            'validators' => [
-                Explode::class => [
-                    'name' => Explode::class,
-                    'options' => [
-                        'validator' => [
-                            'name'    => InArray::class,
-                            'options' => [
-                                'haystack' => [
-                                    'granted',
-                                    'denied',
-                                    'requested'
-                                ],
-                            ]
-                        ],
-                    ],
-                ],
-            ],
-            'filters'  => [
-                Callback::class => [
-                    'name' => Callback::class,
-                    'options' => [
-                        'callback' => '\fid\Controller\UserController::combinePermissionsArray'
-                    ]
-                ],
-            ],
-        ],
-        'submit'               => [
-            'name'       => 'submit',
-            'required'   => true,
+        'submit'         => [
+            'name'     => 'submit',
+            'required' => true,
         ],
     ],
-];
+];
\ No newline at end of file
diff --git a/module/fid/src/Controller/UserController.php b/module/fid/src/Controller/UserController.php
index b6b0678ccc471651441995b17e3dbf9b8240ef51..4f32010a402b708e726e14de03dc54bc39703e87 100644
--- a/module/fid/src/Controller/UserController.php
+++ b/module/fid/src/Controller/UserController.php
@@ -24,21 +24,16 @@ namespace fid\Controller;
 
 use fid\FormModel\PasswordChangeModel;
 use fid\FormModel\PasswordResetModel;
-use fid\FormModel\UserCreateModel;
-use fid\FormModel\UserInitModel;
-use fid\FormModel\UserUpdateModel;
 use fid\FormModel\UsernameChangeModel;
 use fid\Service\Client;
 use fid\Service\ClientException;
-use fid\Service\DataTransferObject\Address;
 use fid\Service\DataTransferObject\Library;
 use fid\Service\DataTransferObject\User;
 use fid\Service\UserNotAuthorizedException;
-use Symfony\Component\Serializer\SerializerAwareTrait;
 use VuFind\Auth\Manager as AuthManager;
 use VuFind\Controller\AbstractBase;
 use VuFind\Exception\Auth as AuthException;
-use Zend\Form\Element\MultiCheckbox;
+use Zend\Form\Element\Checkbox;
 use Zend\Form\Element\Radio;
 use Zend\Form\Element\Select;
 use Zend\Form\Form;
@@ -50,8 +45,6 @@ use Zend\View\Model\ViewModel;
 
 class UserController extends AbstractBase
 {
-    use SerializerAwareTrait;
-
     /**
      * @var AuthManager
      */
@@ -93,9 +86,10 @@ class UserController extends AbstractBase
      */
     public function initAction()
     {
+        /** @var Form $form */
         /** @var Request $request */
         $request = $this->getRequest();
-        $form = $this->serviceLocator->get(UserInitModel::class);
+        $form = $this->serviceLocator->get('user-init-form');
         $forwarded = $this->params()->fromRoute('forwarded', false);
 
         if ($submitted = $this->formWasSubmitted()) {
@@ -105,6 +99,10 @@ class UserController extends AbstractBase
             }
         }
 
+        $action = $this->url()->fromRoute('fid/user/init');
+        $form->setAttribute('action', $action);
+        $form->prepare();
+
         $view = $this->createViewModel();
         $view->setVariables(compact('form'));
         $view->setTemplate('fid/user/init');
@@ -112,13 +110,60 @@ class UserController extends AbstractBase
         return $view;
     }
 
+    /**
+     * @param Form $form
+     *
+     * @return mixed|Response
+     */
+    protected function init(Form $form)
+    {
+        /** @var User $user */
+        $messenger = $this->getMessenger();
+        $user = $form->getHydrator()->hydrate($form->getData(), new User());
+
+        /** @noinspection PhpUndefinedFieldInspection */
+        $query['lng'] = $this->layout()->userLang;
+        $username = $query['username'] = $user->getUsername();
+        $firstname = $query['firstname'] = $user->getFirstname();
+        $lastname = $query['lastname'] = $user->getLastname();
+        $baseUrl = $this->url()->fromRoute('fid/user/create',
+            [], ['query' => $query, 'force_canonical' => true]);
+
+        try {
+            $this->client->requestRegistrationLink($baseUrl, $username,
+                $firstname, $lastname);
+        } catch (ClientException $exception) {
+            $message = $exception->getCode() === 400
+                ? 'fid::user_init_error_username'
+                : 'fid::user_init_error';
+            $messenger->addErrorMessage($this->translate($message));
+            return $this->forward()->dispatch(self::class, [
+                'action'    => 'init',
+                'forwarded' => true
+            ]);
+        }
+
+        $message = $this->translate('fid::user_init_success');
+        $messenger->addSuccessMessage(sprintf($message, $username));
+        return $this->redirect()->toRoute('myresearch-home');
+    }
+
+    protected function getMessenger(): FlashMessenger
+    {
+        /** @noinspection PhpUndefinedMethodInspection */
+        /** @var FlashMessenger $messenger */
+        return $this->flashMessenger();
+    }
+
     /**
      * @noinspection PhpUnused
      * @return Response|ViewModel
      */
     public function createAction()
     {
+        /** @var Form $form */
         /** @var Request $request */
+        /** @var Select $homeLibraryElement */
         $request = $this->getRequest();
         $query = $request->getQuery();
         $messenger = $this->getMessenger();
@@ -150,21 +195,10 @@ class UserController extends AbstractBase
             return $this->redirect()->toRoute('fid/user/init');
         }
 
-        /** @var Form $form */
-        $form = $this->serviceLocator->get(UserCreateModel::class);
-
-        /** @var Select $homeLibraryElement */
-        $homeLibraryElement = $form->get('homeLibrary');
+        $form = $this->serviceLocator->get('user-create-form');
+        $homeLibraryElement = $form->get('home_library');
         $homeLibraryElement->setValueOptions($libraries);
 
-        /** @var Radio $accessLevelElement */
-        $accessLevelElement = $form->get('accessLevel');
-        $accessLevels = $this->config['Security']['access_levels'];
-        $accessLevelValueOptions = array_map(function ($accessLevel) {
-            return "label_access_level_$accessLevel";
-        }, array_combine($accessLevels, $accessLevels));
-        $accessLevelElement->setValueOptions($accessLevelValueOptions);
-
         if ($this->formWasSubmitted()) {
             $form->setData($request->getPost());
             if ($form->isValid()) {
@@ -174,6 +208,10 @@ class UserController extends AbstractBase
             $form->setData($query);
         }
 
+        $action = $this->url()->fromRoute('fid/user/create');
+        $form->setAttribute('action', $action);
+        $form->prepare();
+
         $view = $this->createViewModel();
         $view->setVariables(compact('form'));
         $view->setTemplate('fid/user/create');
@@ -181,17 +219,43 @@ class UserController extends AbstractBase
         return $view;
     }
 
+    protected function create(Form $form)
+    {
+        /** @var User $user */
+        $messenger = $this->getMessenger();
+        $user = $form->getHydrator()->hydrate($form->getData(), new User());
+
+        try {
+            $this->client->requestUserCreation($user);
+            $message = $this->translate('fid::user_create_success');
+            $messenger->addSuccessMessage($message);
+            /** @noinspection PhpParamsInspection */
+            $this->authManager->create($this->getRequest());
+        } catch (ClientException $exception) {
+            $message = $this->translate('fid::user_create_error');
+            $messenger->addErrorMessage($message);
+        } catch (AuthException $e) {
+            $message = $this->translate('fid::user_create_error_autologon');
+            $messenger->addWarningMessage($message);
+        }
+
+        return $this->redirect()->toRoute('myresearch-home', [], [
+            'query' => ['redirect' => false]
+        ]);
+    }
 
     /**
-     * @noinspection PhpUnused
      * @return Response|ViewModel
+     * @throws UserNotAuthorizedException
      */
     public function updateAction()
     {
+        /** @var Form $form */
         /** @var Request $request */
-        $request = $this->getRequest();
+        /** @var Select $homeLibraryElement */
 
         try {
+            $request = $this->getRequest();
             $user = $this->client->requestUserDetails();
             $libraries = array_map(function (Library $libary) {
                 return $libary->getLabel();
@@ -205,11 +269,8 @@ class UserController extends AbstractBase
             return $this->redirect()->toRoute('myresearch-home');
         }
 
-        /** @var Form $form */
-        $form = $this->serviceLocator->get(UserUpdateModel::class);
-
-        /** @var Select $homeLibraryElement */
-        $homeLibraryElement = $form->get('homeLibrary');
+        $form = $this->serviceLocator->get('user-update-form');
+        $homeLibraryElement = $form->get('home_library');
         $homeLibraryElement->setValueOptions($libraries);
 
         if ($this->formWasSubmitted()) {
@@ -217,15 +278,56 @@ class UserController extends AbstractBase
             if ($form->isValid()) {
                 return $this->update($form);
             }
+        } else {
+            $form->setData($form->getHydrator()->extract($user));
         }
 
+        $action = $this->url()->fromRoute('fid/user/update');
+        $form->setAttribute('action', $action);
+        $form->prepare();
+
         $viewModel = $this->createViewModel();
-        $viewModel->setVariables(compact('form', 'user'));
+        $viewModel->setVariables(compact('form'));
         $viewModel->setTemplate('fid/user/update');
 
         return $viewModel;
     }
 
+    /**
+     * @param Form   $form
+     * @param string $redirect
+     *
+     * @return Response
+     * @throws UserNotAuthorizedException
+     */
+    protected function update(Form $form, string $redirect = 'myresearch-home')
+    {
+        $data = $form->getData();
+        $messenger = $this->getMessenger();
+
+        try {
+            $user = $this->client->requestUserDetails($data['id']);
+            $form->getHydrator()->hydrate($data, $user);
+            $this->client->requestUserUpdate($user);
+            $message = $this->translate('fid::user_update_success');
+            $messenger->addSuccessMessage($message);
+        } catch (ClientException $exception) {
+            if (in_array($exception->getCode(), [403])) {
+                $message = $this->translate('fid::user_update_error_'
+                    . $exception->getCode());
+            } else {
+                $message = $this->translate('fid::user_update_error');
+            }
+
+            $messenger->addErrorMessage($message);
+        }
+
+        $this->client->flushUserList();
+        return $this->redirect()->toRoute($redirect, [], [
+            'query' => ['redirect' => false]
+        ]);
+    }
+
     /**
      * @noinspection PhpUnused
      * @return ViewModel
@@ -251,6 +353,36 @@ class UserController extends AbstractBase
         return $view;
     }
 
+    protected function changeUsername(Form $form)
+    {
+        $messenger = $this->getMessenger();
+        $model = $form->getHydrator()->hydrate(
+            $form->getData(), new UsernameChangeModel());
+
+        /** @noinspection PhpUndefinedFieldInspection */
+        $query['lng'] = $this->layout()->userLang;
+        $username = $query['username'] = $model->getUsername();
+        $baseUrl = $this->url()->fromRoute('fid/user/update-username',
+            [], ['query' => $query, 'force_canonical' => true]);
+
+        try {
+            $this->client->requestUsernameLink($baseUrl, $username);
+        } catch (ClientException $exception) {
+            $message = $exception->getCode() === 400
+                ? 'fid::username_change_error_username'
+                : 'fid::username_change_error';
+            $messenger->addErrorMessage($this->translate($message));
+            return $this->forward()->dispatch(self::class, [
+                'action'    => 'changeUsername',
+                'forwarded' => true
+            ]);
+        }
+
+        $message = $this->translate('fid::username_change_success');
+        $messenger->addSuccessMessage(sprintf($message, $username));
+        return $this->redirect()->toRoute('myresearch-home');
+    }
+
     /**
      * @noinspection PhpUnused
      * @return Response
@@ -344,6 +476,33 @@ class UserController extends AbstractBase
         return $view;
     }
 
+    protected function sendResetPassword(Form $form)
+    {
+        /** @var PasswordResetModel $model */
+        $messenger = $this->getMessenger();
+        $model = $form->getHydrator()->hydrate(
+            $form->getData(), new PasswordResetModel());
+        $username = $model->getUsername();
+
+        try {
+            /** @noinspection PhpUndefinedFieldInspection */
+            $query['lng'] = $this->layout()->userLang;
+            $baseUrl = $this->url()->fromRoute('fid/user/change-password',
+                [], ['query' => $query, 'force_canonical' => true]);
+            $this->client->requestPasswordLink($baseUrl, $username);
+            $message = $this->translate('fid::password_reset_success');
+            $messenger->addSuccessMessage(sprintf($message, $username));
+        } catch (ClientException $exception) {
+            $message = $exception->getCode() === 400
+                ? $this->translate('fid::password_reset_error_username')
+                : $this->translate('fid::password_reset_error');
+            $messenger->addErrorMessage(sprintf($message, $username));
+            return $this->redirect()->toRoute('fid/user/reset-password');
+        }
+
+        return $this->redirect()->toRoute('myresearch-home');
+    }
+
     /**
      * Reset password action - Allows the change password form to appear.
      *
@@ -391,178 +550,6 @@ class UserController extends AbstractBase
         return $view;
     }
 
-    /**
-     * @param Form $form
-     *
-     * @return mixed|Response
-     */
-    protected function init(Form $form)
-    {
-        /** @var UserInitModel $model */
-        $messenger = $this->getMessenger();
-        $model = $form->getHydrator()
-            ->hydrate($form->getData(), new UserInitModel());
-
-        /** @noinspection PhpUndefinedFieldInspection */
-        $query['lng'] = $this->layout()->userLang;
-        $username = $query['username'] = $model->getUsername();
-        $firstname = $query['firstname'] = $model->getFirstname();
-        $lastname = $query['lastname'] = $model->getLastname();
-        $baseUrl = $this->url()->fromRoute('fid/user/create',
-            [], ['query' => $query, 'force_canonical' => true]);
-
-        try {
-            $this->client->requestRegistrationLink($baseUrl, $username,
-                $firstname, $lastname);
-        } catch (ClientException $exception) {
-            $message = $exception->getCode() === 400
-                ? 'fid::user_init_error_username'
-                : 'fid::user_init_error';
-            $messenger->addErrorMessage($this->translate($message));
-            return $this->forward()->dispatch(self::class, [
-                'action'    => 'init',
-                'forwarded' => true
-            ]);
-        }
-
-        $message = $this->translate('fid::user_init_success');
-        $messenger->addSuccessMessage(sprintf($message, $username));
-        return $this->redirect()->toRoute('myresearch-home');
-    }
-
-    protected function create(Form $form)
-    {
-        $messenger = $this->getMessenger();
-        /** @var UserCreateModel $model */
-        $model = $form->getHydrator()
-            ->hydrate($form->getData(), new UserCreateModel());
-
-        $user = new User();
-        $user->setUsername($username = $model->getUsername());
-        $user->setPassword($password = $model->getPassword());
-        $user->setHomeLibrary($model->getHomeLibrary());
-        $user->setPermissions([$model->getAccessLevel() => 'requested']);
-        $user->setSalutation($model->getSalutation());
-        $user->setAcademicTitle($model->getAcademicTitle());
-        $user->setFirstname($model->getFirstname());
-        $user->setLastname($model->getLastname());
-        $user->setYearOfBirth($model->getYearOfBirth());
-        $user->setCollege($model->getCollege());
-        $user->setJobTitle($model->getJobTitle());
-
-        if ($model->getAddressLine1()) {
-            $address = new Address();
-            $address->setLine1($model->getAddressLine1());
-            $address->setLine2($model->getAddressLine2());
-            $address->setZip($model->getAddressZip());
-            $address->setCity($model->getAddressCity());
-            $address->setCountry($model->getAddressCountry());
-            $user->setAddresses([$address]);
-        }
-
-        try {
-            $this->client->requestUserCreation($user);
-            $message = $this->translate('fid::user_create_success');
-            $messenger->addSuccessMessage($message);
-            /** @noinspection PhpParamsInspection */
-            $this->authManager->create($this->getRequest());
-        } catch (ClientException $exception) {
-            $message = $this->translate('fid::user_create_error');
-            $messenger->addErrorMessage($message);
-        } catch (AuthException $e) {
-            $message = $this->translate('fid::user_create_error_autologon');
-            $messenger->addWarningMessage($message);
-        }
-
-        return $this->redirect()->toRoute('myresearch-home', [], [
-            'query' => ['redirect' => false]
-        ]);
-    }
-
-    /**
-     * @param Form $form
-     *
-     * @return Response
-     */
-    protected function update(Form $form,string $redirect = 'myresearch-home')
-    {
-        $messenger = $this->getMessenger();
-        /** @var UserUpdateModel $model */
-        $model = $form->getHydrator()->hydrate($form->getData(),
-            new UserUpdateModel());
-
-        try {
-            $userId = $model->getUserId();
-            $user = $this->client->requestUserDetails($userId);
-            $user->setHomeLibrary($model->getHomeLibrary());
-            $user->setSalutation($model->getSalutation());
-            $user->setAcademicTitle($model->getAcademicTitle());
-            $user->setFirstname($model->getFirstname());
-            $user->setLastname($model->getLastname());
-            $user->setYearOfBirth($model->getYearOfBirth());
-            $user->setCollege($model->getCollege());
-            $user->setJobTitle($model->getJobTitle());
-            $user->setPermissions($model->getPermissions());
-            $addresses = $user->getAddresses();
-            foreach ($model->getAddresses() as $index => $address) {
-                $addr = $addresses[$index] ?? new Address();
-                $addr->setLine1($address['line1']);
-                $addr->setLine2($address['line2']);
-                $addr->setZip($address['zip']);
-                $addr->setCity($address['city']);
-                $addr->setCountry($address['country']);
-                $addresses[$index] = $addr;
-            }
-            $user->setAddresses($addresses);
-            $this->client->requestUserUpdate($user);
-            $message = $this->translate('fid::user_update_success');
-            $messenger->addSuccessMessage($message);
-
-
-        } catch (ClientException $exception) {
-            if (in_array($exception->getCode(), [403])) {
-                $message = $this->translate('fid::user_update_error_'
-                    . $exception->getCode());
-            } else {
-                $message = $this->translate('fid::user_update_error');
-            }
-
-            $messenger->addErrorMessage($message);
-        }
-
-        $this->client->flushUserList();
-        return $this->redirect()->toRoute($redirect, [], [
-            'query' => ['redirect' => false]
-        ]);
-    }
-
-    protected function sendResetPassword(Form $form)
-    {
-        /** @var PasswordResetModel $model */
-        $messenger = $this->getMessenger();
-        $model = $form->getHydrator()->hydrate(
-            $form->getData(), new PasswordResetModel());
-        $username = $model->getUsername();
-
-        try {
-            /** @noinspection PhpUndefinedFieldInspection */
-            $query['lng'] = $this->layout()->userLang;
-            $baseUrl = $this->url()->fromRoute('fid/user/change-password',
-                [], ['query' => $query, 'force_canonical' => true]);
-            $this->client->requestPasswordLink($baseUrl, $username);
-            $message = $this->translate('fid::password_reset_success');
-            $messenger->addSuccessMessage(sprintf($message, $username));
-        } catch (ClientException $exception) {
-            $message = $exception->getCode() === 400
-                ? $this->translate('fid::password_reset_error_username')
-                : $this->translate('fid::password_reset_error');
-            $messenger->addErrorMessage(sprintf($message, $username));
-            return $this->redirect()->toRoute('fid/user/reset-password');
-        }
-
-        return $this->redirect()->toRoute('myresearch-home');
-    }
-
     protected function changePassword(Form $form)
     {
         /** @var PasswordChangeModel $model */
@@ -597,47 +584,9 @@ class UserController extends AbstractBase
         ]);
     }
 
-    protected function changeUsername(Form $form)
-    {
-        $messenger = $this->getMessenger();
-        $model = $form->getHydrator()->hydrate(
-            $form->getData(), new UsernameChangeModel());
-
-        /** @noinspection PhpUndefinedFieldInspection */
-        $query['lng'] = $this->layout()->userLang;
-        $username = $query['username'] = $model->getUsername();
-        $baseUrl = $this->url()->fromRoute('fid/user/update-username',
-            [], ['query' => $query, 'force_canonical' => true]);
-
-        try {
-            $this->client->requestUsernameLink($baseUrl, $username);
-        } catch (ClientException $exception) {
-            $message = $exception->getCode() === 400
-                ? 'fid::username_change_error_username'
-                : 'fid::username_change_error';
-            $messenger->addErrorMessage($this->translate($message));
-            return $this->forward()->dispatch(self::class, [
-                'action'    => 'changeUsername',
-                'forwarded' => true
-            ]);
-        }
-
-        $message = $this->translate('fid::username_change_success');
-        $messenger->addSuccessMessage(sprintf($message, $username));
-        return $this->redirect()->toRoute('myresearch-home');
-    }
-
-    protected function getMessenger(): FlashMessenger
-    {
-        /** @noinspection PhpUndefinedMethodInspection */
-        /** @var FlashMessenger $messenger */
-        return $this->flashMessenger();
-    }
-
     public function editAction()
     {
-        // Not logged in?  Force user to log in:
-        if (!($user = $this->getUser())) {
+        if (!$this->getUser()) {
             return $this->forceLogin();
         }
 
@@ -650,20 +599,24 @@ class UserController extends AbstractBase
             return $this->redirect()->toRoute('fid/admin/list');
         }
 
-        $editableFields = $this->config['Admin']['editable_user_fields'] ?? [];
-
         try {
             try {
                 $user = $this->client->requestUserDetails($userId);
             } catch (UserNotAuthorizedException $ex) {
                 // either user does not exist, or we are not allowed to edit it
-                $this->getMessenger()->addErrorMessage($this->translate('fid::user_edit_not_allowed', ['%%userid%%' => $userId]));
+                $this->getMessenger()
+                    ->addErrorMessage($this->translate('fid::user_edit_not_allowed',
+                        ['%%userid%%' => $userId]));
                 return $this->redirect()->toRoute('fid/admin/list');
             } catch (\Exception $ex) {
-                $this->getMessenger()->addErrorMessage($this->translate('fid::user_read_error',['%%userid%%' => $userId]));
+                $this->getMessenger()
+                    ->addErrorMessage($this->translate('fid::user_read_error',
+                        ['%%userid%%' => $userId]));
                 return $this->redirect()->toRoute('fid/admin/list');
             }
-            $libraries = $this->client->requestLibraryList();
+            $libraries = array_map(function (Library $libary) {
+                return $libary->getLabel();
+            }, $this->client->requestLibraryList());
         } catch (ClientException $exception) {
             $this->setFollowupUrlToReferer();
             $message = $exception->getCode() === 401
@@ -674,116 +627,76 @@ class UserController extends AbstractBase
         }
 
         /** @var Form $form */
-        $form = $this->serviceLocator->get(UserUpdateModel::class);
-
-        if (!empty($libraries)) {
-            $userLibrary = $user->getHomeLibrary();
-            $libraries = array_map(function (Library $libary) use ($editableFields,$userLibrary) {
-                $option = [
-                    'label' => $libary->getLabel(),
-                    'value' => $id = $libary->getId(),
-                    'selected' => $id == $userLibrary
-                ];
-                if ($id != $userLibrary && !in_array('HomeLibrary', $editableFields)) {
-                    $option['attributes']['disabled'] = 'disabled';
-                }
-                return $option;
-            }, $libraries);
-
-            /** @var Select $homeLibraryElement */
-            $homeLibraryElement = $form->get('homeLibrary');
-            $homeLibraryElement->setValueOptions($libraries);
-        }
-
-        if ($permissions = $this->config['Admin']['permission_options'] ?? []) {
-
-            $userPermissions = $user->getPermissions();
-            if (!isset(self::$currentPermissions)) {
-                $valid = [];
-                foreach ($permissions as $permission) {
-                    $valid[$permission] = $userPermissions[$permission] ?? 'denied';
-                }
-                self::$currentPermissions = $valid;
-            }
-            /** @var MultiCheckbox $permissionsElement */
-            $permissionsElement = $form->get('permissions');
-            $permissions = array_map(function ($permission) use ($editableFields,$userPermissions) {
-                $option = [
-                    'label' => $this->translate('fid::permission_' . $permission),
-                    'name' => $permission,
-                    'label_attributes' => ['class' => 'permission-label col-sm-11'],
-                    'attributes' => ['class' => 'permission-option col-sm-1'],
-                    'selected' => isset($userPermissions[$permission]) && $userPermissions[$permission] == 'granted',
-                    'value' => $permission . '::' . ($userPermissions[$permission] ?? 'denied'),
-                ];
-                if (!in_array('Permissions', $editableFields)) {
-                    $option['disabled'] = '1';
-                }
-                return $option;
-            }, $permissions);
-            $permissionsElement->setDisableInArrayValidator(true);
-            $permissionsElement->setValueOptions($permissions);
-        }
+        /** @var Select $homeLibraryElement */
+        $form = $this->serviceLocator->get('admin-edit-form');
+        $homeLibraryElement = $form->get('home_library');
+        $homeLibraryElement->setValueOptions($libraries);
+        $homeLibraryElement->setUnselectedValue($user->getHomeLibrary());
 
         if ($this->formWasSubmitted()) {
             $form->setData($request->getPost());
             if ($form->isValid()) {
-                return $this->update($form,'fid/admin/list');
+                return $this->update($form, 'fid/admin/list');
             }
+        } else {
+            $form->setData($form->getHydrator()->extract($user));
         }
 
+        $action = $this->url()->fromRoute('fid/admin/edit', [
+            'userid' => $userId
+        ]);
+        $form->setAttribute('action', $action);
+        $form->prepare();
+
+        $config = $this->config;
         $viewModel = $this->createViewModel();
-        $viewModel->setVariables(compact('form', 'user','editableFields'));
+        $viewModel->setVariables(compact('config', 'form', 'user'));
         $viewModel->setTemplate('fid/admin/edit');
 
         return $viewModel;
     }
 
-    public static $currentPermissions;
-
-    public static function combinePermissionsArray($input) {
-        if (empty($input)) {
-            return [];
-        }
-
-        $output = [];
-        foreach ($input as $line) {
-            list($key,) = explode('::',$line);
-            if (isset(self::$currentPermissions[$key])) {
-                $output[$key] = 'granted';
-            }
-        }
-        foreach (self::$currentPermissions as $key => $state) {
-            if ($state === 'granted' && !isset($output[$key])) {
-                $output[$key] = 'denied';
-            }
-        }
-        return array_intersect_key($output,['full_access'=>'0','limited_access'=>1]);
-    }
-
-    public function listAction() {
-
-        // Not logged in?  Force user to log in:
-        if (!($user = $this->getUser())) {
+    public function listAction()
+    {
+        if (!$this->getUser()) {
             return $this->forceLogin();
         }
 
         $viewModel = $this->createViewModel();
         try {
             $list = $this->client->requestUserList();
-            $viewModel->setVariable('list',$list);
+            $viewModel->setVariable('list', $list);
         } catch (UserNotAuthorizedException $exception) {
-            $this->getMessenger()->addErrorMessage('fid::read_user_list_not_allowed');
+            $this->getMessenger()
+                ->addErrorMessage('fid::read_user_list_not_allowed');
         } catch (ClientException $exception) {
             $this->getMessenger()->addErrorMessage('fid::read_user_list_error');
         }
 
         if ($fields = $this->config['Admin']['overview_fields']) {
-            $viewModel->setVariable('fields',$fields);
+            $viewModel->setVariable('fields', $fields);
         }
 
+        $viewModel->setVariable('config', $this->config);
         $viewModel->setTemplate('fid/admin/list');
         return $viewModel;
     }
 
+    public function ordersAction()
+    {
+        if (!$this->getUser()) {
+            return $this->forceLogin();
+        }
+        try {
+            $user = $this->client->requestUserDetails();
+            $viewModel = $this->createViewModel();
+            $viewModel->setVariable('orders',$user->getOrders());
+            $viewModel->setVariable('addresses',$user->getAddresses());
+            $viewModel->setTemplate('fid/user/orders');
+            return $viewModel;
+        } catch (ClientException $exception) {
+            $this->getMessenger()->addErrorMessage('fid::orders_error');
+            $this->redirect()->toRoute('myresearch-profile');
+        }
+    }
 }
diff --git a/module/fid/src/FormModel/UserCreateModel.php b/module/fid/src/FormModel/UserCreateModel.php
deleted file mode 100644
index 1ddbc526dad894899e173a372a67d1949c456284..0000000000000000000000000000000000000000
--- a/module/fid/src/FormModel/UserCreateModel.php
+++ /dev/null
@@ -1,426 +0,0 @@
-<?php
-/**
- * Copyright (C) 2019 Leipzig University Library
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
- * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
- */
-
-namespace fid\FormModel;
-
-class UserCreateModel
-{
-    /**
-     * @var string
-     */
-    protected $username;
-
-    /**
-     * @var string
-     */
-    protected $password;
-
-    /**
-     * @var string
-     */
-    protected $passwordConfirmation;
-
-    /**
-     * @var string
-     */
-    protected $salutation;
-
-    /**
-     * @var string
-     */
-    protected $academicTitle;
-
-    /**
-     * @var string
-     */
-    protected $firstname;
-
-    /**
-     * @var string
-     */
-    protected $lastname;
-
-    /**
-     * @var string
-     */
-    protected $homeLibrary;
-
-    /**
-     * @var string
-     */
-    protected $accessLevel;
-
-    /**
-     * @var int|null
-     */
-    protected $yearOfBirth;
-
-    /**
-     * @var string
-     */
-    protected $jobTitle;
-
-    /**
-     * @var string
-     */
-    protected $college;
-
-    /**
-     * @var string
-     */
-    protected $addressLine1;
-
-    /**
-     * @var string
-     */
-    protected $addressLine2;
-
-    /**
-     * @var string
-     */
-    protected $addressZip;
-
-    /**
-     * @var string
-     */
-    protected $addressCity;
-
-    /**
-     * @var string
-     */
-    protected $addressCountry;
-
-    /**
-     * @var bool
-     */
-    protected $eulaAccepted;
-
-    /**
-     * @var string
-     */
-    protected $submit;
-
-    /**
-     * @return string
-     */
-    public function getUsername(): string
-    {
-        return $this->username;
-    }
-
-    /**
-     * @param string $username
-     */
-    public function setUsername(string $username): void
-    {
-        $this->username = $username;
-    }
-
-    /**
-     * @return string
-     */
-    public function getFirstname(): string
-    {
-        return $this->firstname;
-    }
-
-    /**
-     * @param string $firstname
-     */
-    public function setFirstname(string $firstname): void
-    {
-        $this->firstname = $firstname;
-    }
-
-    /**
-     * @return string
-     */
-    public function getLastname(): string
-    {
-        return $this->lastname;
-    }
-
-    /**
-     * @param string $lastname
-     */
-    public function setLastname(string $lastname): void
-    {
-        $this->lastname = $lastname;
-    }
-
-    /**
-     * @return string
-     */
-    public function getSalutation(): string
-    {
-        return $this->salutation;
-    }
-
-    /**
-     * @param string $salutation
-     */
-    public function setSalutation(string $salutation): void
-    {
-        $this->salutation = $salutation;
-    }
-
-    /**
-     * @return string
-     */
-    public function getAcademicTitle(): string
-    {
-        return $this->academicTitle;
-    }
-
-    /**
-     * @param string $academicTitle
-     */
-    public function setAcademicTitle(string $academicTitle): void
-    {
-        $this->academicTitle = $academicTitle;
-    }
-
-    /**
-     * @return string
-     */
-    public function getHomeLibrary(): string
-    {
-        return $this->homeLibrary;
-    }
-
-    /**
-     * @param string $homeLibrary
-     */
-    public function setHomeLibrary(string $homeLibrary): void
-    {
-        $this->homeLibrary = $homeLibrary;
-    }
-
-    /**
-     * @return int|null
-     */
-    public function getYearOfBirth(): ?int
-    {
-        return $this->yearOfBirth;
-    }
-
-    /**
-     * @param int|null $yearOfBirth
-     */
-    public function setYearOfBirth(?int $yearOfBirth): void
-    {
-        $this->yearOfBirth = $yearOfBirth;
-    }
-
-
-    /**
-     * @return string
-     */
-    public function getAccessLevel(): string
-    {
-        return $this->accessLevel;
-    }
-
-    /**
-     * @param string $accessLevel
-     */
-    public function setAccessLevel(string $accessLevel): void
-    {
-        $this->accessLevel = $accessLevel;
-    }
-
-    /**
-     * @return bool
-     */
-    public function isEulaAccepted(): bool
-    {
-        return $this->eulaAccepted;
-    }
-
-    /**
-     * @param bool $eulaAccepted
-     */
-    public function setEulaAccepted(bool $eulaAccepted): void
-    {
-        $this->eulaAccepted = $eulaAccepted;
-    }
-
-    /**
-     * @return string
-     */
-    public function getSubmit(): string
-    {
-        return $this->submit;
-    }
-
-    /**
-     * @param string $submit
-     */
-    public function setSubmit(string $submit): void
-    {
-        $this->submit = $submit;
-    }
-
-    /**
-     * @return string
-     */
-    public function getJobTitle(): string
-    {
-        return $this->jobTitle;
-    }
-
-    /**
-     * @param string $jobTitle
-     */
-    public function setJobTitle(string $jobTitle): void
-    {
-        $this->jobTitle = $jobTitle;
-    }
-
-    /**
-     * @return string
-     */
-    public function getCollege(): string
-    {
-        return $this->college;
-    }
-
-    /**
-     * @param string $college
-     */
-    public function setCollege(string $college): void
-    {
-        $this->college = $college;
-    }
-
-    /**
-     * @return string
-     */
-    public function getPassword(): string
-    {
-        return $this->password;
-    }
-
-    /**
-     * @param string $password
-     */
-    public function setPassword(string $password): void
-    {
-        $this->password = $password;
-    }
-
-    /**
-     * @return string
-     */
-    public function getPasswordConfirmation(): string
-    {
-        return $this->passwordConfirmation;
-    }
-
-    /**
-     * @param string $passwordConfirmation
-     */
-    public function setPasswordConfirmation(string $passwordConfirmation): void
-    {
-        $this->passwordConfirmation = $passwordConfirmation;
-    }
-
-    /**
-     * @return string
-     */
-    public function getAddressLine1(): string
-    {
-        return $this->addressLine1;
-    }
-
-    /**
-     * @param string $addressLine1
-     */
-    public function setAddressLine1(string $addressLine1): void
-    {
-        $this->addressLine1 = $addressLine1;
-    }
-
-    /**
-     * @return string
-     */
-    public function getAddressLine2(): string
-    {
-        return $this->addressLine2;
-    }
-
-    /**
-     * @param string $addressLine2
-     */
-    public function setAddressLine2(string $addressLine2): void
-    {
-        $this->addressLine2 = $addressLine2;
-    }
-
-    /**
-     * @return string
-     */
-    public function getAddressZip(): string
-    {
-        return $this->addressZip;
-    }
-
-    /**
-     * @param string $addressZip
-     */
-    public function setAddressZip(string $addressZip): void
-    {
-        $this->addressZip = $addressZip;
-    }
-
-    /**
-     * @return string
-     */
-    public function getAddressCity(): string
-    {
-        return $this->addressCity;
-    }
-
-    /**
-     * @param string $addressCity
-     */
-    public function setAddressCity(string $addressCity): void
-    {
-        $this->addressCity = $addressCity;
-    }
-
-    /**
-     * @return string
-     */
-    public function getAddressCountry(): string
-    {
-        return $this->addressCountry;
-    }
-
-    /**
-     * @param string $addressCountry
-     */
-    public function setAddressCountry(string $addressCountry): void
-    {
-        $this->addressCountry = $addressCountry;
-    }
-}
diff --git a/module/fid/src/FormModel/UserInitModel.php b/module/fid/src/FormModel/UserInitModel.php
deleted file mode 100644
index 6d48255e633f5a55cced7f8c63386a1c37ff30d2..0000000000000000000000000000000000000000
--- a/module/fid/src/FormModel/UserInitModel.php
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-/**
- * Copyright (C) 2019 Leipzig University Library
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
- * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
- */
-
-namespace fid\FormModel;
-
-class UserInitModel
-{
-    /**
-     * @var string
-     */
-    protected $username;
-
-    /**
-     * @var string
-     */
-    protected $usernameConfirmation;
-
-    /**
-     * @var string
-     */
-    protected $firstname;
-
-    /**
-     * @var string
-     */
-    protected $lastname;
-
-    /**
-     * @var bool
-     */
-    protected $eulaAccepted;
-
-    /**
-     * @var string
-     */
-    protected $submit;
-
-    /**
-     * @return string
-     */
-    public function getUsername(): string
-    {
-        return $this->username;
-    }
-
-    /**
-     * @param string $username
-     */
-    public function setUsername(string $username): void
-    {
-        $this->username = $username;
-    }
-
-    /**
-     * @return string
-     */
-    public function getUsernameConfirmation(): string
-    {
-        return $this->usernameConfirmation;
-    }
-
-    /**
-     * @param string $usernameConfirmation
-     */
-    public function setUsernameConfirmation(string $usernameConfirmation): void
-    {
-        $this->usernameConfirmation = $usernameConfirmation;
-    }
-
-    /**
-     * @return string
-     */
-    public function getFirstname(): string
-    {
-        return $this->firstname;
-    }
-
-    /**
-     * @param string $firstname
-     */
-    public function setFirstname(string $firstname): void
-    {
-        $this->firstname = $firstname;
-    }
-
-    /**
-     * @return string
-     */
-    public function getLastname(): string
-    {
-        return $this->lastname;
-    }
-
-    /**
-     * @param string $lastname
-     */
-    public function setLastname(string $lastname): void
-    {
-        $this->lastname = $lastname;
-    }
-
-    /**
-     * @return bool
-     */
-    public function isEulaAccepted(): bool
-    {
-        return $this->eulaAccepted;
-    }
-
-    /**
-     * @param bool $eulaAccepted
-     */
-    public function setEulaAccepted(bool $eulaAccepted): void
-    {
-        $this->eulaAccepted = $eulaAccepted;
-    }
-
-    /**
-     * @return string
-     */
-    public function getSubmit(): string
-    {
-        return $this->submit;
-    }
-
-    /**
-     * @param string $submit
-     */
-    public function setSubmit(string $submit): void
-    {
-        $this->submit = $submit;
-    }
-}
diff --git a/module/fid/src/FormModel/UserUpdateModel.php b/module/fid/src/FormModel/UserUpdateModel.php
deleted file mode 100644
index 6366da868a461ce38c818f76c6acffa12c669eda..0000000000000000000000000000000000000000
--- a/module/fid/src/FormModel/UserUpdateModel.php
+++ /dev/null
@@ -1,278 +0,0 @@
-<?php
-/**
- * Copyright (C) 2019 Leipzig University Library
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
- * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
- */
-
-namespace fid\FormModel;
-
-class UserUpdateModel
-{
-    /**
-     * @var string
-     */
-    protected $userId;
-
-    /**
-     * @var string
-     */
-    protected $salutation;
-
-    /**
-     * @var string
-     */
-    protected $academicTitle;
-
-    /**
-     * @var string
-     */
-    protected $firstname;
-
-    /**
-     * @var string
-     */
-    protected $lastname;
-
-    /**
-     * @var string
-     */
-    protected $homeLibrary;
-
-    /**
-     * @var int|null
-     */
-    protected $yearOfBirth;
-
-    /**
-     * @var string
-     */
-    protected $jobTitle;
-
-    /**
-     * @var string
-     */
-    protected $college;
-
-    /**
-     * @var string[][]
-     */
-    protected $addresses = [];
-
-    /**
-     * @var array
-     */
-    protected $permissions;
-
-    /**
-     * @var string|null
-     */
-    protected $submit;
-
-    /**
-     * @return string|null
-     */
-    public function getUserId(): ?string
-    {
-        return $this->userId;
-    }
-
-    /**
-     * @param string $userId
-     */
-    public function setUserId(string $userId = null): void
-    {
-        $this->userId = $userId;
-    }
-
-    /**
-     * @return string
-     */
-    public function getSalutation(): ?string
-    {
-        return $this->salutation;
-    }
-
-    /**
-     * @param string $salutation
-     */
-    public function setSalutation(string $salutation): void
-    {
-        $this->salutation = $salutation;
-    }
-
-    /**
-     * @return string|null
-     */
-    public function getAcademicTitle(): ?string
-    {
-        return $this->academicTitle;
-    }
-
-    /**
-     * @param string $academicTitle
-     */
-    public function setAcademicTitle(string $academicTitle): void
-    {
-        $this->academicTitle = $academicTitle;
-    }
-
-    /**
-     * @return string
-     */
-    public function getFirstname(): string
-    {
-        return $this->firstname;
-    }
-
-    /**
-     * @param string $firstname
-     */
-    public function setFirstname(string $firstname): void
-    {
-        $this->firstname = $firstname;
-    }
-
-    /**
-     * @return string
-     */
-    public function getLastname(): string
-    {
-        return $this->lastname;
-    }
-
-    /**
-     * @param string $lastname
-     */
-    public function setLastname(string $lastname): void
-    {
-        $this->lastname = $lastname;
-    }
-
-    /**
-     * @return string
-     */
-    public function getHomeLibrary(): string
-    {
-        return $this->homeLibrary;
-    }
-
-    /**
-     * @param string $homeLibrary
-     */
-    public function setHomeLibrary(string $homeLibrary): void
-    {
-        $this->homeLibrary = $homeLibrary;
-    }
-
-    /**
-     * @return int|null
-     */
-    public function getYearOfBirth(): ?int
-    {
-        return $this->yearOfBirth;
-    }
-
-    /**
-     * @param int|null $yearOfBirth
-     */
-    public function setYearOfBirth(?int $yearOfBirth): void
-    {
-        $this->yearOfBirth = $yearOfBirth;
-    }
-
-    /**
-     * @return string
-     */
-    public function getJobTitle(): string
-    {
-        return $this->jobTitle;
-    }
-
-    /**
-     * @param string $jobTitle
-     */
-    public function setJobTitle(string $jobTitle): void
-    {
-        $this->jobTitle = $jobTitle;
-    }
-
-    /**
-     * @return string
-     */
-    public function getCollege(): string
-    {
-        return $this->college;
-    }
-
-    /**
-     * @param string $college
-     */
-    public function setCollege(string $college): void
-    {
-        $this->college = $college;
-    }
-
-    /**
-     * @return string[]
-     */
-    public function getAddresses(): array
-    {
-        return $this->addresses;
-    }
-
-    /**
-     * @param string[] $addresses
-     */
-    public function setAddresses(array $addresses): void
-    {
-        $this->addresses = $addresses;
-    }
-
-    /**
-     * @param array $permissions
-     */
-    public function setPermissions(array $permissions): void
-    {
-        $this->permissions = $permissions;
-    }
-
-    /**
-     * @return array
-     */
-    public function getPermissions(): array
-    {
-        return $this->permissions;
-    }
-
-    /**
-     * @return string|null
-     */
-    public function getSubmit(): ?string
-    {
-        return $this->submit;
-    }
-
-    /**
-     * @param string|null $submit
-     */
-    public function setSubmit(?string $submit): void
-    {
-        $this->submit = $submit;
-    }
-}
diff --git a/module/fid/src/Helper/FormLabel.php b/module/fid/src/Helper/FormLabel.php
index 2517f6a50eaa112d0a9105eb51b0d70d944f4ae8..90c3b067e00bc969c739d5edb73b1f5eb255391f 100644
--- a/module/fid/src/Helper/FormLabel.php
+++ b/module/fid/src/Helper/FormLabel.php
@@ -29,9 +29,13 @@ class FormLabel extends Base
 {
     public function openTag($element = null)
     {
-        if ($element instanceof Element && $element->getAttribute('required')) {
-            $element->setLabelAttributes(['data-required' => null]
-                + $element->getLabelAttributes());
+        if ($element instanceof Element) {
+            $element->setLabelAttributes([
+                    'data-type'     => $element->getAttribute('type'),
+                    'data-name'     => $element->getAttribute('name'),
+                    'data-required' => $element->getAttribute('required')
+                        ? 'true' : 'false'
+                ] + $element->getLabelAttributes());
         }
 
         return parent::openTag($element);
diff --git a/module/fid/src/Hydrator/Strategy/UserDataStrategy.php b/module/fid/src/Hydrator/Strategy/UserDataStrategy.php
new file mode 100644
index 0000000000000000000000000000000000000000..0195938ebcdfa2bb8ece5abf8256f4390dc9de16
--- /dev/null
+++ b/module/fid/src/Hydrator/Strategy/UserDataStrategy.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
+ * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Hydrator\Strategy;
+
+use Zend\Hydrator\NamingStrategy\NamingStrategyInterface;
+use Zend\Hydrator\Strategy\StrategyInterface;
+
+class UserDataStrategy implements StrategyInterface
+{
+    /**
+     * @var NamingStrategyInterface
+     */
+    protected $namingStrategy;
+
+    public function __construct(NamingStrategyInterface $namingStrategy)
+    {
+        $this->namingStrategy = $namingStrategy;
+    }
+
+    public function extract($value)
+    {
+        if (!is_array($value)) {
+            return $value;
+        }
+
+        foreach ($value as $key => $val) {
+            $name = $this->namingStrategy->extract($key);
+            $result[$name] = $this->extract($val);
+        }
+
+        return $result ?? [];
+    }
+
+    public function hydrate($value)
+    {
+        if (!is_array($value)) {
+            return $value;
+        }
+
+        foreach ($value as $key => $val) {
+            $name = $this->namingStrategy->hydrate($key);
+            $result[$name] = $this->hydrate($val);
+        }
+
+        return $result;
+    }
+}
\ No newline at end of file
diff --git a/module/fid/src/Hydrator/UserHydrator.php b/module/fid/src/Hydrator/UserHydrator.php
new file mode 100644
index 0000000000000000000000000000000000000000..264dfe9cdb2a235206e669e6aa9535036ccfdc41
--- /dev/null
+++ b/module/fid/src/Hydrator/UserHydrator.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
+ * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Hydrator;
+
+use Zend\Hydrator\ClassMethods;
+
+class UserHydrator extends ClassMethods
+{
+}
diff --git a/module/fid/src/Hydrator/UserHydratorDelegatorFactory.php b/module/fid/src/Hydrator/UserHydratorDelegatorFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..823d81475692e03ba95c2a8df0f65ac107cd2859
--- /dev/null
+++ b/module/fid/src/Hydrator/UserHydratorDelegatorFactory.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Hydrator;
+
+use fid\Hydrator\Strategy\UserDataStrategy;
+use fid\Service\DataTransferObject\Address;
+use Interop\Container\ContainerInterface;
+use Zend\Hydrator\AbstractHydrator;
+use Zend\Hydrator\ClassMethods;
+use Zend\Hydrator\Strategy\CollectionStrategy;
+use Zend\ServiceManager\Factory\DelegatorFactoryInterface;
+
+class UserHydratorDelegatorFactory implements DelegatorFactoryInterface
+{
+    public function __invoke(
+        ContainerInterface $container,
+        $name,
+        callable $callback,
+        array $options = null
+    ) {
+        /** @var AbstractHydrator $hydrator */
+        $hydrator = call_user_func($callback);
+        $namingStrategy = $hydrator->getNamingStrategy();
+        $hydrator->addStrategy('data', new UserDataStrategy($namingStrategy));
+        $strategy = new CollectionStrategy(new ClassMethods(), Address::class);
+        $hydrator->addStrategy('addresses', $strategy);
+
+        return $hydrator;
+    }
+}
diff --git a/module/fid/src/FormModel/AddressValidator.php b/module/fid/src/InputFilter/RootAwareBaseInputFilter.php
similarity index 65%
rename from module/fid/src/FormModel/AddressValidator.php
rename to module/fid/src/InputFilter/RootAwareBaseInputFilter.php
index 32c518eea81e09758c5cfebed46c2c6bd2d1c62f..193dd8809ee8f5a2d127c834d02bfafdf77d18b9 100644
--- a/module/fid/src/FormModel/AddressValidator.php
+++ b/module/fid/src/InputFilter/RootAwareBaseInputFilter.php
@@ -19,21 +19,19 @@
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-namespace fid\FormModel;
+namespace fid\InputFilter;
 
-class AddressValidator
+use Zend\InputFilter\BaseInputFilter;
+
+class RootAwareBaseInputFilter extends BaseInputFilter
 {
-    public static function init($value, $data): bool
-    {
-        return $data['addressLine1'] . $data['addressLine2']
-            . $data['addressZip'] . $data['addressCity']
-            . $data['addressCountry'] === ''
-            || $value !== '';
-    }
+    public const ROOT = '__ROOT__';
 
-    public static function validate($value, $data): bool
+    public function isValid($context = null)
     {
-        unset($data['id']);
-        return implode('', $data) === '' || $value !== '';
+        $values = array_merge($this->getRawValues(), (array)$this->data);
+        $values[self::ROOT] = $context[self::ROOT] ?? $this;
+
+        return parent::isValid($values);
     }
-}
+}
\ No newline at end of file
diff --git a/module/fid/src/Serializer/OrderCreationRequestUserNormalizer.php b/module/fid/src/Serializer/OrderCreationRequestUserNormalizer.php
new file mode 100644
index 0000000000000000000000000000000000000000..cee9d122a1c754336d7498ae048c05dc04394dd0
--- /dev/null
+++ b/module/fid/src/Serializer/OrderCreationRequestUserNormalizer.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
+ * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Serializer;
+
+use fid\Service\DataTransferObject\User;
+use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
+use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
+use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
+
+class OrderCreationRequestUserNormalizer implements
+    ContextAwareNormalizerInterface, NormalizerAwareInterface
+{
+    use NormalizerAwareTrait;
+
+    public function supportsNormalization(
+        $data,
+        $format = null,
+        array $context = []
+    ) {
+        return $data instanceof User
+            && in_array('order:creation:request', $context['groups'] ?? []);
+    }
+
+    /**
+     * @param mixed|User $object
+     * @param null       $format
+     * @param array      $context
+     *
+     * @return array|bool|float|int|string
+     */
+    public function normalize($object, $format = null, array $context = [])
+    {
+        return $object->getId();
+    }
+}
\ No newline at end of file
diff --git a/module/fid/src/Service/Client.php b/module/fid/src/Service/Client.php
index 339fe1462e4da63c3f989e868045fda13232d9b2..c1bcdac820d41fd2a222198e45e794f00726bca0 100644
--- a/module/fid/src/Service/Client.php
+++ b/module/fid/src/Service/Client.php
@@ -23,6 +23,7 @@ namespace fid\Service;
 
 use fid\Service\DataTransferObject\Library;
 use fid\Service\DataTransferObject\Logon;
+use fid\Service\DataTransferObject\Order;
 use fid\Service\DataTransferObject\User;
 use InvalidArgumentException;
 use Psr\Http\Client\ClientExceptionInterface as HttpClientExceptionInterface;
@@ -199,6 +200,59 @@ class Client
         }
     }
 
+    /**
+     * @param Order $order
+     *
+     * @return Order
+     * @throws ClientException
+     */
+    public function requestOrderCreation(Order $order): Order
+    {
+        $body = $this->serializer->serialize($order, 'json', [
+            'groups' => ['order:creation:request'],
+        ]);
+
+        $request = $this->buildRequest('post', 'orders', $body);
+        $response = $this->sendAuthenticatedRequest($request);
+
+        if ($response->getStatusCode() !== 201) {
+            $this->throwException($response);
+        }
+        /** @var Order $result */
+        $result = $this->serializer->deserialize((string)$response->getBody(),
+            Order::class, 'json', ['groups' => ['order:creation:response']]);
+
+        return $result;
+    }
+
+    /**
+     * @return array|Order[]
+     * @throws ClientException
+     */
+    public function requestOrderList(): array
+    {
+        if ($list = $this->session['orders'] ?? null) {
+            return $list;
+        }
+
+        $request = $this->buildRequest('get', 'orders');
+        $response = $this->sendAuthenticatedRequest($request);
+
+        if ($response->getStatusCode() !== 200) {
+            $this->throwException($response);
+        }
+        /** @var Order[] $list */
+        $list = $this->serializer->deserialize((string)$response->getBody(),
+            Order::class . '[]', 'json');
+
+        $keys = array_map(function (Order $order) {
+            return $order->getId();
+        }, $list);
+
+        return $this->session['orders'] = array_combine($keys, $list);
+
+    }
+
     /**
      * @param string $baseUrl
      * @param string $username
@@ -398,21 +452,31 @@ class Client
      * @param String $permission Name of the permission
      * @param User|null $user user object or null if we want to validate the currently logged in user
      * @throws ClientException
+     * @throws UserNotLoggedinException
      * @throws UserNotAuthorizedException
      */
     protected function authorize(String $permission,User $user = null) {
 
+        if (!$this->isLoggedOn()) {
+            throw new UserNotLoggedinException();
+        }
         $user = $this->requestUserDetails();
         if (!$user->hasPermission($permission)) {
             throw new UserNotAuthorizedException();
         }
     }
 
+    /**
+     * @param String $permission
+     *
+     * @return bool
+     * @throws ClientException
+     */
     public function isAuthorized(String $permission) {
 
         try {
             $this->authorize($permission);
-        } catch (\Exception $ex) {
+        } catch (UserAuthorizationException $exception) {
             return FALSE;
         }
         return TRUE;
diff --git a/module/fid/src/Service/DataTransferObject/Order.php b/module/fid/src/Service/DataTransferObject/Order.php
new file mode 100644
index 0000000000000000000000000000000000000000..33b931a1e82fe83518938267a0fb455ec344c2b0
--- /dev/null
+++ b/module/fid/src/Service/DataTransferObject/Order.php
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author  Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Service\DataTransferObject;
+
+namespace fid\Service\DataTransferObject;
+
+use Symfony\Component\Serializer\Annotation\Groups;
+
+class Order
+{
+    /**
+     * @var string|null
+     * @Groups({
+     *     "order:creation:response",
+     *     "order:list:response",
+     *     "user:details:response",
+     *     "user:creation:response",
+     *     "user:update:response"
+     * })
+     */
+    protected $id;
+
+    /**
+     * @var string
+     * @Groups({
+     *     "order:creation:request",
+     *     "order:creation:response",
+     *     "order:list:response",
+     *     "user:details:response",
+     *     "user:creation:response",
+     *     "user:update:response"
+     * })
+     */
+    protected $type = '';
+
+    /**
+     * @var User|null
+     * @Groups({
+     *     "order:creation:request",
+     *     "order:creation:response",
+     *     "order:list:response",
+     *     "user:details:response",
+     *     "user:creation:response",
+     *     "user:update:response"
+     * })
+     */
+    protected $user;
+
+    /**
+     * @var array
+     * @Groups({
+     *     "order:creation:request",
+     *     "order:creation:response",
+     *     "order:list:response",
+     *     "user:details:response",
+     *     "user:creation:response",
+     *     "user:update:response"
+     * })
+     */
+    protected $data = [];
+
+    /**
+     * @var \DateTime
+     * @Groups({
+     *     "order:creation:response",
+     *     "order:list:response",
+     *     "user:details:response",
+     *     "user:creation:response",
+     *     "user:update:response"
+     * })
+     */
+    protected $createdAt;
+
+    /**
+     * @return string|null
+     */
+    public function getId(): ?string
+    {
+        return $this->id;
+    }
+
+    /**
+     * @param string|null $id
+     */
+    public function setId(?string $id): void
+    {
+        $this->id = $id;
+    }
+
+    /**
+     * @return string
+     */
+    public function getType(): string
+    {
+        return $this->type;
+    }
+
+    /**
+     * @param string $type
+     */
+    public function setType(string $type): void
+    {
+        $this->type = $type;
+    }
+
+    /**
+     * @return User|null
+     */
+    public function getUser(): ?User
+    {
+        return $this->user;
+    }
+
+    /**
+     * @param User|null $user
+     */
+    public function setUser(?User $user): void
+    {
+        $this->user = $user;
+    }
+
+    /**
+     * @return array
+     */
+    public function getData(): array
+    {
+        return $this->data;
+    }
+
+    /**
+     * @param array $data
+     */
+    public function setData(array $data): void
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * @return \DateTime
+     */
+    public function getCreatedAt(): \DateTime
+    {
+        return $this->createdAt;
+    }
+
+    /**
+     * @param \DateTime $createdAt
+     */
+    public function setCreatedAt(\DateTime $createdAt): void
+    {
+        $this->createdAt = $createdAt;
+    }
+}
\ No newline at end of file
diff --git a/module/fid/src/Service/DataTransferObject/User.php b/module/fid/src/Service/DataTransferObject/User.php
index e7cc1bfc025370279a619e67d4bc94217c89b3d8..33d4f760265d38dd8093197cae5588b5c8710def 100644
--- a/module/fid/src/Service/DataTransferObject/User.php
+++ b/module/fid/src/Service/DataTransferObject/User.php
@@ -28,6 +28,9 @@ class User
     /**
      * @var null|string
      * @Groups({
+     *     "order:creation:response",
+     *     "order:details:response",
+     *     "order:list:response",
      *     "user:details:response",
      *     "user:creation:response",
      *     "user:update:response"
@@ -38,6 +41,9 @@ class User
     /**
      * @var null|string
      * @Groups({
+     *     "order:creation:response",
+     *     "order:details:response",
+     *     "order:list:response",
      *     "user:details:response",
      *     "user:creation:request",
      *     "user:creation:response",
@@ -84,6 +90,9 @@ class User
     /**
      * @var null|string
      * @Groups({
+     *     "order:creation:response",
+     *     "order:details:response",
+     *     "order:list:response",
      *     "user:details:response",
      *     "user:creation:request",
      *     "user:creation:response",
@@ -96,6 +105,9 @@ class User
     /**
      * @var null|string
      * @Groups({
+     *     "order:creation:response",
+     *     "order:details:response",
+     *     "order:list:response",
      *     "user:details:response",
      *     "user:creation:request",
      *     "user:creation:response",
@@ -106,7 +118,7 @@ class User
     protected $lastname;
 
     /**
-     * @var null|int
+     * @var null|string
      * @Groups({
      *     "user:details:response",
      *     "user:creation:request",
@@ -115,10 +127,10 @@ class User
      *     "user:update:response"
      * })
      */
-    protected $yearOfBirth;
+    protected $homeLibrary;
 
     /**
-     * @var null|string
+     * @var string
      * @Groups({
      *     "user:details:response",
      *     "user:creation:request",
@@ -127,7 +139,7 @@ class User
      *     "user:update:response"
      * })
      */
-    protected $homeLibrary;
+    protected $college = '';
 
     /**
      * @var string
@@ -139,10 +151,10 @@ class User
      *     "user:update:response"
      * })
      */
-    protected $college;
+    protected $jobTitle = '';
 
     /**
-     * @var string
+     * @var Address[]
      * @Groups({
      *     "user:details:response",
      *     "user:creation:request",
@@ -151,19 +163,17 @@ class User
      *     "user:update:response"
      * })
      */
-    protected $jobTitle;
+    protected $addresses = [];
 
     /**
-     * @var Address[]
+     * @var Order[]
      * @Groups({
      *     "user:details:response",
-     *     "user:creation:request",
      *     "user:creation:response",
-     *     "user:update:request",
      *     "user:update:response"
      * })
      */
-    protected $addresses = [];
+    protected $orders = [];
 
     /**
      * @var array[]
@@ -177,19 +187,34 @@ class User
      */
     protected $permissions = [];
 
+    /**
+     * @var array
+     * @Groups({
+     *     "user:details:response",
+     *     "user:creation:request",
+     *     "user:creation:response",
+     *     "user:update:request",
+     *     "user:update:response"
+     * })
+     */
+    protected $data = [];
+
     /**
      * Magic function as shortcut for all getters
      * TODO ensure public visibility of all called getters
+     *
      * @param $name
+     *
      * @return |null
      */
     public function __get($name)
     {
-        $methodName = 'get'.ucfirst($name);
-        if (method_exists($this,$methodName)) {
+        $methodName = 'get' . ucfirst($name);
+        if (method_exists($this, $methodName)) {
             return $this->$methodName();
+        } else {
+            return null;
         }
-        else return null;
     }
 
     /**
@@ -304,24 +329,6 @@ class User
         $this->lastname = $lastname;
     }
 
-
-    /**
-     * @return int|null
-     */
-    public function getYearOfBirth(): ?int
-    {
-        return $this->yearOfBirth;
-    }
-
-
-    /**
-     * @param int|null $yearOfBirth
-     */
-    public function setYearOfBirth(?int $yearOfBirth): void
-    {
-        $this->yearOfBirth = $yearOfBirth;
-    }
-
     /**
      * @return string
      */
@@ -388,6 +395,26 @@ class User
             $address->setUser($this);
         }
     }
+
+    /**
+     * @return Order[]
+     */
+    public function getOrders(): array
+    {
+        return $this->orders;
+    }
+
+    /**
+     * @param Order[] $orders
+     */
+    public function setOrders(array $orders): void
+    {
+        $this->orders = $orders;
+        foreach ($orders as $order) {
+            $order->setUser($this);
+        }
+    }
+
     /**
      * @return array[]
      */
@@ -408,11 +435,26 @@ class User
     {
         if (
             isset($this->permissions[$permission])
-        &&
-            $this->permissions[$permission] === 'granted'
+            && $this->permissions[$permission] === 'granted'
         ) {
             return true;
         }
         return false;
     }
+
+    /**
+     * @return array
+     */
+    public function getData(): array
+    {
+        return $this->data;
+    }
+
+    /**
+     * @param array $data
+     */
+    public function setData(array $data): void
+    {
+        $this->data = $data;
+    }
 }
diff --git a/module/fid/src/Service/UserAuthorizationException.php b/module/fid/src/Service/UserAuthorizationException.php
new file mode 100644
index 0000000000000000000000000000000000000000..33d2772ba94072b56c4e95fb33714e3b6c65ead1
--- /dev/null
+++ b/module/fid/src/Service/UserAuthorizationException.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author  Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Service;
+
+class UserAuthorizationException extends \Exception
+{
+}
diff --git a/module/fid/src/Service/UserNotAuthorizedException.php b/module/fid/src/Service/UserNotAuthorizedException.php
index a802c48c66a9517551223bea7644e50c2fe066f1..8d7dadc44f1c4589b18d7c01062d720583cd0547 100644
--- a/module/fid/src/Service/UserNotAuthorizedException.php
+++ b/module/fid/src/Service/UserNotAuthorizedException.php
@@ -21,6 +21,6 @@
 
 namespace fid\Service;
 
-class UserNotAuthorizedException extends \Exception
+class UserNotAuthorizedException extends UserAuthorizationException
 {
 }
diff --git a/module/fid/src/Service/UserNotLoggedinException.php b/module/fid/src/Service/UserNotLoggedinException.php
new file mode 100644
index 0000000000000000000000000000000000000000..dced38c128d139e60562b60f3466af2aede2a373
--- /dev/null
+++ b/module/fid/src/Service/UserNotLoggedinException.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright (C) 2019 Leipzig University Library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * @author  Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
+ */
+
+namespace fid\Service;
+
+class UserNotLoggedinException extends UserAuthorizationException
+{
+}
diff --git a/module/fid/src/View/Helper/Root/Factory.php b/module/fid/src/View/Helper/Root/Factory.php
index 1e9473f152c141a64e784495b0de29e0dd361c3d..89a4248bf602714bb621e068a5e185147f66e45c 100644
--- a/module/fid/src/View/Helper/Root/Factory.php
+++ b/module/fid/src/View/Helper/Root/Factory.php
@@ -27,6 +27,7 @@
  */
 namespace fid\View\Helper\Root;
 
+use fid\Service\Client;
 use Interop\Container\ContainerInterface;
 
 /**
@@ -55,4 +56,19 @@ class Factory
             $container->get('VuFind\Config')->get('fid')
         );
     }
+
+    /**
+     * Construct the Record helper.
+     *
+     * @param ContainerInterface $container Service manager.
+     *
+     * @return GetIt
+     */
+    public static function getGetIt(ContainerInterface $container)
+    {
+        return new GetIt(
+            $container->get('VuFind\Config')->get('getit'),
+            $container->get(Client::class)
+        );
+    }
 }
diff --git a/module/fid/src/View/Helper/Root/GetIt.php b/module/fid/src/View/Helper/Root/GetIt.php
new file mode 100644
index 0000000000000000000000000000000000000000..1811d51ee72c892ae3a89942c20bbcfbc688e437
--- /dev/null
+++ b/module/fid/src/View/Helper/Root/GetIt.php
@@ -0,0 +1,401 @@
+<?php
+/**
+ * Get It box view helper
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Leipzig University Library 2019.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  View_Helpers
+ * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace fid\View\Helper\Root;
+
+use VuFind\I18n\Translator\TranslatorAwareTrait;
+use VuFind\RecordDriver\AbstractBase;
+use VuFind\View\Helper\Root\Config;
+use Zend\View\Helper\AbstractHelper;
+
+/**
+ * Get It box view helper
+ *
+ * @category VuFind
+ * @package  View_Helpers
+ * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+class GetIt extends AbstractHelper
+{
+    use TranslatorAwareTrait;
+
+    /**
+     * @var \Zend\Config\Config
+     */
+    protected $config;
+
+    /**
+     * @var AbstractBase
+     */
+    protected $driver;
+
+    /**
+     * FID API Client
+     *
+     * @var \fid\Service\Client
+     */
+    protected $fidClient;
+    protected $sid;
+    protected $format;
+    protected $facetAvail;
+    protected $multipart;
+    protected $institution;
+    protected $megacollection;
+    protected $collection;
+    protected $isbn;
+    protected $issn;
+
+    /**
+     * GetIt constructor.
+     *
+     * @param Config $getItConfig Get It box Configuration
+     */
+    public function __construct($getItConfig, $fidClient)
+    {
+        $this->config = $getItConfig;
+        $this->fidClient = $fidClient;
+    }
+
+    /**
+     * @param $driver
+     */
+    public function __invoke($driver)
+    {
+        $this->driver = $driver;
+        $this->sid = $this->driver->tryMethod('getSourceID');
+        $this->format = $this->driver->tryMethod('getFormats');
+        $this->facetAvail = $this->driver->tryMethod('getFacetAvail');
+        $this->multipart = $this->driver->tryMethod('getMultiPart');
+        $this->institution = $this->driver->tryMethod('getInstitutions');
+        $this->megacollection = $this->driver->tryMethod('getMegaCollection');
+        $this->collection = $this->driver->tryMethod('getCollection');
+        $this->isbn = $this->driver->tryMethod('getISBNs');
+        $this->issn = $this->driver->tryMethod('getISSNs');
+
+        return $this;
+    }
+
+    /**
+     * @param $value
+     *
+     * @return array
+     */
+    private function _getSourceIds($value)
+    {
+        $sids = $this->config->SourceIds;
+        return isset($sids->$value) ? explode(',', $sids->$value) : [];
+    }
+
+    /**
+     * @param $value
+     *
+     * @return string
+     */
+    private function _getNonSourceIds($value)
+    {
+        $sids = $this->config->NonSourceIds;
+        return isset($sids->$value) ? $sids->$value : '';
+    }
+
+    /**
+     * @param $defaultval
+     * @param $additionalval
+     *
+     * @return mixed
+     */
+    private function _accordeon($defaultval, $additionalval)
+    {
+        if (in_array($this->sid, $this->_getSourceIds('source_idsV1'))) {
+            return $additionalval;
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV5'))) {
+            return $additionalval;
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV7'))) {
+            return $additionalval;
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV8'))) {
+            if (in_array('Free', $this->facetAvail)) {
+                return $additionalval;
+            } elseif (in_array('Online', $this->facetAvail)
+                && (preg_grep($this->_getNonSourceIds('source_idsV8_5'), $this->format)
+                    || preg_grep($this->_getNonSourceIds('source_idsV8_6'), $this->format)
+                    || preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format))
+            ) {
+                return $additionalval;
+            }
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV10'))) {
+            return $additionalval;
+        }
+        return $defaultval;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAccordeonColor()
+    {
+        return $this->_accordeon('azure', 'amber');
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getAccordeonHeadline()
+    {
+        $links = $this->translate('getit_links');
+        $infos = $this->translate('getit_infos');
+        return $this->_accordeon($infos, $links);
+    }
+
+    /**
+     * @return string
+     */
+    public function getBoxHeadline()
+    {
+        if (in_array($this->sid, $this->_getSourceIds('source_idsV3'))) {
+            if (!$this->fidClient->isLoggedOn()) {
+                return $this->translate('getit_logged_not');
+            }
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV4'))) {
+            if (!$this->fidClient->isLoggedOn()) {
+                return $this->translate('getit_logged_not');
+            }
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV5'))) {
+            if (!$this->fidClient->isLoggedOn()) {
+                return $this->translate('getit_logged_not');
+            }
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV8'))) {
+            $isLogin = false;
+            if (in_array('Local', $this->facetAvail)
+                && ((preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+                    && !in_array($this->_getNonSourceIds('source_idsV8_1'), $this->institution))
+                    || preg_grep($this->_getNonSourceIds('source_idsV8_3'), $this->format)
+                )
+            ) {
+                $isLogin = true;
+            } elseif (in_array('Online', $this->facetAvail)
+                && !in_array('Free', $this->facetAvail)
+                && (preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format))
+            ) {
+                $isLogin = true;
+            }
+            if (in_array($this->sid, $this->_getSourceIds('source_idsV9'))
+                && preg_grep($this->_getNonSourceIds('source_idsV8_8'), $this->format)
+            ) {
+                $isLogin = true;
+            }
+            if ($isLogin && !$this->fidClient->isLoggedOn()) {
+                return $this->translate('getit_logged_not');
+            }
+        }
+        return $this->translate('Get it');
+    }
+
+    /**
+     * @return string
+     */
+    public function getNotices()
+    {
+        if (in_array($this->sid, $this->_getSourceIds('source_idsV1'))) {
+            return $this->translate('getit_text_1');
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV4'))) {
+            if (!$this->fidClient->isLoggedOn()) {
+                return $this->translate('getit_text_2');
+            } else {
+                return $this->translate('getit_text_12');
+            }
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV5'))) {
+            if (!$this->fidClient->isLoggedOn()) {
+                return $this->translate('getit_text_3_1');
+            } else {
+                return $this->translate('getit_text_13');
+            }
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV6'))) {
+            return $this->translate('getit_text_4');
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV7'))) {
+            return $this->translate('getit_text_5');
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV10'))) {
+            return $this->translate('getit_text_6');
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV11'))) {
+            return $this->translate('getit_text_7');
+        } elseif (in_array($this->sid, $this->_getSourceIds('source_idsV8'))) {
+            if (in_array($this->sid, $this->_getSourceIds('source_idsV9'))) {
+                if (preg_grep($this->_getNonSourceIds('source_idsV8_8'), $this->collection)) {
+                    if (!$this->fidClient->isLoggedOn()) {
+                        return $this->translate('getit_text_3_1');
+                    } else {
+                        return $this->translate('getit_text_13');
+                    }
+                }
+                if (preg_grep($this->_getNonSourceIds('source_idsV8_4'), [$this->multipart])) {
+                    return $this->translate('getit_text_11');
+                }
+                if (preg_grep($this->_getNonSourceIds('source_idsV8_7'), $this->megacollection)
+                ) {
+                    return $this->translate('getit_text_1');
+                }
+                if (in_array('Local', $this->facetAvail)
+                    && (preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+                        || preg_grep($this->_getNonSourceIds('source_idsV8_3'), $this->format))
+                ) {
+                    if (!$this->fidClient->isLoggedOn()) {
+                        return $this->translate('getit_text_10');
+                    } elseif (preg_grep($this->_getNonSourceIds('source_idsV8_3'), $this->format)) {
+                        return $this->translate('getit_text_14');
+                    } else {
+                        return '';
+                    }
+                }
+                if (in_array('Local', $this->facetAvail)
+                    && preg_grep($this->_getNonSourceIds('source_idsV8_2'), $this->format)
+                ) {
+                    return $this->translate('getit_text_8');
+                }
+                if (in_array('Free', $this->facetAvail)) {
+                    return $this->translate('getit_text_1');
+                }
+                if (in_array('Online', $this->facetAvail)
+                    && (preg_grep($this->_getNonSourceIds('source_idsV8_5'), $this->format)
+                        || preg_grep($this->_getNonSourceIds('source_idsV8_6'), $this->format))
+                ) {
+                    return $this->translate('getit_text_9');
+                } elseif (in_array('Online', $this->facetAvail)
+                    && preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+                ) {
+                    if (!$this->fidClient->isLoggedOn()) {
+                        return $this->translate('getit_text_10');
+                    } else {
+                        return $this->translate('getit_text_15');
+                    }
+                }
+            } elseif (preg_grep($this->_getNonSourceIds('source_idsV8_4'), [$this->multipart])) {
+                return $this->translate('getit_text_11');
+            } elseif (in_array('Local', $this->facetAvail)
+                && preg_grep($this->_getNonSourceIds('source_idsV8_2'), $this->format)
+            ) {
+                return $this->translate('getit_text_8');
+            } elseif (in_array('Local', $this->facetAvail)) {
+                if (!$this->fidClient->isLoggedOn()) {
+                    return $this->translate('getit_text_10');
+                } elseif (preg_grep($this->_getNonSourceIds('source_idsV8_3'), $this->format)) {
+                    return $this->translate('getit_text_14');
+                }
+            } elseif (in_array('Free', $this->facetAvail)) {
+                return $this->translate('getit_text_1');
+            } elseif (in_array('Online', $this->facetAvail)
+                && (preg_grep($this->_getNonSourceIds('source_idsV8_5'), $this->format)
+                    || preg_grep($this->_getNonSourceIds('source_idsV8_6'), $this->format))
+            ) {
+                return $this->translate('getit_text_9');
+            } elseif (in_array('Online', $this->facetAvail)
+                && preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+            ) {
+                if (!$this->fidClient->isLoggedOn()) {
+                    return $this->translate('getit_text_10');
+                } else {
+                    return $this->translate('getit_text_15');
+                }
+            }
+        }
+        return $this->translate('getit_text_default');
+    }
+
+    /**
+     * @return bool
+     */
+    public function showLinks()
+    {
+        if (in_array($this->sid, $this->_getSourceIds('source_idsV5'))
+            || (in_array($this->sid, $this->_getSourceIds('source_idsV9'))
+                && preg_grep($this->_getNonSourceIds('source_idsV8_8'), $this->collection))
+        ) {
+            if (!$this->fidClient->isLoggedOn()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @return bool
+     */
+    public function showOrderButton()
+    {
+        if (in_array($this->sid, $this->_getSourceIds('source_idsV9'))
+            && preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+            && !in_array($this->_getNonSourceIds('source_idsV8_9'), $this->institution)
+            && !preg_grep($this->_getNonSourceIds('source_idsV8_4'), [$this->multipart])
+            && !preg_grep($this->_getNonSourceIds('source_idsV8_8'), $this->collection)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return bool
+     */
+    public function showPartCopyButton()
+    {
+        if (!in_array('Free', $this->facetAvail)
+            && in_array($this->sid, $this->_getSourceIds('source_idsV9'))
+            && preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+            && !preg_grep($this->_getNonSourceIds('source_idsV8_4'), [$this->multipart])
+            && !preg_grep($this->_getNonSourceIds('source_idsV8_8'), $this->collection)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return bool
+     */
+    public function showBOSSData()
+    {
+        if (!in_array('Free', $this->facetAvail)
+            && in_array($this->sid, $this->_getSourceIds('source_idsV8'))
+            && (!empty($this->isbn) || !empty($this->issn))
+            && !preg_grep($this->_getNonSourceIds('source_idsV8_8'), $this->collection)
+        ) {
+            if (in_array($this->sid, $this->_getSourceIds('source_idsV9'))
+                && preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+            ) {
+                return true;
+            } elseif (in_array('Local', $this->facetAvail)
+                && preg_grep($this->_getNonSourceIds('source_idsV8_3'), $this->format)
+            ) {
+                return true;
+            } elseif (in_array('Online', $this->facetAvail)
+                && preg_grep($this->_getNonSourceIds('source_idsV8_1'), $this->format)
+            ) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/module/fid/src/VuFind/Auth/Authenticator.php b/module/fid/src/VuFind/Auth/Authenticator.php
index bc518469402a520946fddd402ced6bb25d77b0d7..5a2cd6130fe2f4dc329d9f1276fb76691a8e6756 100644
--- a/module/fid/src/VuFind/Auth/Authenticator.php
+++ b/module/fid/src/VuFind/Auth/Authenticator.php
@@ -25,7 +25,7 @@ use fid\Service\Client;
 use fid\Service\ClientException;
 use fid\VuFind\Db\Row\User as VuFindUser;
 use VuFind\Auth\AbstractBase;
-use VuFind\Exception\Auth;
+use VuFind\Db\Row\User as UserRow;
 use VuFind\Exception\Auth as AuthException;
 use Zend\Http\PhpEnvironment\Request;
 
@@ -37,6 +37,9 @@ class Authenticator extends AbstractBase
     protected const AUTH_ERROR_UNKNOWN_REASON
         = 'fid::auth_error_unknown_reason';
 
+    protected const AUTH_ERROR_ACCOUNT_BLOCKED
+        = 'fid::auth_error_account_blocked';
+
     /**
      * @var Client
      */
@@ -47,6 +50,13 @@ class Authenticator extends AbstractBase
         $this->client = $client;
     }
 
+    /**
+     * @param Request $request
+     *
+     * @return VuFindUser|UserRow
+     * @throws AuthException
+     * @throws ClientException
+     */
     public function create($request)
     {
         return $this->authenticate($request);
@@ -56,8 +66,9 @@ class Authenticator extends AbstractBase
     /**
      * @param Request $request
      *
-     * @return VuFindUser
+     * @return VuFindUser|UserRow
      * @throws AuthException
+     * @throws ClientException
      */
     public function authenticate($request)
     {
@@ -76,6 +87,11 @@ class Authenticator extends AbstractBase
             }
         }
 
+        if (!$this->client->isAuthorized('basic_access')) {
+            $this->client->logoff();
+            throw new AuthException(self::AUTH_ERROR_ACCOUNT_BLOCKED);
+        }
+
         if ($ownerId = $logon->getOwnerId()) {
             /** @var VuFindUser $userRow */
             $userRow = $this->getUserTable()->getByUsername($ownerId);
diff --git a/module/fid/src/VuFind/Auth/ILSAuthenticator.php b/module/fid/src/VuFind/Auth/ILSAuthenticator.php
index 5f342a0349b714f0c193b2669c6fb071b07a1105..8ccc5c2d07e2f0876af02a5386e1431482ba43b0 100644
--- a/module/fid/src/VuFind/Auth/ILSAuthenticator.php
+++ b/module/fid/src/VuFind/Auth/ILSAuthenticator.php
@@ -24,6 +24,7 @@ namespace fid\VuFind\Auth;
 use fid\Service\Client;
 use fid\Service\ClientException;
 use fid\Service\DataTransferObject\Address;
+use fid\Service\UserNotAuthorizedException;
 
 class ILSAuthenticator
 {
@@ -40,11 +41,23 @@ class ILSAuthenticator
     /**
      * @return array|null
      * @throws ClientException
+     * @throws UserNotAuthorizedException
+     */
+    public function newCatalogLogin()
+    {
+        return $this->storedCatalogLogin();
+    }
+
+    /**
+     * @return array|null
+     * @throws ClientException
+     * @throws UserNotAuthorizedException
      */
     public function storedCatalogLogin()
     {
         try {
             $user = $this->client->requestUserDetails();
+            $libs = $this->client->requestLibraryList();
         } catch (ClientException $e) {
             if ($e->getCode() == '401') {
                 return null;
@@ -62,7 +75,8 @@ class ILSAuthenticator
             'firstname'   => $user->getFirstname(),
             'lastname'    => $user->getLastname(),
             'email'       => $user->getUsername(),
-            'user'        => $user
+            'user'        => $user,
+            'libs'        => $libs,
         ];
 
         foreach ($user->getAddresses() as $index => $address) {
diff --git a/module/finc/config/module.config.php b/module/finc/config/module.config.php
index 2565b99febbebdfd64babbbb940273062fea034b..babf9b01b08f308ae2c0645381be52e3bf597f49 100644
--- a/module/finc/config/module.config.php
+++ b/module/finc/config/module.config.php
@@ -9,7 +9,6 @@ use VuFind\Controller\AbstractBaseWithConfigFactory;
 $config = [
     'service_manager' => [
         'factories' => [
-            'VuFind\Mailer' => 'finc\Mailer\Factory',
             'VuFind\Cache\Manager' => 'finc\Service\Factory::getCacheManager',
             'VuFind\BranchesReader' => 'finc\Service\Factory::getBranchesReader',
             'VuFind\ILS\Connection' => 'finc\Service\Factory::getILSConnection',
@@ -113,11 +112,19 @@ $config = [
             ],
             'resolver_driver' => [
                 'factories' => [
-                    'ezb' => 'finc\Resolver\Driver\Factory::getEzb',
-                    'redi' => 'finc\Resolver\Driver\Factory::getRedi'
+                    'finc\Resolver\Driver\Ezb' => 'finc\Resolver\Driver\FincResolverDriverFactory',
+                    'finc\Resolver\Driver\Redi' => 'finc\Resolver\Driver\FincResolverDriverFactory'
                 ],
+                'aliases' => [
+                    'ezb' => 'finc\Resolver\Driver\Ezb',
+                    'redi' => 'finc\Resolver\Driver\Redi'
+                ]
             ],
             'hierarchy_treedataformatter' => [
+                'factories' => [
+                    'finc\Hierarchy\TreeDataFormatter\NoCollections' =>
+                        'Zend\ServiceManager\Factory\InvokableFactory',
+                ],
                 'aliases' => [
                     'json' => 'finc\Hierarchy\TreeDataFormatter\NoCollections',
                 ],
@@ -237,7 +244,8 @@ $config = [
             'aliases' => [
                 'CatUserType' => 'finc\Role\PermissionProvider\CatUserType',
                 'IpRangeFoFor' => 'finc\Role\PermissionProvider\IpRangeFoFor',
-                'IpRegExFoFor' => 'finc\Role\PermissionProvider\IpRegExFoFor'
+                'IpRegExFoFor' => 'finc\Role\PermissionProvider\IpRegExFoFor',
+                'IpRange' => 'VuFind\Role\PermissionProvider\IpRange',
             ]
         ]
     ],
diff --git a/module/finc/sql/migrations/mysql/migrateData_Vufind1ToVufind5_Common.sql b/module/finc/sql/migrations/mysql/migrateData_Vufind1ToVufind5_Common.sql
new file mode 100644
index 0000000000000000000000000000000000000000..8717ced29bc9fefe0b4a026e64f43bd22ce87678
--- /dev/null
+++ b/module/finc/sql/migrations/mysql/migrateData_Vufind1ToVufind5_Common.sql
@@ -0,0 +1,308 @@
+-- Transform VUFIND1 data tables into the structures of the vufind2 resp. VUFIND5 tables, #16251, HR
+
+-- Part "Common": Common structure of a VUFIND1 database
+
+-- Please note:
+-- There also exists the script "migrateData_Vufind1ToVufind5_First_Adlr.sql" in /module/fid_adlr/sql/migrations/mysql.
+-- It contains the handling of the Adlr specific structure which has to be done before running the current Common script when using an vufind1 Adlr database.
+-- First run the Additional_Adlr part if you use the vufind1 Adlr database, then run the common part. 
+
+-- Written for mysql statements. It should be compatible with MariaDB statements.
+
+-- Run the following table changes on a dump of a VUFIND1 database (e.g. "2019-03-31-vufind_adlr-mysql.sql").
+
+-- Example for import data statements:
+-- The dump is imported into database "vufind" by running the following command within a Terminal Window:
+--     sudo mysql -h localhost -u root -p -vvf vufind < 2019-03-31-vufind_adlr-mysql.sql
+
+-- Example for running the script:
+-- Within a mysql session type
+--     source migrateDataToVufind5_First_Adlr.sql
+--        - only necessary for an Adlr database dump
+--        - otherwise this common script would fail
+--        - for other databases with additinal structure features write a new script for the special things
+--     source migrateDataToVufind5_Common.sql
+
+-- Example for export data statements to a dump within a Terminal Window:
+--    sudo mysqldump --opt -Q -u root -p -h localhost vufind > 2019-10-25-vufind_adlr-migration-mysql.sql
+
+
+start transaction;
+begin;
+
+ 
+-- Table "user"
+select 'USER table';
+
+-- Apply column modifications  
+
+ALTER TABLE user
+  MODIFY COLUMN username varchar(255),
+  MODIFY COLUMN email varchar(255),
+  MODIFY COLUMN cat_password varchar(70),
+  MODIFY COLUMN created datetime NOT NULL DEFAULT '2000-01-01 00:00:00';
+  
+-- Add missing columns
+
+ALTER TABLE user
+  ADD COLUMN pass_hash varchar(60) DEFAULT NULL,
+  ADD COLUMN cat_id varchar(255),
+  ADD COLUMN cat_pass_enc varchar(255) DEFAULT NULL,
+  ADD COLUMN verify_hash varchar(42) NOT NULL DEFAULT '',
+  ADD COLUMN last_login timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',
+  ADD COLUMN auth_method varchar(50) DEFAULT NULL;
+
+ALTER TABLE user
+  ADD UNIQUE(username),
+  ADD UNIQUE(cat_id); 
+
+-- Set chracter set to utf8  
+
+ALTER TABLE user
+  CONVERT TO CHARACTER SET utf8;
+
+  
+-- Table "resource"
+select 'RESURCE table';
+
+
+-- Add missing columns
+
+ALTER TABLE resource
+  ADD COLUMN author varchar(255) DEFAULT NULL,
+  ADD COLUMN year mediumint(6) DEFAULT NULL,
+  ADD COLUMN extra_metadata mediumtext DEFAULT NULL;
+
+-- Apply column modifications  
+
+ALTER TABLE resource
+  MODIFY COLUMN record_id varchar(255) NOT NULL DEFAULT '',
+  MODIFY COLUMN title varchar(255) NOT NULL DEFAULT '',
+  MODIFY COLUMN source varchar(50) NOT NULL DEFAULT 'Solr';
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE resource
+  CONVERT TO CHARACTER SET utf8;
+
+
+-- Table "comments"
+select 'COMMENTS table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE comments
+  MODIFY COLUMN user_id int(11) DEFAULT NULL,
+  MODIFY COLUMN created datetime NOT NULL DEFAULT '2000-01-01 00:00:00';
+  
+-- Apply constraint modifications  
+
+ALTER TABLE comments
+  DROP FOREIGN KEY comments_ibfk_1;
+
+ALTER TABLE comments
+   ADD CONSTRAINT comments_ibfk_1 FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE SET NULL;
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE comments
+  CONVERT TO CHARACTER SET utf8;
+ 
+
+-- Table "change_tracker"
+select 'CHANGE_TRACKER table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE change_tracker
+  MODIFY COLUMN id varchar(120) NOT NULL,
+  MODIFY COLUMN first_indexed datetime DEFAULT NULL,
+  MODIFY COLUMN last_indexed datetime DEFAULT NULL,
+  MODIFY COLUMN last_record_change datetime DEFAULT NULL,
+  MODIFY COLUMN deleted datetime DEFAULT NULL;
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE change_tracker
+  CONVERT TO CHARACTER SET utf8;
+
+
+-- Table "oai_resumption"
+select 'OAI_RESUMPTION table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE oai_resumption
+  MODIFY COLUMN expires datetime NOT NULL DEFAULT '2000-01-01 00:00:00';
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE oai_resumption
+  CONVERT TO CHARACTER SET utf8;
+
+
+-- Table "resource_tags"
+select 'RESOURCE_TAGS table';
+
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE resource_tags
+  CONVERT TO CHARACTER SET utf8;
+
+
+-- Table "search"
+select 'SEARCH table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE search
+  MODIFY COLUMN session_id varchar(128) DEFAULT NULL,
+  MODIFY COLUMN created datetime NOT NULL DEFAULT '2000-01-01 00:00:00';
+  
+-- Add missing columns
+
+ALTER TABLE search
+  ADD COLUMN checksum int(11) DEFAULT NULL;
+
+-- Apply constraint modifications  
+
+ALTER TABLE search
+   DROP INDEX session_id;
+   
+ALTER TABLE search
+  ADD UNIQUE(session_id); 
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE search
+  CONVERT TO CHARACTER SET utf8;
+
+  
+-- Table "session"
+select 'SESSION table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE session
+  MODIFY COLUMN session_id varchar(128) DEFAULT NULL,
+  MODIFY COLUMN data mediumtext,
+  MODIFY COLUMN created datetime NOT NULL DEFAULT '2000-01-01 00:00:00';
+  
+-- Apply constraint modifications  
+
+ALTER TABLE session
+   DROP INDEX last_used;
+   
+ALTER TABLE session
+  ADD UNIQUE(last_used); 
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE session
+  CONVERT TO CHARACTER SET utf8;
+
+  
+-- Table "tags"
+select 'TAGS table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE tags
+  MODIFY COLUMN tag varchar(64) NOT NULL DEFAULT '';
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE tags
+  CONVERT TO CHARACTER SET utf8;
+   
+ 
+-- Table "user_list"
+select 'USER_LIST table';
+
+
+-- Apply column modifications  
+
+ALTER TABLE user_list
+  MODIFY COLUMN created datetime NOT NULL DEFAULT '2000-01-01 00:00:00';
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE user_list
+  CONVERT TO CHARACTER SET utf8;
+
+   
+-- Table "user_resource"
+select 'USER_RESOURCE table';
+
+  
+-- Apply constraint modifications  
+
+ALTER TABLE user_resource
+   DROP FOREIGN KEY user_resource_ibfk_3,
+   DROP FOREIGN KEY user_resource_ibfk_4;
+  
+-- Set chracter set to utf8  
+
+ALTER TABLE user_resource
+  CONVERT TO CHARACTER SET utf8;
+   
+   
+select 'create new tables';
+
+-- Create the new tables of VUFIND5 not being in VUFIND1  
+  
+-- Table "external_session"
+
+CREATE TABLE external_session (
+  id int(11) NOT NULL AUTO_INCREMENT,
+  session_id varchar(128) NOT NULL,
+  external_session_id varchar(255) NOT NULL,
+  created datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
+  PRIMARY KEY (id),
+  UNIQUE KEY session_id (session_id),
+  KEY external_session_id (external_session_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
+
+
+-- Table "user_card"
+
+CREATE TABLE user_card (
+  id int(11) NOT NULL AUTO_INCREMENT,
+  user_id int(11) NOT NULL,
+  card_name varchar(255) NOT NULL DEFAULT '',
+  cat_username varchar(50) NOT NULL DEFAULT '',
+  cat_password varchar(70) DEFAULT NULL,
+  cat_pass_enc varchar(255) DEFAULT NULL,
+  home_library varchar(100) NOT NULL DEFAULT '',
+  created datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
+  saved timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (id),
+  KEY user_id (user_id),
+  KEY user_card_cat_username (cat_username),
+  CONSTRAINT user_card_ibfk_1 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+-- Table "record"
+
+CREATE TABLE record (
+  id int(11) NOT NULL AUTO_INCREMENT,
+  record_id varchar(255) DEFAULT NULL,
+  source varchar(50) DEFAULT NULL,
+  version varchar(20) NOT NULL,
+  data longtext DEFAULT NULL,
+  updated datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
+  PRIMARY KEY (id),
+  UNIQUE KEY record_id_source (record_id, source)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+    
+
+
+commit;
diff --git a/module/finc/src/finc/Config/BranchesReader.php b/module/finc/src/finc/Config/BranchesReader.php
index c19aab120115a02e74ede6f4cb63714110a4838a..857641cf4f56fc0f15515cfb83fa0916ef003a25 100644
--- a/module/finc/src/finc/Config/BranchesReader.php
+++ b/module/finc/src/finc/Config/BranchesReader.php
@@ -28,8 +28,6 @@
  */
 namespace finc\Config;
 
-use VuFind\Config\Locator as Locator;
-
 /**
  * VuFind Branches.yaml Configuration Reader
  *
@@ -40,83 +38,12 @@ use VuFind\Config\Locator as Locator;
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     https://vufind.org Main Site
  */
-class BranchesReader extends \VuFind\Config\SearchSpecsReader
+class BranchesReader extends \VuFind\Config\YamlReader
 {
     /**
-     * Cache manager
-     *
-     * @var \finc\Cache\Manager
-     */
-    protected $cacheManager;
-
-    /**
-     * Cache of loaded branches
-     *
-     * @var array
-     */
-    protected $branches = [];
-
-    /**
-     * Constructor
+     * Cache directory name
      *
-     * @param \finc\Cache\Manager $cacheManager Cache manager (optional)
+     * @var string
      */
-    public function __construct(\finc\Cache\Manager $cacheManager = null)
-    {
-        $this->cacheManager = $cacheManager;
-    }
-
-    /**
-     * Return branches
-     *
-     * @param string $filename config file name
-     *
-     * @return array
-     */
-    public function get($filename)
-    {
-        // Load data if it is not already in the object's cache:
-        if (!isset($this->branches[$filename])) {
-            $this->branches[$filename] = $this->getFromPaths(
-                Locator::getBaseConfigPath($filename),
-                Locator::getLocalConfigPath($filename)
-            );
-        }
-
-        return $this->branches[$filename];
-    }
-
-    /**
-     * Given core and local filenames, retrieve the searchspecs data.
-     *
-     * @param string $defaultFile Full path to file containing default YAML
-     * @param string $customFile  Full path to file containing local customizations
-     * (may be null if no local file exists).
-     *
-     * @return array
-     */
-    protected function getFromPaths($defaultFile, $customFile = null)
-    {
-        // Connect to searchspecs cache:
-        $cache = (null !== $this->cacheManager)
-            ? $this->cacheManager->getCache('branches') : false;
-
-        // Generate cache key:
-        $cacheKey = basename($defaultFile) . '-'
-            . (file_exists($defaultFile) ? filemtime($defaultFile) : 0);
-        if (!empty($customFile)) {
-            $cacheKey .= '-local-' . filemtime($customFile);
-        }
-        $cacheKey = md5($cacheKey);
-
-        // Generate data if not found in cache:
-        if ($cache === false || !($results = $cache->getItem($cacheKey))) {
-            $results = $this->parseYaml($customFile, $defaultFile);
-            if ($cache !== false) {
-                $cache->setItem($cacheKey, $results);
-            }
-        }
-
-        return $results;
-    }
+    protected $cacheName = 'branches';
 }
diff --git a/module/finc/src/finc/ILS/Driver/FincILS.php b/module/finc/src/finc/ILS/Driver/FincILS.php
index cdde4452a2e3ebbaaf129640579e629039f1b593..9affda6b4babeeb996508232c83bfd9f73cf0434 100644
--- a/module/finc/src/finc/ILS/Driver/FincILS.php
+++ b/module/finc/src/finc/ILS/Driver/FincILS.php
@@ -1415,7 +1415,7 @@ class FincILS extends PAIA implements LoggerAwareInterface
      *
      * @return bool
      */
-    private function hasILSData($id)
+    protected function hasILSData($id)
     {
         $retVal = [];
         foreach ($this->config['General']['queryIls'] as $value) {
diff --git a/module/finc/src/finc/Mailer/Factory.php b/module/finc/src/finc/Mailer/Factory.php
deleted file mode 100644
index eca49ea9ba4bf9c1623c0f9d7d8618d4fb114cac..0000000000000000000000000000000000000000
--- a/module/finc/src/finc/Mailer/Factory.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-/**
- * Factory for instantiating Mailer objects
- *
- * PHP version 5
- *
- * Copyright (C) Villanova University 2009.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category VuFind
- * @package  Mailer
- * @author   Demian Katz <demian.katz@villanova.edu>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
- * @link     https://vufind.org/wiki/development Wiki
- */
-namespace finc\Mailer;
-
-use Zend\Mail\Transport\InMemory;
-use Zend\Mail\Transport\Smtp;
-use Zend\Mail\Transport\SmtpOptions;
-use Zend\ServiceManager\ServiceLocatorInterface;
-
-/**
- * Factory for instantiating Mailer objects
- *
- * @category VuFind
- * @package  Mailer
- * @author   Demian Katz <demian.katz@villanova.edu>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
- * @link     https://vufind.org/wiki/development Wiki
- *
- * @codeCoverageIgnore
- */
-class Factory implements \Zend\ServiceManager\FactoryInterface
-{
-    /**
-     * Build the mail transport object.
-     *
-     * @param \Zend\Config\Config $config Configuration
-     *
-     * @return InMemory|Smtp
-     */
-    protected function getTransport($config)
-    {
-        // In test mode? Return fake object:
-        if (isset($config->Mail->testOnly) && $config->Mail->testOnly) {
-            return new InMemory();
-        }
-
-        // Create mail transport:
-        $settings = [
-            'host' => $config->Mail->host, 'port' => $config->Mail->port
-        ];
-        if (isset($config->Mail->username) && isset($config->Mail->password)) {
-            $settings['connection_class'] = 'login';
-            $settings['connection_config'] = [
-                'username' => $config->Mail->username,
-                'password' => $config->Mail->password
-            ];
-            if (isset($config->Mail->secure)) {
-                // always set user defined secure connection
-                $settings['connection_config']['ssl'] = $config->Mail->secure;
-            } else {
-                // set default secure connection based on configured port
-                if ($settings['port'] == '587') {
-                    $settings['connection_config']['ssl'] = 'tls';
-                } elseif ($settings['port'] == '487') {
-                    $settings['connection_config']['ssl'] = 'ssl';
-                }
-            }
-        }
-        return new Smtp(new SmtpOptions($settings));
-    }
-
-    /**
-     * Create service
-     *
-     * @param ServiceLocatorInterface $sm Service manager
-     *
-     * @return mixed
-     */
-    public function createService(ServiceLocatorInterface $sm)
-    {
-        // Load configurations:
-        $config = $sm->get('VuFind\Config')->get('config');
-
-        // Create service:
-        return new \VuFind\Mailer\Mailer($this->getTransport($config));
-    }
-}
diff --git a/module/finc/src/finc/RecordDriver/SolrDefaultFincTrait.php b/module/finc/src/finc/RecordDriver/SolrDefaultFincTrait.php
index 4c7a60fe0bd03061cf04511064904a2a115e337c..66f1bdf859f692283513a2009b8e09df3e5c5d01 100644
--- a/module/finc/src/finc/RecordDriver/SolrDefaultFincTrait.php
+++ b/module/finc/src/finc/RecordDriver/SolrDefaultFincTrait.php
@@ -449,6 +449,29 @@ trait SolrDefaultFincTrait
         return isset($this->fields['mega_collection']) ? $this->fields['mega_collection'] : [];
     }
 
+    /**
+     * Get an array of all collections in the record.
+     *
+     * @return array
+     * @access public
+     */
+    public function getCollection()
+    {
+        return isset($this->fields['collection']) ? $this->fields['collection'] : [];
+    }
+
+    /**
+     * refs #15271
+     * Check for Multipart resource record level 'set' MARC21 LEADER Pos 19
+     *
+     * @return boolean True if record has multiple parts, otherwise False
+     * @access public
+     */
+    public function isMultiPartSet()
+    {
+        return $this->getMultiPart() == 'a' ? true : false;
+    }
+
     /**
      * Get the content of field multipart_set.
      *
diff --git a/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php b/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php
index fa4375a70a1e118106b27e8b8bcc352ea378dd7d..5cda6922424a9fd260c07e793aac08e22371ff36 100644
--- a/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php
+++ b/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php
@@ -2022,6 +2022,12 @@ trait SolrMarcFincTrait
             return $array;
         } // end if
         foreach ($rvk as $key => $line) {
+            /* refs #15339 use rvk notation only */
+            if ($line->getIndicator('1') !== 'r' ||
+                $line->getIndicator('2') !== 'v') {
+                continue;
+            }
+
             // if subfield with rvk exists
             if ($line->getSubfield('a')) {
                 // get rvk
@@ -2268,4 +2274,38 @@ trait SolrMarcFincTrait
 
         return $retval;
     }
+
+    /**
+     * Get the delivery status of the record.
+     *
+     * @return string
+     * @access public
+     */
+    public function getDeliveryStatus()
+    {
+        $retVal = [];
+        $statuses = $this->getMarcRecord()->getFields('366');
+
+        if(!is_null($statuses)) {
+            foreach ($statuses as $status) {
+                $value = $status->getSubfield('e');
+                if($value) {
+                    // is subfield u exists
+                    $retVal[] = $value->getData();
+                }
+            }
+        }
+        return implode(', ', array_unique($retVal));
+    }
+
+    /**
+     * Return contained works.
+     * Reads MARC field 501 - With Note
+     *
+     * @return mixed
+     */
+    public function getContainedWorks()
+    {
+        return $this->getFieldArray('501');
+    }
 }
diff --git a/module/finc/src/finc/Resolver/Driver/Factory.php b/module/finc/src/finc/Resolver/Driver/Factory.php
deleted file mode 100644
index 2393eade3a1a29261874836f160999718bd354ec..0000000000000000000000000000000000000000
--- a/module/finc/src/finc/Resolver/Driver/Factory.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-/**
- * Resolver Driver Factory Class
- *
- * PHP version 5
- *
- * Copyright (C) Villanova University 2014.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category VuFind
- * @package  Resolver_Drivers
- * @author   Demian Katz <demian.katz@villanova.edu>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
- * @link     https://vufind.org/wiki/development:plugins:hierarchy_components Wiki
- */
-namespace finc\Resolver\Driver;
-
-use Zend\ServiceManager\ServiceManager;
-
-/**
- * Resolver Driver Factory Class
- *
- * @category VuFind
- * @package  Resolver_Drivers
- * @author   Demian Katz <demian.katz@villanova.edu>
- * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
- * @link     https://vufind.org/wiki/development:plugins:hierarchy_components Wiki
- *
- * @codeCoverageIgnore
- */
-class Factory
-{
-    /**
-     * Factory for Ezb record driver.
-     *
-     * @param ServiceManager $sm Service manager.
-     *
-     * @return Ezb
-     */
-    public static function getEzb(ServiceManager $sm)
-    {
-        $config = $sm->getServiceLocator()->get('VuFind\Config')->get('Resolver');
-        return new Ezb(
-            $config->Ezb,
-            $sm->getServiceLocator()->get('VuFind\Http')->createClient()
-        );
-    }
-
-    /**
-     * Factory for Redi record driver.
-     *
-     * @param ServiceManager $sm Service manager.
-     *
-     * @return finc\Resolver\Driver\Redi
-     */
-    public static function getRedi(ServiceManager $sm)
-    {
-        $config = $sm->getServiceLocator()->get('VuFind\Config')->get('Resolver');
-        return new Redi(
-            $config->Redi->url,
-            $sm->getServiceLocator()->get('VuFind\Http')->createClient()
-        );
-    }
-}
diff --git a/module/finc/src/finc/Resolver/Driver/FincResolverDriverFactory.php b/module/finc/src/finc/Resolver/Driver/FincResolverDriverFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..c57fb6595b8c041aa12ad4aa164e468a91782ef8
--- /dev/null
+++ b/module/finc/src/finc/Resolver/Driver/FincResolverDriverFactory.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Copyright (C) Leipzig University Library 2019.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @author   Dorian Merz <merz@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ */
+
+namespace finc\Resolver\Driver;
+
+use VuFind\Resolver\Driver\DriverInterface;
+use Zend\Config\Config;
+use Zend\ServiceManager\Factory\FactoryInterface;
+use Interop\Container\ContainerInterface;
+
+class FincResolverDriverFactory implements FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     *
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     * creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        $resolverConfig = $this->getResolverConfig($container,$requestedName);
+        $client = $container->get('VuFindHttp\HttpService')->createClient();
+        return new $requestedName(
+            $resolverConfig,
+            $client
+        );
+    }
+
+    protected function getResolverConfig(ContainerInterface $container,$requestedName) {
+        /** @var Config $resolverConfig */
+        $resolverConfig = $container->get('VuFind\Config')->get('Resolver');
+        $name = $requestedName;
+        if ($config = $resolverConfig->get($name)) return $config;
+        $parts = explode('\\',$requestedName);
+        $name = array_pop($parts);
+        if ($config = $resolverConfig->get($name)) return $config;
+        $name = strtolower($name);
+        if ($config = $resolverConfig->get($name)) return $config;
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLink.php b/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLink.php
index e650605d4d91d8e6989108adfab2b429606725a2..3218af2a6108144cae40d22ef394f5a47e9f5f41 100644
--- a/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLink.php
+++ b/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLink.php
@@ -73,9 +73,9 @@ class ExternalCatalogueLink extends \Zend\View\Helper\AbstractHelper
      * @param array $extCatConf External catalogue link configuration
      */
     public function __construct(
-        $config,
-        $extCatConf = []
-    ) {
+        $config, $extCatConf = []
+    )
+    {
         $this->config = $config;
         $this->extCatConf = $extCatConf;
     }
@@ -117,30 +117,18 @@ class ExternalCatalogueLink extends \Zend\View\Helper\AbstractHelper
         $extUrls = [];
 
         foreach ($this->extCatConf as $recordType => $accessUrls) {
-            $replaceId = null;
-            // get identifier of record id type
-            switch ($recordType) {
-                case "id":
-                    $replaceId = $this->driver->getUniqueID();
-                    break;
-                case "record_id":
-                    $replaceId = $this->driver->tryMethod('getRID');
-                    break;
-                default:
-                    $replaceId = $this->driver->tryMethod('get'.ucfirst($recordType));
+            $replaceId = $this->driver->tryMethod('getField', [$recordType]);
+            if (empty($replaceId)) {
+                continue;
             }
             foreach ($accessUrls as $institution => $accessUrl) {
+                // check institution
+                if (false === in_array($institution, $institutions)) {
+                    continue;
+                }
                 foreach ($accessUrl as $v) {
-                    // pre-filter replaceId
-                    if (isset($v['filter'])) {
-                        $isReplaceId = (
-                            true === $this->filterAccessibilityUrl($v['filter'])
-                        ) ? $replaceId : null;
-                    }
-                    // institution filter
-                    if (true === in_array($institution, $institutions)
-                        && !empty($isReplaceId)
-                    ) {
+                    // check filter
+                    if (!isset($v['filter']) || true === $this->filterAccessibilityUrl($v['filter'])) {
                         $extUrls[++$i]['desc'] = $institution;
                         $extUrls[$i]['url'] = sprintf($v['pattern'], $replaceId);
                     }
diff --git a/module/finc/src/finc/View/Helper/Root/RecordLink.php b/module/finc/src/finc/View/Helper/Root/RecordLink.php
index 21fe564f6cf1775e144ce71dc7a2a9b34ed85fbb..402b732b9ad00dd63ae44d9f53f008257303a3c2 100644
--- a/module/finc/src/finc/View/Helper/Root/RecordLink.php
+++ b/module/finc/src/finc/View/Helper/Root/RecordLink.php
@@ -28,9 +28,10 @@
  */
 namespace finc\View\Helper\Root;
 
-use VuFindSearch\Query\Query as Query;
+use VuFind\Exception\RecordMissing as RecordMissingException;
 use VuFind\Record\Loader as Loader;
 use VuFind\Record\Router as Router;
+use VuFindSearch\Query\Query as Query;
 use VuFindSearch\Service as SearchService;
 
 /**
@@ -139,4 +140,23 @@ class RecordLink extends \VuFind\View\Helper\Root\RecordLink
         }
         return null;
     }
+
+    /**
+     * Get the default URL for a record.
+     * refs #16002 - don't throw exception if record not found
+     *
+     * @param \VuFind\RecordDriver\AbstractBase|string $driver Record driver
+     * representing record to link to, or source|id pipe-delimited string
+     *
+     * @return string
+     */
+    public function getUrl($driver)
+    {
+        try {
+            return $this->getTabUrl($driver);
+        }
+        catch (RecordMissingException $exception) {
+            return "";
+        }
+    }
 }
diff --git a/module/finc/tests/fixtures/externallink/testexternallink1.json b/module/finc/tests/fixtures/externallink/testexternallink1.json
index 0dc98abbbb0f813a676fc04ad33c5bd9bf78bf14..a003aa3234013507e659fe5dd78f4acec55064df 100644
--- a/module/finc/tests/fixtures/externallink/testexternallink1.json
+++ b/module/finc/tests/fixtures/externallink/testexternallink1.json
@@ -1,9 +1,9 @@
 {
   "ppn": {
-    "DE-540": {
+    "DE-540": [{
       "pattern": "http://194.94.197.6/libero/WebopacOpenURL.cls?ACTION=DISPLAY&LANG=DE&RID=%s"
-    },
-    "DE-D13": {
+    }],
+    "DE-D13": [{
       "pattern": "http://webopac.skd.museum/libero/WebopacOpenURL.cls?ACTION=DISPLAY&RID=%s",
       "filter": {
         "getSourceID": [
@@ -11,17 +11,17 @@
           "22"
         ]
       }
-    },
-    "DE-L242": {
+    }],
+    "DE-L242": [{
       "pattern": "http://webpac.bibhgb.hgb-leipzig.de/libero/WebopacOpenURL.cls?ACTION=DISPLAY&RID=%s"
-    },
-    "DE-D117": {
+    }],
+    "DE-D117": [{
       "pattern": "http://www.hfmdd.de:8060/alipac/-/item-local?VID=%s"
-    }
+    }]
   },
   "id": {
-    "DE-14": {
+    "DE-14": [{
       "pattern": "http://katalogbeta.slub-dresden.de/id/%s/"
-    }
+    }]
   }
 }
\ No newline at end of file
diff --git a/module/finc/tests/unit-tests/src/fincTest/View/Helper/Root/ExternalCatalogueLinkTest.php b/module/finc/tests/unit-tests/src/fincTest/View/Helper/Root/ExternalCatalogueLinkTest.php
index f9a6557cb94b42314da6e0c7c902bbede0be67ad..5a2559ae84f3c67558be3a6617ed01f400535d02 100644
--- a/module/finc/tests/unit-tests/src/fincTest/View/Helper/Root/ExternalCatalogueLinkTest.php
+++ b/module/finc/tests/unit-tests/src/fincTest/View/Helper/Root/ExternalCatalogueLinkTest.php
@@ -52,7 +52,7 @@ class ExternalCatalogueLinkTest extends \VuFindTest\Unit\ViewHelperTestCase
      */
     public function testGetLinks()
     {
-        //fwrite(STDERR, print_r($driver, true));
+        // @codingStandardsIgnoreStart
         $externalCatalogue
             = $this->getExternalCatalogueLink(
                 [],
@@ -85,7 +85,6 @@ class ExternalCatalogueLinkTest extends \VuFindTest\Unit\ViewHelperTestCase
                 )
             );
         $links = $externalCatalogue->getLinks();
-        fwrite(STDERR, print_r($links, true));
         $expectedArray = [
             [
                 "desc" => "DE-540",
@@ -93,6 +92,7 @@ class ExternalCatalogueLinkTest extends \VuFindTest\Unit\ViewHelperTestCase
             ]
         ];
         $this->assertEquals(json_encode($expectedArray), json_encode($links));
+        // @codingStandardsIgnoreEnd
     }
 
     /**
@@ -114,6 +114,7 @@ class ExternalCatalogueLinkTest extends \VuFindTest\Unit\ViewHelperTestCase
         $institutions = ["DE-14", "DE-D13"]
     ) {
         $driver = $this->getMockBuilder($class)
+            ->setMethods(['getUniqueID', 'getSourceID', 'tryMethod'])
             ->disableOriginalConstructor()->getMock();
         $driver->expects($this->any())->method('getUniqueID')
             ->will($this->returnValue($id));
@@ -124,6 +125,7 @@ class ExternalCatalogueLinkTest extends \VuFindTest\Unit\ViewHelperTestCase
             ->withConsecutive(
                 [$this->equalTo('getInstitutions')],
                 [$this->equalTo('getRID')],
+                [$this->equalTo('getPpn')],
                 [$this->equalTo('getSourceID')]
             )
             ->willReturnOnConsecutiveCalls(
@@ -180,5 +182,4 @@ class ExternalCatalogueLinkTest extends \VuFindTest\Unit\ViewHelperTestCase
             new ExternalCatalogueLink(new Config($config), $rules);
         return $externalCatalogueLink;
     }
-
-}
\ No newline at end of file
+}
diff --git a/themes/fid/languages/fid/de.ini b/themes/fid/languages/fid/de.ini
index ea754dd28ad75bdb1d87a43a782cef158925c98a..e2e6d6f8fe36f233054722e99ccefa496f1222af 100644
--- a/themes/fid/languages/fid/de.ini
+++ b/themes/fid/languages/fid/de.ini
@@ -1,5 +1,6 @@
 auth_error_bad_credentials = Nutzername oder Passwort falsch.
 auth_error_unknown_reason = Anmeldung derzeit nicht möglich.
+auth_error_account_blocked = Ihr Konto wurde aus Sicherheitsgründen gesperrt. Bitte kontaktieren Sie uns unter info@adlr.link, um eine Entsperrung vorzunehmen.
 
 user_init_form_title = "Registrierung"
 user_create_form_title = "Registrierung abschließen"
@@ -22,6 +23,7 @@ label_home_library = "Heimatbibliothek"
 label_year_of_birth = "Geburtsjahr"
 label_job_title = "Berufsbezeichnung"
 label_college = "Hochschule"
+label_phone = Telefon
 label_password = "Passwort"
 label_password_confirmation = "Passwort wiederholen"
 label_access_level = "Ich gehöre folgender Nutzergruppe an"
@@ -30,7 +32,11 @@ label_access_level_limited_access = "Studierende, Sonstige"
 label_access_level_read_user_list = "Nutzerverwaltung: Nutzerliste lesen"
 label_access_level_edit_user = "Nutzerverwaltung: Nutzerprofile bearbeiten"
 label_access_level_unknown = "Unbekannte oder unbestätigte Gruppe(n)."
-label_permissions = "autorisiert für"
+label_permissions = "Zugriffsstatus"
+
+permission_status_denied = verwehrt
+permission_status_requested = angefragt
+permission_status_granted = bewilligt
 
 label_delivery_address = "Privatadresse als Lieferadresse verwenden"
 Business = "Dienstadresse"
@@ -41,13 +47,12 @@ label_address_zip = "Postleitzahl"
 label_address_city = "Ort"
 label_address_country = "Land"
 label_submit = "Abschicken"
+label_update_submit = Ändern
 terms = "Nutzungsbedingungen"
 policy = "Datenschutzerklärung"
 policy_text = "Ich akzeptiere die "
 terms_text = " und die "
 
-error_empty_address_value = "Dieser Wert wird benötigt, wenn Sie ein Adresse angeben wollen."
-
 error_username = "Bitte geben Sie eine E-Mail-Adresse an."
 error_username_confirmation = "Die angegebenen Email-Adressen stimmen nicht überein."
 error_password_confirmation = "Die angegebenen Passwörter stimmen nicht überein."
@@ -69,6 +74,12 @@ user_update_error_403 = "Es ist ein Fehler beim Aktualisieren Ihres Profils aufg
 user_update_error_expired = "Bitte loggen Sie sich ein, um Ihr Profil zu editieren."
 user_update_success = "Ihr Profil wurde erfolgreich aktualisiert."
 
+user_my_orders = "Buchbestellungen"
+orders_error = "Fehler beim Abrufen der Bestellungen"
+user_orders_empty = "Für Ihr Profil liegen in den letzten 3 Monaten keine Bestellungen vor."
+order_delivery_address = "Lieferadresse"
+order_record = "Bestellter Titel"
+
 password_reset_error = "Es ist ein unerwarteter Fehler aufgetreten."
 password_reset_error_username = "Bitte überprüfen Sie die angegebende E-Mail-Adresse %s."
 password_reset_success = "Ein Link zum Ändern Ihres Passworts wurde soeben an Ihre angegebene E-Mail-Adresse %s gesendet. Die Zustellung kann einige Minuten in Anspruch nehmen. Bitte schauen Sie ggf. auch in Ihren Spamordner."
@@ -106,5 +117,6 @@ permission_read_user_list = "Liste aller Nutzer einsehen"
 permission_edit_user = "Daten anderer Nutzer bearbeiten"
 permission_full_access = "Zugriff auf FID-Lizenzen"
 permission_limited_access = "Einfacher Zugriff"
+permission_basic_access = "aktiviert"
 
-admin_section = "Nutzerverwaltung"
\ No newline at end of file
+admin_section = "Nutzerverwaltung"
diff --git a/themes/fid/languages/fid/en.ini b/themes/fid/languages/fid/en.ini
index 505dac4030f48d7fdbb87dbbd231e40b43a53972..a8a6a1d2e89510bfc65ce0f0620fa91f364fe578 100644
--- a/themes/fid/languages/fid/en.ini
+++ b/themes/fid/languages/fid/en.ini
@@ -1,5 +1,6 @@
 auth_error_bad_credentials = Invalid username or password.
 auth_error_unknown_reason = Login currently impossible.
+auth_error_account_blocked = Your account has been disabled due to security reasons. Please contact us at info@adlr.link for more details.
 
 user_init_form_title = "Registration"
 user_create_form_title = "Complete registration"
@@ -24,13 +25,18 @@ label_home_library = "Home library"
 label_year_of_birth = "Year of birth (YYYY)"
 label_job_title = "Occupation"
 label_college = "University"
+label_phone = Phone
 label_access_level = "Please select your membership level"
 label_access_level_full_access = "Professor, Research Associate/Assistant, Member of an Academic Association, PhD Student (also Scholarships), (Visiting) Lecturer"
 label_access_level_limited_access = "Student, Other"
 label_access_level_read_user_list = "User Administration: Read User List"
 label_access_level_edit_user = "User Administration: Edit User Profiles"
 label_access_level_unknown = "Unknown or ungranted user group(s)."
-label_permissions = "authorized for"
+label_permissions = "Access status"
+
+permission_status_denied = denied
+permission_status_requested = requested
+permission_status_granted = granted
 
 label_delivery_address = "Use the following address for deliveries"
 Business = "Office Address"
@@ -41,13 +47,12 @@ label_address_zip = "Zip code"
 label_address_city = "City"
 label_address_country = "Country"
 label_submit = "Submit"
+label_update_submit = Change
 terms = "Terms of Use"
 policy = "Privacy Policy"
 policy_text = "I hereby accept the "
 terms_text = " and "
 
-error_empty_address_value = "This field is required in case you want to provide an address."
-
 error_username = "Please specify an email address."
 error_username_confirmation = "The email addresses do not match."
 error_password_confirmation = "The passwords do not match."
@@ -69,6 +74,12 @@ user_update_error = "An unexpected error has occurred when updating your profile
 user_update_error_403 = "An error has occurred when updating your profile: Forbidden."
 user_update_error_expired = "Please log in to edit your profile."
 
+user_my_orders = "PDA Order Items"
+orders_error = "Error when retrieving list of orders"
+user_orders_empty = "There are no orders for your profile within the last 3 months."
+order_delivery_address = "Delivery Address"
+order_record = "Ordered Record"
+
 password_reset_error = "An unexpected error has occurred."
 password_reset_error_username = "Please check if the email address %s is valid."
 password_reset_success = "We have sent you an e-mail to %s containing a link for changing your password. This may take several minutes. Please check also the junk folder of your mailbox."
@@ -105,5 +116,6 @@ permission_read_user_list = "Read list of all users"
 permission_edit_user = "Edit other user's data"
 permission_full_access = "privileged access"
 permission_limited_access = "basic access"
+permission_basic_access = "enabled"
 
-admin_section = "User Administration"
\ No newline at end of file
+admin_section = "User Administration"
diff --git a/themes/fid/scss/compiled.scss b/themes/fid/scss/compiled.scss
index 83fbd068b2e3550e225d45042b09adcd10fe9410..5250f4d6391d7070a830e7f11d4618fa8a39052b 100644
--- a/themes/fid/scss/compiled.scss
+++ b/themes/fid/scss/compiled.scss
@@ -18,67 +18,14 @@
 @import 'customVariables';
 @import 'customMixins';
 
-.fid-user-create-form {
-  padding: 10px 0;
-
-  h2 {
-    padding-bottom: 10px;
-  }
-
-  .form-control {
-    margin: 0;
-  }
-
-  .form-group {
-    & > .col-md-4, .col-md-6, .col-md-8, .col-md-10 {
-      padding: 0;
-    }
-
-    &:after {
-      display: table;
-      content: " ";
-      clear: both;
-    }
-
-    &.eula > :first-child * {
-      display: inline;
-    }
-  }
-
-  input[type=checkbox] {
-    margin-top: 0;
-    margin-right: 10px;
-    vertical-align: middle;
-    display: inline-block;
-  }
-
-  input[type=radio] {
-    margin-top: 0;
-    margin-right: 10px;
-    vertical-align: middle;
-    display: inline-block;
-  }
-
-  label.col-md-4,
-  label[for=eulaAccepted],
-  .col-md-4 > label {
-    &[data-required]::after {
-      content: ' *';
-    }
-  }
-}
-
-.fid-user-init-form {
-  label[data-required]::after {
-    content: ' *';
-  }
-}
-
-.registration {
+#user-init-form, #user-create-form, #user-update-form {
   input[type="checkbox"] {
     display: inline-flex;
     margin-right: $margin-right-width;
-    padding-left: $margin-right-width;
+  }
+
+  label[data-required="true"][for]::after {
+    content: ' *';
   }
 }
 
diff --git a/themes/fid/templates/fid/admin/edit.phtml b/themes/fid/templates/fid/admin/edit.phtml
index ef46743db675a7b319d20895342e2c86223a6260..81cc338eaf41898ad6c5798bd92d1ae877389f45 100644
--- a/themes/fid/templates/fid/admin/edit.phtml
+++ b/themes/fid/templates/fid/admin/edit.phtml
@@ -20,214 +20,190 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-use fid\Service\DataTransferObject\User;
 use Zend\Form\Element as Element;
 use Zend\Form\Element\Submit;
-use Zend\Form\Form;
 use Zend\Form\View\Helper\FormElementErrors;
 use Zend\Form\View\Helper\FormLabel;
 use Zend\Form\View\Helper\FormRadio;
 use Zend\Form\View\Helper\FormSelect;
 use Zend\Form\View\Helper\FormSubmit;
-use Zend\I18n\Translator\TranslatorInterface;
 
 /** @var FormLabel $formLabel */
+/** @var FormRadio $formRadio */
+/** @var FormSelect $formSelect */
+/** @var FormSubmit $formSubmit */
+/** @var FormElementErrors $formElementErrors */
 $formLabel = $this->formLabel();
-/**@var FormRadio $formRadio */
 $formRadio = $this->formRadio();
-/**@var FormSelect $formSelect */
 $formSelect = $this->formSelect();
-/**@var FormSubmit $formSubmit */
 $formSubmit = $this->formSubmit();
-/** @var FormElementErrors $formElementErrors */
 $formElementErrors = $this->formElementErrors();
 $formLabel->setTranslatorTextDomain('fid');
 $formSelect->setTranslatorTextDomain('fid');
 $formRadio->setTranslatorTextDomain('fid');
 $formSubmit->setTranslatorTextDomain('fid');
 $formElementErrors->setTranslatorTextDomain('fid');
-/** @var TranslatorInterface $translator */
-$translator = $this->getHelperPluginManager()->get('translate')
-    ->getTranslator();
-
-$formLabel->setTranslator($translator);
-$formElementErrors->setTranslator($translator);
-
-/** @var Form $form */
-/** @var User $user */
-$user = $this->user;
-$userId = $user->getId();
-$form = $this->form;
-$form->setAttribute('method', 'post');
-$form->setAttribute('action', $this->url('fid/admin/edit',['userid' => $userId]));
-$form->setAttribute('class', 'form-horizontal');
-$form->prepare();
-
-$this->headTitle($this->translate('Profile Form'));
-$this->headTitle($this->translate("fid::user_update_form_title"));
+$this->headTitle($title = $this->translate("fid::user_edit_form_title", [
+    '%%userid%%'   => $user->getId(),
+    '%%username%%' => $user->getUsername()
+]));
+$editableFields = $this->config['Admin']['editable_user_fields'] ?? [];
+$form->setAttributes(['class' => 'row']);
+echo $this->form()->openTag($form);
 ?>
-<!-- fid: admin - edit -->
-    <h2><?= $this->translate("fid::user_edit_form_title",['%%userid%%' => $userId, '%%username%%' => $user->getUsername()]) ?></h2>
+
+<h2 class="row col-md-12"><?= $title ?></h2>
+<div class="row col-md-12"><?= $this->transEsc("fid::required_fields_note") ?></div>
 <?= $this->flashmessages() ?>
-<?= $this->form()->openTag($form) ?>
-<?php
-/** @var Element\Hidden $elemUserId */
-$elemUserId = $form->get('userId');
-$elemUserId->setValue($userId);
-?>
-    <div class="form-group"><?=$this->formHidden($elemUserId)?></div>
+<br/>
 
-<? /* home library */ ?>
-<?php
-/** @var Element\Select $elemHomeLibrary */
-$elemHomeLibrary = $form->get('homeLibrary');
-$elemHomeLibrary->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemHomeLibrary->setAttributes(['class' => 'form-control inline col-sm-6']);
-$elemHomeLibrary->setValue($user->getHomeLibrary());
-if (!in_array('HomeLibrary',$editableFields)) {
-    $elemHomeLibrary->setAttribute('readonly', '1');
-}
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemHomeLibrary) ?>
-        <?= $this->formSelect($elemHomeLibrary) ?>
-        <?= $this->formElementErrors($elemHomeLibrary) ?>
-    </div>
+<? /* id */ ?>
+<?= $this->formElement($form->get('id')) ?>
 
 <? /* salutation */ ?>
 <?php
 /** @var Element\Select $elemSalutation */
 $elemSalutation = $form->get('salutation');
-$elemSalutation->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemSalutation->setAttributes(['class' => 'form-control inline col-sm-2']);
-$elemSalutation->setValue($user->getSalutation());
-if (!in_array('Salutation',$editableFields)) {
+$elemSalutation->setLabelAttributes(['class' => 'col-md-3']);
+$elemSalutation->setAttributes(['class' => 'col-md-3']);
+if (!in_array('Salutation', $editableFields)) {
     $elemSalutation->setAttribute('readonly', '1');
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemSalutation) ?>
-        <?= $this->formSelect($elemSalutation) ?>
-
-    </div>
+<div class="row">
+    <?= $this->formLabel($elemSalutation) ?>
+    <?= $this->formSelect($elemSalutation) ?>
+</div>
 
 <? /* academic title */ ?>
 <?php
 /** @var Element\Text $elemAcademicTitle */
-$elemAcademicTitle = $form->get('academicTitle');
-$elemAcademicTitle->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemAcademicTitle->setAttributes(['class' => 'form-control']);
-$elemAcademicTitle->setValue($user->getAcademicTitle());
-if (!in_array('AcademicTitle',$editableFields)) {
+$elemAcademicTitle = $form->get('academic_title');
+$elemAcademicTitle->setLabelAttributes(['class' => 'col-md-3']);
+$elemAcademicTitle->setAttributes(['class' => 'col-md-3']);
+if (!in_array('AcademicTitle', $editableFields)) {
     $elemAcademicTitle->setAttribute('readonly', '1');
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemAcademicTitle) ?>
-        <?= $this->formElement($elemAcademicTitle) ?>
-    </div>
+<div class="row">
+    <?= $this->formLabel($elemAcademicTitle) ?>
+    <?= $this->formElement($elemAcademicTitle) ?>
+    <br/>
+</div>
 
 <? /* firstname */ ?>
 <?php
 /** @var Element\Text $elemFirstname */
 $elemFirstname = $form->get('firstname');
-$elemFirstname->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemFirstname->setAttributes(['class' => 'form-control']);
-$elemFirstname->setValue($user->getFirstname());
-if (!in_array('Firstname',$editableFields)) {
+$elemFirstname->setLabelAttributes(['class' => 'col-md-3']);
+$elemFirstname->setAttributes(['class' => 'col-md-3']);
+if (!in_array('Firstname', $editableFields)) {
     $elemFirstname->setAttribute('readonly', '1');
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemFirstname) ?>
-        <?= $this->formElement($elemFirstname) ?>
-        <?= $this->formElementErrors($elemFirstname) ?>
-    </div>
+<div class="row">
+    <?= $this->formLabel($elemFirstname) ?>
+    <?= $this->formElement($elemFirstname) ?>
+    <?= $this->formElementErrors($elemFirstname) ?>
+</div>
 
 <? /* lastname */ ?>
 <?php
 /** @var Element\Text $elemLastname */
 $elemLastname = $form->get('lastname');
-$elemLastname->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemLastname->setAttributes(['class' => 'form-control']);
-$elemLastname->setValue($user->getLastname());
-if (!in_array('Lastname',$editableFields)) {
+$elemLastname->setLabelAttributes(['class' => 'col-md-3']);
+$elemLastname->setAttributes(['class' => 'col-md-3']);
+if (!in_array('Lastname', $editableFields)) {
     $elemLastname->setAttribute('readonly', '1');
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemLastname) ?>
-        <?= $this->formElement($elemLastname) ?>
-        <?= $this->formElementErrors($elemLastname) ?>
-    </div>
-
-<? /* year of birth */ ?>
-<?php
-/** @var Element\Text $elemYearOfBirth */
-$elemYearOfBirth = $form->get('yearOfBirth');
-$elemYearOfBirth->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemYearOfBirth->setAttributes(['class' => 'form-control']);
-$elemYearOfBirth->setValue($user->getYearOfBirth());
-if (!in_array('YearOfBirth',$editableFields)) {
-    $elemYearOfBirth->setAttribute('readonly', '1');
-}
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemYearOfBirth) ?>
-        <?= $this->formElement($elemYearOfBirth) ?>
-        <?= $this->formElementErrors($elemYearOfBirth) ?>
-    </div>
+<div class="row">
+    <?= $this->formLabel($elemLastname) ?>
+    <?= $this->formElement($elemLastname) ?>
+    <?= $this->formElementErrors($elemLastname) ?>
+</div>
 
 <? /* college */ ?>
 <?php
 /** @var Element\Text $elemCollege */
 $elemCollege = $form->get('college');
-$elemCollege->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemCollege->setAttributes(['class' => 'form-control']);
-$elemCollege->setValue($user->getCollege());
-if (!in_array('College',$editableFields)) {
+$elemCollege->setLabelAttributes(['class' => 'col-md-3']);
+$elemCollege->setAttributes(['class' => 'col-md-3']);
+if (!in_array('College', $editableFields)) {
     $elemCollege->setAttribute('readonly', '1');
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemCollege) ?>
-        <?= $this->formElement($elemCollege) ?>
-        <?= $this->formElementErrors($elemCollege) ?>
-    </div>
+<div class="row">
+    <?= $this->formLabel($elemCollege) ?>
+    <?= $this->formElement($elemCollege) ?>
+    <?= $this->formElementErrors($elemCollege) ?>
+</div>
+
+<? /* home library */ ?>
+<?php
+/** @var Element\Select $elemHomeLibrary */
+$elemHomeLibrary = $form->get('home_library');
+$elemHomeLibrary->setLabelAttributes(['class' => 'col-md-3']);
+$elemHomeLibrary->setAttributes(['class' => 'col-md-6']);
+if (!in_array('HomeLibrary', $editableFields)) {
+    $elemHomeLibrary->setAttribute('disabled', '1');
+}
+?>
+<div class="row">
+    <?= $this->formLabel($elemHomeLibrary) ?>
+    <?= $this->formSelect($elemHomeLibrary) ?>
+    <?= $this->formElementErrors($elemHomeLibrary) ?>
+</div>
 
-<? /* job title */ ?>
+<? /* user group */ ?>
 <?php
-/** @var Element\Text $elemJobTitle */
-$elemJobTitle = $form->get('jobTitle');
-$elemJobTitle->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemJobTitle->setAttributes(['class' => 'form-control']);
-$elemJobTitle->setValue($user->getJobTitle());
-if (!in_array('JobTitle',$editableFields)) {
+/** @var Element\Radio $elemJobTitle */
+$elemJobTitle = $form->get('job_title');
+$elemJobTitle->setLabelAttributes(['class' => 'col-md-3']);
+$elemJobTitle->setAttributes(['class' => 'col-md-6']);
+if (!in_array('JobTitle', $editableFields)) {
     $elemJobTitle->setAttribute('readonly', '1');
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemJobTitle) ?>
-        <?= $this->formElement($elemJobTitle) ?>
-        <?= $this->formElementErrors($elemJobTitle) ?>
-    </div>
+<div class="row">
+    <?= $this->formLabel($elemJobTitle) ?>
+    <?= $this->formElement($elemJobTitle) ?>
+    <?= $this->formElementErrors($elemJobTitle) ?>
+</div>
 
 <? /* Permissions */ ?>
 <?php
-/** @var Element\MultiCheckbox $elemPermissions */
+/** @var Element\Collection $elemPermissions */
+/** @var Element\Select[] $elemPermissionsElems */
 $elemPermissions = $form->get('permissions');
-$elemPermissions->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-if (!in_array('Permissions',$editableFields)) {
-    $elemPermissions->setAttribute('readonly', '1');
+$elemPermissions->setLabelAttributes(['class' => 'col-md-3']);
+$elemPermissionsElems = $elemPermissions->getElements();
+$permissions = $this->config['Admin']['permission_options'] ?? [];
+foreach ($permissions as $permission) {
+    $element = $elemPermissionsElems[$permission];
+    $element->setLabel("permission_$permission");
+    $element->setAttributes(['class' => 'col-md-2']);
+    $element->setLabelAttributes(['class' => 'col-md-6']);
+    $element->setAttribute('id', $element->getName());
+    $elements[] = $element;
 }
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemPermissions) ?>
-        <div class="permission-options col-sm-7">
-            <?= $this->formMultiCheckbox($elemPermissions) ?>
+
+<div class="row">
+    <?= $this->formLabel($elemPermissions) ?>
+    <div class="col-md-9">
+        <?php foreach ($elements as $element): ?>
+        <div class="row">
+            <?= $this->formElement($element) ?>
+            <?= $this->formLabel($element) ?>
+        </div>
+        <? endforeach; ?>
+        <div class="row">
+            <?= $this->formElementErrors($elemPermissions) ?>
         </div>
-        <?= $this->formElementErrors($elemPermissions) ?>
     </div>
+</div>
+
 
 <? /* submit button */ ?>
 <?php
@@ -235,15 +211,15 @@ if (!in_array('Permissions',$editableFields)) {
 $elemSubmit = $form->get('submit');
 $elemSubmit->setAttributes(['class' => 'btn btn-primary']);
 ?>
-    <div class="form-group">
-        <div class="col-lg-11 col-md-9 col-sm-11 col-xs-12">
-            <?= $this->formSubmit($elemSubmit) ?>
-            <a href="<?= $this->url('fid/admin/list') ?>"
-               class="btn btn-primary">
-                <?= $this->transEsc('Cancel') ?>
-            </a>
-        </div>
+<div class="row">
+    <div class="col-lg-11 col-md-9 col-sm-11 col-xs-12">
+        <?= $this->formSubmit($elemSubmit) ?>
+        <a href="<?= $this->url('fid/admin/list') ?>"
+           class="btn btn-primary">
+            <?= $this->transEsc('Cancel') ?>
+        </a>
     </div>
+</div>
 
 <?= $this->form()->closeTag($form) ?>
 <!-- fid: admin - edit - END -->
\ No newline at end of file
diff --git a/themes/fid/templates/fid/admin/list-entry.phtml b/themes/fid/templates/fid/admin/list-entry.phtml
index 758790b43d39c1df1c9ad2ff3ee9d62db0b45931..54c1aef9c4ee9b185cdc7c0b84c87fbc2c88c2cf 100644
--- a/themes/fid/templates/fid/admin/list-entry.phtml
+++ b/themes/fid/templates/fid/admin/list-entry.phtml
@@ -1,11 +1,11 @@
 <!-- fid: user - admin - list-entry -->
 <?php
 $permissions  = [];
-if (!empty($permissions = $user->getPermissions())) {
-    foreach ($permissions as $key => &$perm) {
-        $perm = $this->escapeHtml($key).' ('.$this->escapeHtml($perm).')';
-    }
+foreach ($config['Admin']['permission_options'] ?? [] as $perm) {
+    $state = $user->getPermissions()[$perm];
+    $permissions[$perm] = $this->escapeHtml($perm).' ('.$this->escapeHtml($state).')';
 }
+
 foreach ($this->fields as $field):
 ?>
 <?php if ($field === 'Permissions'): ?>
diff --git a/themes/fid/templates/fid/admin/list.phtml b/themes/fid/templates/fid/admin/list.phtml
index a5c81b74f18d84ec99feefed4fec3d6b9dbd1965..3d0c9fefd4a38cab6481f753689144e0074ce41b 100644
--- a/themes/fid/templates/fid/admin/list.phtml
+++ b/themes/fid/templates/fid/admin/list.phtml
@@ -12,7 +12,7 @@
     </tr>
     <?php foreach ($list as $id => $user):?>
         <?php $tooltip = $this->translate('fid::user_edit',['%%userid%%' => $id]); ?>
-        <tr><td><a href="<?=$this->url('fid/admin/edit',['userid' => $id])?>" title="<?=$tooltip?>"><i class="fa fa-pencil-square-o"></i><span class="sr-only"><?=$tooltip?></span></a></td><th><?=$id?></th><?= $this->render('fid/admin/list-entry',['user' => $user,'fields'=>$fields]) ?></tr>
+        <tr><td><a href="<?=$this->url('fid/admin/edit',['userid' => $id])?>" title="<?=$tooltip?>"><i class="fa fa-pencil-square-o"></i><span class="sr-only"><?=$tooltip?></span></a></td><th><?=$id?></th><?= $this->render('fid/admin/list-entry',['user' => $user,'fields'=>$fields, 'config' => $config]) ?></tr>
     <?php endforeach; ?>
     </table>
 <?php else: ?>
diff --git a/themes/fid/templates/fid/user/update-addresses.phtml b/themes/fid/templates/fid/user/address-collection.phtml
similarity index 54%
rename from themes/fid/templates/fid/user/update-addresses.phtml
rename to themes/fid/templates/fid/user/address-collection.phtml
index 90142130e64d6d8b950a48a4948dd6ace45268d0..8b2e620bcb47ec7b6596af326d7a16446aa951ad 100644
--- a/themes/fid/templates/fid/user/update-addresses.phtml
+++ b/themes/fid/templates/fid/user/address-collection.phtml
@@ -19,25 +19,14 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-use fid\Service\DataTransferObject\Address;
-use fid\Service\DataTransferObject\User;
-use Zend\Form\Element\Collection;
+use Zend\Form\Fieldset;
 use Zend\Form\Form;
 
 /** @var Form $form */
-/** @var User $user */
-/** @var Collection $collection */
-list($user, $form) = [$this->user, $this->form];
-$collection = $form->get('addresses');
-$addresses = $user->getAddresses() ?: [new Address()];
-$collection->setCount($count = count($addresses));
-if (count($collection->getFieldsets()) < $count) {
-  $collection->prepareElement($form);
-}
-$context = compact('form', 'user', 'collection', 'addresses');
-foreach ($collection->getFieldsets() as $index => $fieldset) {
-  $address = $addresses[$index];
-  $context = compact('index', 'address', 'fieldset') + $context;
-  echo $this->render('update-address.phtml', $context);
+/** @var Fieldset $address */
+$form = $this->form;
+foreach ($form->get('addresses')->getFieldsets() as $address) {
+    $context = compact('form', 'address');
+    echo $this->render($address->getOption('template'), $context);
 }
 ?>
diff --git a/themes/fid/templates/fid/user/address-display-inline.phtml b/themes/fid/templates/fid/user/address-display-inline.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..2a4f6007cdb6a8e949d23de587f37fc1ea6c5f81
--- /dev/null
+++ b/themes/fid/templates/fid/user/address-display-inline.phtml
@@ -0,0 +1,5 @@
+<!-- fid: fid - user - address-display-inline -->
+<span class="address-inline">
+    <?=$this->escapeHtml($address->getLine1())?><?=$address->getLine2() ? ' '.$this->escapeHtml($address->getLine2()) : ''?>; <?=$this->escapeHtml($address->getZip())?> <?=$this->escapeHtml($address->getCity())?>, <?=$this->escapeHtml($address->getCountry())?>
+</span>
+<!-- fid: fid - user - address-display-inline - END -->
\ No newline at end of file
diff --git a/themes/fid/templates/fid/user/update-address.phtml b/themes/fid/templates/fid/user/address-fieldset.phtml
similarity index 59%
rename from themes/fid/templates/fid/user/update-address.phtml
rename to themes/fid/templates/fid/user/address-fieldset.phtml
index 54aac2961f2177d022e055258441d7abfb884cd5..c26c1973cd3a033b54fcd5d552dec601d94e68ee 100644
--- a/themes/fid/templates/fid/user/update-address.phtml
+++ b/themes/fid/templates/fid/user/address-fieldset.phtml
@@ -23,89 +23,81 @@ use fid\Service\DataTransferObject\Address;
 use Zend\Form\Fieldset;
 use Zend\Form\Element;
 
-/** @var Address $address */
-/** @var Fieldset $fieldset */
+/** @var Fieldset $address */
 $address = $this->address;
-$fieldset = $this->fieldset;
 ?>
 
+<h3><?= $this->translate('fid::' . $address->getLabel()) ?></h3>
+
 <? /* id */ ?>
 <?php
-
-if ($id = $address->getId()) {
-  /** @var Element\Hidden $elemId */
-  $elemId = $fieldset->get('id');
-  $elemId->setValue($address->getId());
-  echo $this->formElement($elemId);
+$elemId = $address->get('id');
+if ($elemId->getValue()) {
+    echo $this->formElement($elemId);
 }
 ?>
 
 <? /* first line */ ?>
 <?php
 /** @var Element\Text $elemLine1 */
-$elemLine1 = $fieldset->get('line1');
-$elemLine1->setValue($elemLine1->getValue() ?? $address->getLine1());
+$elemLine1 = $address->get('line1');
 $elemLine1->setLabelAttributes(['class' => 'col-md-4']);
 $elemLine1->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemLine1) ?>
-  <?= $this->formElement($elemLine1) ?>
-  <?= $this->formElementErrors($elemLine1) ?>
+    <?= $this->formLabel($elemLine1) ?>
+    <?= $this->formElement($elemLine1) ?>
+    <?= $this->formElementErrors($elemLine1) ?>
 </div>
 
 <? /* second line */ ?>
 <?php
 /** @var Element\Text $elemLine2 */
-$elemLine2 = $fieldset->get('line2');
-$elemLine2->setValue($elemLine2->getValue() ?? $address->getLine2());
+$elemLine2 = $address->get('line2');
 $elemLine2->setLabelAttributes(['class' => 'col-md-4']);
 $elemLine2->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemLine2) ?>
-  <?= $this->formElement($elemLine2) ?>
-  <?= $this->formElementErrors($elemLine2) ?>
+    <?= $this->formLabel($elemLine2) ?>
+    <?= $this->formElement($elemLine2) ?>
+    <?= $this->formElementErrors($elemLine2) ?>
 </div>
 
 <? /* address zip */ ?>
 <?php
 /** @var Element\Text $elemZip */
-$elemZip = $fieldset->get('zip');
-$elemZip->setValue($elemZip->getValue() ?? $address->getZip());
+$elemZip = $address->get('zip');
 $elemZip->setLabelAttributes(['class' => 'col-md-4']);
 $elemZip->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemZip) ?>
-  <?= $this->formElement($elemZip) ?>
-  <?= $this->formElementErrors($elemZip) ?>
+    <?= $this->formLabel($elemZip) ?>
+    <?= $this->formElement($elemZip) ?>
+    <?= $this->formElementErrors($elemZip) ?>
 </div>
 
 <? /* city */ ?>
 <?php
 /** @var Element\Text $elemCity */
-$elemCity = $fieldset->get('city');
-$elemCity->setValue($elemCity->getValue() ?? $address->getCity());
+$elemCity = $address->get('city');
 $elemCity->setLabelAttributes(['class' => 'col-md-4']);
 $elemCity->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemCity) ?>
-  <?= $this->formElement($elemCity) ?>
-  <?= $this->formElementErrors($elemCity) ?>
+    <?= $this->formLabel($elemCity) ?>
+    <?= $this->formElement($elemCity) ?>
+    <?= $this->formElementErrors($elemCity) ?>
 </div>
 
 <? /* country */ ?>
 <?php
 /** @var Element\Text $elemCountry */
-$elemCountry = $fieldset->get('country');
-$elemCountry->setValue($elemCountry->getValue() ?? $address->getCountry());
+$elemCountry = $address->get('country');
 $elemCountry->setLabelAttributes(['class' => 'col-md-4']);
 $elemCountry->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemCountry) ?>
-  <?= $this->formElement($elemCountry) ?>
-  <?= $this->formElementErrors($elemCountry) ?>
+    <?= $this->formLabel($elemCountry) ?>
+    <?= $this->formElement($elemCountry) ?>
+    <?= $this->formElementErrors($elemCountry) ?>
 </div>
diff --git a/themes/fid/templates/fid/user/create.phtml b/themes/fid/templates/fid/user/create.phtml
index 7a2bf2e0360b3ca5615af9954f400c8c2a05e9eb..d192ffb938178ed86dbe1feccfab4dcb21609d7f 100644
--- a/themes/fid/templates/fid/user/create.phtml
+++ b/themes/fid/templates/fid/user/create.phtml
@@ -20,56 +20,42 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
+use Zend\Form\Element;
 use Zend\Form\Element\Checkbox;
 use Zend\Form\Element\Submit;
 use Zend\Form\Form;
-use Zend\Form\Element as Element;
 use Zend\Form\View\Helper\FormElementErrors;
 use Zend\Form\View\Helper\FormLabel;
 use Zend\Form\View\Helper\FormRadio;
 use Zend\Form\View\Helper\FormSelect;
 use Zend\Form\View\Helper\FormSubmit;
-use Zend\I18n\Translator\TranslatorInterface;
 
+/** @var Form $form */
 /** @var FormLabel $formLabel */
+/** @var FormRadio $formRadio */
+/** @var FormSelect $formSelect */
+/** @var FormSubmit $formSubmit */
+/** @var FormElementErrors $formElementErrors */
+$form = $this->form;
 $formLabel = $this->formLabel();
-/**@var FormRadio $formRadio */
 $formRadio = $this->formRadio();
-/**@var FormSelect $formSelect */
 $formSelect = $this->formSelect();
-/**@var FormSubmit $formSubmit */
 $formSubmit = $this->formSubmit();
-/** @var FormElementErrors $formElementErrors */
 $formElementErrors = $this->formElementErrors();
 $formLabel->setTranslatorTextDomain('fid');
 $formSelect->setTranslatorTextDomain('fid');
 $formRadio->setTranslatorTextDomain('fid');
 $formSubmit->setTranslatorTextDomain('fid');
 $formElementErrors->setTranslatorTextDomain('fid');
-/** @var TranslatorInterface $translator */
-$translator = $this->getHelperPluginManager()->get('translate')
-    ->getTranslator();
-
-$formLabel->setTranslator($translator);
-$formElementErrors->setTranslator($translator);
-
-/** @var Form $form */
-$form = $this->form;
-$form->setAttribute('method', 'post');
-$form->setAttribute('action', $this->url('fid/user/create'));
-$form->setAttribute('class', 'fid-user-create-form registration');
-$form->prepare();
-
-$this->headTitle($this->translate('Profile Form'));
-$this->headTitle($this->translate("fid::user_create_form_title"));
+$this->headTitle($title = $this->translate("fid::user_create_form_title"));
 ?>
 
+<h2><?= $title ?></h2>
+<div><?= $this->transEsc("fid::required_fields_note") ?></div>
 <?= $this->flashmessages() ?>
 <?= $this->form()->openTag($form) ?>
-    <h2><?= $this->translate("fid::user_create_form_title") ?></h2>
-    <div class="subito-pg">
-        * <?= $this->transEsc("This field is required") ?>
-    </div>
+<br/>
+
 <? /* username */ ?>
 <?php
 /** @var Element\Text $elemUsername */
@@ -77,16 +63,16 @@ $elemUsername = $form->get('username');
 $elemUsername->setLabelAttributes(['class' => 'col-md-4']);
 $elemUsername->setAttributes(['class' => 'form-control', 'disabled' => 1]);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemUsername) ?>
-        <?= $this->formElement($elemUsername) ?>
-        <?= $this->formElementErrors($elemUsername) ?>
-        <?php
-        $elemUsername->removeAttribute('disabled');
-        $elemUsername->setAttribute('type', 'hidden');
-        echo $this->formElement($elemUsername);
-        ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemUsername) ?>
+    <?= $this->formElement($elemUsername) ?>
+    <?= $this->formElementErrors($elemUsername) ?>
+    <?php
+    $elemUsername->removeAttribute('disabled');
+    $elemUsername->setAttribute('type', 'hidden');
+    ?>
+    <?= $this->formElement($elemUsername) ?>
+</div>
 
 <? /* password */ ?>
 <?php
@@ -95,37 +81,24 @@ $elemPassword = $form->get('password');
 $elemPassword->setLabelAttributes(['class' => 'col-md-4']);
 $elemPassword->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemPassword) ?>
-        <?= $this->formElement($elemPassword) ?>
-        <?= $this->formElementErrors($elemPassword) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemPassword) ?>
+    <?= $this->formElement($elemPassword) ?>
+    <?= $this->formElementErrors($elemPassword) ?>
+</div>
 
 <? /* password confirmation */ ?>
 <?php
 /** @var Element\Password $elemPasswordConfirmation */
-$elemPasswordConfirmation = $form->get('passwordConfirmation');
+$elemPasswordConfirmation = $form->get('password_confirmation');
 $elemPasswordConfirmation->setLabelAttributes(['class' => 'col-md-4']);
 $elemPasswordConfirmation->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemPasswordConfirmation) ?>
-        <?= $this->formElement($elemPasswordConfirmation) ?>
-        <?= $this->formElementErrors($elemPasswordConfirmation) ?>
-    </div>
-
-<? /* home library */ ?>
-<?php
-/** @var Element\Select $elemHomeLibrary */
-$elemHomeLibrary = $form->get('homeLibrary');
-$elemHomeLibrary->setLabelAttributes(['class' => 'col-md-4']);
-$elemHomeLibrary->setAttributes(['class' => 'form-control']);
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemHomeLibrary) ?>
-        <?= $this->formSelect($elemHomeLibrary) ?>
-        <?= $this->formElementErrors($elemHomeLibrary) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemPasswordConfirmation) ?>
+    <?= $this->formElement($elemPasswordConfirmation) ?>
+    <?= $this->formElementErrors($elemPasswordConfirmation) ?>
+</div>
 
 <? /* salutation */ ?>
 <?php
@@ -134,22 +107,22 @@ $elemSalutation = $form->get('salutation');
 $elemSalutation->setLabelAttributes(['class' => 'col-md-4']);
 $elemSalutation->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemSalutation) ?>
-        <?= $this->formSelect($elemSalutation) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemSalutation) ?>
+    <?= $this->formSelect($elemSalutation) ?>
+</div>
 
 <? /* academic title */ ?>
 <?php
 /** @var Element\Text $elemAcademicTitle */
-$elemAcademicTitle = $form->get('academicTitle');
+$elemAcademicTitle = $form->get('academic_title');
 $elemAcademicTitle->setLabelAttributes(['class' => 'col-md-4']);
 $elemAcademicTitle->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemAcademicTitle) ?>
-        <?= $this->formElement($elemAcademicTitle) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemAcademicTitle) ?>
+    <?= $this->formElement($elemAcademicTitle) ?>
+</div>
 
 <? /* firstname */ ?>
 <?php
@@ -158,11 +131,11 @@ $elemFirstname = $form->get('firstname');
 $elemFirstname->setLabelAttributes(['class' => 'col-md-4']);
 $elemFirstname->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemFirstname) ?>
-        <?= $this->formElement($elemFirstname) ?>
-        <?= $this->formElementErrors($elemFirstname) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemFirstname) ?>
+    <?= $this->formElement($elemFirstname) ?>
+    <?= $this->formElementErrors($elemFirstname) ?>
+</div>
 
 <? /* lastname */ ?>
 <?php
@@ -170,151 +143,79 @@ $elemFirstname->setAttributes(['class' => 'form-control']);
 $elemLastname = $form->get('lastname');
 $elemLastname->setLabelAttributes(['class' => 'col-md-4']);
 $elemLastname->setAttributes(['class' => 'form-control']);
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemLastname) ?>
-        <?= $this->formElement($elemLastname) ?>
-        <?= $this->formElementErrors($elemLastname) ?>
-    </div>
-
-<? /* year of birth */ ?>
-<?php
-/** @var Element\Text $elemYearOfBirth */
-$elemYearOfBirth = $form->get('yearOfBirth');
-$elemYearOfBirth->setLabelAttributes(['class' => 'col-md-4']);
-$elemYearOfBirth->setAttributes(['class' => 'form-control']);
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemYearOfBirth) ?>
-        <?= $this->formElement($elemYearOfBirth) ?>
-        <?= $this->formElementErrors($elemYearOfBirth) ?>
-    </div>
-
-<? /* college */ ?>
-<?php
-/** @var Element\Text $elemCollege */
-$elemCollege = $form->get('college');
-$elemCollege->setLabelAttributes(['class' => 'col-md-4']);
-$elemCollege->setAttributes(['class' => 'form-control']);
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemCollege) ?>
-        <?= $this->formElement($elemCollege) ?>
-        <?= $this->formElementErrors($elemCollege) ?>
-    </div>
-
-<? /* job title */ ?>
-<?php
-/** @var Element\Text $elemJobTitle */
-$elemJobTitle = $form->get('jobTitle');
-$elemJobTitle->setLabelAttributes(['class' => 'col-md-4']);
-$elemJobTitle->setAttributes(['class' => 'form-control']);
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemJobTitle) ?>
-        <?= $this->formElement($elemJobTitle) ?>
-        <?= $this->formElementErrors($elemJobTitle) ?>
-    </div>
-
-<? /* role (user group)  */ ?>
-<?php
-/** @var Element\Radio $accessLevel */
-$accessLevel = $form->get('accessLevel');
-?>
-    <div class="form-group">
-        <div class="col-md-4">
-            <?= $this->formLabel($accessLevel) ?>
-        </div>
-        <div class="col-md-8">
-            <?= $this->formRadio($accessLevel) ?>
-        </div>
-        <?= $this->formElementErrors($accessLevel) ?>
-    </div>
-
-<?php
-/** @var Element\Text $elemAddressLine1 */
-$elemAddressLine1 = $form->get('addressLine1');
-$elemAddressLine1->setLabelAttributes(['class' => 'col-md-4']);
-$elemAddressLine1->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemAddressLine1) ?>
-  <?= $this->formElement($elemAddressLine1) ?>
-  <?= $this->formElementErrors($elemAddressLine1) ?>
+    <?= $this->formLabel($elemLastname) ?>
+    <?= $this->formElement($elemLastname) ?>
+    <?= $this->formElementErrors($elemLastname) ?>
 </div>
 
-<? /* second address line */ ?>
+<? /* home library */ ?>
 <?php
-/** @var Element\Text $elemAddressLine2 */
-$elemAddressLine2 = $form->get('addressLine2');
-$elemAddressLine2->setLabelAttributes(['class' => 'col-md-4']);
-$elemAddressLine2->setAttributes(['class' => 'form-control']);
+/** @var Element\Select $elemHomeLibrary */
+$elemHomeLibrary = $form->get('home_library');
+$elemHomeLibrary->setLabelAttributes(['class' => 'col-md-4']);
+$elemHomeLibrary->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemAddressLine2) ?>
-  <?= $this->formElement($elemAddressLine2) ?>
-  <?= $this->formElementErrors($elemAddressLine2) ?>
+    <?= $this->formLabel($elemHomeLibrary) ?>
+    <?= $this->formSelect($elemHomeLibrary) ?>
+    <?= $this->formElementErrors($elemHomeLibrary) ?>
 </div>
 
-<? /* address zip */ ?>
+<? /* user group */ ?>
 <?php
-/** @var Element\Text $elemAddressZip */
-$elemAddressZip = $form->get('addressZip');
-$elemAddressZip->setLabelAttributes(['class' => 'col-md-4']);
-$elemAddressZip->setAttributes(['class' => 'form-control']);
+/** @var Element\Radio $elemJobTitle */
+$elemJobTitle = $form->get('job_title');
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemAddressZip) ?>
-  <?= $this->formElement($elemAddressZip) ?>
-  <?= $this->formElementErrors($elemAddressZip) ?>
+    <div class="col-md-4">
+        <?= $this->formLabel($elemJobTitle) ?>
+    </div>
+    <div class="col-md-8">
+        <?= $this->formRadio($elemJobTitle) ?>
+    </div>
+    <?= $this->formElementErrors($elemJobTitle) ?>
 </div>
 
-<? /* address city */ ?>
+<? /* college */ ?>
 <?php
-/** @var Element\Text $elemAddressCity */
-$elemAddressCity = $form->get('addressCity');
-$elemAddressCity->setLabelAttributes(['class' => 'col-md-4']);
-$elemAddressCity->setAttributes(['class' => 'form-control']);
+/** @var Element\Text $elemCollege */
+$elemCollege = $form->get('college');
+$elemCollege->setLabelAttributes(['class' => 'col-md-4']);
+$elemCollege->setAttributes(['class' => 'form-control']);
 ?>
 <div class="form-group">
-  <?= $this->formLabel($elemAddressCity) ?>
-  <?= $this->formElement($elemAddressCity) ?>
-  <?= $this->formElementErrors($elemAddressCity) ?>
+    <?= $this->formLabel($elemCollege) ?>
+    <?= $this->formElement($elemCollege) ?>
+    <?= $this->formElementErrors($elemCollege) ?>
 </div>
 
-<? /* address country */ ?>
-<?php
-/** @var Element\Text $elemAddressCountry */
-$elemAddressCountry = $form->get('addressCountry');
-$elemAddressCountry->setLabelAttributes(['class' => 'col-md-4']);
-$elemAddressCountry->setAttributes(['class' => 'form-control']);
-?>
-<div class="form-group">
-  <?= $this->formLabel($elemAddressCountry) ?>
-  <?= $this->formElement($elemAddressCountry) ?>
-  <?= $this->formElementErrors($elemAddressCountry) ?>
-</div>
+<? /* addresses */ ?>
+<?= $this->render('address-collection.phtml') ?>
 
 <? /* eula privacy policy */ ?>
 <?php
 /** @var Checkbox $elemEulaAccepted */
-$elemEulaAccepted = $form->get('eulaAccepted');
-$elemEulaAccepted->setAttribute('id', 'eulaAccepted');
-?>
-    <div class="form-group eula">
-        <div>
-            <?= $this->formElement($elemEulaAccepted) ?>
-            <?= $this->formLabel()->openTag($elemEulaAccepted) ?>
-            <?= $this->translate("fid::policy_text") ?>
-            <?php $url = $this->url('fid/user/policy') ?>
-            <a data-lightbox href="<?= $url ?>"><?= $this->translate("fid::policy") ?></a>
-            <?= $this->translate("fid::terms_text") ?>
-            <?php $url = $this->url('fid/user/terms') ?>
-            <a data-lightbox href="<?= $url ?>"><?= $this->translate("fid::terms") ?></a>.
-            <?= $this->formLabel()->closeTag($elemEulaAccepted) ?>
-        </div>
-        <?= $this->formElementErrors($elemEulaAccepted) ?>
+$elemEulaAccepted = $form->get('eula_accepted');
+$elemEulaAccepted->setAttribute('id', 'eula-accepted');
+?>
+<div class="form-group eula">
+    <div>
+        <?= $this->formElement($elemEulaAccepted) ?>
+        <?= $this->formLabel()->openTag($elemEulaAccepted) ?>
+        <?= $this->translate("fid::policy_text") ?>
+        <?php $url = $this->url('fid/user/policy') ?>
+        <a data-lightbox
+           href="<?= $url ?>"><?= $this->translate("fid::policy") ?></a>
+        <?= $this->translate("fid::terms_text") ?>
+        <?php $url = $this->url('fid/user/terms') ?>
+        <a data-lightbox
+           href="<?= $url ?>"><?= $this->translate("fid::terms") ?></a>.
+        <?= $this->formLabel()->closeTag($elemEulaAccepted) ?>
     </div>
+    <?= $this->formElementErrors($elemEulaAccepted) ?>
+</div>
 
 <? /* submit button */ ?>
 <?php
@@ -322,10 +223,6 @@ $elemEulaAccepted->setAttribute('id', 'eulaAccepted');
 $elemSubmit = $form->get('submit');
 $elemSubmit->setAttributes(['class' => 'btn btn-primary']);
 ?>
-    <div class="form-group">
-        <div class="">
-            <?= $this->formSubmit($elemSubmit) ?>
-        </div>
-    </div>
 
+<div class="form-group"><?= $this->formSubmit($elemSubmit) ?></div>
 <?= $this->form()->closeTag($form) ?>
diff --git a/themes/fid/templates/fid/user/init.phtml b/themes/fid/templates/fid/user/init.phtml
index f444dca47ce27bd1eb09e35b7a76c55fc7cddc11..059c44c0effbe2016cbe9ce6e0b46723560d4939 100644
--- a/themes/fid/templates/fid/user/init.phtml
+++ b/themes/fid/templates/fid/user/init.phtml
@@ -25,43 +25,27 @@ use Zend\Form\Form;
 use Zend\Form\View\Helper\FormElementErrors;
 use Zend\Form\View\Helper\FormLabel;
 use Zend\Form\View\Helper\FormSubmit;
-use Zend\I18n\Translator\TranslatorInterface;
 
+/** @var Form $form */
 /** @var FormLabel $formLabel */
-$formLabel = $this->formLabel();
-/**@var FormSubmit $formSubmit */
-$formSubmit = $this->formSubmit();
+/** @var FormSubmit $formSubmit */
 /** @var FormElementErrors $formElementErrors */
-$formElementErrors = $this->formElementErrors();
-
+$form = $this->form;
+$formLabel = $this->formLabel();
 $formLabel->setTranslatorTextDomain('fid');
+$formSubmit = $this->formSubmit();
 $formSubmit->setTranslatorTextDomain('fid');
+$formElementErrors = $this->formElementErrors();
 $formElementErrors->setTranslatorTextDomain('fid');
-/** @var TranslatorInterface $translator */
-$translator = $this->getHelperPluginManager()->get('translate')
-    ->getTranslator();
-$formLabel->setTranslator($translator);
-$formElementErrors->setTranslator($translator);
-
-/** @var Form $form */
-$form = $this->form;
-$form->setAttribute('method', 'post');
-$form->setAttribute('action', $this->url('fid/user/init'));
-$form->setAttribute('class', 'fid-user-init-form registration');
-$form->prepare();
-
-$this->headTitle($this->translate("fid::user_init_form_title"));
+$this->headTitle($title = $this->translate("fid::user_init_form_title"));
 ?>
 
-<h2><?= $this->translate("fid::user_init_form_title") ?></h2>
-
-<div class="subito-pg">
-    * <?=$this->transEsc("This field is required")?>
-</div>
-
+<h2><?= $title ?></h2>
+<div><?= $this->transEsc("fid::required_fields_note") ?></div>
 <?= $this->flashmessages() ?>
 <?= $this->form()->openTag($form) ?>
 <br/>
+
 <? /* username */ ?>
 <?php
 /** @var Element\Text $elemUsername */
@@ -78,7 +62,7 @@ $elemUsername->setAttributes(['class' => 'form-control']);
 <? /* username confirmation */ ?>
 <?php
 /** @var Element\Text $elemUsernameConfirmation */
-$elemUsernameConfirmation = $form->get('usernameConfirmation');
+$elemUsernameConfirmation = $form->get('username_confirmation');
 $elemUsernameConfirmation->setLabelAttributes(['class' => 'control-label']);
 $elemUsernameConfirmation->setAttributes(['class' => 'form-control']);
 ?>
@@ -117,8 +101,8 @@ $elemLastname->setAttributes(['class' => 'form-control']);
 <? /* eula privacy policy */ ?>
 <?php
 /** @var Element\Checkbox $elemEulaAccepted */
-$elemEulaAccepted = $form->get('eulaAccepted');
-$elemEulaAccepted->setAttribute('id', $elemEulaAccepted->getAttribute('name') ?? 'eulaAccepted');
+$elemEulaAccepted = $form->get('eula_accepted');
+$elemEulaAccepted->setAttribute('id', 'eula-accepted');
 ?>
 <div class="form-group">
     <?= $this->formElement($elemEulaAccepted) ?>
@@ -142,9 +126,10 @@ $elemSubmit = $form->get('submit');
 $elemSubmit->setAttributes(['class' => 'btn btn-primary']);
 ?>
 <div class="form-group">
-    <a class="back-to-login btn btn-link" href="<?=$this->url('myresearch-userlogin') ?>">
+    <a class="back-to-login btn btn-link"
+       href="<?= $this->url('myresearch-userlogin') ?>">
         <i class="fa fa-chevron-left" aria-hidden="true"></i>
-        <?=$this->transEsc('Back')?>
+        <?= $this->transEsc('Back') ?>
     </a>
     <?= $this->formElement($elemSubmit) ?>
 </div>
diff --git a/themes/fid/templates/fid/user/orders.phtml b/themes/fid/templates/fid/user/orders.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..27c692d023e16439c4be116c2731931652dff166
--- /dev/null
+++ b/themes/fid/templates/fid/user/orders.phtml
@@ -0,0 +1,15 @@
+<!-- fid: fid - user - orders -->
+<?php if (empty($orders)): ?>
+  <?=$this->translate('fid::user_orders_empty')?>
+<?php else: ?>
+  <table class="table">
+    <tr>
+      <th><?=$this->translate('Date')?></th>
+      <?=$this->render('fid/user/orders/additionals-headers');?>
+    </tr>
+    <?php foreach ($orders as $order): ?>
+      <?=$this->render('fid/user/orders/entry', compact('order', 'addresses'))?>
+    <?php endforeach; ?>
+  </table>
+<?php endif; ?>
+<!-- fid: fid - user - orders - END -->
diff --git a/themes/fid/templates/fid/user/orders/additionals-data.phtml b/themes/fid/templates/fid/user/orders/additionals-data.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..3b352ec39a5f9fd1e7174a0d88d0aa88face618e
--- /dev/null
+++ b/themes/fid/templates/fid/user/orders/additionals-data.phtml
@@ -0,0 +1,20 @@
+<!-- fid: fid - user - orders - data -->
+<td>
+  <?php $recordId = $data['recordId'];
+  $recordLabel = $data['recordTitle'] ?? $recordId;
+  //$recordLabel = "Ê™";
+  if ($recordLink = $this->recordLink()->getRecordLink($recordId, 'id')):
+    ?>
+    <a onClick="$('#modal').modal('hide');" target="_self" href="<?=$recordLink?>"><?=$this->escapeHtml($recordLabel)?></a>
+  <?php else: ?>
+    <?=$this->escapeHtml($recordLabel)?>
+  <?php endif; ?>
+</td>
+<td>
+  <?php $addressId = $data['deliveryAddress'];
+  if (isset($addresses[$addressId])):
+    ?>
+    <?=$this->render('fid/user/address-display-inline', ['address' => $addresses[$addressId]])?>
+  <?php endif; ?>
+</td>
+<!-- fid: fid - user - orders - data - END -->
diff --git a/themes/fid/templates/fid/user/orders/additionals-headers.phtml b/themes/fid/templates/fid/user/orders/additionals-headers.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..33716cf0adf1906ecf076386107055524824a639
--- /dev/null
+++ b/themes/fid/templates/fid/user/orders/additionals-headers.phtml
@@ -0,0 +1,4 @@
+<!-- fid: fid - user - orders - additionals-headers -->
+<th><?=$this->translate('fid::order_record')?></th>
+<th><?=$this->translate('fid::order_delivery_address')?></th>
+<!-- fid: fid - user - orders - additionals-headers - END -->
\ No newline at end of file
diff --git a/themes/fid/templates/fid/user/orders/entry.phtml b/themes/fid/templates/fid/user/orders/entry.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..706274c226d6a3a9fc5f5ac21f7b84cd3279fa45
--- /dev/null
+++ b/themes/fid/templates/fid/user/orders/entry.phtml
@@ -0,0 +1,7 @@
+<!-- fid: fid - user - orders - entry -->
+<tr name="order-<?=$this->escapeHtml($order->getId())?>">
+  <td><?=$this->escapeHtml($order->getCreatedAt()->format('d.m.Y'))?></td>
+  <?php $data = $order->getData() ?>
+  <?=$this->render('fid/user/orders/additionals-data', compact('data', 'addresses'))?>
+</tr>
+<!-- fid: fid - user - orders - entry - END -->
\ No newline at end of file
diff --git a/themes/fid/templates/fid/user/update.phtml b/themes/fid/templates/fid/user/update.phtml
index 80338ec90c2c1913f9f1e6fadb3db464bcf6c4ae..1152a947b4631977aaa64b26460eb1f5cc5d8050 100644
--- a/themes/fid/templates/fid/user/update.phtml
+++ b/themes/fid/templates/fid/user/update.phtml
@@ -20,9 +20,7 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
-use fid\Service\DataTransferObject\Address;
-use fid\Service\DataTransferObject\User;
-use Zend\Form\Element as Element;
+use Zend\Form\Element;
 use Zend\Form\Element\Submit;
 use Zend\Form\Form;
 use Zend\Form\View\Helper\FormElementErrors;
@@ -30,161 +28,114 @@ use Zend\Form\View\Helper\FormLabel;
 use Zend\Form\View\Helper\FormRadio;
 use Zend\Form\View\Helper\FormSelect;
 use Zend\Form\View\Helper\FormSubmit;
-use Zend\I18n\Translator\TranslatorInterface;
 
+/** @var Form $form */
 /** @var FormLabel $formLabel */
+/** @var FormRadio $formRadio */
+/** @var FormSelect $formSelect */
+/** @var FormSubmit $formSubmit */
+/** @var FormElementErrors $formElementErrors */
+$form = $this->form;
 $formLabel = $this->formLabel();
-/**@var FormRadio $formRadio */
 $formRadio = $this->formRadio();
-/**@var FormSelect $formSelect */
 $formSelect = $this->formSelect();
-/**@var FormSubmit $formSubmit */
 $formSubmit = $this->formSubmit();
-/** @var FormElementErrors $formElementErrors */
 $formElementErrors = $this->formElementErrors();
 $formLabel->setTranslatorTextDomain('fid');
 $formSelect->setTranslatorTextDomain('fid');
 $formRadio->setTranslatorTextDomain('fid');
 $formSubmit->setTranslatorTextDomain('fid');
 $formElementErrors->setTranslatorTextDomain('fid');
-/** @var TranslatorInterface $translator */
-$translator = $this->getHelperPluginManager()->get('translate')
-    ->getTranslator();
-
-$formLabel->setTranslator($translator);
-$formElementErrors->setTranslator($translator);
-
-/** @var Form $form */
-/** @var User $user */
-$user = $this->user;
-$form = $this->form;
-$form->setAttribute('method', 'post');
-$form->setAttribute('action', $this->url('fid/user/update'));
-$form->setAttribute('class', 'form-horizontal');
-$form->prepare();
-
-$this->headTitle($this->translate('Profile Form'));
-$this->headTitle($this->translate("fid::user_update_form_title"));
+$this->headTitle($title = $this->translate("fid::user_update_form_title"));
 ?>
 
-    <h2><?= $this->translate("fid::user_update_form_title") ?></h2>
+<h2><?= $title ?></h2>
+<div><?= $this->transEsc("fid::required_fields_note") ?></div>
 <?= $this->flashmessages() ?>
 <?= $this->form()->openTag($form) ?>
-    <br/>
+<br/>
 
-<? /* home library */ ?>
-<?php
-/** @var Element\Select $elemHomeLibrary */
-$elemHomeLibrary = $form->get('homeLibrary');
-$elemHomeLibrary->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemHomeLibrary->setAttributes(['class' => 'inline col-sm-6']);
-$elemHomeLibrary->setValue($elemHomeLibrary->getValue() ?? $user->getHomeLibrary());
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemHomeLibrary) ?>
-        <?= $this->formSelect($elemHomeLibrary) ?>
-        <?= $this->formElementErrors($elemHomeLibrary) ?>
-    </div>
+<? /* id */ ?>
+<?= $this->formElement($form->get('id')) ?>
 
 <? /* salutation */ ?>
 <?php
 /** @var Element\Select $elemSalutation */
 $elemSalutation = $form->get('salutation');
-$elemSalutation->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemSalutation->setAttributes(['class' => 'inline col-sm-2']);
-$elemSalutation->setValue($elemSalutation->getValue() ?? $user->getSalutation());
+$elemSalutation->setLabelAttributes(['class' => 'col-md-4']);
+$elemSalutation->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemSalutation) ?>
-        <?= $this->formSelect($elemSalutation) ?>
-
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemSalutation) ?>
+    <?= $this->formSelect($elemSalutation) ?>
+</div>
 
 <? /* academic title */ ?>
 <?php
 /** @var Element\Text $elemAcademicTitle */
-$elemAcademicTitle = $form->get('academicTitle');
-$elemAcademicTitle->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
+$elemAcademicTitle = $form->get('academic_title');
+$elemAcademicTitle->setLabelAttributes(['class' => 'col-md-4']);
 $elemAcademicTitle->setAttributes(['class' => 'form-control']);
-$elemAcademicTitle->setValue($elemAcademicTitle->getValue() ?? $user->getAcademicTitle());
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemAcademicTitle) ?>
-        <?= $this->formElement($elemAcademicTitle) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemAcademicTitle) ?>
+    <?= $this->formElement($elemAcademicTitle) ?>
+</div>
 
 <? /* firstname */ ?>
 <?php
 /** @var Element\Text $elemFirstname */
 $elemFirstname = $form->get('firstname');
-$elemFirstname->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
+$elemFirstname->setLabelAttributes(['class' => 'col-md-4']);
 $elemFirstname->setAttributes(['class' => 'form-control']);
-$elemFirstname->setValue($elemFirstname->getValue() ?? $user->getFirstname());
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemFirstname) ?>
-        <?= $this->formElement($elemFirstname) ?>
-        <?= $this->formElementErrors($elemFirstname) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemFirstname) ?>
+    <?= $this->formElement($elemFirstname) ?>
+    <?= $this->formElementErrors($elemFirstname) ?>
+</div>
 
 <? /* lastname */ ?>
 <?php
 /** @var Element\Text $elemLastname */
 $elemLastname = $form->get('lastname');
-$elemLastname->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
+$elemLastname->setLabelAttributes(['class' => 'col-md-4']);
 $elemLastname->setAttributes(['class' => 'form-control']);
-$elemLastname->setValue($elemLastname->getValue() ?? $user->getLastname());
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemLastname) ?>
-        <?= $this->formElement($elemLastname) ?>
-        <?= $this->formElementErrors($elemLastname) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemLastname) ?>
+    <?= $this->formElement($elemLastname) ?>
+    <?= $this->formElementErrors($elemLastname) ?>
+</div>
 
-<? /* year of birth */ ?>
+<? /* home library */ ?>
 <?php
-/** @var Element\Text $elemYearOfBirth */
-$elemYearOfBirth = $form->get('yearOfBirth');
-$elemYearOfBirth->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemYearOfBirth->setAttributes(['class' => 'form-control']);
-$elemYearOfBirth->setValue($elemYearOfBirth->getValue() ?? $user->getYearOfBirth());
+/** @var Element\Select $elemHomeLibrary */
+$elemHomeLibrary = $form->get('home_library');
+$elemHomeLibrary->setLabelAttributes(['class' => 'col-md-4']);
+$elemHomeLibrary->setAttributes(['class' => 'form-control']);
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemYearOfBirth) ?>
-        <?= $this->formElement($elemYearOfBirth) ?>
-        <?= $this->formElementErrors($elemYearOfBirth) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemHomeLibrary) ?>
+    <?= $this->formSelect($elemHomeLibrary) ?>
+    <?= $this->formElementErrors($elemHomeLibrary) ?>
+</div>
 
 <? /* college */ ?>
 <?php
 /** @var Element\Text $elemCollege */
 $elemCollege = $form->get('college');
-$elemCollege->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
+$elemCollege->setLabelAttributes(['class' => 'col-md-4']);
 $elemCollege->setAttributes(['class' => 'form-control']);
-$elemCollege->setValue($elemCollege->getValue() ?? $user->getCollege());
-?>
-    <div class="form-group">
-        <?= $this->formLabel($elemCollege) ?>
-        <?= $this->formElement($elemCollege) ?>
-        <?= $this->formElementErrors($elemCollege) ?>
-    </div>
-
-<? /* job title */ ?>
-<?php
-/** @var Element\Text $elemJobTitle */
-$elemJobTitle = $form->get('jobTitle');
-$elemJobTitle->setLabelAttributes(['class' => 'inline col-md-4 col-sm-10']);
-$elemJobTitle->setAttributes(['class' => 'form-control']);
-$elemJobTitle->setValue($elemJobTitle->getValue() ?? $user->getJobTitle());
 ?>
-    <div class="form-group">
-        <?= $this->formLabel($elemJobTitle) ?>
-        <?= $this->formElement($elemJobTitle) ?>
-        <?= $this->formElementErrors($elemJobTitle) ?>
-    </div>
+<div class="form-group">
+    <?= $this->formLabel($elemCollege) ?>
+    <?= $this->formElement($elemCollege) ?>
+    <?= $this->formElementErrors($elemCollege) ?>
+</div>
 
 <? /* addresses */ ?>
-<?= $this->render('update-addresses.phtml', compact('form', 'user')) ?>
+<?= $this->render('address-collection.phtml') ?>
 
 <? /* submit button */ ?>
 <?php
@@ -192,14 +143,14 @@ $elemJobTitle->setValue($elemJobTitle->getValue() ?? $user->getJobTitle());
 $elemSubmit = $form->get('submit');
 $elemSubmit->setAttributes(['class' => 'btn btn-primary']);
 ?>
-    <div class="form-group">
-        <div class="col-lg-11 col-md-9 col-sm-11 col-xs-12">
-            <?= $this->formSubmit($elemSubmit) ?>
-            <a href="<?= $this->url('myresearch-profile') ?>"
-               class="btn btn-primary">
-                <?= $this->transEsc('Cancel') ?>
-            </a>
-        </div>
+<div class="form-group">
+    <div class="col-lg-11 col-md-9 col-sm-11 col-xs-12">
+        <?= $this->formSubmit($elemSubmit) ?>
+        <a href="<?= $this->url('myresearch-profile') ?>"
+           class="btn btn-primary">
+            <?= $this->transEsc('Cancel') ?>
+        </a>
     </div>
+</div>
 
 <?= $this->form()->closeTag($form) ?>
diff --git a/themes/fid/templates/myresearch/menu.phtml b/themes/fid/templates/myresearch/menu.phtml
index b76675dcecb38abefb5d9300615d3a31ce1bf88f..8d8d8aa4c5ed1b46fef6fe900770a49f13ed48a3 100644
--- a/themes/fid/templates/myresearch/menu.phtml
+++ b/themes/fid/templates/myresearch/menu.phtml
@@ -13,73 +13,34 @@
 <!-- fid: myresearch - menu.phtml -->
 <?php
   $user = $this->auth()->isLoggedIn();
-  $patron = $user ? $this->auth()->getILSPatron() : false;
-  $capabilityParams = $patron ? ['patron' => $patron] : [];
 ?>
+<?php /* Offcanvas closing button missing in BS3! CK*/ ?>
+<button class="close-offcanvas btn btn-link" data-toggle="offcanvas"><?=$this->transEsc('navigate_back') ?></button>
+
 <?php if (!empty($user)): ?>
   <h4><?=$this->transEsc('Your Account')?></h4>
   <div class="myresearch-menu account-menu">
     <?php if ('ils-none' !== $this->ils()->getOfflineMode()): ?>
-      <?php if ($this->ils()->checkCapability('getMyTransactions', $capabilityParams)): ?>
-        <a href="<?=$this->url('myresearch-checkedout')?>" class="flex checkedout<?=$this->active == 'checkedout' ? ' active' : ''?>">
-          <span class="flex-col"><i class="fa fa-fw fa-book" aria-hidden="true"></i>&nbsp;<?=$this->transEsc('Checked Out Items')?></span>
-          <span class="checkedout-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span>
-        </a>
-      <?php endif; ?>
-      <?php if ($this->ils()->checkFunction('getMyTransactionHistory', $capabilityParams)): ?>
-        <a href="<?=$this->url('myresearch-historicloans')?>"<?=$this->active == 'historicloans' ? ' class="active"' : ''?>>
-          <i class="fa fa-fw fa-history" aria-hidden="true"></i> <?=$this->transEsc('Loan History')?>
-        </a>
-      <?php endif; ?>
-      <?php if ($this->ils()->checkCapability('getMyHolds', $capabilityParams)): ?>
-        <a href="<?=$this->url('myresearch-holds')?>" class="flex<?=$this->active == 'holds' ? ' active' : ''?>">
-          <span class="flex-col"><i class="fa fa-fw fa-flag" aria-hidden="true"></i>&nbsp;<?=$this->transEsc('Holds and Recalls')?></span>
-          <span class="holds-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span>
-        </a>
-      <?php endif; ?>
-      <?php if ($this->ils()->checkFunction('StorageRetrievalRequests', $capabilityParams)): ?>
-        <a href="<?=$this->url('myresearch-storageretrievalrequests')?>" class="flex<?=$this->active == 'storageRetrievalRequests' ? ' active' : ''?>">
-          <span class="flex-col"><i class="fa fa-fw fa-archive" aria-hidden="true"></i> <?=$this->transEsc('Storage Retrieval Requests')?></span>
-          <span class="storageretrievalrequests-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span>
-        </a>
-      <?php endif; ?>
-      <?php if ($this->ils()->checkFunction('ILLRequests', $capabilityParams)): ?>
-        <a href="<?=$this->url('myresearch-illrequests')?>" class="flex<?=$this->active == 'ILLRequests' ? ' active' : ''?>">
-          <span class="flex-col"><i class="fa fa-fw fa-exchange" aria-hidden="true"></i> <?=$this->transEsc('Interlibrary Loan Requests')?></span>
-          <span class="illrequests-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span>
-        </a>
-      <?php endif; ?>
-      <?php if ($this->ils()->checkCapability('getMyFines', $capabilityParams)): ?>
-        <a href="<?=$this->url('myresearch-fines')?>" class="flex<?=$this->active == 'fines' ? ' active' : ''?>">
-          <span class="flex-col"><i class="fa fa-fw fa-usd" aria-hidden="true"></i>&nbsp;<?=$this->transEsc('Fines')?></span>
-          <span class="fines-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span>
-        </a>
-      <?php endif; ?>
       <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active"' : ''?>>
         <i class="fa fa-fw fa-user" aria-hidden="true"></i>
         <?=$this->transEsc('Profile')?>
       </a>
 
       <?php /* refs #15480 add password change to fid profile */ ?>
-      <?php if ($this->auth()->isLoggedIn()): ?>
         <a href="<?=$this->url('fid/user/reset-password')?>" data-lightbox>
           <span class="no-padding">
             <i class="fa fa-fw fa-lock" aria-hidden="true"></i>
           </span>
           <?=$this->transEsc('Change Password')?>
         </a>
-      <? endif; ?>
 
-      <?php if ($this->auth()->isLoggedIn()): ?>
         <a href="<?=$this->url('fid/user/change-username')?>" data-lightbox>
           <span class="no-padding">
             <i class="fa fa-fw fa-envelope" aria-hidden="true"></i>
           </span>
           <?=$this->transEsc('fid::username_change_link')?>
         </a>
-      <? endif; ?>
 
-      <?php if ($user = $this->auth()->isLoggedIn()): ?>
         <span class="logout-button">
           <a href="<?=$this->url('myresearch-logout')?>">
             <span class="no-padding">
@@ -89,9 +50,8 @@
             <?=$this->transEsc("Logout")?>
           </a>
         </span>
-      <? endif; ?>
 
-      <?php if ($user && $user->libraryCardsEnabled()): ?>
+      <?php if ($user->libraryCardsEnabled()): ?>
         <a href="<?=$this->url('librarycards-home')?>"<?=$this->active == 'librarycards' ? ' class="active"' : ''?>>
           <i class="fa fa-fw fa-barcode" aria-hidden="true"></i> <?=$this->transEsc('Library Cards')?>
         </a>
@@ -100,7 +60,7 @@
   </div>
 
   <br />
-  <?php if ($user && $this->userlist()->getMode() !== 'disabled'): ?>
+  <?php if ($this->userlist()->getMode() !== 'disabled'): ?>
     <h4><?=$this->transEsc('Your Lists')?></h4>
 
     <div class="myresearch-menu">
diff --git a/themes/fid/templates/myresearch/profile.phtml b/themes/fid/templates/myresearch/profile.phtml
index 9e3f89921018c549a3847090b7a966e15ef27f5d..544647769b11241335f3360012030ff1ca6da14b 100644
--- a/themes/fid/templates/myresearch/profile.phtml
+++ b/themes/fid/templates/myresearch/profile.phtml
@@ -1,4 +1,4 @@
-<!-- finc: myresearch - profile -->
+<!-- fid: myresearch - profile -->
 <?php
 // Set up page title:
 $this->headTitle($this->translate('My Profile'));
@@ -18,6 +18,9 @@ if (is_array($profile)) {
 ?>
 
 <div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: compare with finc template after update to ensure identical toggler code snippet is used - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
   <h2><?=$this->transEsc('Your Profile')?></h2>
   <?=$this->flashmessages();?>
   <?php if (is_array($this->profile)): ?>
@@ -50,4 +53,4 @@ if (is_array($profile)) {
 <div class="<?=$this->layoutClass('sidebar')?>">
   <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'profile'])?>
 </div>
-<!-- finc: myresearch - profile - END -->
+<!-- fid: myresearch - profile - END -->
diff --git a/themes/fid/theme.config.php b/themes/fid/theme.config.php
index 9287fac95d9c6235650fd6914cf64d0ccd816c23..c796f8532d90bd9c52cf90744dc362fa94063dba 100644
--- a/themes/fid/theme.config.php
+++ b/themes/fid/theme.config.php
@@ -16,6 +16,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * @author  Sebastian Kehr <kehr@ub.uni-leipzig.de>
+ * @author  Dorian Merz <merz@ub.uni-leipzig.de>
+ * @author  Gregor Gawol <gawol@ub.uni-leipzig.de>
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
@@ -25,11 +27,15 @@ return [
         'factories' => array(
             'fid\View\Helper\Root\UserGroups' =>
                 'fid\View\Helper\Root\Factory::getUserGroups',
+            'fid\View\Helper\Root\GetIt' =>
+                'fid\View\Helper\Root\Factory::getGetIt',
             'fid\Helper\SearchTabs' => 'VuFind\View\Helper\Root\SearchTabsFactory',
         ),
         'aliases' => array(
             'usergroups' => 'UserGroups',
             'UserGroups' => 'fid\View\Helper\Root\UserGroups',
+            'getit' => 'fid\View\Helper\Root\GetIt',
+            'GetIt' => 'fid\View\Helper\Root\GetIt',
             'searchTabs' => 'fid\Helper\SearchTabs',
         )
     ),
diff --git a/themes/finc/scss/compiled.scss b/themes/finc/scss/compiled.scss
index f46adc1cd27fcdf62b82b0957e925cd63c49267f..a9a001450909e26328c93cd1ba2d8cae0e3badfe 100644
--- a/themes/finc/scss/compiled.scss
+++ b/themes/finc/scss/compiled.scss
@@ -1442,7 +1442,7 @@ footer {
   }
 
   // Float this left for lefthand-side sidebar
-  .offcanvas-toogler {
+  .offcanvas-toggler {
     float: right;
   }
 }
@@ -1946,6 +1946,23 @@ footer {
   background: $sidebar-item-active-hover-bg;
 }
 
+// #13625 avoid ugly linebreaks with overlong words
+.sidebar {
+  -webkit-hyphens: auto;
+  -moz-hyphens: auto;
+  -ms-hyphens: auto;
+  -o-hyphens: auto;
+  hyphens: auto;
+  .facet {
+    -ms-word-break: break-all;
+    word-break: break-all; // fallback for hyphens: auto (Chrome on Desktop and bug on Mozilla for capitalized words)
+    .text {
+      -ms-word-break: break-word;
+      word-break: break-word;
+    }
+  }
+}
+
 //// OR- and AND-Facets + item counts in My Account
 ///// Style the exclude icons
 .exclude i {
diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler-myresearch.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler-myresearch.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..7baae6a4f2e2399adf117d7e988285877211aed8
--- /dev/null
+++ b/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler-myresearch.phtml
@@ -0,0 +1,8 @@
+<!-- finc: RecordDriver - DefaultRecord - offcanvas-toggler-myresearch -->
+<?php ?>
+<span class="offcanvas-toggler">
+  <button class="search-filter-toggle btn btn-primary visible-xs" href="#search-sidebar" data-toggle="offcanvas" title="<?=$this->transEsc('sidebar_expand')?>">
+    <?=$this->transEsc('offcanvas-toggler-myresearch')?>
+  </button>
+</span>
+<!-- finc: RecordDriver - DefaultRecord - offcanvas-toggler-myresearch - END -->
diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler.phtml
index a56f446a03445e70a2a4572f0a9a84b3169e4278..a44ef4dfd660838528306c85c3d9ba81ebe41eb6 100644
--- a/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler.phtml
+++ b/themes/finc/templates/RecordDriver/DefaultRecord/offcanvas-toggler.phtml
@@ -1,7 +1,8 @@
-<!-- finc: RecordDriver - DefaultRecord - offcanvas-toogler -->
+<!-- finc: RecordDriver - DefaultRecord - offcanvas-toggler -->
 <?php ?>
-<span class="offcanvas-toogler">
+<span class="offcanvas-toggler">
   <button class="search-filter-toggle btn btn-primary visible-xs" href="#search-sidebar" data-toggle="offcanvas" title="<?=$this->transEsc('sidebar_expand')?>">
     <?=$this->transEsc('offcanvas-toggler-record-view')?>
   </button>
 </span>
+<!-- finc: RecordDriver - DefaultRecord - offcanvas-toggler - END -->
\ No newline at end of file
diff --git a/themes/finc/templates/myresearch/acquisition.phtml b/themes/finc/templates/myresearch/acquisition.phtml
index 4df9804f49bc335983f09aa2bef790c3546b288c..12bae3a4324b506437da43754096d90d44233b4f 100644
--- a/themes/finc/templates/myresearch/acquisition.phtml
+++ b/themes/finc/templates/myresearch/acquisition.phtml
@@ -8,6 +8,9 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') .
 ?>
 
 <div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
   <h2><?=$this->transEsc('PDA::pda_form_subtitle')?></h2>
   <?=$this->flashmessages()?>
   <form method="post" action="" name="acquisitionForm">
diff --git a/themes/finc/templates/myresearch/checkedout.phtml b/themes/finc/templates/myresearch/checkedout.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..bc99ff9602cee8e7e36d6f40cec8d5a8aeeac449
--- /dev/null
+++ b/themes/finc/templates/myresearch/checkedout.phtml
@@ -0,0 +1,215 @@
+<!-- finc: myresearch - checkedout -->
+<?php
+  // Set up page title:
+  $this->headTitle($this->translate('Checked Out Items'));
+
+  // Set up breadcrumbs:
+  $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Checked Out Items') . '</li>';
+
+  // Check if "Renew All" button can be displayed:
+  $renewAll = !$this->ilsPaging || !$paginator;
+?>
+
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <h2><?=$this->transEsc('Your Checked Out Items')?></h2>
+  <?=$this->flashmessages()?>
+
+  <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?>
+
+  <?php if (!empty($this->transactions)): ?>
+    <nav class="search-header hidden-print">
+      <?php if ($paginator): ?>
+        <div class="search-stats">
+          <?php
+            $end = min(
+              $paginator->getAbsoluteItemNumber($paginator->getItemCountPerPage()),
+              $paginator->getTotalItemCount()
+            );
+            $transParams = [
+              '%%start%%' => $this->localizedNumber($paginator->getAbsoluteItemNumber(1)),
+              '%%end%%' => $this->localizedNumber($end),
+              '%%total%%' => $this->localizedNumber($paginator->getTotalItemCount())
+            ];
+          ?>
+          <?=$this->translate('showing_items_of_html', $transParams); ?>
+        </div>
+      <?php endif; ?>
+      <?php if ($this->sortList): ?>
+        <?=$this->context($this)->renderInContext('myresearch/controls/sort.phtml', ['sortList' => $this->sortList]); ?>
+      <?php endif; ?>
+    </nav>
+    <?php if ($this->renewForm): ?>
+    <form name="renewals" method="post" id="renewals">
+      <div class="toolbar">
+        <div class="checkbox">
+          <label>
+            <input type="checkbox" name="selectAll" class="checkbox-select-all"/>
+            <?=$this->transEsc('select_page')?>
+          </label>
+          <input type="submit" class="btn btn-default" id="renewSelected" name="renewSelected" value="<?=$this->transEsc("renew_selected")?>" />
+          <?php if ($renewAll): ?>
+            <input type="submit" class="btn btn-default" id="renewAll" name="renewAll" value="<?=$this->transEsc('renew_all')?>" />
+          <?php endif; ?>
+        </div>
+      </div>
+    <?php endif; ?>
+
+
+    <?php foreach ($hiddenTransactions as $ilsDetails): ?>
+      <?php if (isset($this->renewResult[$ilsDetails['item_id']])): ?>
+        <?php $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?>
+        <?php $prefix = $ilsDetails['title'] ?? $ilsDetails['item_id']; ?>
+        <?php if (isset($renewDetails['success']) && $renewDetails['success']): ?>
+          <div class="alert alert-success"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_success')?></div>
+        <?php else: ?>
+          <div class="alert alert-danger"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></div>
+        <?php endif; ?>
+      <?php endif; ?>
+      <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?>
+        <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $ilsDetails['renew_details']); ?>
+        <input class="pull-left flip" type="hidden" name="renewAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" />
+      <?php endif; ?>
+    <?php endforeach; ?>
+
+    <?php $i = 0; foreach ($this->transactions as $resource): ?>
+      <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?>
+      <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId())?>" class="result">
+        <?php if ($this->renewForm): ?>
+          <div class="checkbox">
+            <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?>
+              <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $ilsDetails['renew_details']); ?>
+              <label>
+                <input class="checkbox-select-item" type="checkbox" name="renewSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" id="checkbox_<?=$safeId?>" />
+              </label>
+              <input type="hidden" name="selectAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" />
+              <input type="hidden" name="renewAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" />
+            <?php else: ?>
+              <label> </label>
+            <?php endif; ?>
+          </div>
+        <?php endif; ?>
+
+        <?php
+          $coverDetails = $this->record($resource)->getCoverDetails('checkedout', 'small', $this->recordLink()->getUrl($resource));
+          $cover = $coverDetails['html'];
+          $thumbnail = false;
+          $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account');
+          if ($cover):
+            ob_start(); ?>
+            <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
+              <?=$cover ?>
+            </div>
+            <?php $thumbnail = ob_get_contents(); ?>
+          <?php ob_end_clean(); ?>
+        <?php endif; ?>
+        <div class="media">
+          <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+          <div class="media-body">
+            <?php
+              // If this is a non-missing Solr record, we should display a link:
+              if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) {
+                $title = $resource->getTitle();
+                $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title);
+                echo '<a href="' . $this->recordLink()->getUrl($resource) .
+                  '" class="title">' . $title . '</a>';
+              } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){
+                // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show...
+                echo $this->escapeHtml($ilsDetails['title']);
+              } else {
+                // Last resort -- indicate that no title could be found.
+                echo $this->transEsc('Title not available');
+              }
+            ?><br/>
+            <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?>
+              <?=$this->transEsc('by')?>:
+              <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/>
+            <?php endif; ?>
+            <?php if (count($resource->getFormats()) > 0): ?>
+              <?=$this->record($resource)->getFormatList() ?>
+              <br/>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['volume'])): ?>
+              <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['publication_year'])): ?>
+              <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?>
+              <br />
+            <?php endif; ?>
+            <?php if ($this->displayItemBarcode && !empty($ilsDetails['barcode'])): ?>
+              <strong><?=$this->transEsc('Barcode')?>:</strong> <?=$this->escapeHtml($ilsDetails['barcode'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?>
+              <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['borrowingLocation'])): ?>
+              <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['renew'])): ?>
+              <strong><?=$this->transEsc('Renewed')?>:</strong> <?=$this->transEsc($ilsDetails['renew'])?>
+              <?php if (isset($ilsDetails['renewLimit'])): ?>
+                / <?=$this->transEsc($ilsDetails['renewLimit'])?>
+              <?php endif; ?>
+              <br />
+            <?php endif; ?>
+
+            <?php $showStatus = true; ?>
+
+            <?php if (isset($this->renewResult[$ilsDetails['item_id']])): ?>
+              <?php $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?>
+              <?php if (isset($renewDetails['success']) && $renewDetails['success']): ?>
+                <?php $showStatus = false; ?>
+                <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($renewDetails['new_date'])?> <?php if (isset($renewDetails['new_time'])): ?><?=$this->escapeHtml($renewDetails['new_time'])?><?php endif; ?></strong>
+                <div class="alert alert-success"><?=$this->transEsc('renew_success')?></div>
+              <?php else: ?>
+                <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><?php endif; ?></strong>
+                <div class="alert alert-danger"><?=$this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></div>
+              <?php endif; ?>
+            <?php else: ?>
+              <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><?php endif; ?></strong>
+              <?php if ($showStatus): ?>
+                <?php if (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "overdue"): ?>
+                  <div class="alert alert-danger"><?=$this->transEsc("renew_item_overdue")?></div>
+                <?php elseif (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "due"): ?>
+                  <div class="alert alert-info"><?=$this->transEsc("renew_item_due")?></div>
+                <?php endif; ?>
+              <?php endif; ?>
+            <?php endif; ?>
+
+            <?php if ($showStatus && isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?>
+              <div class="alert alert-info"><?=$this->transEsc($ilsDetails['message'])?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_link'])): ?>
+              <a href="<?=$this->escapeHtmlAttr($ilsDetails['renew_link'])?>"><?=$this->transEsc('renew_item')?></a>
+            <?php endif; ?>
+          </div>
+          <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+        </div>
+        <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?>
+      </div>
+    <?php endforeach; ?>
+    <?php if ($this->renewForm): ?></form><?php endif; ?>
+    <?=$paginator ? $this->paginationControl($paginator, 'Sliding', 'Helpers/pagination.phtml', compact('params')) : ''?>
+  <?php else: ?>
+    <?=$this->transEsc('You do not have any items checked out')?>.
+  <?php endif; ?>
+</div>
+
+<div class="<?=$this->layoutClass('sidebar')?>">
+  <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'checkedout'])?>
+</div>
+<!-- finc: myresearch - checkedout - END -->
diff --git a/themes/finc/templates/myresearch/fines.phtml b/themes/finc/templates/myresearch/fines.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..61a03b84cbad2bee52d7842d5f4cc231d67b0715
--- /dev/null
+++ b/themes/finc/templates/myresearch/fines.phtml
@@ -0,0 +1,92 @@
+<!-- finc: myresearch - fines -->
+<?php
+    // Set up page title:
+    $this->headTitle($this->translate('My Fines'));
+
+    // Set up breadcrumbs:
+    $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Fines') . '</li>';
+?>
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <h2><?=$this->transEsc('Your Fines')?></h2>
+  <?=$this->flashmessages()?>
+
+  <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?>
+
+  <?php if (empty($this->fines)): ?>
+    <?=$this->transEsc('You do not have any fines')?>
+  <?php else: ?>
+    <?php
+      // Collect the data to build the table; we process this in advance so we
+      // can omit empty columns and simplify customization by separating data
+      // processing from rendering.
+      $tableData = [];
+      $totalDue = 0;
+      foreach ($this->fines as $record) {
+          if (empty($record['title'])) {
+              $title = $this->transEsc('not_applicable');
+          } elseif (!is_object($record['driver'] ?? null)) {
+              $title = $this->escapeHtml(trim($record['title'], '/:'));
+          } else {
+              $title = '<a href="'
+                  . $this->recordLink()->getUrl($record['driver'])
+                  . '">' . $this->escapeHtml(trim($record['title'], '/:')) . '</a>';
+          }
+          $tableData['Title'][] = $title;
+          $tableData['Checked Out'][] = $this->escapeHtml($record['checkout'] ?? '');
+          $tableData['Due Date'][] = $this->escapeHtml($record['duedate'] ?? '');
+          $tableData['Fine'][] = $this->escapeHtml($record['fine'] ?? '');
+          $tableData['Fine Date'][] = $this->escapeHtml($record['createdate'] ?? '');
+          $tableData['Fee'][] = isset($record['amount'])
+              ? $this->safeMoneyFormat($record['amount'] / 100.00) : '';
+          $tableData['Balance'][] = isset($record['balance'])
+              ? $this->safeMoneyFormat($record['balance'] / 100.00) : '';
+          $totalDue += $record['balance'] ?? 0;
+      }
+
+      // Now empty out any unused columns:
+      foreach ($tableData as $column => $values) {
+          $empty = true;
+          foreach ($values as $value) {
+              if (strlen($value) > 0) {
+                  $empty = false;
+                  break;
+              }
+          }
+          if ($empty) {
+              unset($tableData[$column]);
+          }
+      }
+
+      // Create the final list of columns and count of rows:
+      $columns = array_keys($tableData);
+      $rowCount = count($this->fines);
+    ?>
+    <table class="table table-striped">
+      <caption class="sr-only"><?=$this->transEsc('Your Fines')?></caption>
+      <tr>
+        <?php foreach ($columns as $header): ?>
+          <th><?=$this->transEsc($header)?></th>
+        <?php endforeach; ?>
+      </tr>
+      <?php for ($row = 0; $row < $rowCount; $row++): ?>
+        <tr>
+          <?php foreach ($columns as $column): ?>
+            <td><?=$tableData[$column][$row]?></td>
+          <?php endforeach; ?>
+        </tr>
+      <?php endfor; ?>
+      <tr style="font-weight:bold">
+        <td colspan="<?=count($columns) - 1?>"><?=$this->transEsc('Total Balance Due')?></td>
+        <td><?=$this->safeMoneyFormat($totalDue / 100.00) ?></td>
+      </tr>
+    </table>
+  <?php endif; ?>
+</div>
+
+<div class="<?=$this->layoutClass('sidebar')?>">
+  <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'fines'])?>
+</div>
+<!-- finc: myresearch - fines - END -->
diff --git a/themes/finc/templates/myresearch/historicloans.phtml b/themes/finc/templates/myresearch/historicloans.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..b7b7458f9f0c35d62d317a652e287967b9eced44
--- /dev/null
+++ b/themes/finc/templates/myresearch/historicloans.phtml
@@ -0,0 +1,136 @@
+<!-- finc: myresearch - historicloans -->
+<?php
+  // Set up page title:
+  $this->headTitle($this->translate('Loan History'));
+
+  // Set up breadcrumbs:
+  $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Loan History') . '</li>';
+?>
+
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <h2><?=$this->transEsc('Loan History')?></h2>
+  <?=$this->flashmessages()?>
+
+  <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?>
+
+  <?php if (!empty($this->transactions)): ?>
+    <nav class="search-header hidden-print">
+      <?php if ($this->paginator): ?>
+        <div class="search-stats">
+          <?php
+            $end = min(
+              $this->paginator->getAbsoluteItemNumber($this->paginator->getItemCountPerPage()),
+              $this->paginator->getTotalItemCount()
+            );
+            $transParams = [
+              '%%start%%' => $this->localizedNumber($this->paginator->getAbsoluteItemNumber(1)),
+              '%%end%%' => $this->localizedNumber($end),
+              '%%total%%' => $this->localizedNumber($this->paginator->getTotalItemCount())
+            ];
+          ?>
+          <?=$this->translate('showing_items_of_html', $transParams); ?>
+        </div>
+      <?php endif; ?>
+      <?php if ($this->sortList): ?>
+        <?=$this->context($this)->renderInContext('myresearch/controls/sort.phtml', ['sortList' => $this->sortList]); ?>
+      <?php endif; ?>
+    </nav>
+
+    <?php $i = 0; foreach ($this->transactions as $resource): ?>
+      <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?>
+      <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId())?>" class="result">
+        <?php
+          $coverDetails = $this->record($resource)->getCoverDetails('checkedout', 'small', $this->recordLink()->getUrl($resource));
+          $cover = $coverDetails['html'];
+          $thumbnail = false;
+          $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account');
+          if ($cover):
+            ob_start(); ?>
+            <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
+              <?=$cover ?>
+            </div>
+            <?php $thumbnail = ob_get_contents();
+            ob_end_clean();
+          endif; ?>
+        <div class="media">
+          <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+          <div class="media-body">
+            <?php
+              // If this is a non-missing Solr record, we should display a link:
+              if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) {
+                $title = $resource->getTitle();
+                $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title);
+                echo '<a href="' . $this->recordLink()->getUrl($resource) .
+                  '" class="title">' . $title . '</a>';
+              } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){
+                // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show...
+                echo $this->escapeHtml($ilsDetails['title']);
+              } else {
+                // Last resort -- indicate that no title could be found.
+                echo $this->transEsc('Title not available');
+              }
+            ?><br/>
+            <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?>
+              <?=$this->transEsc('by')?>:
+              <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/>
+            <?php endif; ?>
+            <?php if (count($resource->getFormats()) > 0): ?>
+              <?=$this->record($resource)->getFormatList() ?>
+              <br/>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['volume'])): ?>
+              <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['publication_year'])): ?>
+              <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?>
+              <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['borrowingLocation'])): ?>
+              <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['checkoutDate'])): ?>
+              <strong><?=$this->transEsc('Checkout Date')?>:</strong> <?=$this->escapeHtml($ilsDetails['checkoutDate'])?><?php if (isset($ilsDetails['checkoutTime'])): ?> <span class="checkout-time"><?=$this->escapeHtml($ilsDetails['checkoutTime'])?><?php endif; ?></span><br/>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['returnDate'])): ?>
+              <strong><?=$this->transEsc('Return Date')?>:</strong> <?=$this->escapeHtml($ilsDetails['returnDate'])?><?php if (isset($ilsDetails['returnTime'])): ?> <span class="return-time"><?=$this->escapeHtml($ilsDetails['returnTime'])?><?php endif; ?></span><br/>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['dueDate'])): ?>
+              <strong><?=$this->transEsc('Due Date')?>:</strong> <?=$this->escapeHtml($ilsDetails['dueDate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <span class="due-time"><?=$this->escapeHtml($ilsDetails['dueTime'])?></span><?php endif; ?>
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?>
+              <div class="alert alert-info"><?=$this->transEsc($ilsDetails['message'])?></div>
+            <?php endif; ?>
+          </div>
+          <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+        </div>
+        <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?>
+      </div>
+    <?php endforeach; ?>
+    <?=$this->paginator ? $this->paginationControl($this->paginator, 'Sliding', 'Helpers/pagination.phtml', ['params' => $this->params]) : ''?>
+  <?php else: ?>
+    <?=$this->transEsc('loan_history_empty')?>
+  <?php endif; ?>
+</div>
+
+<div class="<?=$this->layoutClass('sidebar')?>">
+  <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'historicloans'])?>
+</div>
+<!-- finc: myresearch - historicloans - END -->
diff --git a/themes/finc/templates/myresearch/holds.phtml b/themes/finc/templates/myresearch/holds.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..107d964055f43490adb0074407b00010be4d7f4a
--- /dev/null
+++ b/themes/finc/templates/myresearch/holds.phtml
@@ -0,0 +1,188 @@
+<!-- finc: myresearch - holds -->
+<?php
+  // Set up page title:
+  $this->headTitle($this->translate('My Holds'));
+
+  // Set up breadcrumbs:
+  $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('My Holds') . '</li>';
+?>
+
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <h2><?=$this->transEsc('Your Holds and Recalls') ?></h2>
+
+  <?=$this->flashmessages()?>
+
+  <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?>
+
+  <?php if (!empty($this->recordList)): ?>
+    <?php if ($this->cancelForm): ?>
+      <form name="cancelForm" class="inline" method="post" id="cancelHold">
+        <input type="hidden" id="submitType" name="cancelSelected" value="1"/>
+        <input type="hidden" id="cancelConfirm" name="confirm" value="0"/>
+        <div class="btn-group">
+          <input id="cancelSelected" name="cancelSelected" type="submit" value="<?=$this->transEsc("hold_cancel_selected") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown"/>
+          <ul class="dropdown-menu">
+            <li class="disabled"><a><?=$this->transEsc("confirm_hold_cancel_selected_text") ?></a></li>
+            <li><a href="#" onClick="$('#cancelConfirm').val(1);$('#submitType').attr('name','cancelSelected');$(this).parents('form').submit(); return false;"><?=$this->transEsc('confirm_dialog_yes') ?></a></li>
+            <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li>
+          </ul>
+        </div>
+        <div class="btn-group">
+          <input id="cancelAll" name="cancelAll" type="submit" value="<?=$this->transEsc("hold_cancel_all") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown"/>
+          <ul class="dropdown-menu">
+            <li class="disabled"><a><?=$this->transEsc("confirm_hold_cancel_all_text") ?></a></li>
+            <li><a href="#" onClick="$('#cancelConfirm').val(1);$('#submitType').attr('name','cancelAll');$(this).parents('form').submit(); return false;"><?=$this->transEsc('confirm_dialog_yes') ?></a></li>
+            <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li>
+          </ul>
+        </div>
+    <?php endif; ?>
+
+    <?php $iteration = 0; ?>
+    <?php foreach ($this->recordList as $resource): ?>
+      <?php $iteration++; ?>
+      <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?>
+      <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result">
+        <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?>
+          <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?>
+          <input type="hidden" name="cancelAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" />
+          <div class="checkbox">
+            <label>
+              <input type="checkbox" name="cancelSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" id="checkbox_<?=$safeId?>" />
+            </label>
+          </div>
+        <?php endif; ?>
+
+        <?php
+          $coverDetails = $this->record($resource)->getCoverDetails('holds', 'small', $this->recordLink()->getUrl($resource));
+          $cover = $coverDetails['html'];
+          $thumbnail = false;
+          $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account');
+          if ($cover):
+            ob_start(); ?>
+            <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
+              <?=$cover ?>
+            </div>
+            <?php $thumbnail = ob_get_contents(); ?>
+          <?php ob_end_clean(); ?>
+        <?php endif; ?>
+        <div class="media">
+          <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+          <div class="media-body">
+            <?php
+              // If this is a non-missing Solr record, we should display a link:
+              if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) {
+                $title = $resource->getTitle();
+                $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title);
+                echo '<a href="' . $this->recordLink()->getUrl($resource)
+                  . '" class="title">' . $title . '</a>';
+              } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){
+                // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show...
+                echo '<span class="title">' . $this->escapeHtml($ilsDetails['title']) . '</span>';
+              } else {
+                // Last resort -- indicate that no title could be found.
+                echo $this->transEsc('Title not available');
+              }
+            ?><br/>
+            <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?>
+              <?=$this->transEsc('by')?>:
+              <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/>
+            <?php endif; ?>
+
+            <?php if (count($resource->getFormats()) > 0): ?>
+              <?=$this->record($resource)->getFormatList() ?>
+              <br/>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?>
+              <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?>
+              <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['requestGroup'])): ?>
+              <strong><?=$this->transEsc('hold_requested_group') ?>:</strong> <?=$this->transEsc('request_group_' . $ilsDetails['requestGroup'], null, $ilsDetails['requestGroup'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best
+               value to display... */ ?>
+            <?php $pickupDisplay = ''; ?>
+            <?php $pickupTranslate = false; ?>
+            <?php if (isset($ilsDetails['location'])): ?>
+              <?php if ($this->pickup): ?>
+                <?php foreach ($this->pickup as $library): ?>
+                  <?php if ($library['locationID'] == $ilsDetails['location']): ?>
+                    <?php $pickupDisplay = $library['locationDisplay']; ?>
+                    <?php $pickupTranslate = true; ?>
+                  <?php endif; ?>
+                <?php endforeach; ?>
+              <?php endif; ?>
+              <?php if (empty($pickupDisplay)): ?>
+                <?php $pickupDisplay = $ilsDetails['location']; ?>
+              <?php endif; ?>
+            <?php endif; ?>
+            <?php if (!empty($pickupDisplay)): ?>
+              <strong><?=$this->transEsc('pick_up_location') ?>:</strong>
+              <?=$pickupTranslate ? $this->transEsc('location_' . $pickupDisplay, null, $pickupDisplay) : $this->escapeHtml($pickupDisplay)?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['create'])): ?>
+              <strong><?=$this->transEsc('Created') ?>:</strong> <?=$this->escapeHtml($ilsDetails['create']) ?>
+              <?php if (!empty($ilsDetails['expire'])): ?>|<?php endif; ?>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['expire'])): ?>
+              <strong><?=$this->transEsc('Expires') ?>:</strong> <?=$this->escapeHtml($ilsDetails['expire']) ?>
+            <?php endif; ?>
+            <br />
+
+            <?php if (isset($this->cancelResults['items'])): ?>
+              <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?>
+                <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?>
+                  <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div>
+                <?php endif; ?>
+              <?php endforeach; ?>
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['available']) && $ilsDetails['available'] == true): ?>
+              <div class="text-success">
+                <?php if (!empty($ilsDetails['last_pickup_date'])): ?>
+                  <?=$this->transEsc('hold_available_until', ['%%date%%' => $ilsDetails['last_pickup_date']]) ?>
+                <?php else: ?>
+                  <?=$this->transEsc('hold_available') ?>
+                <?php endif; ?>
+              </div>
+            <?php elseif (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?>
+              <div class="text-success"><?=$this->transEsc('request_in_transit') . (is_string($ilsDetails['in_transit']) ? ': ' . $this->transEsc('institution_' . $ilsDetails['in_transit'], [], $ilsDetails['in_transit']) : '') ?></div>
+            <?php elseif (isset($ilsDetails['position'])): ?>
+              <p><strong><?=$this->transEsc("hold_queue_position") ?>:</strong> <?=$this->escapeHtml($ilsDetails['position']) ?></p>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['cancel_link'])): ?>
+              <p><a href="<?=$this->escapeHtmlAttr($ilsDetails['cancel_link']) ?>"><?=$this->transEsc("hold_cancel") ?></a></p>
+            <?php endif; ?>
+          </div>
+          <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+        </div>
+        <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?>
+      </div>
+    <?php endforeach; ?>
+    <?php if ($this->cancelForm): ?></form><?php endif; ?>
+  <?php else: ?>
+    <?=$this->transEsc('You do not have any holds or recalls placed') ?>.
+  <?php endif; ?>
+</div>
+
+<div class="<?=$this->layoutClass('sidebar')?>">
+  <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'holds'])?>
+</div>
+<!-- finc: myresearch - holds - END -->
diff --git a/themes/finc/templates/myresearch/illrequests.phtml b/themes/finc/templates/myresearch/illrequests.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..80c771161d09985ac53693b3697f5a37d7fab434
--- /dev/null
+++ b/themes/finc/templates/myresearch/illrequests.phtml
@@ -0,0 +1,182 @@
+<!-- finc: myresearch - illrequests -->
+<?php
+    // Set up page title:
+    $this->headTitle($this->translate('Interlibrary Loan Requests'));
+
+    // Set up breadcrumbs:
+    $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>'
+        . '<li class="active">' . $this->transEsc('Interlibrary Loan Requests') . '</li>';
+?>
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <h2><?=$this->transEsc('Interlibrary Loan Requests') ?></h2>
+
+  <?=$this->flashmessages()?>
+
+  <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?>
+
+  <?php if (!empty($this->recordList)): ?>
+    <?php if ($this->cancelForm): ?>
+      <form name="cancelForm" class="inline" method="post" id="cancelILLRequest">
+        <input type="hidden" id="submitType" name="cancelSelected" value="1"/>
+        <input type="hidden" id="cancelConfirm" name="confirm" value="0"/>
+        <div class="btn-group">
+          <input id="cancelSelected" name="cancelSelected" type="submit" value="<?=$this->transEsc("ill_request_cancel_selected") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown"/>
+          <ul class="dropdown-menu">
+            <li class="disabled"><a><?=$this->transEsc("confirm_ill_request_cancel_selected_text") ?></a></li>
+            <li><a href="#" onClick="$('#cancelConfirm').val(1);$('#submitType').attr('name','cancelSelected');$(this).parents('form').submit(); return false;"><?=$this->transEsc('confirm_dialog_yes') ?></a></li>
+            <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li>
+          </ul>
+        </div>
+        <div class="btn-group">
+          <input id="cancelAll" name="cancelAll" type="submit" value="<?=$this->transEsc("ill_request_cancel_all") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown"/>
+          <ul class="dropdown-menu">
+            <li class="disabled"><a><?=$this->transEsc("confirm_ill_request_cancel_all_text") ?></a></li>
+            <li><a href="#" onClick="$('#cancelConfirm').val(1);$('#submitType').attr('name','cancelAll');$(this).parents('form').submit(); return false;"><?=$this->transEsc('confirm_dialog_yes') ?></a></li>
+            <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li>
+          </ul>
+        </div>
+    <?php endif; ?>
+
+    <?php $iteration = 0; ?>
+    <?php foreach ($this->recordList as $resource): ?>
+      <?php $iteration++; ?>
+      <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?>
+      <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result">
+        <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?>
+          <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?>
+          <div class="checkbox">
+            <input type="hidden" name="cancelAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" />
+            <label>
+              <input type="checkbox" name="cancelSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" id="checkbox_<?=$safeId?>" />
+            </label>
+          </div>
+        <?php endif; ?>
+
+        <?php
+          $coverDetails = $this->record($resource)->getCoverDetails('illrequests', 'small', $this->recordLink()->getUrl($resource));
+          $cover = $coverDetails['html'];
+          $thumbnail = false;
+          $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account');
+          if ($cover):
+            ob_start(); ?>
+            <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
+              <?=$cover ?>
+            </div>
+            <?php $thumbnail = ob_get_contents(); ?>
+          <?php ob_end_clean(); ?>
+        <?php endif; ?>
+        <div class="media">
+          <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+          <div class="media-body">
+            <?php
+              // If this is a non-missing Solr record, we should display a link:
+              if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) {
+                $title = $resource->getTitle();
+                $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title);
+                echo '<a href="' . $this->recordLink()->getUrl($resource)
+                  . '" class="title">' . $title . '</a>';
+              } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){
+                // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show...
+                echo $this->escapeHtml($ilsDetails['title']);
+              } else {
+                // Last resort -- indicate that no title could be found.
+                echo $this->transEsc('Title not available');
+              }
+            ?><br/>
+            <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?>
+              <?=$this->transEsc('by')?>:
+              <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/>
+            <?php endif; ?>
+
+            <?php if (count($resource->getFormats()) > 0): ?>
+              <?=$this->record($resource)->getFormatList() ?>
+              <br/>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?>
+              <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?>
+              <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best
+               value to display... */ ?>
+            <?php $pickupDisplay = ''; ?>
+            <?php $pickupTranslate = false; ?>
+            <?php if (isset($ilsDetails['location'])): ?>
+              <?php if ($this->pickup): ?>
+                <?php foreach ($this->pickup as $library): ?>
+                  <?php if ($library['locationID'] == $ilsDetails['location']): ?>
+                    <?php $pickupDisplay = $library['locationDisplay']; ?>
+                    <?php $pickupTranslate = true; ?>
+                  <?php endif; ?>
+                <?php endforeach; ?>
+              <?php endif; ?>
+              <?php if (empty($pickupDisplay)): ?>
+                <?php $pickupDisplay = $ilsDetails['location']; ?>
+              <?php endif; ?>
+            <?php endif; ?>
+            <?php if (!empty($pickupDisplay)): ?>
+              <strong><?=$this->transEsc('pick_up_location') ?>:</strong>
+              <?=$pickupTranslate ? $this->transEsc($pickupDisplay) : $this->escapeHtml($pickupDisplay)?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['create'])): ?>
+              <strong><?=$this->transEsc('Created') ?>:</strong> <?=$this->escapeHtml($ilsDetails['create']) ?>
+              <?php if (!empty($ilsDetails['expire'])): ?>|<?php endif; ?>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['expire'])): ?>
+              <strong><?=$this->transEsc('Expires') ?>:</strong> <?=$this->escapeHtml($ilsDetails['expire']) ?>
+            <?php endif; ?>
+            <br />
+
+            <?php if (isset($this->cancelResults['items'])): ?>
+              <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?>
+                <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?>
+                  <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div>
+                <?php endif; ?>
+              <?php endforeach; ?>
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?>
+              <div class="text-success"><?=$this->transEsc("request_in_transit") . (is_string($ilsDetails['in_transit']) ? ': ' . $this->transEsc('institution_' . $ilsDetails['in_transit'], [], $ilsDetails['in_transit']) : '') ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['processed']) && $ilsDetails['processed']): ?>
+              <div class="text-success"><?=$this->transEsc("ill_request_processed") . (is_string($ilsDetails['processed']) ? ': ' . $ilsDetails['processed'] : '') ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['available']) && $ilsDetails['available']): ?>
+              <div class="text-success"><?=$this->transEsc("ill_request_available") ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['canceled']) && $ilsDetails['canceled']): ?>
+              <div class="text-success"><?=$this->transEsc("ill_request_canceled") . (is_string($ilsDetails['canceled']) ? ': ' . $ilsDetails['canceled'] : '') ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['cancel_link'])): ?>
+              <p><a href="<?=$this->escapeHtmlAttr($ilsDetails['cancel_link']) ?>"><?=$this->transEsc("ill_request_cancel") ?></a></p>
+            <?php endif; ?>
+          </div>
+          <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+        </div>
+        <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?>
+      </div>
+    <?php endforeach; ?>
+    <?php if ($this->cancelForm): ?></form><?php endif; ?>
+  <?php else: ?>
+    <?=$this->transEsc('You do not have any interlibrary loan requests placed') ?>.
+  <?php endif; ?>
+</div>
+
+<div class="<?=$this->layoutClass('sidebar')?>">
+  <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'ILLRequests'])?>
+</div>
+<!-- finc: myresearch - illrequests - END -->
diff --git a/themes/finc/templates/myresearch/menu.phtml b/themes/finc/templates/myresearch/menu.phtml
index 540876002894e0f2b6aa196e9d63c7c8027e8d54..1e62d72f79ff02f51fde3bfa1df03db64bf22529 100644
--- a/themes/finc/templates/myresearch/menu.phtml
+++ b/themes/finc/templates/myresearch/menu.phtml
@@ -4,6 +4,9 @@ $user = $this->auth()->isLoggedIn();
 $patron = $user ? $this->auth()->getILSPatron() : false;
 $capabilityParams = $patron ? ['patron' => $patron] : [];
 ?>
+<?php /* Offcanvas closing button missing in BS3! CK*/ ?>
+<button class="close-offcanvas btn btn-link" data-toggle="offcanvas"><?=$this->transEsc('navigate_back') ?></button>
+
 <h4><?=$this->transEsc('Your Account')?></h4>
 <?php /* finc needs to add .facet-group class and classes on sub items for borders - CK */ ?>
 <div class="myresearch-menu account-menu facet-group">
diff --git a/themes/finc/templates/myresearch/mylist.phtml b/themes/finc/templates/myresearch/mylist.phtml
index 16a97100b770982188891fe6cea499a17f8c2f56..0c9489e95e5adc7fe88112c0ca7ea5a2cb83154f 100644
--- a/themes/finc/templates/myresearch/mylist.phtml
+++ b/themes/finc/templates/myresearch/mylist.phtml
@@ -29,6 +29,9 @@ $user = $this->auth()->isLoggedIn();
 <?=$this->flashmessages()?>
 
 <div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
   <h2><?=$list ? $this->escapeHtml($list->title) : $this->transEsc("Your Favorites")?></h2>
   <nav class="search-header hidden-print">
     <div class="search-stats">
diff --git a/themes/finc/templates/myresearch/profile.phtml b/themes/finc/templates/myresearch/profile.phtml
index 23a563176f250ea6d5eb7e388fe4089b3a036762..239d504c955bd4813252cf67a6ecad44964cc90c 100644
--- a/themes/finc/templates/myresearch/profile.phtml
+++ b/themes/finc/templates/myresearch/profile.phtml
@@ -11,6 +11,9 @@ $arrTemplate = '<tr><th>%%LABEL%%:</th><td> %%VALUE%%</td></tr>';
 ?>
 
 <div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
   <h2><?=$this->transEsc('Your Profile')?></h2>
   <?=$this->flashmessages();?>
 
@@ -25,7 +28,8 @@ $arrTemplate = '<tr><th>%%LABEL%%:</th><td> %%VALUE%%</td></tr>';
         ]
     )?>
     <?php if (count($this->pickup ?? []) > 1): // Skip form if only one location: ?>
-      <tr><th><?=$this->transEsc('Preferred Library')?>:</th>
+    <tr>
+      <th><?=$this->transEsc('Preferred Library')?>:</th>
       <?php
         $selected = (strlen($this->profile['home_library'] ?? '') > 0)
         ? $this->profile['home_library'] : $this->defaultPickupLocation
diff --git a/themes/finc/templates/myresearch/storageretrievalrequests.phtml b/themes/finc/templates/myresearch/storageretrievalrequests.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..845eb7726bcddadc227b069ba04a581a5e1e74cc
--- /dev/null
+++ b/themes/finc/templates/myresearch/storageretrievalrequests.phtml
@@ -0,0 +1,179 @@
+<!-- finc: myresearch - storageretrievalrequests -->
+<?php
+  // Set up page title:
+  $this->headTitle($this->translate('Storage Retrieval Requests'));
+
+  // Set up breadcrumbs:
+  $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Storage Retrieval Requests') . '</li>';
+?>
+
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <h2><?=$this->transEsc('Storage Retrieval Requests') ?></h2>
+
+  <?=$this->flashmessages()?>
+
+  <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?>
+
+  <?php if (!empty($this->recordList)): ?>
+    <?php if ($this->cancelForm): ?>
+      <form name="cancelForm" class="inline" method="post" id="cancelStorageRetrievalRequest">
+        <input type="hidden" id="submitType" name="cancelSelected" value="1"/>
+        <input type="hidden" id="cancelConfirm" name="confirm" value="0"/>
+        <div class="btn-group">
+          <input id="cancelSelected" name="cancelSelected" type="submit" value="<?=$this->transEsc("storage_retrieval_request_cancel_selected") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown"/>
+          <ul class="dropdown-menu">
+            <li class="disabled"><a><?=$this->transEsc("confirm_storage_retrieval_request_cancel_selected_text") ?></a></li>
+            <li><a href="#" onClick="$('#cancelConfirm').val(1);$('#submitType').attr('name','cancelSelected');$(this).parents('form').submit(); return false;"><?=$this->transEsc('confirm_dialog_yes') ?></a></li>
+            <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li>
+          </ul>
+        </div>
+        <div class="btn-group">
+          <input id="cancelAll" name="cancelAll" type="submit" value="<?=$this->transEsc("storage_retrieval_request_cancel_all") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown"/>
+          <ul class="dropdown-menu">
+            <li class="disabled"><a><?=$this->transEsc("confirm_storage_retrieval_request_cancel_all_text") ?></a></li>
+            <li><a href="#" onClick="$('#cancelConfirm').val(1);$('#submitType').attr('name','cancelAll');$(this).parents('form').submit(); return false;"><?=$this->transEsc('confirm_dialog_yes') ?></a></li>
+            <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li>
+          </ul>
+        </div>
+    <?php endif; ?>
+
+    <?php $iteration = 0; ?>
+    <?php foreach ($this->recordList as $resource): ?>
+      <?php $iteration++; ?>
+      <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?>
+      <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result">
+        <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?>
+          <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?>
+          <div class="checkbox">
+            <input type="hidden" name="cancelAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" />
+            <label class="pull-left flip">
+              <input type="checkbox" name="cancelSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" id="checkbox_<?=$safeId?>" />
+            </label>
+          </div>
+        <?php endif; ?>
+
+        <?php
+          $coverDetails = $this->record($resource)->getCoverDetails('storageretrievalrequests', 'small', $this->recordLink()->getUrl($resource));
+          $cover = $coverDetails['html'];
+          $thumbnail = false;
+          $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account');
+          if ($cover):
+            ob_start(); ?>
+            <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
+              <?=$cover ?>
+            </div>
+            <?php $thumbnail = ob_get_contents(); ?>
+          <?php ob_end_clean(); ?>
+        <?php endif; ?>
+        <div class="media">
+          <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+          <div class="media-body">
+            <?php
+              // If this is a non-missing Solr record, we should display a link:
+              if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) {
+                $title = $resource->getTitle();
+                $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title);
+                echo '<a href="' . $this->recordLink()->getUrl($resource)
+                  . '" class="title">' . $title . '</a>';
+              } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){
+                // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show...
+                echo $this->escapeHtml($ilsDetails['title']);
+              } else {
+                // Last resort -- indicate that no title could be found.
+                echo $this->transEsc('Title not available');
+              }
+            ?><br/>
+            <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?>
+              <?=$this->transEsc('by')?>:
+              <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/>
+            <?php endif; ?>
+
+            <?php if (count($resource->getFormats()) > 0): ?>
+              <?=$this->record($resource)->getFormatList() ?>
+              <br/>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?>
+              <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?>
+              <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?>
+              <br />
+            <?php endif; ?>
+
+            <?php /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best
+               value to display... */ ?>
+            <?php $pickupDisplay = ''; ?>
+            <?php $pickupTranslate = false; ?>
+            <?php if (isset($ilsDetails['location'])): ?>
+              <?php if ($this->pickup): ?>
+                <?php foreach ($this->pickup as $library): ?>
+                  <?php if ($library['locationID'] == $ilsDetails['location']): ?>
+                    <?php $pickupDisplay = $library['locationDisplay']; ?>
+                    <?php $pickupTranslate = true; ?>
+                  <?php endif; ?>
+                <?php endforeach; ?>
+              <?php endif; ?>
+              <?php if (empty($pickupDisplay)): ?>
+                <?php $pickupDisplay = $ilsDetails['location']; ?>
+              <?php endif; ?>
+            <?php endif; ?>
+            <?php if (!empty($pickupDisplay)): ?>
+              <strong><?=$this->transEsc('pick_up_location') ?>:</strong>
+              <?=$pickupTranslate ? $this->transEsc($pickupDisplay) : $this->escapeHtml($pickupDisplay)?>
+              <br />
+            <?php endif; ?>
+
+            <?php if (!empty($ilsDetails['create'])): ?>
+              <strong><?=$this->transEsc('Created') ?>:</strong> <?=$this->escapeHtml($ilsDetails['create']) ?>
+              <?php if (!empty($ilsDetails['expire'])): ?>|<?php endif; ?>
+            <?php endif; ?>
+            <?php if (!empty($ilsDetails['expire'])): ?>
+              <strong><?=$this->transEsc('Expires') ?>:</strong> <?=$this->escapeHtml($ilsDetails['expire']) ?>
+            <?php endif; ?>
+            <br />
+
+            <?php if (isset($this->cancelResults['items'])): ?>
+              <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?>
+                <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?>
+                  <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div>
+                <?php endif; ?>
+              <?php endforeach; ?>
+            <?php endif; ?>
+
+            <?php if (isset($ilsDetails['processed']) && $ilsDetails['processed']): ?>
+              <div class="text-success"><?=$this->transEsc("storage_retrieval_request_processed") . (is_string($ilsDetails['processed']) ? ': ' . $ilsDetails['processed'] : '') ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['available']) && $ilsDetails['available']): ?>
+              <div class="text-success"><?=$this->transEsc("storage_retrieval_request_available") ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['canceled']) && $ilsDetails['canceled']): ?>
+              <div class="text-success"><?=$this->transEsc("storage_retrieval_request_canceled") . (is_string($ilsDetails['canceled']) ? ': ' . $ilsDetails['canceled'] : '') ?></div>
+            <?php endif; ?>
+            <?php if (isset($ilsDetails['cancel_link'])): ?>
+              <p><a href="<?=$this->escapeHtmlAttr($ilsDetails['cancel_link']) ?>"><?=$this->transEsc("storage_retrieval_request_cancel") ?></a></p>
+            <?php endif; ?>
+          </div>
+          <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?>
+            <?=$thumbnail ?>
+          <?php endif ?>
+        </div>
+        <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?>
+      </div>
+    <?php endforeach; ?>
+    <?php if ($this->cancelForm): ?></form><?php endif; ?>
+  <?php else: ?>
+    <?=$this->transEsc('You do not have any storage retrieval requests placed') ?>.
+  <?php endif; ?>
+</div>
+
+<div class="<?=$this->layoutClass('sidebar')?>">
+  <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'storageRetrievalRequests'])?>
+</div>
+<!-- finc: myresearch - storageretrievalrequests - END -->
diff --git a/themes/finc/templates/search/history.phtml b/themes/finc/templates/search/history.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..c7b90af1e981305550957d4e3c7d711accf436d5
--- /dev/null
+++ b/themes/finc/templates/search/history.phtml
@@ -0,0 +1,41 @@
+<!-- finc - templates - search - history -->
+<?php
+  // Set page title.
+  $this->headTitle($this->translate('Search History'));
+
+  // Set up breadcrumbs:
+  $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>'
+    . '<li class="active">' . $this->transEsc('History') . '</li>';
+
+  $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled';
+?>
+
+<div class="<?=$this->layoutClass('mainbody')?>">
+  <?php /* finc V5: toggler was missing, remove entire template (!) when fixed in BS3 theme - CK */ ?>
+  <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler-myresearch'); ?>
+
+  <?=$this->flashmessages()?>
+  <?php if ($saveSupported && !empty($this->saved)): ?>
+    <h2><?=$this->transEsc("history_saved_searches")?></h2>
+    <?=$this->context()->renderInContext('search/history-table.phtml', ['showSaved' => true]);?>
+  <?php endif; ?>
+
+  <h2><?=$this->transEsc("history_recent_searches")?></h2>
+  <?php if (!empty($this->unsaved)): ?>
+    <?=$this->context()->renderInContext('search/history-table.phtml', ['showSaved' => false]);?>
+    <a href="?purge=true"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_purge")?></a>
+  <?php else: ?>
+    <?=$this->transEsc("history_no_searches")?>
+  <?php endif; ?>
+</div>
+
+<?php if ($saveSupported): ?>
+  <div class="<?=$this->layoutClass('sidebar')?>">
+    <?=$this->context($this)->renderInContext(
+        "myresearch/menu.phtml",
+        // Only activate search history in account menu if user is logged in.
+        $this->auth()->isLoggedIn() ? ['active' => 'history'] : []
+     );
+     ?>
+  </div>
+<?php endif; ?>
diff --git a/themes/finc/templates/search/results.phtml b/themes/finc/templates/search/results.phtml
index fe22d92e56bdd3226eefbce493f4aa52d092b449..49db910d2280c332c90a02ef4ade30869253827f 100644
--- a/themes/finc/templates/search/results.phtml
+++ b/themes/finc/templates/search/results.phtml
@@ -69,7 +69,7 @@ $this->headScript()->appendFile("check_save_statuses.js");
         <span class="hit-stats hidden-xs hidden-sm">
         <?=$this->context()->renderInContext('search/controls/showing.phtml', ['lookfor' => $lookfor, 'recordTotal' => $recordTotal]) ?>
         </span>
-        <span class="offcanvas-toogler">
+        <span class="offcanvas-toggler">
           <button class="search-filter-toggle btn btn-primary visible-xs" href="#search-sidebar" data-toggle="offcanvas" title="<?=$this->transEsc('sidebar_expand') ?>">
           <?=$this->transEsc('Refine Results') ?>
           </button>
diff --git a/themes/finc/theme.config.php b/themes/finc/theme.config.php
index b5635ac379365a3ae95f1fd65ea5e8ce13b60964..5674711edbfaf7418ba1140f70d5905fbb035c70 100644
--- a/themes/finc/theme.config.php
+++ b/themes/finc/theme.config.php
@@ -13,7 +13,7 @@ return [
             'interlibraryloan' => 'finc\View\Helper\Root\InterlibraryLoanLink',
             'sideFacet' => 'finc\View\Helper\Root\SideFacet',
             'resultfeed' => 'finc\View\Helper\Root\ResultFeed',
-            'recordlink' => 'finc\View\Helper\Root\RecordLink',
+            'recordLink' => 'finc\View\Helper\Root\RecordLink',
         ],
         'factories' => [
             'finc\View\Helper\Root\BranchInfo' =>