diff --git a/config/install/private_files_download_permission.settings.yml b/config/install/private_files_download_permission.settings.yml
new file mode 100644
index 0000000..85980ff
--- /dev/null
+++ b/config/install/private_files_download_permission.settings.yml
@@ -0,0 +1 @@
+by_user_checks: true
diff --git a/config/schema/pfdp_directory.schema.yml b/config/schema/pfdp_directory.schema.yml
new file mode 100644
index 0000000..5f03850
--- /dev/null
+++ b/config/schema/pfdp_directory.schema.yml
@@ -0,0 +1,25 @@
+private_files_download_permission.pfdp_directory.*:
+  type: config_entity
+  label: 'Private files download permission - Directory config'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    path:
+        type: path
+        label: 'Path'
+    bypass:
+        type: boolean
+        label: 'Bypass'
+    users:
+      type: sequence
+      label: 'Users'
+      sequence:
+        type: string
+        label: 'User'
+    roles:
+      type: sequence
+      label: 'Roles'
+      sequence:
+        type: string
+        label: 'Role'
diff --git a/private_files_download_permission.info b/private_files_download_permission.info
deleted file mode 100644
index 0700015..0000000
--- a/private_files_download_permission.info
+++ /dev/null
@@ -1,5 +0,0 @@
-name = Private files download permission
-description = Allows by-user and by-role downloads from private file system directories.
-core = 7.x
-package = Access control
-configure = admin/config/media/private-files-download-permission
diff --git a/private_files_download_permission.info.yml b/private_files_download_permission.info.yml
new file mode 100644
index 0000000..543e55d
--- /dev/null
+++ b/private_files_download_permission.info.yml
@@ -0,0 +1,6 @@
+type: module
+name: Private files download permission
+description: Allows by-user and by-role downloads from private file system directories.
+core: 8.x
+package: Access control
+configure: private_files_download_permission.admin
diff --git a/private_files_download_permission.install b/private_files_download_permission.install
deleted file mode 100644
index d5b93e9..0000000
--- a/private_files_download_permission.install
+++ /dev/null
@@ -1,286 +0,0 @@
-<?php
-
-/**
- * @file
- * Installs, updates and uninstalls module variables and settings.
- *
- * Copyright (c) 2011-2015 by Marco Zanon (http://www.marcozanon.com)
- * Released under GPLv2 license
- * Idea and code inspired by http://www.beacon9.ca/labs/drupal-7-private-files-module
- */
-
-/**
- * Implements hook_schema().
- */
-function private_files_download_permission_schema() {
-  return array(
-    'private_files_download_permission_directory' => array(
-      'description'  => 'Directories whose permissions are set.',
-      'fields'       => array(
-        'did'    => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
-        'path'   => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE),
-        'bypass' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE),
-      ),
-      'primary key'  => array('did'),
-      'foreign keys' => array(),
-      'unique keys'  => array(
-        'path' => array('path'),
-      ),
-      'indexes'      => array(),
-    ),
-    'private_files_download_permission_directory_user' => array(
-      'description'  => 'Users having per-directory download permission.',
-      'fields'       => array(
-        'duid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
-        'did'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-        'uid'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-      ),
-      'primary key'  => array('duid'),
-      'foreign keys' => array(
-        'private_files_download_permission_directory' => array(
-          'table'   => 'private_files_download_permission_directory',
-          'columns' => array('did' => 'did'),
-        ),
-        'private_files_download_permission_directory_user' => array(
-          'table'   => 'users',
-          'columns' => array('uid' => 'uid'),
-        ),
-      ),
-      'unique keys'  => array(),
-      'indexes'      => array(),
-    ),
-    'private_files_download_permission_directory_role' => array(
-      'description'  => 'Roles having per-directory download permission.',
-      'fields'       => array(
-        'drid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
-        'did'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-        'rid'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-      ),
-      'primary key'  => array('drid'),
-      'foreign keys' => array(
-        'private_files_download_permission_directory' => array(
-          'table'   => 'private_files_download_permission_directory',
-          'columns' => array('did' => 'did'),
-        ),
-        'private_files_download_permission_directory_role' => array(
-          'table'   => 'role',
-          'columns' => array('rid' => 'rid'),
-        ),
-      ),
-      'unique keys'  => array(),
-      'indexes'      => array(),
-    ),
-  );
-}
-
-/**
- * Implements hook_install().
- */
-function private_files_download_permission_install() {
-  $transaction = db_transaction();
-  try {
-    // Add "real" foreign keys.
-    db_query('ALTER TABLE {private_files_download_permission_directory_user}
-      ADD CONSTRAINT {private_files_download_permission_directory_2}
-      FOREIGN KEY (did) REFERENCES {private_files_download_permission_directory} (did)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-    db_query('ALTER TABLE {private_files_download_permission_directory_user}
-      ADD CONSTRAINT {private_files_download_permission_directory_user}
-      FOREIGN KEY (uid) REFERENCES {users} (uid)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-    db_query('ALTER TABLE {private_files_download_permission_directory_role}
-      ADD CONSTRAINT {private_files_download_permission_directory}
-      FOREIGN KEY (did) REFERENCES {private_files_download_permission_directory} (did)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-    db_query('ALTER TABLE {private_files_download_permission_directory_role}
-      ADD CONSTRAINT {private_files_download_permission_directory_role}
-      FOREIGN KEY (rid) REFERENCES {role} (rid)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    drupal_set_message(st('An error occurred while installing the module. Please check the log for details.'), 'error');
-  }
-  // Set preferences' default values.
-  variable_set('private_files_download_permission_by_user_checks', TRUE);
-}
-
-/**
- * Remove useless settings from version 1.x, implement new schema and add
- * "real" foreign keys.
- */
-function private_files_download_permission_update_7200() {
-  $transaction = db_transaction();
-  try {
-    // Remove useless settings from version 1.x.
-    foreach (user_roles() as $rid => $role_name) {
-      user_role_revoke_permissions($rid, array(
-        'download private files',
-      ));
-    }
-    variable_del('private_files_download_permission_unprotected_subfolder');
-    // Implement new schema (rewritten from scratch, see https://drupal.org/node/150220).
-    $schema = array();
-    $schema['private_files_download_permission_directory'] = array(
-      'description'  => 'Directories whose permissions are set.',
-      'fields'       => array(
-        'did'  => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
-        'path' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE),
-      ),
-      'primary key'  => array('did'),
-      'foreign keys' => array(),
-      'unique keys'  => array(
-        'path' => array('path'),
-      ),
-      'indexes'      => array(),
-    );
-    $schema['private_files_download_permission_directory_role'] = array(
-      'description'  => 'Roles having per-directory download permission.',
-      'fields'       => array(
-        'drid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
-        'did'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-        'rid'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-      ),
-      'primary key'  => array('drid'),
-      'foreign keys' => array(
-        'private_files_download_permission_directory' => array(
-          'table'   => 'private_files_download_permission_directory',
-          'columns' => array('did' => 'did'),
-        ),
-        'private_files_download_permission_directory_role' => array(
-          'table'   => 'role',
-          'columns' => array('rid' => 'rid'),
-        ),
-      ),
-      'unique keys'  => array(),
-      'indexes'      => array(),
-    );
-    db_create_table('private_files_download_permission_directory', $schema['private_files_download_permission_directory']);
-    db_create_table('private_files_download_permission_directory_role', $schema['private_files_download_permission_directory_role']);
-    // Add "real" foreign keys.
-    db_query('ALTER TABLE {private_files_download_permission_directory_role}
-      ADD CONSTRAINT {private_files_download_permission_directory}
-      FOREIGN KEY (did) REFERENCES {private_files_download_permission_directory} (did)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-    db_query('ALTER TABLE {private_files_download_permission_directory_role}
-      ADD CONSTRAINT {private_files_download_permission_directory_role}
-      FOREIGN KEY (rid) REFERENCES {role} (rid)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    drupal_set_message(st('An error occurred while updating the module. Please check the log for details.'), 'error');
-  }
-}
-
-/**
- * Prepare the database for by-user filtering.
- */
-function private_files_download_permission_update_7201() {
-  $transaction = db_transaction();
-  try {
-    // Implement new table schema (rewritten from scratch, see https://drupal.org/node/150220).
-    $schema = array();
-    $schema['private_files_download_permission_directory_user'] = array(
-      'description'  => 'Users having per-directory download permission.',
-      'fields'       => array(
-        'duid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
-        'did'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-        'uid'  => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
-      ),
-      'primary key'  => array('duid'),
-      'foreign keys' => array(
-        'private_files_download_permission_directory' => array(
-          'table'   => 'private_files_download_permission_directory',
-          'columns' => array('did' => 'did'),
-        ),
-        'private_files_download_permission_directory_user' => array(
-          'table'   => 'users',
-          'columns' => array('uid' => 'uid'),
-        ),
-      ),
-      'unique keys'  => array(),
-      'indexes'      => array(),
-    );
-    db_create_table('private_files_download_permission_directory_user', $schema['private_files_download_permission_directory_user']);
-    // Add "real" foreign keys.
-    db_query('ALTER TABLE {private_files_download_permission_directory_user}
-      ADD CONSTRAINT {private_files_download_permission_directory_2}
-      FOREIGN KEY (did) REFERENCES {private_files_download_permission_directory} (did)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-    db_query('ALTER TABLE {private_files_download_permission_directory_user}
-      ADD CONSTRAINT {private_files_download_permission_directory_user}
-      FOREIGN KEY (uid) REFERENCES {users} (uid)
-      ON DELETE CASCADE
-      ON UPDATE CASCADE');
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    drupal_set_message(st('An error occurred while updating the module. Please check the log for details.'), 'error');
-  }
-}
-
-/**
- * Prepare the database to handle the "bypass" option. Set preferences' default
- * values.
- */
-function private_files_download_permission_update_7203() {
-  // Update table schema.
-  db_add_field('private_files_download_permission_directory', 'bypass', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'initial' => 0));
-  // Set preferences' default values.
-  variable_set('private_files_download_permission_by_user_checks', TRUE);
-}
-
-/**
- * Implements hook_enable().
- */
-function private_files_download_permission_enable() {
-  // Purge directory list from cache.
-  drupal_static_reset('private_files_download_permission_directory_list');
-}
-
-/**
- * Implements hook_uninstall().
- */
-function private_files_download_permission_uninstall() {
-  $transaction = db_transaction();
-  try {
-    if (FALSE !== stripos(db_driver(), 'mysql')) {
-      // Remove "real" foreign keys (MySQL database).
-      db_query('ALTER TABLE {private_files_download_permission_directory_user}
-        DROP FOREIGN KEY {private_files_download_permission_directory_2}');
-      db_query('ALTER TABLE {private_files_download_permission_directory_user}
-        DROP FOREIGN KEY {private_files_download_permission_directory_user}');
-      db_query('ALTER TABLE {private_files_download_permission_directory_role}
-        DROP FOREIGN KEY {private_files_download_permission_directory}');
-      db_query('ALTER TABLE {private_files_download_permission_directory_role}
-        DROP FOREIGN KEY {private_files_download_permission_directory_role}');
-    }
-    else {
-      // Remove "real" foreign keys (non-MySQL database).
-      db_query('ALTER TABLE {private_files_download_permission_directory_user}
-        DROP CONSTRAINT IF EXISTS {private_files_download_permission_directory_2}');
-      db_query('ALTER TABLE {private_files_download_permission_directory_user}
-        DROP CONSTRAINT IF EXISTS {private_files_download_permission_directory_user}');
-      db_query('ALTER TABLE {private_files_download_permission_directory_role}
-        DROP CONSTRAINT IF EXISTS {private_files_download_permission_directory}');
-      db_query('ALTER TABLE {private_files_download_permission_directory_role}
-        DROP CONSTRAINT IF EXISTS {private_files_download_permission_directory_role}');
-    }
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    drupal_set_message(st('An error occurred while removing the module. Please check the log for details.'), 'error');
-  }
-  // Remove preferences.
-  variable_del('private_files_download_permission_by_user_checks');
-  // Purge directory list from cache.
-  drupal_static_reset('private_files_download_permission_directory_list');
-}
diff --git a/private_files_download_permission.links.action.yml b/private_files_download_permission.links.action.yml
new file mode 100644
index 0000000..6763894
--- /dev/null
+++ b/private_files_download_permission.links.action.yml
@@ -0,0 +1,5 @@
+entity.pfdp_directory.add_form:
+  route_name: 'entity.pfdp_directory.add_form'
+  title: 'Add directory'
+  appears_on:
+    - entity.pfdp_directory.collection
\ No newline at end of file
diff --git a/private_files_download_permission.links.menu.yml b/private_files_download_permission.links.menu.yml
new file mode 100644
index 0000000..912bc2e
--- /dev/null
+++ b/private_files_download_permission.links.menu.yml
@@ -0,0 +1,5 @@
+entity.pfdp_directory.collection:
+  title: 'Private files download permission'
+  parent: system.admin_config_media
+  description: 'Manage by-directory, by-role and by-user download permissions.'
+  route_name: entity.pfdp_directory.collection
diff --git a/private_files_download_permission.links.task.yml b/private_files_download_permission.links.task.yml
new file mode 100644
index 0000000..af83017
--- /dev/null
+++ b/private_files_download_permission.links.task.yml
@@ -0,0 +1,8 @@
+entity.pfdp_directory.collection:
+  title: List
+  route_name: entity.pfdp_directory.collection
+  base_route: entity.pfdp_directory.collection
+private_files_download_permission.admin:
+  title: Settings
+  route_name: private_files_download_permission.admin
+  base_route: entity.pfdp_directory.collection
diff --git a/private_files_download_permission.module b/private_files_download_permission.module
index 0fed3e3..e4e3d95 100644
--- a/private_files_download_permission.module
+++ b/private_files_download_permission.module
@@ -1,553 +1,19 @@
 <?php
 
-/**
- * @file
- * Handles both module settings and its behaviour.
- *
- * Copyright (c) 2011-2015 by Marco Zanon (http://www.marcozanon.com)
- * Released under GPLv2 license
- * Idea and code inspired by http://www.beacon9.ca/labs/drupal-7-private-files-module
- */
-
-/**
- * Implements hook_permission().
- */
-function private_files_download_permission_permission() {
-  return array(
-    'bypass private files download permission' => array(
-      'title'       => t('Bypass Private files download permission'),
-      'description' => t('Download from private directories regardless of permission restrictions.'),
-    ),
-    'bypass private files download permission for temporary files' => array(
-      'title'       => t('Bypass Private files download permission for temporary files'),
-      'description' => t('Download temporary files regardless of permission restrictions.'),
-    ),
-    'administer private files download permission' => array(
-      'title'       => t('Administer Private files download permission'),
-      'description' => t('Access module configuration.'),
-    ),
-  );
-}
-
-/**
- * Implements hook_menu().
- */
-function private_files_download_permission_menu() {
-  return array(
-    'admin/config/media/private-files-download-permission' => array(
-      'title'            => 'Private files download permission',
-      'description'      => 'Manage by-directory, by-role and by-user download permissions.',
-      'page callback'    => 'private_files_download_permission_list_directories',
-      'access arguments' => array('administer private files download permission'),
-      'type'             => MENU_NORMAL_ITEM,
-    ),
-    'admin/config/media/private-files-download-permission/list' => array(
-      'title'            => 'List directories',
-      'description'      => 'List directories in the control list.',
-      'page callback'    => 'private_files_download_permission_list_directories',
-      'access arguments' => array('administer private files download permission'),
-      'type'             => MENU_DEFAULT_LOCAL_TASK,
-    ),
-    'admin/config/media/private-files-download-permission/add' => array(
-      'title'            => 'Add directory',
-      'description'      => 'Add directory to the control list.',
-      'page callback'    => 'private_files_download_permission_add_directory',
-      'access arguments' => array('administer private files download permission'),
-      'type'             => MENU_LOCAL_ACTION,
-    ),
-    'admin/config/media/private-files-download-permission/%/edit' => array(
-      'title'            => 'Edit directory',
-      'description'      => 'Edit directory in the control list.',
-      'page callback'    => 'private_files_download_permission_edit_directory',
-      'page arguments'   => array(4),
-      'access arguments' => array('administer private files download permission'),
-      'type'             => MENU_NORMAL_ITEM,
-    ),
-    'admin/config/media/private-files-download-permission/%/remove' => array(
-      'title'            => 'Remove directory',
-      'description'      => 'Remove directory from the control list.',
-      'page callback'    => 'drupal_get_form',
-      'page arguments'   => array('private_files_download_permission_remove_directory', 4),
-      'access arguments' => array('administer private files download permission'),
-      'type'             => MENU_CALLBACK,
-    ),
-    'admin/config/media/private-files-download-permission/preferences' => array(
-      'title'            => 'Preferences',
-      'description'      => 'Set module preferences.',
-      'page callback'    => 'private_files_download_permission_set_preferences',
-      'access arguments' => array('administer private files download permission'),
-      'type'             => MENU_LOCAL_TASK,
-    ),
-  );
-}
-
-/**
- * Returns a standard array containing all users.
- */
-function private_files_download_permission_get_users() {
-  // Load user list from database.
-  $user_list = db_select('users', 't')
-    ->fields('t')
-    ->orderBy('t.name', 'ASC')
-    ->execute()
-    ->fetchAllAssoc('uid');
-  // Return a standard (uid, name) array.
-  $users = array();
-  foreach ($user_list as $uid => $user) {
-    $users[$uid] = (!$user->name ? t('anonymous user') : $user->name);
-  }
-  return $users;
-}
-
-/**
- * Returns the list of all directories under control.
- */
-function private_files_download_permission_get_directory_list() {
-  $directory_list = &drupal_static('private_files_download_permission_directory_list');
-  if (!isset($directory_list)) {
-    // Load directory list.
-    $directory_list = db_select('private_files_download_permission_directory', 't')
-      ->fields('t')
-      ->orderBy('t.path', 'ASC')
-      ->execute()
-      ->fetchAllAssoc('did');
-    // Add user id and role id arrays to each directory.
-    foreach ($directory_list as $directory) {
-      $directory->uid = array();
-      $directory->rid = array();
-    }
-    // Load directory user list.
-    $directory_users = db_select('private_files_download_permission_directory_user', 't')
-      ->fields('t')
-      ->orderBy('t.did', 'ASC')
-      ->execute()
-      ->fetchAll();
-    // Load directory role list.
-    $directory_roles = db_select('private_files_download_permission_directory_role', 't')
-      ->fields('t')
-      ->orderBy('t.did', 'ASC')
-      ->execute()
-      ->fetchAll();
-    // Merge array values.
-    foreach ($directory_users as $directory_user) {
-      $did = $directory_user->did;
-      $uid = $directory_user->uid;
-      $directory_list[$did]->uid[$uid] = array('uid' => $uid);
-    }
-    foreach ($directory_roles as $directory_role) {
-      $did = $directory_role->did;
-      $rid = $directory_role->rid;
-      $directory_list[$did]->rid[$rid] = array('rid' => $rid);
-    }
-  }
-  return $directory_list;
-}
-
-/**
- * (Page callback.) Displays the main page and lists directories under control.
- */
-function private_files_download_permission_list_directories() {
-  $output = '';
-  // Check if file system download method is set to private.
-  if ('private' !== file_default_scheme()) {
-    drupal_set_message(t('Your !default_download_method is not set as private. Please keep in mind that these settings only affect private file system downloads.', array('!default_download_method' => l(t('default download method'), 'admin/config/media/file-system'))), 'warning');
-  }
-  // Display the private file system path.
-  $private_path = variable_get('file_private_path');
-  if (!$private_path) {
-    $output .= '<p>' . t('Your private file system path is not set.') . '</p>';
-  }
-  else {
-    $output .= '<p>' . t('Your private file system path is %path.', array('%path' => $private_path)) . '</p>';
-  }
-  // Display a warning if by-user checks are not enabled.
-  if (!variable_get('private_files_download_permission_by_user_checks')) {
-    $output .= '<p>' . t('!by_user_checks are not enabled.', array('!by_user_checks' => l(t('By-user checks'), 'admin/config/media/private-files-download-permission/preferences'))) . '</p>';
-  }
-  // Retrieve directory list and display it as a table.
-  $directory_list = private_files_download_permission_get_directory_list();
-  if (variable_get('private_files_download_permission_by_user_checks')) {
-    $users = private_files_download_permission_get_users();
-  }
-  $roles = user_roles();
-  $rows = array();
-  foreach ($directory_list as $directory) {
-    // Prepare the 'Enabled users' cell.
-    if (variable_get('private_files_download_permission_by_user_checks')) {
-      $enabled_users = array_intersect_key($users, $directory->uid);
-      sort($enabled_users);
-    }
-    // Prepare the 'Enabled roles' cell.
-    $enabled_roles = array_intersect_key($roles, $directory->rid);
-    sort($enabled_roles);
-    // Fill table row.
-    $rows[] = array(
-      $directory->path,
-      $directory->bypass ? t('Yes') : '',
-      variable_get('private_files_download_permission_by_user_checks') && !empty($enabled_users) && !$directory->bypass ? implode('<br />', $enabled_users) : '',
-      !empty($enabled_roles) && !$directory->bypass ? implode('<br />', $enabled_roles) : '',
-      l(t('Edit'), 'admin/config/media/private-files-download-permission/' . $directory->did . '/edit/'),
-      l(t('Remove'), 'admin/config/media/private-files-download-permission/' . $directory->did . '/remove/'),
-    );
-  }
-  $output .= theme('table', array(
-    'header' => array(
-      t('Directory path'),
-      t('Bypass'),
-      t('Enabled users'),
-      t('Enabled roles'),
-      array(
-        'data'    => t('Operations'),
-        'colspan' => 2,
-      ),
-    ),
-    'rows'       => $rows,
-    'attributes' => array(),
-    'caption'    => NULL,
-    'colgroups'  => array(),
-    'sticky'     => FALSE,
-    'empty'      => t('The directory list is empty.'),
-  ));
-  // Display output.
-  return $output;
-}
-
-/**
- * (Form callback.) Displays a form to add/edit a directory.
- */
-function private_files_download_permission_get_directory_form($form, &$form_state, $did) {
-  $directory_list = private_files_download_permission_get_directory_list();
-  $form = array();
-  // Check that $did is actually a valid directory id, if not blank.
-  if (NULL !== $did) {
-    if (!in_array($did, array_keys($directory_list))) {
-      drupal_set_message(t('You need to provide a valid directory id.'), 'error');
-      return;
-    }
-  }
-  // Prepare default values.
-  $default_path = NULL;
-  $default_bypass = FALSE;
-  if (variable_get('private_files_download_permission_by_user_checks')) {
-    $default_users = array();
-  }
-  $default_roles = array();
-  if (NULL !== $did) {
-    $default_path = $directory_list[$did]->path;
-    $default_bypass = $directory_list[$did]->bypass;
-    if (variable_get('private_files_download_permission_by_user_checks')) {
-      $default_users = array_keys($directory_list[$did]->uid);
-    }
-    $default_roles = array_keys($directory_list[$did]->rid);
-  }
-  // Prepare the directory id value to be eventually submitted.
-  $form['did'] = array(
-    '#type'  => 'value',
-    '#value' => $did,
-  );
-  // Prepare the path text field.
-  $form['path'] = array(
-    '#type'          => 'textfield',
-    '#title'         => t('Path'),
-    '#field_prefix'  => variable_get('file_private_path'),
-    '#size'          => 60,
-    '#maxlength'     => 255,
-    '#required'      => TRUE,
-    '#default_value' => $default_path,
-  );
-  // Prepare the bypass checkbox.
-  $form['bypass'] = array(
-    '#type'          => 'checkbox',
-    '#title'         => t('Bypass'),
-    '#default_value' => $default_bypass,
-    '#description'   => t('Enable to make this module ignore the above path.'),
-  );
-  // Prepare the user checkbox fieldset.
-  if (variable_get('private_files_download_permission_by_user_checks')) {
-    $form['users'] = array(
-      '#type'        => 'fieldset',
-      '#title'       => t('Enabled users'),
-      '#collapsible' => TRUE,
-      '#collapsed'   => TRUE,
-    );
-  }
-  // Prepare user checkboxes.
-  if (variable_get('private_files_download_permission_by_user_checks')) {
-    $users = array_flip(private_files_download_permission_get_users());
-    ksort($users);
-    $users = array_flip($users);
-    foreach ($users as $uid => $user) {
-      $form['users']['user_' . $uid] = array(
-        '#type'          => 'checkbox',
-        '#title'         => check_plain($user),
-        '#default_value' => ((NULL === $did) && (1 === $uid) ? TRUE : in_array($uid, $default_users)),
-      );
-    }
-  }
-  // Prepare the role checkbox fieldset.
-  $form['roles'] = array(
-    '#type'        => 'fieldset',
-    '#title'       => t('Enabled roles'),
-    '#collapsible' => TRUE,
-    '#collapsed'   => TRUE,
-  );
-  // Prepare role checkboxes.
-  $roles = array_flip(user_roles());
-  ksort($roles);
-  $roles = array_flip($roles);
-  foreach ($roles as $rid => $role) {
-    $form['roles']['role_' . $rid] = array(
-      '#type'          => 'checkbox',
-      '#title'         => check_plain($role),
-      '#default_value' => in_array($rid, $default_roles),
-    );
-  }
-  // Prepare the submit button.
-  $form['submit'] = array(
-    '#type'  => 'submit',
-    '#value' => t('Save directory to the control list'),
-  );
-  // Return form.
-  return $form;
-}
-
-/**
- * (Form callback.) Validates the directory form.
- */
-function private_files_download_permission_get_directory_form_validate($form, &$form_state) {
-  // Retrieve $path (which, being required, is surely not blank).
-  $path = $form_state['values']['path'];
-  // Perform slash validation:
-  if (0 < drupal_strlen($path)) {
-    $first_character = drupal_substr($path, 0, 1);
-    $last_character = drupal_substr($path, -1, 1);
-    // ...there must be a leading slash.
-    if (('/' !== $first_character) && ('\\' !== $first_character)) {
-      form_set_error('path', t('You must add a leading slash.'));
-    }
-    if (1 < drupal_strlen($path)) {
-      // ...there cannot be multiple consecutive slashes.
-      if ((FALSE !== strpos($path, '//')) || (FALSE !== strpos($path, '\\\\'))) {
-        form_set_error('path', t('You cannot use multiple consecutive slashes.'));
-      }
-      // ...there cannot be trailing slashes.
-      if (('/' === $last_character) || ('\\' === $last_character)) {
-        form_set_error('path', t('You cannot use trailing slashes.'));
-      }
-    }
-  }
-}
-
-/**
- * (Form callback.) Submits the directory form.
- */
-function private_files_download_permission_get_directory_form_submit($form, &$form_state) {
-  $transaction = db_transaction();
-  try {
-    // Retrieve form values.
-    $did = $form_state['values']['did'];
-    $path = $form_state['values']['path'];
-    $bypass = $form_state['values']['bypass'];
-    if (variable_get('private_files_download_permission_by_user_checks')) {
-      $users = array();
-    }
-    $roles = array();
-    foreach ($form_state['values'] as $key => $value) {
-      if (variable_get('private_files_download_permission_by_user_checks')) {
-        if (0 === strpos($key, 'user_')) {
-          $uid = drupal_substr($key, drupal_strlen('user_'));
-          $users[$uid] = $value;
-        }
-      }
-      if (0 === strpos($key, 'role_')) {
-        $rid = drupal_substr($key, drupal_strlen('role_'));
-        $roles[$rid] = $value;
-      }
-    }
-    // Write directory record.
-    $directory_record = array(
-      'did'    => $did,
-      'path'   => $path,
-      'bypass' => $bypass,
-    );
-    if (NULL === $did) {
-      drupal_write_record('private_files_download_permission_directory', $directory_record);
-    }
-    else {
-      drupal_write_record('private_files_download_permission_directory', $directory_record, array('did'));
-    }
-    // Retrieve last record id.
-    if (NULL === $did) {
-      $did = $directory_record['did'];
-    }
-    // Delete old user permissions and write new ones.
-    if (variable_get('private_files_download_permission_by_user_checks')) {
-      db_delete('private_files_download_permission_directory_user')
-        ->condition('did', $did)
-        ->execute();
-      foreach ($users as $uid => $value) {
-        if (TRUE == $value) {
-          db_insert('private_files_download_permission_directory_user')
-            ->fields(array(
-              'did' => $did,
-              'uid' => $uid,
-              ))
-            ->execute();
-        }
-      }
-    }
-    // Delete old role permissions and write new ones.
-    db_delete('private_files_download_permission_directory_role')
-      ->condition('did', $did)
-      ->execute();
-    foreach ($roles as $rid => $value) {
-      if (TRUE == $value) {
-        db_insert('private_files_download_permission_directory_role')
-          ->fields(array(
-            'did' => $did,
-            'rid' => $rid,
-            ))
-          ->execute();
-      }
-    }
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    drupal_set_message(t('An error occurred while saving directory to the control list. Please check the log for details.'), 'error');
-  }
-  // Purge directory list from cache.
-  drupal_static_reset('private_files_download_permission_get_directory_list');
-  // Set form redirection.
-  $form_state['redirect'] = 'admin/config/media/private-files-download-permission';
-}
-
-/**
- * (Page callback.) Adds a directory to the control list.
- */
-function private_files_download_permission_add_directory() {
-  return drupal_get_form('private_files_download_permission_get_directory_form', NULL);
-}
-
-/**
- * (Page callback.) Edits a directory in the control list.
- */
-function private_files_download_permission_edit_directory($did) {
-  return drupal_get_form('private_files_download_permission_get_directory_form', $did);
-}
-
-/**
- * (Form callback.) Displays a confirmation dialog before removing a directory
- * from the control list.
- */
-function private_files_download_permission_remove_directory($form, &$form_state, $did) {
-  $form = array();
-  // Check that $did is actually a valid directory id.
-  $directory_list = private_files_download_permission_get_directory_list();
-  if (!in_array($did, array_keys($directory_list))) {
-    drupal_set_message(t('You need to provide a valid directory id.'), 'error');
-    return;
-  }
-  // Prepare the directory id value to be eventually submitted.
-  $form['did'] = array(
-    '#type'  => 'value',
-    '#value' => $did,
-  );
-  // Display the confirmation form.
-  return confirm_form(
-    $form,
-    t('Are you sure you want to remove @path from the control list?', array('@path' => $directory_list[$did]->path)),
-    'admin/config/media/private-files-download-permission',
-    t('This action cannot be undone.'),
-    t('Remove directory from the control list'),
-    t('Cancel')
-  );
-}
-
-/**
- * (Form callback.) Removes a directory from the control list.
- */
-function private_files_download_permission_remove_directory_submit($form, &$form_state) {
-  $transaction = db_transaction();
-  try {
-    // Check that $form_state['values']['did'] is actually a valid directory id.
-    $directory_list = private_files_download_permission_get_directory_list();
-    if (!in_array($form_state['values']['did'], array_keys($directory_list))) {
-      drupal_set_message(t('You need to provide a valid directory id.'), 'error');
-      return;
-    }
-    // Remove users associated to the directory.
-    db_delete('private_files_download_permission_directory_user')
-      ->condition('did', $form_state['values']['did'])
-      ->execute();
-    // Remove roles associated to the directory.
-    db_delete('private_files_download_permission_directory_role')
-      ->condition('did', $form_state['values']['did'])
-      ->execute();
-    // Remove the directory itself.
-    db_delete('private_files_download_permission_directory')
-      ->condition('did', $form_state['values']['did'])
-      ->execute();
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    drupal_set_message(t('An error occurred while removing directory from the control list. Please check the log for details.'), 'error');
-  }
-  // Purge directory list from cache.
-  drupal_static_reset('private_files_download_permission_get_directory_list');
-  // Set form redirection.
-  $form_state['redirect'] = 'admin/config/media/private-files-download-permission';
-}
-
-/**
- * (Form callback.) Displays a form to set preferences.
- */
-function private_files_download_permission_get_preferences_form($form, &$form_state) {
-  // Prepare settings.
-  $form['private_files_download_permission_by_user_checks'] = array(
-    '#type'          => 'checkbox',
-    '#title'         => t('Enable by-user checks'),
-    '#default_value' => variable_get('private_files_download_permission_by_user_checks'),
-    '#description'   => t('You may wish to disable this feature if there are plenty of users, as it may slow down the entire site.'),
-  );
-  // Prepare the submit button.
-  $form['submit'] = array(
-    '#type'  => 'submit',
-    '#value' => t('Save preferences'),
-  );
-  // Return form.
-  return $form;
-}
-
-/**
- * (Form callback.) Submits the preferences form.
- */
-function private_files_download_permission_get_preferences_form_submit($form, &$form_state) {
-  // Save preferences.
-  variable_set('private_files_download_permission_by_user_checks', $form_state['values']['private_files_download_permission_by_user_checks']);
-  // Purge directory list from cache.
-  drupal_static_reset('private_files_download_permission_get_directory_list');
-  // Display message.
-  drupal_set_message(t('Your preferences have been successfully saved.'), 'status');
-}
-
-/**
- * (Page callback.) Sets module preferences.
- */
-function private_files_download_permission_set_preferences() {
-  return drupal_get_form('private_files_download_permission_get_preferences_form');
-}
+use Drupal\private_files_download_permission\Entity\PrivateFilesDownloadPermissionDirectory;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Implements hook_file_download().
  */
 function private_files_download_permission_file_download($uri) {
-  global $user;
+  $user = \Drupal::currentUser();
   // Check if user may bypass permission restrictions.
-  if (user_access('bypass private files download permission')) {
-    return array('Content-Type' => file_get_mimetype($uri));
+  if ($user->hasPermission('bypass private files download permission')) {
+    return array('Content-Type' => \Drupal::service('file.mime_type.guesser')->guess($uri));
   }
-  else if ((user_access('bypass private files download permission for temporary files')) && ('temporary://' === substr($uri, 0, 12))) {
-    return array('Content-Type' => file_get_mimetype($uri));
+  else if (($user->hasPermission('bypass private files download permission for temporary files')) && ('temporary://' === substr($uri, 0, 12))) {
+    return array('Content-Type' => \Drupal::service('file.mime_type.guesser')->guess($uri));
   }
   // If not, do all the specific checks.
   else {
@@ -561,12 +27,12 @@ function private_files_download_permission_file_download($uri) {
     // Find the directory which best matches $uri_path.
     $best_matching_length = 0;
     $best_matching_directory = NULL;
-    foreach (private_files_download_permission_get_directory_list() as $directory) {
+    foreach (PrivateFilesDownloadPermissionDirectory::loadMultiple() as $directory) {
       // Search for the best matching substring.
       $directory_path = $directory->path;
       if (0 === stripos($uri_path, $directory_path)) {
-        if (drupal_strlen($directory_path) > $best_matching_length) {
-          $best_matching_length = drupal_strlen($directory_path);
+        if (Unicode::strlen($directory_path) > $best_matching_length) {
+          $best_matching_length = Unicode::strlen($directory_path);
           $best_matching_directory = $directory;
         }
       }
@@ -577,18 +43,18 @@ function private_files_download_permission_file_download($uri) {
         return NULL;
       }
       // Evaluate user and role permissions and optionally allow access to $uri.
-      if (variable_get('private_files_download_permission_by_user_checks')) {
-        if (in_array($user->uid, array_keys($best_matching_directory->uid))) {
-          $files = file_load_multiple(array(), array('uri' => $uri));
+      if (\Drupal::config('private_files_download_permission.settings')->get('by_user_checks')) {
+        if (in_array($user->id(), $best_matching_directory->users)) {
+          $files = \Drupal::entityManager()->getStorage('file')->loadByProperties(array('uri' => $uri));
           $file = reset($files);
-          return file_transfer($uri, file_get_content_headers($file));
+          return file_get_content_headers($file);
         }
       }
-      foreach ($user->roles as $rid => $role) {
-        if (in_array($rid, array_keys($best_matching_directory->rid))) {
-          $files = file_load_multiple(array(), array('uri' => $uri));
+      foreach ($user->getRoles() as $rid) {
+        if (in_array($rid, $best_matching_directory->roles)) {
+          $files = \Drupal::entityManager()->getStorage('file')->loadByProperties(array('uri' => $uri));
           $file = reset($files);
-          return file_transfer($uri, file_get_content_headers($file));
+          return file_get_content_headers($file);
         }
       }
     }
diff --git a/private_files_download_permission.permissions.yml b/private_files_download_permission.permissions.yml
new file mode 100644
index 0000000..942557b
--- /dev/null
+++ b/private_files_download_permission.permissions.yml
@@ -0,0 +1,11 @@
+bypass private files download permission:
+  title: 'Bypass Private files download permission'
+  description: 'Download from private directories regardless of permission restrictions.'
+  restrict access: TRUE
+bypass private files download permission for temporary files:
+  title: 'Bypass Private files download permission for temporary files'
+  description: 'Download temporary files regardless of permission restrictions.'
+  restrict access: TRUE
+administer private files download permission:
+  title: 'Administer Private files download permission'
+  description: 'Access module configuration.'
diff --git a/private_files_download_permission.routing.yml b/private_files_download_permission.routing.yml
new file mode 100644
index 0000000..6a814c5
--- /dev/null
+++ b/private_files_download_permission.routing.yml
@@ -0,0 +1,39 @@
+private_files_download_permission.admin:
+  path: '/admin/config/media/private-files-download-permission/preferences'
+  defaults:
+    _form: '\Drupal\private_files_download_permission\Form\PfdpSettingsForm'
+    _title: 'Preferences'
+  requirements:
+    _permission: 'administer private files download permission'
+
+entity.pfdp_directory.collection:
+  path: '/admin/config/media/private-files-download-permission'
+  defaults:
+    _entity_list: 'pfdp_directory'
+    _title: 'List directories'
+  requirements:
+    _permission: 'administer private files download permission'
+
+entity.pfdp_directory.add_form:
+  path: '/admin/config/media/private-files-download-permission/add'
+  defaults:
+    _entity_form: 'pfdp_directory.add'
+    _title: 'Add directory'
+  requirements:
+    _permission: 'administer private files download permission'
+
+entity.pfdp_directory.edit_form:
+  path: '/admin/config/media/private-files-download-permission/{pfdp_directory}'
+  defaults:
+    _entity_form: 'pfdp_directory.edit'
+    _title: 'Edit directory'
+  requirements:
+    _permission: 'administer private files download permission'
+
+entity.pfdp_directory.delete_form:
+  path: '/admin/config/media/private-files-download-permission/{pfdp_directory}/delete'
+  defaults:
+    _entity_form: 'pfdp_directory.delete'
+    _title: 'Delete directory'
+  requirements:
+    _permission: 'administer private files download permission'
diff --git a/src/Controller/PfdpDirectoryListBuilder.php b/src/Controller/PfdpDirectoryListBuilder.php
new file mode 100644
index 0000000..222171c
--- /dev/null
+++ b/src/Controller/PfdpDirectoryListBuilder.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\private_files_download_permission\Controller\PfdpDirectoryListBuilder.
+ */
+
+namespace Drupal\private_files_download_permission\Controller;
+
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Site\Settings;
+use Drupal\user\Entity\User;
+use Drupal\user\Entity\Role;
+
+/**
+ * Provides a listing of Example.
+ */
+class PfdpDirectoryListBuilder extends ConfigEntityListBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    if (file_default_scheme() !== 'private') {
+      drupal_set_message($this->t('Your <a href="@url">default download method</a> is not set as private. Please keep in mind that these settings only affect private file system downloads.', array('@url' => \Drupal::url('system.file_system_settings'))), 'warning');
+    }
+    if (!Settings::get('file_private_path')) {
+      drupal_set_message($this->t('Your private file system path is not set.'), 'warning');
+    }
+
+    return parent::render();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['path'] = $this->t('Directory path');
+    $header['id'] = $this->t('Machine name');
+    $header['bypass'] = $this->t('Bypass');
+    $header['users'] = $this->t('Enabled users');
+    $header['roles'] = $this->t('Enabled roles');
+
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['path'] = $entity->path;
+    $row['id'] = $entity->id();
+    $row['bypass'] = $entity->bypass ? $this->t('yes') : $this->t('no');
+
+    $row['users'] = implode(', ', array_map(function ($uid) {
+      $user = User::load($uid);
+      return $user ? $user->label() : t('missing user');
+    }, $entity->users));
+    $row['roles'] = implode(', ', array_map(function ($rid) {
+      $role = Role::load($rid);
+      return $role ? $role->label() : t('missing role');
+    }, $entity->roles));
+
+    // You probably want a few more properties here...
+
+    return $row + parent::buildRow($entity);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOperations(EntityInterface $entity) {
+    $operations = parent::getDefaultOperations($entity);
+
+    if ($entity->hasLinkTemplate('edit-form')) {
+      $operations['edit'] = array(
+        'title' => t('Edit directory'),
+        'weight' => 20,
+        'url' => $entity->urlInfo('edit-form'),
+      );
+    }
+    return $operations;
+  }
+
+}
diff --git a/src/Entity/PrivateFilesDownloadPermissionDirectory.php b/src/Entity/PrivateFilesDownloadPermissionDirectory.php
new file mode 100644
index 0000000..6b7790a
--- /dev/null
+++ b/src/Entity/PrivateFilesDownloadPermissionDirectory.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\private_files_download_permission\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the private file directory class.
+ *
+ * @ConfigEntityType(
+ *   id = "pfdp_directory",
+ *   label = @Translation("Private files download permission - Directory"),
+ *   handlers = {
+ *     "list_builder" = "Drupal\private_files_download_permission\Controller\PfdpDirectoryListBuilder",
+ *     "form" = {
+ *       "add" = "Drupal\private_files_download_permission\Form\PfdpDirectoryForm",
+ *       "edit" = "Drupal\private_files_download_permission\Form\PfdpDirectoryForm",
+ *       "delete" = "Drupal\private_files_download_permission\Form\PfdpDirectoryDeleteForm",
+ *     }
+ *   },
+ *   admin_permission = "administer private files download permission",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "path",
+ *   },
+ *   links = {
+ *     "edit-form" = "/admin/config/media/private-files-download-permission/{pfdp_directory}",
+ *     "delete-form" = "/admin/config/media/private-files-download-permission/{pfdp_directory}/delete",
+ *   }
+ * )
+ */
+class PrivateFilesDownloadPermissionDirectory extends ConfigEntityBase {
+  public $id;
+  public $path;
+  public $bypass = FALSE;
+  public $users = [];
+  public $roles = [];
+}
diff --git a/src/Form/PfdpDirectoryDeleteForm.php b/src/Form/PfdpDirectoryDeleteForm.php
new file mode 100644
index 0000000..0b051d3
--- /dev/null
+++ b/src/Form/PfdpDirectoryDeleteForm.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\private_files_download_permission\Form\PfdpDirectoryDeleteForm.
+ */
+
+namespace Drupal\private_files_download_permission\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Provides a deletion confirmation form for Ball entity.
+ */
+class PfdpDirectoryDeleteForm extends EntityConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete the directory %name?', array('%name' => $this->entity->label()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('entity.pfdp_directory.collection');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->entity->delete();
+    $this->logger('pfdp_directory')->notice('Directory %name has been deleted.', array('%name' => $this->entity->label()));
+    drupal_set_message($this->t('Directory %name has been deleted.', array('%name' => $this->entity->label())));
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}
diff --git a/src/Form/PfdpDirectoryForm.php b/src/Form/PfdpDirectoryForm.php
new file mode 100644
index 0000000..0397698
--- /dev/null
+++ b/src/Form/PfdpDirectoryForm.php
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\private_files_download_permission\Form\PfdpDirectoryForm.
+ */
+
+namespace Drupal\private_files_download_permission\Form;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityForm;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Site\Settings;
+use Drupal\Component\Utility\Unicode;
+
+class PfdpDirectoryForm extends EntityForm {
+
+  /**
+   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
+   *   The entity query.
+   */
+  public function __construct(QueryFactory $entity_query) {
+    $this->entityQuery = $entity_query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.query')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+
+    $directory = $this->entity;
+
+    $form['path'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Path'),
+      '#field_prefix' => Settings::get('file_private_path'),
+      '#size' => 60,
+      '#maxlength' => 255,
+      '#default_value' => $directory->path,
+      '#required' => TRUE,
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $directory->id(),
+      '#machine_name' => array(
+        'exists' => array($this, 'exist'),
+        'source' => array('path'),
+      ),
+      '#disabled' => !$directory->isNew(),
+    );
+    $form['bypass'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Bypass'),
+      '#default_value' => $directory->bypass,
+      '#description' => t('Enable to make this module ignore the above path.'),
+    );
+
+    $enable_users = $this->config('private_files_download_permission.settings')->get('by_user_checks');
+    if ($enable_users) {
+      $form['users_wrapper'] = array(
+        '#type' => 'details',
+        '#title' => t('Enabled users'),
+        '#collapsible' => TRUE,
+        '#collapsed' => TRUE,
+      );
+      $form['users_wrapper']['users'] = array(
+        '#type' => 'checkboxes',
+        '#default_value' => $directory->users,
+        '#options' => $this->getUsers(),
+      );
+    }
+
+    $form['roles_wrapper'] = array(
+      '#type' => 'details',
+      '#title' => t('Enabled roles'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+    );
+    $form['roles_wrapper']['roles'] = array(
+      '#type' => 'checkboxes',
+      '#options' => user_role_names(),
+      '#default_value' => $directory->roles,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    // Retrieve $path (which, being required, is surely not blank).
+    $path = $form_state->getValue('path');
+    // Perform slash validation:
+    if ($path) {
+      // ...there must be a leading slash.
+      if (strpos($path, '/') !== 0) {
+        $form_state->setErrorByName('path', t('You must add a leading slash.'));
+      }
+      if (Unicode::substr($path, -1) === '/') {
+        $form_state->setErrorByName('path', t('You cannot use trailing slashes.'));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    $directory = $this->entity;
+    $directory->roles = array_filter($directory->roles);
+    $directory->users = array_filter($directory->users);
+    $status = $directory->save();
+
+    if ($status) {
+      drupal_set_message($this->t('Saved the %label directory.', array(
+        '%label' => $directory->label(),
+      )));
+    }
+    else {
+      drupal_set_message($this->t('The %label directory was not saved.', array(
+        '%label' => $directory->label(),
+      )));
+    }
+
+    $form_state->setRedirect('entity.pfdp_directory.collection');
+  }
+
+  public function exist($id) {
+    $entity = $this->entityQuery->get('pfdp_directory')
+      ->condition('id', $id)
+      ->execute();
+    return (bool) $entity;
+  }
+
+  /**
+   * @todo Switch to UserStorage instead.
+   */
+  protected function getUsers() {
+    return db_select('users_field_data', 't')
+      ->fields('t', ['uid', 'name'])
+      ->orderBy('t.name', 'ASC')
+      ->condition('uid', \Drupal\user\RoleInterface::ANONYMOUS_ID, '<>')
+      ->execute()
+      ->fetchAllKeyed();
+  }
+}
diff --git a/src/Form/PfdpSettingsForm.php b/src/Form/PfdpSettingsForm.php
new file mode 100644
index 0000000..66b7750
--- /dev/null
+++ b/src/Form/PfdpSettingsForm.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\private_files_download_permission\Form;
+
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * General settings form for private files download permission module.
+ */
+class PfdpSettingsForm extends ConfigFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'private_files_download_permission_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['private_files_download_permission.settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['private_files_download_permission_by_user_checks'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enable by-user checks'),
+      '#default_value' => $this->config('private_files_download_permission.settings')->get('by_user_checks'),
+      '#description' => t('You may wish to disable this feature if there are plenty of users, as it may slow down the entire site.'),
+    );
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->config('private_files_download_permission.settings')
+      ->set('by_user_checks', $form_state->getValue('private_files_download_permission_by_user_checks'))
+      ->save();
+    // Purge directory list from cache.
+    //drupal_static_reset('private_files_download_permission_get_directory_list');
+
+    parent::submitForm($form, $form_state);
+  }
+
+}
