diff --git a/module/finc/config/module.config.php b/module/finc/config/module.config.php
index 5c218d3ad92e79b8b75ab603375be7f878e37c63..ce432792059bbbdd5ac1895438b70050b97eb4a3 100644
--- a/module/finc/config/module.config.php
+++ b/module/finc/config/module.config.php
@@ -73,12 +73,14 @@ $config = [
                     'finc\ILS\Driver\FincILS' => 'finc\ILS\Driver\Factory::getFincILS',
                     'finc\ILS\Driver\PAIA' => 'finc\ILS\Driver\Factory::getPAIA',
                     //finctheca is deprecated: Remove when Bibliotheca support ends
-                    'finc\ILS\Driver\FincTheca' => 'finc\ILS\Driver\Factory::GetFincTheca'
+                    'finc\ILS\Driver\FincTheca' => 'finc\ILS\Driver\Factory::getFincTheca',
+                    'finc\ILS\Driver\FincLibero' => 'finc\ILS\Driver\Factory::getFincLibero',
                 ],
                 'aliases' => [
                     'fincils' => 'finc\ILS\Driver\FincILS',
                     'paia' => 'finc\ILS\Driver\PAIA',
                     'finctheca' => 'finc\ILS\Driver\FincTheca',
+                    'finclibero' => 'finc\ILS\Driver\FincLibero'
                 ],
             ],
             'recommend' => [
diff --git a/module/finc/src/finc/ILS/Driver/Factory.php b/module/finc/src/finc/ILS/Driver/Factory.php
index dea94ff068ebacb808f88eb04086a5011f2d9f36..56c3a0e7806bbab31d9a85002f23b3ad4f2ac376 100644
--- a/module/finc/src/finc/ILS/Driver/Factory.php
+++ b/module/finc/src/finc/ILS/Driver/Factory.php
@@ -133,4 +133,39 @@ class Factory
 
         return $fl;
     }
+
+    /**
+     * Factory for FincLibero driver.
+     *
+     * @param \Psr\Container\ContainerInterface $container Service manager.
+     *
+     * @return FincLibero
+     */
+    public static function getFincLibero(ContainerInterface $container)
+    {
+        $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($container->get('VuFind\ProxyConfig'));
+
+        $callback = function (& $wrapped, $proxy) use ($container) {
+            $wrapped = $container->get('ZfcRbac\Service\AuthorizationService');
+
+            $proxy->setProxyInitializer(null);
+        };
+
+        $fl = new FincLibero(
+            $container->get('VuFind\DateConverter'),
+            $container->get('VuFind\SessionManager'),
+            $container->get('VuFind\RecordLoader'),
+            $container->get('VuFind\Search'),
+            $container->get('VuFind\Config')->get('config'),
+            $factory->createProxy('ZfcRbac\Service\AuthorizationService', $callback)
+        );
+
+        $fl->setCacheStorage(
+            $container->get('VuFind\CacheManager')->getCache('object')
+        );
+
+        $fl->staticStatusRules = $container->get('VuFind\YamlReader')->get('StaticStatusRules.yaml');
+
+        return $fl;
+    }
 }
diff --git a/module/finc/src/finc/ILS/Driver/FincILS.php b/module/finc/src/finc/ILS/Driver/FincILS.php
index 14eccacc8509a1c99708c6ba1f1bb731aa5873d1..4f0bdfbf5c1d20cc9add1b2ca78a54c99fc2db11 100644
--- a/module/finc/src/finc/ILS/Driver/FincILS.php
+++ b/module/finc/src/finc/ILS/Driver/FincILS.php
@@ -1688,4 +1688,17 @@ class FincILS extends PAIA implements LoggerAwareInterface
             throw new ILSException($e->getMessage());
         }
     }
+
+    /**
+     * Helper function to filter certain limitations.
+     *
+     * @param array $limitations An item's limitations.
+     * @return mixed
+     */
+    protected function filterNonFunctionalLimitations($limitations)
+    {
+        //standard behavior is: do nothing
+        // overriden in FincLibero
+        return $limitations;
+    }
 }
diff --git a/module/finc/src/finc/ILS/Driver/FincLibero.php b/module/finc/src/finc/ILS/Driver/FincLibero.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2325e0b0158612d0157af798c1ca1adc290bfad
--- /dev/null
+++ b/module/finc/src/finc/ILS/Driver/FincLibero.php
@@ -0,0 +1,694 @@
+<?php
+/**
+ * Finc specific Libero ILS Driver for VuFind, using PAIA, DAIA and LiberoDing
+ * services.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Leipzig University Library 2015.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  ILS_Drivers
+ * @author   André Lahmann <lahmann@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki
+ */
+namespace finc\ILS\Driver;
+use VuFind\I18n\Translator\TranslatorAwareTrait;
+use VuFind\I18n\Translator\TranslatorAwareInterface,
+    VuFind\Exception\ILS as ILSException;
+
+/**
+ * Finc specific Libero ILS Driver for VuFind, using PAIA, DAIA and LiberoDing
+ * services.
+ *
+ * @category VuFind2
+ * @package  ILS_Drivers
+ * @author   André Lahmann <lahmann@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki
+ */
+class FincLibero extends FincILS implements TranslatorAwareInterface
+{
+    const DELETE_NOTIFICATIONS_SUCCESS = '1';
+    const DELETE_NOTIFICATIONS_ERROR = '0';
+    use LiberoDingTrait;
+    use TranslatorAwareTrait;
+
+    /**
+     * Contains after init() is done all the limitations that are configured in the
+     * FincLibero.ini as being used for triggering actions etc (e.g.:
+     * requestableLimitations).
+     *
+     * @var array
+     */
+    protected $configuredLimitations = [];
+
+    /**
+     * Limitations that will trigger the placeStorageRetrievalRequest action
+     *
+     * @var array
+     */
+    protected $requestableLimitations = [];
+
+    /**
+     * Limitations that will trigger the placeHold action
+     *
+     * @var array
+     */
+    protected $holdableLimitations = [];
+
+    /**
+     * Limitations that will trigger a recall action (currently not used).
+     *
+     * @var array
+     */
+    protected $recallableLimitations = [];
+
+    /**
+     * Limitations that will identify the item for being bound to another item.
+     *
+     * @var array
+     */
+    protected $awlLimitations = [];
+
+    /**
+     * Patterns that will identify limitations as pickUpLocations
+     *
+     * @var array
+     */
+    protected $pickUpLocationPatterns = [];
+
+    /**
+     * URIs that will be used for stack views
+     *
+     * @var array
+     */
+    protected $stackURIs = [];
+
+    /**
+     * URIs that will be used for reading room views
+     *
+     * @var array
+     */
+    protected $readingRoomURIs = [];
+
+    /**
+     * Helper function to extract the Namespace from the DAIA URI prefix.
+     *
+     * @return string
+     */
+    public function getDaiaIdPrefixNamespace()
+    {
+        return preg_quote(substr($this->daiaIdPrefix, 0, strpos($this->daiaIdPrefix, ':')+1));
+    }
+
+    /**
+     * FincLibero specific overrides of PAIA methods
+     */
+
+    /**
+     * Place Hold
+     *
+     * Attempts to place a hold or recall on a particular item and returns
+     * an array with result details
+     *
+     * Make a request on a specific record
+     *
+     * @param array $holdDetails An array of item and patron data
+     *
+     * @return mixed An array of data on the request including
+     * whether or not it was successful and a system message (if available)
+     */
+    public function placeHold($holdDetails)
+    {
+        $item = $holdDetails['item_id'];
+        $patron = $holdDetails['patron'];
+
+        $doc = [];
+        $doc['item'] = stripslashes($item);
+        if ($confirm = $this->getConfirmations($holdDetails)) {
+            $doc["confirm"] = $confirm;
+        }
+        $post_data['doc'][] = $doc;
+
+        try {
+            $array_response = $this->paiaPostAsArray(
+                'core/'.$patron['cat_username'].'/request', $post_data
+            );
+        } catch (Exception $e) {
+            $this->debug($e->getMessage());
+            return [
+                'success' => false,
+                'sysMessage' => $e->getMessage(),
+            ];
+        }
+
+        $details = [];
+
+        if (array_key_exists('error', $array_response)) {
+            $details = [
+                'success' => false,
+                'sysMessage' => $array_response['error_description']
+            ];
+        } else {
+            $elements = $array_response['doc'];
+            foreach ($elements as $element) {
+                if (array_key_exists('error', $element)) {
+                    $details = [
+                        'success' => false,
+                        'sysMessage' => $element['error']
+                    ];
+                } else {
+                    // FincLibero supports more expressive responses from paialibero
+                    // which should be shown instead to the user (localIlsStatus)
+                    $details = [
+                        'success' => true,
+                        'sysMessage' => isset($element['localIlsStatus'])
+                            ? $element['localIlsStatus'] : 'Successfully requested'
+                    ];
+                    // if caching is enabled for DAIA remove the cached data for the
+                    // current item otherwise the changed status will not be shown
+                    // before the cache expires
+                    if ($this->daiaCacheEnabled) {
+                        $this->removeCachedData($holdDetails['doc_id']);
+                    }
+                    if ($this->paiaCacheEnabled) {
+                        $this->removeCachedData($patron['cat_username']);
+                    }
+                }
+            }
+        }
+        return $details;
+    }
+
+    /**
+     * PAIA authentication function with custom signature -- libraryuserid added for
+     * Libero Id
+     *
+     * @param string $username      Username
+     * @param string $password      Password
+     * @param string $libraryuserid Libero ID
+     *
+     * @return mixed Associative array of patron info on successful login,
+     * null on unsuccessful login, PEAR_Error on error.
+     * @throws ILSException
+     */
+    protected function paiaLogin($username, $password, $libraryuserid = null)
+    {
+        // perform full PAIA auth and get patron info
+        $post_data = [
+            "username"   => $username,
+            "password"   => $password,
+            "grant_type" => "password",
+            "scope"      => self::SCOPE_READ_PATRON . " " .
+                self::SCOPE_READ_FEES . " " .
+                self::SCOPE_READ_ITEMS . " " .
+                self::SCOPE_WRITE_ITEMS . " " .
+                self::SCOPE_CHANGE_PASSWORD
+        ];
+
+        // Customization for internal Libero Id
+        if (isset($libraryuserid)) {
+            $post_data['libraryuserid'] = $libraryuserid;
+        }
+
+        $responseJson = $this->paiaPostRequest('auth/login', $post_data);
+
+        try {
+            $responseArray = $this->paiaParseJsonAsArray($responseJson);
+        } catch (Exception $e) {
+            // all error handling is done in paiaHandleErrors so pass on the excpetion
+            throw $e;
+        }
+
+        if (!isset($responseArray['access_token'])) {
+            throw new ILSException(
+                'Unknown error! Access denied.'
+            );
+        } elseif (!isset($responseArray['patron'])) {
+            throw new ILSException(
+                'Login credentials accepted, but got no patron ID?!?'
+            );
+        } else {
+            // at least access_token and patron got returned which is sufficient for
+            // us, now save all to session
+            $session = $this->getSession();
+
+            // as we do not store the password in the database, write it to the
+            // session (but only if we are not using root-login
+            if ($libraryuserid === null) {
+                $session->cat_password = $password;
+            }
+
+            $session->patron
+                = isset($responseArray['patron'])
+                ? $responseArray['patron'] : null;
+            $session->access_token
+                = isset($responseArray['access_token'])
+                ? $responseArray['access_token'] : null;
+            $session->scope
+                = isset($responseArray['scope'])
+                ? explode(' ', $responseArray['scope']) : null;
+            $session->expires
+                = isset($responseArray['expires_in'])
+                ? (time() + ($responseArray['expires_in'])) : null;
+            // Customization for internal Libero Id
+            $session->borrower_id
+                = isset($responseArray['borrower_id'])
+                ? $responseArray['borrower_id'] : null;
+
+            return true;
+        }
+    }
+
+    /**
+     * PAIA helper function to map session data to return value of patronLogin()
+     *
+     * @param $details  Patron details returned by patronLogin
+     * @param $password Patron cataloge password
+     * @return mixed
+     */
+    protected function enrichUserDetails($details, $password, $username = null)
+    {
+        $details = parent::enrichUserDetails($details, $password, $username);
+
+        // store also the Libero Id
+        $session = $this->getSession();
+        $details['borrower_id']  = $session->borrower_id;
+        return $this->enrichWithUserRestrictions($details);
+    }
+
+    /**
+     * Determines for each of the tokens in @param $details['type'] whether it is
+     * configured to be a restriction type (as configured in FincLibero.ini
+     * [General] userRestrictionMessagePatterns) and saves positive matches in
+     * $details['restrictions'].
+     *
+     * @param $details patron details
+     * @return mixed modified patron details
+     */
+    protected function enrichWithUserRestrictions($details)
+    {
+        //early return
+        if (!isset($details['type'])
+            || empty($details['type'])
+            || !isset($this->config['General']['userRestrictionMessagePatterns'])
+        ) {
+            return $details;
+        }
+
+        foreach ($this->config['General']['userRestrictionMessagePatterns'] as $pattern) {
+            foreach ($details['type'] as $type) {
+                if (preg_match($pattern, $type)) {
+                    $details['restrictions'][] = 'UserRestrictionMessages::' . $type;
+                }
+            }
+        }
+        return $details;
+    }
+
+    /**
+     * FincLibero specific overrides of DAIA methods
+     */
+
+    /**
+     * Returns an array with status information for provided item.
+     *
+     * @param array $item Array with DAIA item data
+     *
+     * @return array
+     */
+    protected function getItemStatus($item)
+    {
+        $return = parent::getItemStatus($item);
+
+        $return['awlRecordId'] = $this->getBoundItemId($item);
+        // is this item bound with another item?
+        if ($return['awlRecordId'] != null) {
+            // overwrite any existing link settings as we need to order this item
+            // via the bound item
+            $return['addLink'] =
+            $return['addStorageRetrievalRequestLink'] =
+            $return['addILLRequestLink'] =
+            $return['addEmailHoldLink'] = false;
+            $return['awlRecordStatus'] =
+                current($this->getStatus($return['awlRecordId']));
+        }
+
+        // add all item specific information from DAIA field about to item_notes
+        // (https://intern.finc.info/issues/7863)
+        $about = (isset($item['about'])) ? [$item['about']] : [];
+
+        $return['item_notes'] = array_unique(
+            array_merge(
+                (array) $return['status'],
+                $return['item_notes'],
+                $about
+            )
+        );
+        return $return;
+    }
+
+    /**
+     * Helper function to return an appropriate status string for current item
+     *
+     * @param $item
+     * @return string
+     */
+    protected function getStatusString($item)
+    {
+        return isset($item['localIlsStatus']) ? $item['localIlsStatus'] : '';
+    }
+
+    /**
+     * FincLibero specific implementation that adds pickUpLocations and the content
+     * of EmailHold limitations to the customData array.
+     *
+     * @param array $item A DAIA item.
+     * @return array
+     */
+    protected function getCustomData($item)
+    {
+        $customData = [];
+        foreach (['available', 'unavailable'] as $availability) {
+            if (isset($item[$availability])) {
+                foreach ($item[$availability] as $available) {
+                    if (isset($available['service'])
+                        && in_array($available['service'], ['presentation', 'loan'])
+                    ) {
+                        // deal with pickuplocations
+                        if (isset($available['limitation'])
+                            && $pickUpLocations = $this->filterPickUpLocations(
+                                $available['limitation']
+                            )
+                        ) {
+                            // if we have limitations qualifying for pickUpLocations,
+                            // save those in customData
+                            $customData['pickUpLocations'] = $pickUpLocations;
+                        } elseif (!isset($customData['pickUpLocations']) && isset($item['department'])) {
+                            // if we have no explicit limitations qualifying for
+                            // pickUpLocations, assume the item's department as single
+                            // pickUpLocation
+                            $customData['pickUpLocations'] = [
+                                [
+                                    'locationID' => $item['department']['id'],
+                                    'locationDisplay' => $item['department']['content']
+                                ]
+                            ];
+                        }
+                        // deal with EmailHold information
+                        if (isset($available['limitation'])) {
+                            foreach ($available['limitation'] as $limitation) {
+                                $criteria = $this->getEmailHoldValidationCriteria();
+                                // we assume that each configured criteria for
+                                // EmailHold will provide information (if existent in
+                                // current item) supposed to be passed to the view
+                                // via customData array
+                                foreach ($criteria as $key => $value) {
+                                    if ($this->checkEmailHoldValidationCriteria(
+                                        [$key=>$limitation['id']])
+                                    ) {
+                                        $customData['emailHoldLimitationContent'][] = $limitation['content'];
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return $customData;
+    }
+
+    /**
+     * Returns the value of item.storage.content (e.g. to be used in VuFind
+     * getStatus/getHolding array as location)
+     *
+     * @param array $item Array with DAIA item data
+     *
+     * @return string
+     */
+    protected function getItemDepartment($item)
+    {
+        return $this->translate(parent::getItemDepartment($item));
+    }
+
+    /**
+     * Returns the evaluated values of the provided limitations element -- FincLibero
+     * specific implementation preprocesses limitations by filtering the non
+     * functional limitations (i.e. limitations that are used for triggering actions
+     * etc.)
+     *
+     * @param array $limitations Array with DAIA limitation data
+     *
+     * @return array
+     */
+    protected function getItemLimitationContent($limitations)
+    {
+        return parent::getItemLimitationContent(
+            $this->filterNonFunctionalLimitations($limitations)
+        );
+    }
+
+    /**
+     * Returns the evaluated values of the provided limitations element -- FincLibero
+     * specific implementation preprocesses limitations by filtering the non
+     * functional limitations (i.e. limitations that are used for triggering actions
+     * etc.)
+     *
+     * @param array $limitations Array with DAIA limitation data
+     *
+     * @return array
+     */
+    protected function getItemLimitationTypes($limitations)
+    {
+        return parent::getItemLimitationTypes(
+            $this->filterNonFunctionalLimitations($limitations)
+        );
+    }
+
+    /**
+     * FincLibero specific MyReSearch views
+     */
+
+    /**
+     * Custom FincLibero-method to return items with properties:
+     *      - document.status = 4
+     *      - document.storage und document.storageid != Lesesaal/Magazin
+     *
+     * @param array $patron Array returned from patronLogin()
+     *
+     * @return array
+     */
+    public function getMyMediaReadyToPickup($patron)
+    {
+        // filters for getMyMediaReadyToPickup are:
+        // status = 4 - provided (the document is ready to be used by the patron)
+        // document.storage und document.storageid != Lesesaal/Magazin
+        $filter = [
+            'status'  => [4],
+            /*'exclude' => [
+                'storageid' => $this->stackURIs
+            ],*/
+            'endtime' => null,
+            'regex'   => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"]
+        ];
+        // get items-docs for given filters
+        $items = $this->paiaGetItems($patron, $filter);
+
+        return $this->mapPaiaItems($items, 'myHoldsMapping');
+    }
+
+    /**
+     * Custom FincLibero-method to return items with properties:
+     *      - document.status = 3
+     *      - document.endtime = 0
+     *
+     * @param array $patron Array returned from patronLogin()
+     *
+     * @return array
+     */
+    public function getMyPermanentLoans($patron)
+    {
+        // filters for getMyPermanentLoans are:
+        // status = 3 - held (the document is on loan by the patron)
+        // endtime = 0
+        $filter = [
+            'status'  => [3],
+            'endtime' => null,
+            'regex'   => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"]
+        ];
+        // get items-docs for given filters
+        $items = $this->paiaGetItems($patron, $filter);
+
+        return $this->mapPaiaItems(
+            $this->itemFieldSort($items, 'starttime', SORT_DESC),
+            'myTransactionsMapping'
+        );
+    }
+
+    /**
+     * Customized getMyHolds for FincLibero to return items with properties:
+     *      - document.status = 1 (reserved)
+     *      - document.storage und document.storageid != Magazin
+     *      - document.endtime = Rückgabedatum (not NULL!)
+     *
+     * @param array $patron Array returned from patronLogin()
+     *
+     * @return array
+     */
+    public function getMyHolds($patron)
+    {
+        // filters for getMyHolds are:
+        // status = 1 - reserved
+        $filter = [
+            'status'  => [1,2],
+            'regex'   => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"]
+        ];
+        // get items-docs for given filters
+        $items = $this->paiaGetItems($patron, $filter);
+
+        return $this->mapPaiaItems($items, 'myHoldsMapping');
+    }
+
+    /**
+     * Customized getMyTransactions for FincLibero to return items with properties:
+     *      - document.status = 3 (held)
+     *      - document.item = UBL:*
+     *      - document.starttime = Ausleihdatum
+     *      - document.endtime = Rückgabedatum (not NULL!)
+     *
+     * @param array $patron Array returned from patronLogin()
+     *
+     * @return array
+     */
+    public function getMyTransactions($patron)
+    {
+        // filters for getMyPermanentLoans are:
+        // status = 3 - held (the document is on loan by the patron)
+        // endtime != null
+        $filter = [
+            'status'  => [3],
+            'exclude' => ['endtime' => null],
+            'regex'   => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"]
+        ];
+        // get items-docs for given filters
+        $items = $this->paiaGetItems($patron, $filter);
+
+        return $this->mapPaiaItems(
+            $this->itemFieldSort($items, 'endtime'),
+            'myTransactionsMapping'
+        );
+    }
+
+    /**
+     * @param $patron
+     * @param null $messageIdList
+     * @param null $toDate won't work here
+     * @throws ILSException
+     * @return TRUE on success, FALSE otherwise
+     */
+    public function removeMySystemMessages(
+        $patron,
+        $messageIdList = null,
+        $toDate = null
+    )
+    {
+        return $this->paiaRemoveSystemMessages($patron,$messageIdList);
+    }
+
+    /**
+     * FincLibero specific helper functions
+     */
+
+    /**
+     * Helper function to filter certain limitations.
+     *
+     * @param array $limitations An item's limitations.
+     * @return mixed
+     */
+    protected function filterNonFunctionalLimitations($limitations)
+    {
+        // remove the configured limitations from the current set of limitations
+        foreach ($this->configuredLimitations as $configuredLimitation) {
+            foreach ($limitations as $key => $limitation) {
+                if (isset($limitation['id'])
+                    && in_array($limitation['id'], $this->{$configuredLimitation})
+                ) {
+                    unset($limitations[$key]);
+                }
+            }
+        }
+
+        // remove all known pickUpLocations from the current set of limitations
+        $pickUpLocations = $this->filterPickUpLocations($limitations);
+        foreach ($limitations as $key => $limitation) {
+            if (isset($limitation['id'])) {
+                foreach ($pickUpLocations as $pickup) {
+                    if ($limitation['id'] == $pickup['locationID']) {
+                        unset($limitations[$key]);
+                    }
+                }
+            }
+        }
+
+        return $limitations;
+    }
+
+    /**
+     * Helper function to compile upon init() a set of limitations that can be used
+     * for filtering.
+     *
+     * @param string $limitation An URI identifying a limitation
+     */
+    protected function setConfiguredLimitation($limitation)
+    {
+        if (is_string($limitation)) {
+            $this->configuredLimitations[] = $limitation;
+        }
+    }
+
+    /**
+     * Helper function to filter PickUpLocations from given limitations based on
+     * pattern configured in ILS ini
+     *
+     * @param array $limitations An item's limitations
+     * @return array
+     */
+    protected function filterPickUpLocations($limitations)
+    {
+        $pickUpLocations = [];
+        // return array(locationID=>URI, locationDisplay=>Content)
+        foreach ($limitations as $limitation) {
+            if (isset($limitation['id'])) {
+                foreach ($this->pickUpLocationPatterns as $pattern) {
+                    if (preg_match($pattern, $limitation['id'])
+                        && !(filter_var($limitation['id'], FILTER_VALIDATE_URL) === false)
+                    ) {
+                        $pickUpLocations[] = [
+                            'locationID' => $limitation['id'],
+                            'locationDisplay' => isset($limitation['content'])
+                                ? $limitation['content'] : $limitation['id'],
+                        ];
+                    }
+                }
+            }
+        }
+        return $pickUpLocations;
+    }
+
+}