diff --git a/config/vufind/config.ini b/config/vufind/config.ini
index c93b533cc4237ad52c4452485fe45b0529650cfb..aa83d3dbfd1422cf137e44aeec260ac4ceb7a117 100644
--- a/config/vufind/config.ini
+++ b/config/vufind/config.ini
@@ -526,6 +526,10 @@ disable_from = false
 ; configuration options.
 sms = enabled
 
+; Set this value to "database" to shorten links sent via email/SMS and
+; store its path in the database (default "none").
+url_shortener = none
+
 ; This section needs to be changed to match your database connection information
 [Database]
 ; Connection string format is [platform]://[username]:[password]@[host]:[port]/[db]
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index ee9e2200024b1f83318627b92c976e5fbdd38dfb..e2eb24506c6f0f423cbc5e41162259a024a04976 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -45,6 +45,19 @@ $config = [
                     ]
                 ],
             ],
+            'shortlink' => [
+                'type'    => 'Zend\Router\Http\Segment',
+                'options' => [
+                    'route'    => '/short/[:id]',
+                    'constraints' => [
+                        'id'     => '[a-zA-Z0-9]+',
+                    ],
+                    'defaults' => [
+                        'controller' => 'Shortlink',
+                        'action'     => 'redirect',
+                    ]
+                ],
+            ],
             'legacy-alphabrowse-results' => [
                 'type' => 'Zend\Router\Http\Literal',
                 'options' => [
@@ -162,6 +175,7 @@ $config = [
             'VuFind\Controller\RelaisController' => 'VuFind\Controller\AbstractBaseFactory',
             'VuFind\Controller\SearchController' => 'VuFind\Controller\AbstractBaseFactory',
             'VuFind\Controller\ShibbolethLogoutNotificationController' => 'VuFind\Controller\AbstractBaseFactory',
+            'VuFind\Controller\ShortlinkController' => 'VuFind\Controller\AbstractBaseFactory',
             'VuFind\Controller\SummonController' => 'VuFind\Controller\AbstractBaseFactory',
             'VuFind\Controller\SummonrecordController' => 'VuFind\Controller\AbstractBaseFactory',
             'VuFind\Controller\TagController' => 'VuFind\Controller\AbstractBaseFactory',
@@ -257,6 +271,8 @@ $config = [
             'search' => 'VuFind\Controller\SearchController',
             'ShibbolethLogoutNotification' => 'VuFind\Controller\ShibbolethLogoutNotificationController',
             'shibbolethlogoutnotification' => 'VuFind\Controller\ShibbolethLogoutNotificationController',
+            'Shortlink' => 'VuFind\Controller\ShortlinkController',
+            'shortlink' => 'VuFind\Controller\ShortlinkController',
             'Summon' => 'VuFind\Controller\SummonController',
             'summon' => 'VuFind\Controller\SummonController',
             'SummonRecord' => 'VuFind\Controller\SummonrecordController',
@@ -396,6 +412,8 @@ $config = [
             'VuFind\SMS\SMSInterface' => 'VuFind\SMS\Factory',
             'VuFind\Solr\Writer' => 'VuFind\Solr\WriterFactory',
             'VuFind\Tags' => 'VuFind\TagsFactory',
+            'VuFind\UrlShortener\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
+            'VuFind\UrlShortener\UrlShortenerInterface' => 'VuFind\UrlShortener\ServiceFactory',
             'VuFind\Validator\Csrf' => 'VuFind\Validator\CsrfFactory',
             'VuFindHttp\HttpService' => 'VuFind\Service\HttpServiceFactory',
             'VuFindSearch\Service' => 'VuFind\Service\SearchServiceFactory',
@@ -500,6 +518,7 @@ $config = [
             'resource_tags'    => ['id', 'resource_tags_id_seq'],
             'search'           => ['id', 'search_id_seq'],
             'session'          => ['id', 'session_id_seq'],
+            'shortlinks'       => ['id', 'shortlinks_id_seq'],
             'tags'             => ['id', 'tags_id_seq'],
             'user'             => ['id', 'user_id_seq'],
             'user_card'        => ['id', 'user_card_id_seq'],
@@ -542,6 +561,7 @@ $config = [
             'search_params' => [ /* See VuFind\Search\Params\PluginManager for defaults */ ],
             'search_results' => [ /* See VuFind\Search\Results\PluginManager for defaults */ ],
             'session' => [ /* see VuFind\Session\PluginManager for defaults */ ],
+            'urlshortener' => [ /* see VuFind\UrlShortener\PluginManager for defaults */ ],
         ],
     ],
     // Authorization configuration:
diff --git a/module/VuFind/sql/migrations/pgsql/6.0/001-add-shortlinks-table.sql b/module/VuFind/sql/migrations/pgsql/6.0/001-add-shortlinks-table.sql
new file mode 100644
index 0000000000000000000000000000000000000000..275661e1cdaa5ec9c2cab92aab52781dd0a053e3
--- /dev/null
+++ b/module/VuFind/sql/migrations/pgsql/6.0/001-add-shortlinks-table.sql
@@ -0,0 +1,10 @@
+--
+-- Table structure for table shortlinks
+--
+
+CREATE TABLE shortlinks (
+id SERIAL,
+path text,
+created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+PRIMARY KEY (id)
+);
diff --git a/module/VuFind/sql/mysql.sql b/module/VuFind/sql/mysql.sql
index 289b1a71549d30f27b56ffd77f640c70d9e4f871..5bcf1df8a83751882428917237a6458613db71c4 100644
--- a/module/VuFind/sql/mysql.sql
+++ b/module/VuFind/sql/mysql.sql
@@ -163,6 +163,20 @@ CREATE TABLE `external_session` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
+--
+-- Table structure for table `shortlinks`
+--
+
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `shortlinks` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `path` MEDIUMTEXT NOT NULL,
+  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
 --
 -- Table structure for table `tags`
 --
diff --git a/module/VuFind/sql/pgsql.sql b/module/VuFind/sql/pgsql.sql
index d0eb1ef33c42a7542a6c6a35d851ecc24ed05014..e31d908e4e8b568ff2824a596c876b8ef3942df8 100644
--- a/module/VuFind/sql/pgsql.sql
+++ b/module/VuFind/sql/pgsql.sql
@@ -84,6 +84,21 @@ CREATE INDEX search_user_id_idx ON search (user_id);
 CREATE INDEX search_folder_id_idx ON search (folder_id);
 CREATE INDEX session_id_idx ON search (session_id);
 
+-- --------------------------------------------------------
+
+--
+-- Table structure for table shortlinks
+--
+
+DROP TABLE IF EXISTS "shortlinks";
+
+CREATE TABLE shortlinks (
+id SERIAL,
+path text,
+created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+PRIMARY KEY (id)
+);
+
 
 -- --------------------------------------------------------
 
diff --git a/module/VuFind/src/VuFind/Controller/ShortlinkController.php b/module/VuFind/src/VuFind/Controller/ShortlinkController.php
new file mode 100644
index 0000000000000000000000000000000000000000..8b8a33eab23a438c173ff027236260a8ef01cc20
--- /dev/null
+++ b/module/VuFind/src/VuFind/Controller/ShortlinkController.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Short link controller
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  Controller
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+namespace VuFind\Controller;
+
+use VuFind\UrlShortener\UrlShortenerInterface;
+
+/**
+ * Short link controller
+ *
+ * @category VuFind
+ * @package  Controller
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+class ShortlinkController extends AbstractBase
+{
+    /**
+     * Resolve full version of shortlink & redirect to target.
+     *
+     * @return mixed
+     */
+    public function redirectAction()
+    {
+        if ($id = $this->params('id')) {
+            $resolver = $this->serviceLocator->get(UrlShortenerInterface::class);
+            if ($url = $resolver->resolve($id)) {
+                return $this->redirect()->toUrl($url);
+            }
+        }
+
+        $this->getResponse()->setStatusCode(404);
+    }
+}
diff --git a/module/VuFind/src/VuFind/Db/Row/PluginManager.php b/module/VuFind/src/VuFind/Db/Row/PluginManager.php
index 92e2138cc49dd21cfcdaf5692a1ed010429acf72..5b402dec18fd5b2a14cc207acfd62c2a5f5930df 100644
--- a/module/VuFind/src/VuFind/Db/Row/PluginManager.php
+++ b/module/VuFind/src/VuFind/Db/Row/PluginManager.php
@@ -53,6 +53,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         'resourcetags' => ResourceTags::class,
         'search' => Search::class,
         'session' => Session::class,
+        'shortlinks' => Shortlinks::class,
         'tags' => Tags::class,
         'user' => User::class,
         'usercard' => UserCard::class,
@@ -75,6 +76,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         ResourceTags::class => RowGatewayFactory::class,
         Search::class => RowGatewayFactory::class,
         Session::class => RowGatewayFactory::class,
+        Shortlinks::class => RowGatewayFactory::class,
         Tags::class => RowGatewayFactory::class,
         User::class => UserFactory::class,
         UserCard::class => RowGatewayFactory::class,
diff --git a/module/VuFind/src/VuFind/Db/Row/Shortlinks.php b/module/VuFind/src/VuFind/Db/Row/Shortlinks.php
new file mode 100644
index 0000000000000000000000000000000000000000..36345f9876b6d792487cdd44b3fa4ed49eaa5b1b
--- /dev/null
+++ b/module/VuFind/src/VuFind/Db/Row/Shortlinks.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Row Definition for shortlinks
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  Db_Row
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+namespace VuFind\Db\Row;
+
+/**
+ * Row Definition for shortlinks
+ *
+ * @category VuFind
+ * @package  Db_Row
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+class Shortlinks extends RowGateway
+{
+    /**
+     * Constructor
+     *
+     * @param \Zend\Db\Adapter\Adapter $adapter Database adapter
+     */
+    public function __construct($adapter)
+    {
+        parent::__construct('id', 'shortlinks', $adapter);
+    }
+}
diff --git a/module/VuFind/src/VuFind/Db/Table/PluginManager.php b/module/VuFind/src/VuFind/Db/Table/PluginManager.php
index a93d859324cf5b2cb4de23bd27f213d20a693335..e08e1e41276ab3cbd179ba235b16a137f213346b 100644
--- a/module/VuFind/src/VuFind/Db/Table/PluginManager.php
+++ b/module/VuFind/src/VuFind/Db/Table/PluginManager.php
@@ -53,6 +53,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         'resourcetags' => ResourceTags::class,
         'search' => Search::class,
         'session' => Session::class,
+        'shortlinks' => Shortlinks::class,
         'tags' => Tags::class,
         'user' => User::class,
         'usercard' => UserCard::class,
@@ -75,6 +76,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         ResourceTags::class => CaseSensitiveTagsFactory::class,
         Search::class => GatewayFactory::class,
         Session::class => GatewayFactory::class,
+        Shortlinks::class => GatewayFactory::class,
         Tags::class => CaseSensitiveTagsFactory::class,
         User::class => UserFactory::class,
         UserCard::class => GatewayFactory::class,
diff --git a/module/VuFind/src/VuFind/Db/Table/Shortlinks.php b/module/VuFind/src/VuFind/Db/Table/Shortlinks.php
new file mode 100644
index 0000000000000000000000000000000000000000..e241ad8a8ecf9f4f007f87418d6d72f0987737bb
--- /dev/null
+++ b/module/VuFind/src/VuFind/Db/Table/Shortlinks.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Table Definition for shortlinks
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  Db_Table
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+namespace VuFind\Db\Table;
+
+use VuFind\Db\Row\RowGateway;
+use Zend\Db\Adapter\Adapter;
+
+/**
+ * Table Definition for shortlinks
+ *
+ * @category VuFind
+ * @package  Db_Table
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+class Shortlinks extends Gateway
+{
+    /**
+     * Constructor
+     *
+     * @param Adapter       $adapter Database adapter
+     * @param PluginManager $tm      Table manager
+     * @param array         $cfg     Zend Framework configuration
+     * @param RowGateway    $rowObj  Row prototype object (null for default)
+     * @param string        $table   Name of database table to interface with
+     */
+    public function __construct(Adapter $adapter, PluginManager $tm, $cfg,
+        RowGateway $rowObj = null, $table = 'shortlinks'
+    ) {
+        parent::__construct($adapter, $tm, $cfg, $rowObj, $table);
+    }
+}
diff --git a/module/VuFind/src/VuFind/UrlShortener/Database.php b/module/VuFind/src/VuFind/UrlShortener/Database.php
new file mode 100644
index 0000000000000000000000000000000000000000..81c09a93eadcbd86a9b57e00c57b3fde422a98e7
--- /dev/null
+++ b/module/VuFind/src/VuFind/UrlShortener/Database.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * Local database-driven URL shortener.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  UrlShortener
+ * @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 VuFind\UrlShortener;
+
+use VuFind\Db\Table\Shortlinks as ShortlinksTable;
+
+/**
+ * Local database-driven URL shortener.
+ *
+ * @category VuFind
+ * @package  UrlShortener
+ * @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
+ */
+class Database implements UrlShortenerInterface
+{
+    const BASE62_ALPHABET
+        = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+    const BASE62_BASE = 62;
+
+    /**
+     * Base URL of current VuFind site
+     *
+     * @var string
+     */
+    protected $baseUrl;
+
+    /**
+     * Table containing shortlinks
+     *
+     * @var ShortlinksTable
+     */
+    protected $table;
+
+    /**
+     * Constructor
+     *
+     * @param string          $baseUrl Base URL of current VuFind site
+     * @param ShortlinksTable $table   Shortlinks database table
+     */
+    public function __construct(string $baseUrl, ShortlinksTable $table)
+    {
+        $this->baseUrl = $baseUrl;
+        $this->table = $table;
+    }
+
+    /**
+     * Common base62 encoding function.
+     * Implemented here so we don't need additional PHP modules like bcmath.
+     *
+     * @param string $base10Number Number to encode
+     *
+     * @return string
+     *
+     * @throws \Exception
+     */
+    protected function base62Encode($base10Number)
+    {
+        $binaryNumber = intval($base10Number);
+        if ($binaryNumber === 0) {
+            throw new \Exception('not a base10 number: "' . $base10Number . '"');
+        }
+
+        $base62Number = '';
+        while ($binaryNumber != 0) {
+            $base62Number = self::BASE62_ALPHABET[$binaryNumber % self::BASE62_BASE]
+                . $base62Number;
+            $binaryNumber = intdiv($binaryNumber, self::BASE62_BASE);
+        }
+
+        return ($base62Number == '') ? '0' : $base62Number;
+    }
+
+    /**
+     * Common base62 decoding function.
+     * Implemented here so we don't need additional PHP modules like bcmath.
+     *
+     * @param string $base62Number Number to decode
+     *
+     * @return int
+     *
+     * @throws \Exception
+     */
+    protected function base62Decode($base62Number)
+    {
+        $binaryNumber = 0;
+        for ($i = 0; $i < strlen($base62Number); ++$i) {
+            $digit = $base62Number[$i];
+            $strpos = strpos(self::BASE62_ALPHABET, $digit);
+            if ($strpos === false) {
+                throw new \Exception('not a base62 digit: "' . $digit . '"');
+            }
+
+            $binaryNumber *= self::BASE62_BASE;
+            $binaryNumber += $strpos;
+        }
+        return $binaryNumber;
+    }
+
+    /**
+     * Generate & store shortened URL in Database.
+     *
+     * @param string $url URL
+     *
+     * @return string
+     */
+    public function shorten($url)
+    {
+        $path = str_replace($this->baseUrl, '', $url);
+        $this->table->insert(['path' => $path]);
+        $id = $this->table->getLastInsertValue();
+
+        $shortUrl = $this->baseUrl . '/short/' . $this->base62Encode($id);
+        return $shortUrl;
+    }
+
+    /**
+     * Resolve URL from Database via id.
+     *
+     * @param string $id ID to resolve
+     *
+     * @return string
+     */
+    public function resolve($id)
+    {
+        $results = $this->table->select(['id' => $this->base62Decode($id)]);
+        if ($results->count() !== 1) {
+            throw new \Exception('Shortlink could not be resolved: ' . $id);
+        }
+
+        return $this->baseUrl . $results->current()['path'];
+    }
+}
diff --git a/module/VuFind/src/VuFind/UrlShortener/DatabaseFactory.php b/module/VuFind/src/VuFind/UrlShortener/DatabaseFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..2790b129e76c066f02dd65549eac02a2c4b63ac3
--- /dev/null
+++ b/module/VuFind/src/VuFind/UrlShortener/DatabaseFactory.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Factory for local database-driven URL shortener.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  UrlShortener
+ * @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 VuFind\UrlShortener;
+
+use Interop\Container\ContainerInterface;
+
+/**
+ * Factory for local database-driven URL shortener.
+ *
+ * @category VuFind
+ * @package  UrlShortener
+ * @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
+ */
+class DatabaseFactory
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if (!empty($options)) {
+            throw new \Exception('Unexpected options passed to factory.');
+        }
+        $router = $container->get('HttpRouter');
+        $baseUrl = $container->get('ViewRenderer')->plugin('serverurl')
+            ->__invoke($router->assemble([], ['name' => 'home']));
+        $table = $container->get(\VuFind\Db\Table\PluginManager::class)
+            ->get('shortlinks');
+        return new $requestedName(rtrim($baseUrl, '/'), $table);
+    }
+}
diff --git a/module/VuFind/src/VuFind/UrlShortener/None.php b/module/VuFind/src/VuFind/UrlShortener/None.php
new file mode 100644
index 0000000000000000000000000000000000000000..5651b7a878447ba6f9eb152d11561ea4e8131d41
--- /dev/null
+++ b/module/VuFind/src/VuFind/UrlShortener/None.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * No-op URL shortener (default version, does nothing).
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  UrlShortener
+ * @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 VuFind\UrlShortener;
+
+/**
+ * No-op URL shortener (default version, does nothing).
+ *
+ * @category VuFind
+ * @package  UrlShortener
+ * @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
+ */
+class None implements UrlShortenerInterface
+{
+    /**
+     * Dummy to return original URL version.
+     *
+     * @param string $url URL
+     *
+     * @return string
+     */
+    public function shorten($url)
+    {
+        return $url;
+    }
+
+    /**
+     * Dummy implementation. Resolving is not necessary because initial URL
+     * has not been shortened.
+     *
+     * @param string $id ID to resolve
+     *
+     * @return string
+     * @throws Exception because this class is not meant to resolve shortlinks.
+     */
+    public function resolve($id)
+    {
+        throw new \Exception('UrlShortener None is unable to resolve shortlinks.');
+    }
+}
diff --git a/module/VuFind/src/VuFind/UrlShortener/PluginManager.php b/module/VuFind/src/VuFind/UrlShortener/PluginManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e8080988c97d1b365cc6d40a49529550efcfb89
--- /dev/null
+++ b/module/VuFind/src/VuFind/UrlShortener/PluginManager.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * URL shortener plugin manager.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  UrlShortener
+ * @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 VuFind\UrlShortener;
+
+use Zend\ServiceManager\Factory\InvokableFactory;
+
+/**
+ * URL shortener plugin manager.
+ *
+ * @category VuFind
+ * @package  UrlShortener
+ * @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
+ */
+class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
+{
+    /**
+     * Default plugin aliases.
+     *
+     * @var array
+     */
+    protected $aliases = [
+        'none' => None::class,
+        'database' => Database::class,
+    ];
+
+    /**
+     * Default plugin factories.
+     *
+     * @var array
+     */
+    protected $factories = [
+        None::class => InvokableFactory::class,
+        Database::class => DatabaseFactory::class,
+    ];
+
+    /**
+     * Return the name of the base class or interface that plug-ins must conform
+     * to.
+     *
+     * @return string
+     */
+    protected function getExpectedInterface()
+    {
+        return UrlShortenerInterface::class;
+    }
+}
diff --git a/module/VuFind/src/VuFind/UrlShortener/ServiceFactory.php b/module/VuFind/src/VuFind/UrlShortener/ServiceFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..286a405f24995395e5fd96a4c48365d33874706d
--- /dev/null
+++ b/module/VuFind/src/VuFind/UrlShortener/ServiceFactory.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Factory to construct the configured UrlShortener service.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  UrlShortener
+ * @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 VuFind\UrlShortener;
+
+use Interop\Container\ContainerInterface;
+
+/**
+ * Factory to construct the configured UrlShortener service.
+ *
+ * @category VuFind
+ * @package  UrlShortener
+ * @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
+ */
+class ServiceFactory
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if (!empty($options)) {
+            throw new \Exception('Unexpected options passed to factory.');
+        }
+        $config = $container->get(\VuFind\Config\PluginManager::class)
+            ->get('config');
+        $service = empty($config->Mail->url_shortener)
+            ? 'none' : $config->Mail->url_shortener;
+        return $container->get(PluginManager::class)->get($service);
+    }
+}
diff --git a/module/VuFind/src/VuFind/UrlShortener/UrlShortenerInterface.php b/module/VuFind/src/VuFind/UrlShortener/UrlShortenerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..3f9d02e66354605112a3df63d204f67f01856630
--- /dev/null
+++ b/module/VuFind/src/VuFind/UrlShortener/UrlShortenerInterface.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * URL shortener interface.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  UrlShortener
+ * @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 VuFind\UrlShortener;
+
+/**
+ * URL shortener interface.
+ *
+ * @category VuFind
+ * @package  UrlShortener
+ * @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
+ */
+interface UrlShortenerInterface
+{
+    /**
+     * Generate and return shortened version of a URL.
+     *
+     * @param string $url URL
+     *
+     * @return string
+     */
+    public function shorten($url);
+
+    /**
+     * Resolve a shortened URL by its id.
+     *
+     * @param string $id ID to resolve
+     *
+     * @return string
+     */
+    public function resolve($id);
+}
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrl.php b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrl.php
new file mode 100644
index 0000000000000000000000000000000000000000..ea70d1be214eab732cedc3550a5eaf5f2142f1e4
--- /dev/null
+++ b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrl.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * View helper for shortening URLs.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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   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 VuFind\View\Helper\Root;
+
+use VuFind\UrlShortener\UrlShortenerInterface;
+
+/**
+ * View helper for formatting dates and times
+ *
+ * @category VuFind
+ * @package  View_Helpers
+ * @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
+ */
+class ShortenUrl extends \Zend\View\Helper\AbstractHelper
+{
+    /**
+     * URL shortener
+     *
+     * @var UrlShortenerInterface
+     */
+    protected $shortener;
+
+    /**
+     * Constructor
+     *
+     * @param UrlShortenerInterface $shortener URL shortener
+     */
+    public function __construct(UrlShortenerInterface $shortener)
+    {
+        $this->shortener = $shortener;
+    }
+
+    /**
+     * Shorten a URL
+     *
+     * @param string $url URL to shorten
+     *
+     * @return string
+     */
+    public function __invoke($url)
+    {
+        return $this->shortener->shorten($url);
+    }
+}
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrlFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..cbf78fc8e03b17d2c1480513e5fd894e1e4412bb
--- /dev/null
+++ b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrlFactory.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * ShortenUrl helper factory.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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   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 VuFind\View\Helper\Root;
+
+use Interop\Container\ContainerInterface;
+use Zend\ServiceManager\Factory\FactoryInterface;
+
+/**
+ * ShortenUrl helper factory.
+ *
+ * @category VuFind
+ * @package  View_Helpers
+ * @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
+ */
+class ShortenUrlFactory 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
+    ) {
+        if (!empty($options)) {
+            throw new \Exception('Unexpected options sent to factory.');
+        }
+        return new $requestedName(
+            $container->get(\VuFind\UrlShortener\UrlShortenerInterface::class)
+        );
+    }
+}
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/DatabaseTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/DatabaseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..43ea7065d211bbb9ad0c0c0eaf5c694b50f39c25
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/DatabaseTest.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * "Database" URL shortener test.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  Tests
+ * @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:testing:unit_tests Wiki
+ */
+namespace VuFindTest\UrlShortener;
+
+use VuFind\UrlShortener\Database;
+
+/**
+ * "Database" URL shortener test.
+ *
+ * @category VuFind
+ * @package  Tests
+ * @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:testing:unit_tests Wiki
+ */
+class DatabaseTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * Get the object to test.
+     *
+     * @param object $table Database table object/mock
+     *
+     * @return Database
+     */
+    public function getShortener($table)
+    {
+        return new Database('http://foo', $table);
+    }
+
+    /**
+     * Get the mock table object.
+     *
+     * @param array $methods Methods to mock.
+     *
+     * @return object
+     */
+    public function getMockTable($methods)
+    {
+        return $this->getMockBuilder(\VuFind\Db\Table\Shortlinks::class)
+            ->disableOriginalConstructor()
+            ->setMethods($methods)
+            ->getMock();
+    }
+
+    /**
+     * Test that the shortener works correctly under "happy path."
+     *
+     * @return void
+     */
+    public function testShortener()
+    {
+        $table = $this->getMockTable(['insert', 'getLastInsertValue']);
+        $table->expects($this->once())->method('insert')
+            ->with($this->equalTo(['path' => '/bar']));
+        $table->expects($this->once())->method('getLastInsertValue')
+            ->will($this->returnValue('10'));
+        $db = $this->getShortener($table);
+        $this->assertEquals('http://foo/short/A', $db->shorten('http://foo/bar'));
+    }
+
+    /**
+     * Test that resolve is supported.
+     *
+     * @return void
+     */
+    public function testResolution()
+    {
+        $table = $this->getMockTable(['select']);
+        $mockResults = $this->getMockBuilder(\Zend\Db\ResultSet::class)
+            ->setMethods(['count', 'current'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $mockResults->expects($this->once())->method('count')
+            ->will($this->returnValue(1));
+        $mockResults->expects($this->once())->method('current')
+            ->will($this->returnValue(['path' => '/bar']));
+        $table->expects($this->once())->method('select')
+            ->with($this->equalTo(['id' => 10]))
+            ->will($this->returnValue($mockResults));
+        $db = $this->getShortener($table);
+        $this->assertEquals('http://foo/bar', $db->resolve('A'));
+    }
+
+    /**
+     * Test that resolve errors correctly when given bad input
+     *
+     * @return void
+     *
+     * @expectedException        Exception
+     * @expectedExceptionMessage Shortlink could not be resolved: B
+     */
+    public function testResolutionOfBadInput()
+    {
+        $table = $this->getMockTable(['select']);
+        $mockResults = $this->getMockBuilder(\Zend\Db\ResultSet::class)
+            ->setMethods(['count'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $mockResults->expects($this->once())->method('count')
+            ->will($this->returnValue(0));
+        $table->expects($this->once())->method('select')
+            ->with($this->equalTo(['id' => 11]))
+            ->will($this->returnValue($mockResults));
+        $db = $this->getShortener($table);
+        $db->resolve('B');
+    }
+}
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/NoneTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/NoneTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b978f255e3da2641169abc5a446af9aee2b5338
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/NoneTest.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * "None" URL shortener test.
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  Tests
+ * @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:testing:unit_tests Wiki
+ */
+namespace VuFindTest\UrlShortener;
+
+use VuFind\UrlShortener\None;
+
+/**
+ * "None" URL shortener test.
+ *
+ * @category VuFind
+ * @package  Tests
+ * @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:testing:unit_tests Wiki
+ */
+class NoneTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * Test that the shortener does nothing.
+     *
+     * @return void
+     */
+    public function testShortener()
+    {
+        $none = new None();
+        $url = 'http://foo';
+        $this->assertEquals($url, $none->shorten($url));
+    }
+
+    /**
+     * Test that resolve is not supported.
+     *
+     * @return void
+     *
+     * @expectedException        Exception
+     * @expectedExceptionMessage UrlShortener None is unable to resolve shortlinks.
+     */
+    public function testNoResolution()
+    {
+        $none = new None();
+        $none->resolve('foo');
+    }
+}
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/ShortenUrlTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/ShortenUrlTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c0a053754dfaec4cf25989426c03c22b8afed75
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/ShortenUrlTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * ShortenUrl view helper Test Class
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 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  Tests
+ * @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:testing:unit_tests Wiki
+ */
+namespace VuFindTest\View\Helper\Root;
+
+use VuFind\UrlShortener\Database;
+use VuFind\UrlShortener\UrlShortenerInterface;
+use VuFind\View\Helper\Root\ShortenUrl;
+use VuFind\View\Helper\Root\ShortenUrlFactory;
+
+/**
+ * ShortenUrl view helper Test Class
+ *
+ * @category VuFind
+ * @package  Tests
+ * @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:testing:unit_tests Wiki
+ */
+class ShortenUrlTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * Test that helper proxies to appropriate service.
+     *
+     * @return void
+     */
+    public function testHelper()
+    {
+        $container = new \VuFindTest\Container\MockContainer($this);
+        $service = $container->createMock(Database::class, ['shorten']);
+        $service->expects($this->once())->method('shorten')
+            ->with($this->equalTo('foo'))->will($this->returnValue('bar'));
+        $container->set(UrlShortenerInterface::class, $service);
+        $factory = new ShortenUrlFactory();
+        $helper = $factory($container, ShortenUrl::class);
+        $this->assertEquals('bar', $helper('foo'));
+    }
+}
diff --git a/themes/root/templates/Email/record-sms.phtml b/themes/root/templates/Email/record-sms.phtml
index 268c4a5cd5e0ad8539c336c54bbc863759b26f79..71cb8a8a7815526f827b4f5bd3c1b80645924b86 100644
--- a/themes/root/templates/Email/record-sms.phtml
+++ b/themes/root/templates/Email/record-sms.phtml
@@ -6,6 +6,9 @@
     // we'll just show URL and title.  This prioritization is important,
     // since text messages can be short, and we want the most important stuff
     // at the top!
+    $recordUrl = $this->shortenUrl(
+        $this->serverUrl($this->recordLink()->getUrl($this->driver))
+    );
     if ($this->driver->supportsAjaxStatus()) {
         $holdings = $this->driver->getRealTimeHoldings();
 
@@ -46,9 +49,9 @@
             echo $this->translate('Location') . ': ' . trim($location) . "\n";
         }
         echo $this->driver->getBreadcrumb() . "\n";
-        echo $this->serverUrl($this->recordLink()->getUrl($this->driver)) . "\n";
+        echo $recordUrl . "\n";
     } else {
-        echo $this->serverUrl($this->recordLink()->getUrl($this->driver)) . "\n";
+        echo $recordUrl . "\n";
         echo $this->driver->getBreadcrumb() . "\n";
     }
 ?>
diff --git a/themes/root/templates/Email/share-link.phtml b/themes/root/templates/Email/share-link.phtml
index bcfca31ed209b944c6962da8e891dbe0314f48ef..80839e3cf4ba6239e2a4914820053cec8b1954b4 100644
--- a/themes/root/templates/Email/share-link.phtml
+++ b/themes/root/templates/Email/share-link.phtml
@@ -7,7 +7,7 @@
 
 
 <?php endif; ?>
-  <?=$this->translate('email_link')?>: <<?=$this->msgUrl?>>
+  <?=$this->translate('email_link')?>: <<?=$this->shortenUrl($this->msgUrl)?>>
 
 ------------------------------------------------------------
 
diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php
index dbeb543746df015f00c2eae3018e794f07451b0c..09708f831c049f2a97426bd06df83e2578c4f0f0 100644
--- a/themes/root/theme.config.php
+++ b/themes/root/theme.config.php
@@ -53,6 +53,7 @@ return [
             'VuFind\View\Helper\Root\SearchOptions' => 'VuFind\View\Helper\Root\SearchOptionsFactory',
             'VuFind\View\Helper\Root\SearchParams' => 'VuFind\View\Helper\Root\SearchParamsFactory',
             'VuFind\View\Helper\Root\SearchTabs' => 'VuFind\View\Helper\Root\SearchTabsFactory',
+            'VuFind\View\Helper\Root\ShortenUrl' => 'VuFind\View\Helper\Root\ShortenUrlFactory',
             'VuFind\View\Helper\Root\SortFacetList' => 'Zend\ServiceManager\Factory\InvokableFactory',
             'VuFind\View\Helper\Root\Summaries' => 'VuFind\View\Helper\Root\ContentLoaderFactory',
             'VuFind\View\Helper\Root\Summon' => 'Zend\ServiceManager\Factory\InvokableFactory',
@@ -115,6 +116,7 @@ return [
             'searchParams' => 'VuFind\View\Helper\Root\SearchParams',
             'searchTabs' => 'VuFind\View\Helper\Root\SearchTabs',
             'searchbox' => 'VuFind\View\Helper\Root\SearchBox',
+            'shortenUrl' => 'VuFind\View\Helper\Root\ShortenUrl',
             'sortFacetList' => 'VuFind\View\Helper\Root\SortFacetList',
             'summaries' => 'VuFind\View\Helper\Root\Summaries',
             'summon' => 'VuFind\View\Helper\Root\Summon',