diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 3dd3a6d..4e61b9a 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -2,7 +2,6 @@ CHANGELOG for Disqus for Drupal 7
 
 Disqus 7.x-1.x-dev
 =================================
-- #2021905 by tkngdwn: Record file usage for SSO logo.
 - #2040057 by mandar.harkare, RobLoach | steven.wichers: Hook_uninstall() removes variables not belonging to Disqus.
 - #2021905 by Grayside, pblissmt: Custom SSO logo admin.
 - #2017385 by marcingy: Fixed Disqus creates a notice when no domain is defined.
diff --git a/disqus.admin.inc b/disqus.admin.inc
deleted file mode 100644
index 0b2d912..0000000
--- a/disqus.admin.inc
+++ /dev/null
@@ -1,187 +0,0 @@
-<?php
-
-/**
- * @file
- * Administration forms for the Disqus module.
- */
-
-/**
- * Menu callback; Displays the administration settings for Disqus.
- */
-function disqus_admin_settings() {
-  $form = array();
-  $form['disqus_domain'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Shortname'),
-    '#description' => t('The website shortname that you registered Disqus with. If you registered http://example.disqus.com, you would enter "example" here.'),
-    '#default_value' => variable_get('disqus_domain', ''),
-  );
-  $form['settings'] = array(
-    '#type' => 'vertical_tabs',
-    '#weight' => 50,
-  );
-  // Visibility settings.
-  $form['visibility'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Visibility'),
-    '#group' => 'settings',
-  );
-  $types = node_type_get_types();
-  $options = array();
-  foreach ($types as $type) {
-    $options[$type->type] = $type->name;
-  }
-  $form['visibility']['disqus_nodetypes'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Node Types'),
-    '#description' => t('Apply comments to only the following node types.'),
-    '#default_value' => variable_get('disqus_nodetypes', array()),
-    '#options' => $options,
-  );
-  $form['visibility']['disqus_location'] = array(
-    '#type' => 'select',
-    '#title' => t('Location'),
-    '#description' => t('Display the Disqus comments in the given location. When "Block" is selected, the comments will appear in the <a href="@disquscomments">Disqus Comments block</a>.', array('@disquscomments' => url('admin/structure/block'))),
-    '#default_value' => variable_get('disqus_location', 'content_area'),
-    '#options' => array(
-      'content_area' => t('Content Area'),
-      'block' => t('Block'),
-    ),
-  );
-  $form['visibility']['disqus_weight'] = array(
-    '#type' => 'select',
-    '#title' => t('Weight'),
-    '#description' => t('When the comments are displayed in the content area, you can change the position at which they will be shown.'),
-    '#default_value' => variable_get('disqus_weight', 50),
-    '#options' => drupal_map_assoc(array(-100, -75, -50, -25, 0, 25, 50, 75, 100)),
-    '#states' => array(
-      'visible' => array(
-        'select[name="disqus_location"]' => array('value' => 'content_area'),
-      ),
-    ),
-  );
-  // Behavior settings.
-  $form['behavior'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Behavior'),
-    '#group' => 'settings',
-  );
-  $form['behavior']['disqus_userapikey'] = array(
-    '#type' => 'textfield',
-    '#title' => t('User API Key'),
-    '#description' => t('The API key of the administrator account on Disqus. You can get yours <a href="@key">here</a>.', array('@key' => 'http://disqus.com/api/get_my_key/')),
-    '#default_value' => variable_get('disqus_userapikey', ''),
-  );
-  $form['behavior']['disqus_localization'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Localization support'),
-    '#description' => t("When enabled, overrides the language set by Disqus with the language provided by the site."),
-    '#default_value' => variable_get('disqus_localization', FALSE),
-  );
-  $form['behavior']['disqus_inherit_login'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Inherit User Credentials'),
-    '#description' => t("When enabled and a user is logged in, the Disqus 'Post as Guest' login form will be pre-filled with the user's name and email address."),
-    '#default_value' => variable_get('disqus_inherit_login', TRUE),
-  );
-  $form['behavior']['disqus_developer'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Testing'),
-    '#description' => t('When enabled, uses the <a href="http://docs.disqus.com/help/2/">disqus_developer</a> flag to tell Disqus that you are in a testing environment. Threads will not display on the public community page with this set.'),
-    '#default_value' => variable_get('disqus_developer', FALSE),
-  );
-  // Advanced settings.
-  $form['advanced'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Advanced'),
-    '#group' => 'settings',
-    '#description' => t('Use these settings to configure the more advanced uses of Disqus. You can find more information about these in the <a href="@applications">Applications</a> section of Disqus. To enable some of these features, you will require a <a href="@addons">Disqus Add-on Package</a>.', array(
-      '@applications' => 'http://disqus.com/api/applications/',
-      '@addons' => 'http://disqus.com/addons/',
-    )),
-  );
-  $form['advanced']['disqus_publickey'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Public Key'),
-    '#default_value' => variable_get('disqus_publickey', ''),
-  );
-  $form['advanced']['disqus_secretkey'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Secret Key'),
-    '#default_value' => variable_get('disqus_secretkey', ''),
-  );
-
-  $form['advanced']['sso'] = array(
-    '#weight' => 5,
-    '#type' => 'fieldset',
-    '#title' => t('Single Sign-on'),
-    '#collapsible' => FALSE,
-    '#collapsed' => FALSE,
-    '#states' => array(
-      'visible' => array(
-        'input[name="disqus_publickey"]' => array('empty' => FALSE),
-        'input[name="disqus_secretkey"]' => array('empty' => FALSE),
-      ),
-    ),
-  );
-  $form['advanced']['sso']['disqus_sso'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Use Single Sign-On'),
-    '#description' => t('Provide <a href="@sso">Single Sign-On</a> access to your site.', array(
-      '@sso' => 'http://disqus.com/api/sso/',
-    )),
-    '#default_value' => variable_get('disqus_sso', FALSE),
-  );
-  $form['advanced']['sso']['disqus_use_site_logo'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Use Site Logo'),
-    '#description' => t('Pass the site logo to Disqus for use as SSO login button.'),
-    '#default_value' => variable_get('disqus_use_site_logo', TRUE),
-    '#states' => array(
-      'disabled' => array(
-        'input[name="disqus_sso"]' => array('checked' => FALSE),
-      ),
-    ),
-  );
-  $form['advanced']['sso']['disqus_logo'] = array(
-    '#type' => 'managed_file',
-    '#title' => t('Custom Logo'),
-    '#upload_location' => 'public://images',
-    '#default_value' => variable_get('disqus_logo', ''),
-    '#states' => array(
-      'disabled' => array(
-        'input[name="disqus_sso"]' => array('checked' => FALSE),
-      ),
-      'visible' => array(
-        'input[name="disqus_use_site_logo"]' => array('checked' => FALSE),
-      ),
-    ),
-  );
-  // Make sure the validation is called to handle the custom sso logo.
-  $form['#submit'][] = 'disqus_admin_settings_submit';
-  return system_settings_form($form);
-}
-
-/**
- * Form callback; Make sure we process the sso logo and set it to a permanent status.
- */
-function disqus_admin_settings_submit($form, &$form_state) {
-  $old_logo = variable_get('disqus_logo', '');
-  $new_logo = (isset($form_state['values']['disqus_logo'])) ? $form_state['values']['disqus_logo'] : '';
-  // Ignore if the file hasn't changed.
-  if ($new_logo != $old_logo) {
-    // Remove the old file and usage if previously set.
-    if ($old_logo != '') {
-      $file = file_load($old_logo);
-      file_usage_delete($file, 'disqus', 'disqus');
-      file_delete($file);
-    }
-    // Update the new file and usage.
-    if ($new_logo != '') {
-      $file = file_load($new_logo);
-      file_usage_add($file, 'disqus', 'disqus', 0);
-      $file->status = FILE_STATUS_PERMANENT;
-      file_save($file);
-    }
-  }
-}
diff --git a/disqus.info b/disqus.info
deleted file mode 100644
index 1cad6d8..0000000
--- a/disqus.info
+++ /dev/null
@@ -1,12 +0,0 @@
-name = Disqus
-description = Uses the Disqus web service to enhance comments.
-core = 7.x
-php = 5
-
-files[] = disqus.admin.inc
-files[] = disqus.install
-files[] = disqus.module
-files[] = disqus.php
-files[] = disqus.views.inc
-
-configure = admin/config/services/disqus
diff --git a/disqus.info.yml b/disqus.info.yml
new file mode 100644
index 0000000..5f0fac8
--- /dev/null
+++ b/disqus.info.yml
@@ -0,0 +1,7 @@
+name: Disqus
+description: Uses the Disqus web service to enhance comments.
+core: 8.x
+configure: admin/config/services/disqus
+dependencies:
+  - node
+  - user
diff --git a/disqus.module b/disqus.module
index eee24e2..0da8b67 100644
--- a/disqus.module
+++ b/disqus.module
@@ -50,34 +50,20 @@ function disqus_permission() {
  * Implements hook_menu().
  */
 function disqus_menu() {
-  $items = array();
   $items['admin/config/services/disqus'] = array(
+    'route_name' => 'disqus_admin',
     'title' => 'Disqus',
     'description' => 'Provides configuration options for the Disqus comment system.',
-    'access arguments' => array('administer disqus'),
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('disqus_admin_settings'),
-    'file' => 'disqus.admin.inc',
   );
+
   $items['disqus/closewindow'] = array(
+    'route_name' => 'disqus_close_window',
     'title' => 'Please wait',
     'description' => 'Once the user logs in through the Disqus login workflow, they are redirected here to automatically close the popup window.',
-    'access arguments' => array('access content'),
-    'page callback' => 'disqus_closewindow',
     'type' => MENU_CALLBACK,
   );
-  return $items;
-}
 
-/**
- * Menu callback; Automatically closes the window after the user logs in.
- *
- * @return
- *   Confirmation message and link that closes overlay window.
- */
-function disqus_closewindow() {
-  drupal_add_js('window.close();', 'inline');
-  return t('Thank you for logging in. Please close this window, or <a href="@clickhere">click here</a> to continue.', array('@clickhere' => 'javascript:window.close();'));
+  return $items;
 }
 
 /**
@@ -393,293 +379,6 @@ function disqus_user_view($account, $view_mode, $langcode) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function disqus_block_info() {
-  $blocks['disqus_recent_comments'] = array(
-    'info' => t('Disqus: Recent Comments'),
-    'cache' => DRUPAL_CACHE_GLOBAL,
-  );
-  $blocks['disqus_popular_threads'] = array(
-    'info' => t('Disqus: Popular Threads'),
-    'cache' => DRUPAL_CACHE_GLOBAL,
-  );
-  $blocks['disqus_top_commenters'] = array(
-    'info' => t('Disqus: Top Commenters'),
-    'cache' => DRUPAL_CACHE_GLOBAL,
-  );
-  $blocks['disqus_combination_widget'] = array(
-    'info' => t('Disqus: Combination Widget'),
-    'cache' => DRUPAL_CACHE_GLOBAL,
-  );
-  $blocks['disqus_comments'] = array(
-    'info' => t('Disqus: Comments'),
-    'cache' => DRUPAL_CACHE_CUSTOM,
-  );
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function disqus_block_configure($delta = '') {
-  $form = array();
-  $form['disqus'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Disqus settings'),
-  );
-  if ($delta == 'disqus_comments') {
-    $form['disqus']['#description'] = t('This block will be used to display the comments from Disqus when comments are applied to the given page. Visit the <a href="@disqussettings">Disqus settings</a> to configure when this is visible.', array('@disqussettings' => url('admin/config/services/disqus')));
-  }
-  $form['disqus'][$delta . '_items'] = array(
-    '#type' => 'select',
-    '#title' => t('Number of items to show'),
-    '#options' => array(1 => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20),
-    '#default_value' => variable_get($delta .'_items', 5),
-    '#access' => ($delta != 'disqus_comments'),
-  );
-  $form['disqus'][$delta . '_showavatars'] = array(
-    '#type' => 'select',
-    '#title' => t('Show avatars'),
-    '#options' => array(FALSE => t('No'), TRUE => t('Yes')),
-    '#default_value' => variable_get($delta .'_showavatars', TRUE),
-    '#access' => ($delta == 'disqus_recent_comments') || ($delta == 'disqus_top_commenters'),
-  );
-  $form['disqus'][$delta . '_avatarsize'] = array(
-    '#type' => 'select',
-    '#title' => t('Avatar size'),
-    '#options' => array(
-      24 => t('X-Small (24px)'),
-      32 => t('Small (32px)'),
-      48 => t('Medium (48px)'),
-      92 => t('Large (92px)'),
-      128 => t('X-Large (128px)'),
-    ),
-    '#default_value' => variable_get($delta .'_avatarsize', 32),
-    '#access' => ($delta == 'disqus_recent_comments') || ($delta == 'disqus_top_commenters'),
-  );
-  $form['disqus'][$delta . '_colortheme'] = array(
-    '#type' => 'select',
-    '#title' => t('Color Theme'),
-    '#options' => array(
-      'blue' => t('Blue'),
-      'grey' => t('Grey'),
-      'green' => t('Green'),
-      'red' => t('Red'),
-      'orange' => t('Orange'),
-    ),
-    '#default_value' => variable_get($delta .'_colortheme', 'blue'),
-    '#access' => $delta == 'disqus_combination_widget',
-  );
-  $form['disqus'][$delta . '_defaulttabview'] = array(
-    '#type' => 'select',
-    '#title' => t('Default Tab View'),
-    '#options' => array(
-      'people' => t('People'),
-      'recent' => t('Recent'),
-      'popular' => t('Popular'),
-    ),
-    '#default_value' => variable_get($delta .'_defaulttabview', 'people'),
-    '#access' => $delta == 'disqus_combination_widget',
-  );
-  $form['disqus'][$delta . '_excerpt_length'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Comment Except Length'),
-    '#default_value' => variable_get($delta .'_excerpt_length', '200'),
-    '#access' => ($delta == 'disqus_recent_comments') || ($delta == 'disqus_combination_widget'),
-    '#size' => 4,
-  );
-  $form['disqus'][$delta . '_hide_mods'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Hide moderators in ranking'),
-    '#default_value' => variable_get($delta .'_hide_mods', FALSE),
-    '#access' => ($delta == 'disqus_top_commenters') || ($delta == 'disqus_combination_widget'),
-  );
-  return $form;
-}
-
-/**
- * Implements hook_block_save().
- */
-function disqus_block_save($delta = '', $edit = array()) {
-  // The Disqus comments block doesn't have any configuration.
-  if ($delta != 'disqus_comments') {
-    variable_set($delta . '_items', $edit[$delta . '_items']);
-    // Recent comments and top commenters have avatars.
-    if (($delta == 'disqus_recent_comments') || ($delta == 'disqus_top_commenters')) {
-      variable_set($delta . '_showavatars', $edit[$delta . '_showavatars']);
-      variable_set($delta . '_avatarsize', $edit[$delta . '_avatarsize']);
-    }
-    // The excerpt length is only available for recent comments and combination.
-    if (($delta == 'disqus_recent_comments') || ($delta == 'disqus_combination_widget')) {
-      variable_set($delta . '_excerpt_length', $edit[$delta . '_excerpt_length']);
-    }
-    // Combination widget has the color theme and the default tab view.
-    if ($delta == 'disqus_combination_widget') {
-      variable_set($delta . '_colortheme', $edit[$delta . '_colortheme']);
-      variable_set($delta . '_defaulttabview', $edit[$delta . '_defaulttabview']);
-    }
-    // Hide moderators appears in top commenters and combination widget.
-    if (($delta == 'disqus_top_commenters') || ($delta == 'disqus_combination_widget')) {
-      variable_set($delta . '_hide_mods', $edit[$delta . '_hide_mods']);
-    }
-  }
-}
-
-/**
- * Implements hook_block_view().
- */
-function disqus_block_view($delta = '') {
-  $options = array(
-    'num_items' => variable_get($delta . '_items', 5),
-    'avatars' => variable_get($delta . '_showavatars', TRUE) ? array('avatar_size' => variable_get($delta . '_avatarsize', 32)) : array('hide_avatars=1'),
-    'color' => variable_get($delta . '_colortheme', 'blue'),
-    'default_tab' => variable_get($delta . '_defaulttabview', 'people'),
-    'excerpt_length' => variable_get($delta . '_excerpt_length', '200'),
-    'hide_mods' => variable_get($delta . '_hide_mods', FALSE) ? '1' : '0',
-    'domain' => variable_get('disqus_domain', ''),
-  );
-  if (!empty($options['domain'])) {
-    $subject = '';
-    $content = '';
-    switch ($delta) {
-      case 'disqus_recent_comments':
-        $subject = t('Recent Comments');
-        $content = _disqus_block_content('recent_comments_widget', $options);
-        break;
-      case 'disqus_popular_threads':
-        $subject = t('Popular Threads');
-        $content = _disqus_block_content('popular_threads_widget', $options);
-        break;
-      case 'disqus_top_commenters':
-        $subject = t('Top Commenters');
-        $content = _disqus_block_content('top_commenters_widget', $options);
-        break;
-      case 'disqus_combination_widget':
-        $subject = t('Comments');
-        $content = _disqus_block_content('combination_widget', $options);
-        break;
-      case 'disqus_comments':
-        if (variable_get('disqus_location', 'content_area') == 'block' && user_access('view disqus comments')) {
-          if ($object = menu_get_object()) {
-            // For nodes, display if the Disqus object is enabled.
-            if (isset($object->disqus) && $object->disqus['status']) {
-              $content = array(
-                'disqus' => array(
-                  '#type' => 'disqus',
-                  '#disqus' => $object->disqus,
-                ),
-                '#cache' => array(
-                  'bin' => 'cache_block',
-                  'expire' => CACHE_TEMPORARY,
-                  'keys' => array(
-                    'disqus',
-                    'disqus_comments',
-                    'node',
-                    (int) $object->nid,
-                  ),
-                ),
-              );
-            }
-          }
-          else if ($object = menu_get_object('user')) {
-            if (isset($object->disqus)) {
-              $content = array(
-                'disqus' => array(
-                  '#type' => 'disqus',
-                  '#disqus' => $object->disqus,
-                ),
-                '#cache' => array(
-                  'bin' => 'cache_block',
-                  'expire' => CACHE_TEMPORARY,
-                  'keys' => array(
-                    'disqus',
-                    'disqus_comments',
-                    'user',
-                    (int) $object->uid,
-                  ),
-                ),
-              );
-            }
-          }
-        }
-        break;
-    }
-
-    return array('subject' => $subject, 'content' => $content);
-  }
-}
-
-/**
- * Helper function for disqus widget blocks content.
- *
- * @param $function
- *   Name of the function (widget) that needs to be returned. Same as widget
- *   API call name (w/o .json suffix).
- * @param $options
- *   Options array (query variables, domain, ...).
- * @return
- *   Render array that can be directly used for block content.
- */
-function _disqus_block_content($function, $options) {
-  $configuration = array(
-    'recent_comments_widget' => array(
-      'id' => 'dsq-recentcomments',
-      'query_items' => array('num_items', 'excerpt_length', 'avatars'),
-    ),
-    'popular_threads_widget' => array(
-      'id' => 'dsq-popthreads',
-      'query_items' => array('num_items'),
-    ),
-    'top_commenters_widget' => array(
-      'id' => 'dsq-topcommenters',
-      'query_items' => array('num_items', 'hide_mods', 'avatars'),
-    ),
-    'combination_widget' => array(
-      'id' => 'dsq-combinationwidget',
-      'query_items' => array('num_items', 'hide_mods', 'excerpt_length', 'color', 'default_tab'),
-    ),
-  );
-
-  if (empty($configuration[$function])) {
-    return FALSE;
-  }
-
-  $query = array();
-  foreach ($configuration[$function]['query_items'] as $query_item) {
-    if ($query_item == 'avatars') {
-      $query += $options[$query_item];
-    }
-    else {
-      $query[$query_item] = $options[$query_item];
-    }
-  }
-
-  return array(
-    'widget' => array(
-      '#theme' => 'html_tag',
-      '#tag' => 'script',
-      '#value' => '',
-      '#attributes' => array(
-        'type' => 'text/javascript',
-        'src' => url(
-          "//disqus.com/forums/${options['domain']}/$function.js",
-          array(
-            'external' => TRUE,
-            'query' => $query,
-          )
-        ),
-      ),
-    ),
-    '#theme_wrappers' => array('container'),
-    '#attributes' => array(
-      'id' => $configuration[$function]['id'],
-      'class' => array('dsq-widget'),
-    ),
-  );
-}
-
-/**
  * Implementation of hook_form_alter().
  */
 function disqus_form_alter(&$form, $form_state, $form_id) {
@@ -724,6 +423,19 @@ function disqus_form_alter(&$form, $form_state, $form_id) {
 }
 
 /**
+ * Implements hook_views_data_alter().
+ */
+function disqus_views_data_alter(&$data) {
+  // Number of Disqus comments made on the given node.
+  $data['node']['disqus_comment_count']['field'] = array(
+    'title' => t('Disqus Comment Count'),
+    'group' => t('Content'),
+    'help' => t('The number of Disqus comments made on the post. Note that this will not work in the preview.'),
+    'id' => 'disqus_comment_count',
+  );
+}
+
+/**
  * Implements hook_theme().
  */
 function disqus_theme() {
@@ -765,10 +477,3 @@ function disqus($user_api_key = NULL, $forum_api_key = NULL) {
   module_load_include('php', 'disqus', 'disqus');
   return new Disqus($user_api_key, $forum_api_key);
 }
-
-/**
- * Implementation of hook_views_api().
- */
-function disqus_views_api() {
-  return array('api' => 3);
-}
diff --git a/disqus.routing.yml b/disqus.routing.yml
new file mode 100644
index 0000000..3617194
--- /dev/null
+++ b/disqus.routing.yml
@@ -0,0 +1,17 @@
+disqus_admin:
+  path: 'admin/config/services/disqus'
+  _title: 'Disqus'
+  _description: 'Provides configuration options for the Disqus comment system.'
+  requirements:
+    _permission: administer disqus
+  defaults:
+    _form: 'disqus_admin_settings'
+
+disqus_close_window:
+  path: 'disqus/closewindow'
+  _title: Please wait
+  _description: 'Once the user logs in through the Disqus login workflow they are redirected here to automatically close the popup window.'
+  requirements:
+    _permission: access content
+  defaults:
+    _content: 'disqus_closewindow'
diff --git a/disqus.views.inc b/disqus.views.inc
deleted file mode 100644
index e836f6e..0000000
--- a/disqus.views.inc
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-/**
- * @file
- * Views integration with the Disqus module.
- */
-
-/**
- * Implements hook_views_data_alter().
- */
-function disqus_views_data_alter(&$data) {
-  // Number of Disqus comments made on the given node.
-  $data['node']['disqus_comment_count'] = array(
-    'field' => array(
-      'title' => t('Disqus Comment Count'),
-      'help' => t('The number of Disqus comments made on the post. Note that this will not work in the preview.'),
-      'handler' => 'views_handler_field_node_disqus_comment_count',
-    ),
-  );
-}
-
-/**
- * Field handler to present the number of Disqus comments on a node.
- */
-class views_handler_field_node_disqus_comment_count extends views_handler_field {
-  function init(&$view, &$options) {
-    parent::init($view, $options);
-  }
-
-  function query() {
-    // Override parent::query() without altering query.
-  }
-
-  /**
-   * When rendering the field.
-   */
-  function render($values) {
-    // Ensure Disqus comments are available on the node user has access to edit this node.
-    $node = node_load($values->nid);
-    if (user_access('view disqus comments') && isset($node->disqus)) {
-      // Extract the Disqus values.
-      $disqus = $node->disqus;
-      // Build a renderable array for the link.
-      $content = array(
-        '#theme' => 'link',
-        '#text' => t('Comments'),
-        '#path' => $disqus['identifier'],
-        '#options' => array(
-          'fragment' => 'disqus_thread',
-          'attributes' => array(
-            // Identify the node for Disqus with the unique identifier:
-            // http://docs.disqus.com/developers/universal/#comment-count
-            'data-disqus-identifier' => $disqus['identifier'],
-          ),
-          'html' => FALSE,
-        ),
-      );
-
-      /**
-       * This attaches disqus.js, which will look for the DOM variable
-       * disqusComments which is set below. When found, the disqus javascript
-       * api replaces the html element with the attribute:
-       * "data-disqus-identifier" and replaces the element with the number of
-       * comments on the node.
-       */
-      $content['#attached'] = array(
-        'js' => array(
-          array('data' => drupal_get_path('module', 'disqus') . '/disqus.js'),
-          array(
-            'data' => array('disqusComments' => $disqus['domain']),
-            'type' => 'setting',
-          ),
-        ),
-      );
-      return drupal_render($content);
-    }
-  }
-}
diff --git a/lib/Drupal/disqus/Controller/DisqusController.php b/lib/Drupal/disqus/Controller/DisqusController.php
new file mode 100644
index 0000000..82fb868
--- /dev/null
+++ b/lib/Drupal/disqus/Controller/DisqusController.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\disqus\Controller;
+
+use Drupal\Core\Controller\ControllerInterface;
+
+class DisqusController implements ControllerInterface {
+  /**
+   * Menu callback; Automatically closes the window after the user logs in.
+   *
+   * @return
+   *   Confirmation message and link that closes overlay window.
+   */
+  public function closeWindow() {
+    drupal_add_js('window.close();', 'inline');
+    return t('Thank you for logging in. Please close this window, or <a href="@clickhere">click here</a> to continue.', array('@clickhere' => 'javascript:window.close();'));
+  }
+}
diff --git a/lib/Drupal/disqus/Form/DisqusSettingsForm b/lib/Drupal/disqus/Form/DisqusSettingsForm
new file mode 100644
index 0000000..0c7728b
--- /dev/null
+++ b/lib/Drupal/disqus/Form/DisqusSettingsForm
@@ -0,0 +1,185 @@
+<?php
+
+namespace Drupal\disqus\Form;
+
+use Drupal\system\SystemConfigFormBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class DisqusSettingsForm extends SystemConfigFormBase {
+  /**
+   * Constructs a SiteInformationForm object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The factory for configuration objects.
+   * @param \Drupal\Core\Config\Context\ContextInterface $context
+   *   The configuration context used for this configuration object.
+   */
+  public function __construct(ConfigFactory $config_factory, ContextInterface $context) {
+    parent::__construct($config_factory, $context);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('config.context.free'),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $disqus_config = $this->configFactory->get('disqus.settings');
+
+    $form['disqus_domain'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Shortname'),
+      '#description' => t('The website shortname that you registered Disqus with. If you registered http://example.disqus.com, you would enter "example" here.'),
+      '#default_value' => $disqus_config->get('disqus_domain'),
+    );
+    $form['settings'] = array(
+      '#type' => 'vertical_tabs',
+      '#weight' => 50,
+    );
+    // Visibility settings.
+    $form['visibility'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Visibility'),
+      '#group' => 'settings',
+    );
+    $types = node_type_get_types();
+    $options = array();
+    foreach ($types as $type) {
+      $options[$type->type] = $type->name;
+    }
+    $form['visibility']['disqus_nodetypes'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Node Types'),
+      '#description' => t('Apply comments to only the following node types.'),
+      '#default_value' => $disqus_config->get('disqus_nodetypes'),
+      '#options' => $options,
+    );
+    $form['visibility']['disqus_location'] = array(
+      '#type' => 'select',
+      '#title' => t('Location'),
+      '#description' => t('Display the Disqus comments in the given location. When "Block" is selected, the comments will appear in the <a href="@disquscomments">Disqus Comments block</a>.', array('@disquscomments' => url('admin/structure/block'))),
+      '#default_value' => $disqus_config->get('disqus_location'),
+      '#options' => array(
+        'content_area' => t('Content Area'),
+        'block' => t('Block'),
+      ),
+    );
+    $form['visibility']['disqus_weight'] = array(
+      '#type' => 'select',
+      '#title' => t('Weight'),
+      '#description' => t('When the comments are displayed in the content area, you can change the position at which they will be shown.'),
+      '#default_value' => $disqus_config->get('disqus_weight'),
+      '#options' => drupal_map_assoc(array(-100, -75, -50, -25, 0, 25, 50, 75, 100)),
+      '#states' => array(
+        'visible' => array(
+          'select[name="disqus_location"]' => array('value' => 'content_area'),
+        ),
+      ),
+    );
+    // Behavior settings.
+    $form['behavior'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Behavior'),
+      '#group' => 'settings',
+    );
+    $form['behavior']['disqus_userapikey'] = array(
+      '#type' => 'textfield',
+      '#title' => t('User API Key'),
+      '#description' => t('The API key of the administrator account on Disqus. You can get yours <a href="@key">here</a>.', array('@key' => 'http://disqus.com/api/get_my_key/')),
+      '#default_value' => $disqus_config->get('disqus_userapikey'),
+    );
+    $form['behavior']['disqus_localization'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Localization support'),
+      '#description' => t("When enabled, overrides the language set by Disqus with the language provided by the site."),
+      '#default_value' => $disqus_config->get('disqus_localization'),
+    );
+    $form['behavior']['disqus_inherit_login'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Inherit User Credentials'),
+      '#description' => t("When enabled and a user is logged in, the Disqus 'Post as Guest' login form will be pre-filled with the user's name and email address."),
+      '#default_value' => $disqus_config->get('disqus_inherit_login'),
+    );
+    $form['behavior']['disqus_developer'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Testing'),
+      '#description' => t('When enabled, uses the <a href="http://docs.disqus.com/help/2/">disqus_developer</a> flag to tell Disqus that you are in a testing environment. Threads will not display on the public community page with this set.'),
+      '#default_value' => $disqus_config->get('disqus_developer'),
+    );
+    // Advanced settings.
+    $form['advanced'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Advanced'),
+      '#group' => 'settings',
+      '#description' => t('Use these settings to configure the more advanced uses of Disqus. You can find more information about these in the <a href="@applications">Applications</a> section of Disqus. To enable some of these features, you will require a <a href="@addons">Disqus Add-on Package</a>.', array(
+        '@applications' => 'http://disqus.com/api/applications/',
+        '@addons' => 'http://disqus.com/addons/',
+      )),
+    );
+    $form['advanced']['disqus_publickey'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Public Key'),
+      '#default_value' => $disqus_config->get('disqus_publickey'),
+    );
+    $form['advanced']['disqus_secretkey'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Secret Key'),
+      '#default_value' => $disqus_config->get('disqus_secretkey'),
+    );
+
+    $form['advanced']['sso'] = array(
+      '#weight' => 5,
+      '#type' => 'fieldset',
+      '#title' => t('Single Sign-on'),
+      '#collapsible' => FALSE,
+      '#collapsed' => FALSE,
+      '#states' => array(
+        'visible' => array(
+          'input[name="disqus_publickey"]' => array('empty' => FALSE),
+          'input[name="disqus_secretkey"]' => array('empty' => FALSE),
+        ),
+      ),
+    );
+    $form['advanced']['sso']['disqus_sso'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Use Single Sign-On'),
+      '#description' => t('Provide <a href="@sso">Single Sign-On</a> access to your site.', array(
+        '@sso' => 'http://disqus.com/api/sso/',
+      )),
+      '#default_value' => $disqus_config->get('disqus_sso'),
+    );
+    $form['advanced']['sso']['disqus_use_site_logo'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Use Site Logo'),
+      '#description' => t('Pass the site logo to Disqus for use as SSO login button.'),
+      '#default_value' => $disqus_config->get('disqus_use_site_logo'),
+      '#states' => array(
+        'disabled' => array(
+          'input[name="disqus_sso"]' => array('checked' => FALSE),
+        ),
+      ),
+    );
+    $form['advanced']['sso']['disqus_logo'] = array(
+      '#type' => 'managed_file',
+      '#title' => t('Custom Logo'),
+      '#upload_location' => 'public://images',
+      '#default_value' => $disqus_config->get('disqus_logo'),
+      '#states' => array(
+        'disabled' => array(
+          'input[name="disqus_sso"]' => array('checked' => FALSE),
+        ),
+        'visible' => array(
+          'input[name="disqus_use_site_logo"]' => array('checked' => FALSE),
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/Block/DisqusBaseBlock.php b/lib/Drupal/disqus/Plugin/Block/DisqusBaseBlock.php
new file mode 100644
index 0000000..855792b
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/Block/DisqusBaseBlock.php
@@ -0,0 +1,211 @@
+<?php
+
+namespace Drupal\disqus\Plugin\Block;
+
+use Drupal\block\BlockBase;
+
+abstract class DisqusBaseBlock extends BlockBase {
+  /**
+   * Overrides \Drupal\block\BlockBase::settings().
+   */
+  public function settings() {
+    return array(
+      'cache' => DRUPAL_CACHE_GLOBAL,
+    );
+  }
+
+  /**
+   * Helper method to get configuration value.
+   *
+   * @param  string $key
+   * @param  mixed  $default_value
+   * @return mixed
+   */
+  protected function configuration($key, $default_value = NULL) {
+    if (isset($this->configuration[$key])) {
+      return $this->configuration[$key];
+    }
+
+    if (!is_null($default_value)) {
+      return $default_value;
+    }
+
+    throw new UnexpectedValueException('Missing default value for ' . $key);
+  }
+
+  /**
+   * Overrides \Drupal\block\BlockBase::blockForm().
+   */
+  public function blockForm($form, &$form_state) {
+    return $this->_blockForm($form, $form_state, $this->id);
+  }
+
+  /**
+   * Helper for blockForm() method.
+   */
+  public function _blockForm($form, &$form_state, $delta) {
+    $form['disqus'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Disqus settings'),
+      '#tree' => TRUE,
+    );
+
+    $form['disqus'][$delta . '_items'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of items to show'),
+      '#options' => array(1 => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20),
+      '#default_value' => $this->configuration($delta .'_items', 5),
+      '#access' => TRUE,
+    );
+    $form['disqus'][$delta . '_showavatars'] = array(
+      '#type' => 'select',
+      '#title' => t('Show avatars'),
+      '#options' => array(FALSE => t('No'), TRUE => t('Yes')),
+      '#default_value' => $this->configuration($delta .'_showavatars', TRUE),
+      '#access' => ($delta == 'disqus_recent_comments') || ($delta == 'disqus_top_commenters'),
+    );
+    $form['disqus'][$delta . '_avatarsize'] = array(
+      '#type' => 'select',
+      '#title' => t('Avatar size'),
+      '#options' => array(
+        24 => t('X-Small (24px)'),
+        32 => t('Small (32px)'),
+        48 => t('Medium (48px)'),
+        92 => t('Large (92px)'),
+        128 => t('X-Large (128px)'),
+      ),
+      '#default_value' => $this->configuration($delta .'_avatarsize', 32),
+      '#access' => ($delta == 'disqus_recent_comments') || ($delta == 'disqus_top_commenters'),
+    );
+    $form['disqus'][$delta . '_colortheme'] = array(
+      '#type' => 'select',
+      '#title' => t('Color Theme'),
+      '#options' => array(
+        'blue' => t('Blue'),
+        'grey' => t('Grey'),
+        'green' => t('Green'),
+        'red' => t('Red'),
+        'orange' => t('Orange'),
+      ),
+      '#default_value' => $this->configuration($delta .'_colortheme', 'blue'),
+      '#access' => $delta == 'disqus_combination_widget',
+    );
+    $form['disqus'][$delta . '_defaulttabview'] = array(
+      '#type' => 'select',
+      '#title' => t('Default Tab View'),
+      '#options' => array(
+        'people' => t('People'),
+        'recent' => t('Recent'),
+        'popular' => t('Popular'),
+      ),
+      '#default_value' => $this->configuration($delta .'_defaulttabview', 'people'),
+      '#access' => $delta == 'disqus_combination_widget',
+    );
+    $form['disqus'][$delta . '_excerpt_length'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Comment Except Length'),
+      '#default_value' => $this->configuration($delta .'_excerpt_length', '200'),
+      '#access' => ($delta == 'disqus_recent_comments') || ($delta == 'disqus_combination_widget'),
+      '#size' => 4,
+    );
+    $form['disqus'][$delta . '_hide_mods'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Hide moderators in ranking'),
+      '#default_value' => $this->configuration($delta .'_hide_mods', FALSE),
+      '#access' => ($delta == 'disqus_top_commenters') || ($delta == 'disqus_combination_widget'),
+    );
+    return $form;
+  }
+
+  /**
+   * Overrides \Drupal\block\BlockBase::blockSubmit().
+   */
+  public function blockSubmit($form, &$form_state) {
+    foreach ($form_state['values']['disqus'] as $k => $v) {
+      if ($form['disqus'][$K]['#access']) {
+        $this->configuration[$k] = $v;
+      }
+    }
+  }
+
+  protected function getOptions() {
+    return array(
+      'num_items' => $this->configuration($delta . '_items', 5),
+      'avatars' => $this->configuration($delta . '_showavatars', TRUE) ? array('avatar_size' => $this->configuration($delta . '_avatarsize', 32)) : array('hide_avatars=1'),
+      'color' => $this->configuration($delta . '_colortheme', 'blue'),
+      'default_tab' => $this->configuration($delta . '_defaulttabview', 'people'),
+      'excerpt_length' => $this->configuration($delta . '_excerpt_length', '200'),
+      'hide_mods' => $this->configuration($delta . '_hide_mods', FALSE) ? '1' : '0',
+      'domain' => $this->configuration('disqus_domain', ''),
+    );
+  }
+
+  /**
+   * Helper function for disqus widget blocks content.
+   *
+   * @param $function
+   *   Name of the function (widget) that needs to be returned. Same as widget
+   *   API call name (w/o .json suffix).
+   * @param $options
+   *   Options array (query variables, domain, ...).
+   * @return
+   *   Render array that can be directly used for block content.
+   */
+  function render($function, $options) {
+    $configuration = array(
+      'recent_comments_widget' => array(
+        'id' => 'dsq-recentcomments',
+        'query_items' => array('num_items', 'excerpt_length', 'avatars'),
+      ),
+      'popular_threads_widget' => array(
+        'id' => 'dsq-popthreads',
+        'query_items' => array('num_items'),
+      ),
+      'top_commenters_widget' => array(
+        'id' => 'dsq-topcommenters',
+        'query_items' => array('num_items', 'hide_mods', 'avatars'),
+      ),
+      'combination_widget' => array(
+        'id' => 'dsq-combinationwidget',
+        'query_items' => array('num_items', 'hide_mods', 'excerpt_length', 'color', 'default_tab'),
+      ),
+    );
+
+    if (empty($configuration[$function])) {
+      return FALSE;
+    }
+
+    $query = array();
+    foreach ($configuration[$function]['query_items'] as $query_item) {
+      if ($query_item == 'avatars') {
+        $query += $options[$query_item];
+      }
+      else {
+        $query[$query_item] = $options[$query_item];
+      }
+    }
+
+    return array(
+      'widget' => array(
+        '#theme' => 'html_tag',
+        '#tag' => 'script',
+        '#value' => '',
+        '#attributes' => array(
+          'type' => 'text/javascript',
+          'src' => url(
+            "//disqus.com/forums/${options['domain']}/$function.js",
+            array(
+              'external' => TRUE,
+              'query' => $query,
+            )
+          ),
+        ),
+      ),
+      '#theme_wrappers' => array('container'),
+      '#attributes' => array(
+        'id' => $configuration[$function]['id'],
+        'class' => array('dsq-widget'),
+      ),
+    );
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/Block/DisqusCombinationWidgetBlock.php b/lib/Drupal/disqus/Plugin/Block/DisqusCombinationWidgetBlock.php
new file mode 100644
index 0000000..3b8837a
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/Block/DisqusCombinationWidgetBlock.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\disqus\Plugin\Block;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ *
+ * @Plugin(
+ *   id = "disqus_combination_widget",
+ *   admin_label = @Translation("Disqus: Combination Widget"),
+ *   module = "disqus"
+ * )
+ */
+class DisqusCombinationWidgetBlock extends DisqusBaseBlock {
+  protected $id = 'disqus_combination_widget';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    return array(
+      '#title' => t('Comments'),
+      $this->render('combination_widget')
+    );
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/Block/DisqusCommentsBlock.php b/lib/Drupal/disqus/Plugin/Block/DisqusCommentsBlock.php
new file mode 100644
index 0000000..c8a1cae
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/Block/DisqusCommentsBlock.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace Drupal\disqus\Plugin\Block;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ *
+ * @Plugin(
+ *   id = "disqus_comments",
+ *   admin_label = @Translation("Disqus: Comments"),
+ *   module = "disqus"
+ * )
+ */
+class DisqusCommentsBlock extends DisqusBaseBlock {
+  protected $id = 'disqus_comments';
+
+  /**
+   * Overrides DisqusBaseBlock::settings().
+   */
+  public function settings() {
+    return array(
+      'cache' => DRUPAL_CACHE_CUSTOM,
+    );
+  }
+
+  /**
+   * Overrides DisqusBaseBlock::blockForm().
+   */
+  public function blockForm($form, &$form_state) {
+    $form['disqus'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Disqus settings'),
+      '#tree' => TRUE,
+    );
+
+    if ($delta == 'disqus_comments') {
+      $form['disqus']['#description'] = t('This block will be used to display the comments from Disqus when comments are applied to the given page. Visit the <a href="@disqussettings">Disqus settings</a> to configure when this is visible.', array('@disqussettings' => url('admin/config/services/disqus')));
+    }
+  }
+
+  /**
+   * Overrides DisqusBaseBlock::blockSubmit().
+   */
+  public function blockSubmit($form, &$form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    if (variable_get('disqus_location', 'content_area') == 'block' && user_access('view disqus comments')) {
+      if ($object = menu_get_object()) {
+        return $this->buildForNodeEntity($object);
+      }
+
+      if ($object = menu_get_object('user')) {
+        return  $this->buildForUserEntity($object);
+      }
+    }
+  }
+
+  /**
+   * Build the disqus comment block for node entity.
+   */
+  protected function buildForNodeEntity($object) {
+      // For nodes, display if the Disqus object is enabled.
+      if (isset($object->disqus) && $object->disqus['status']) {
+        return array(
+          'disqus' => array(
+            '#type' => 'disqus',
+            '#disqus' => $object->disqus,
+          ),
+          '#cache' => array(
+            'bin' => 'cache_block',
+            'expire' => CACHE_TEMPORARY,
+            'keys' => array(
+              'disqus',
+              'disqus_comments',
+              'node',
+              (int) $object->nid,
+            ),
+          ),
+        );
+      }
+  }
+
+  /**
+   * Build the disqus comment block for user entity.
+   */
+  protected function buildForUserEntity($object) {
+    if (isset($object->disqus)) {
+      return array(
+        'disqus' => array(
+          '#type' => 'disqus',
+          '#disqus' => $object->disqus,
+        ),
+        '#cache' => array(
+          'bin' => 'cache_block',
+          'expire' => CACHE_TEMPORARY,
+          'keys' => array(
+            'disqus',
+            'disqus_comments',
+            'user',
+            (int) $object->uid,
+          ),
+        ),
+      );
+    }
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/Block/DisqusPopularThreadsBlock.php b/lib/Drupal/disqus/Plugin/Block/DisqusPopularThreadsBlock.php
new file mode 100644
index 0000000..ba749b0
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/Block/DisqusPopularThreadsBlock.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\disqus\Plugin\Block;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ *
+ * @Plugin(
+ *   id = "disqus_popular_threads",
+ *   admin_label = @Translation("Disqus: Popular Threads"),
+ *   module = "disqus"
+ * )
+ */
+class DisqusPopularThreadsBlock extends DisqusBaseBlock {
+  protected $id = 'disqus_popular_threads';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    return array(
+      '#title' => t('Popular Threads'),
+      $this->render('popular_threads_widget')
+    );
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/Block/DisqusRecentCommentBlock.php b/lib/Drupal/disqus/Plugin/Block/DisqusRecentCommentBlock.php
new file mode 100644
index 0000000..3cb1dcb
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/Block/DisqusRecentCommentBlock.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\disqus\Plugin\Block;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ *
+ * @Plugin(
+ *   id = "disqus_recent_comments",
+ *   admin_label = @Translation("Disqus: Recent Comments"),
+ *   module = "disqus"
+ * )
+ */
+class DisqusRecentCommentBlock extends DisqusBaseBlock {
+  protected $id = 'disqus_recent_comments';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    return array(
+      '#title' => t('Recent Comments'),
+      $this->render('recent_comments_widget')
+    );
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/Block/DisqusTopCommentersBlock.php b/lib/Drupal/disqus/Plugin/Block/DisqusTopCommentersBlock.php
new file mode 100644
index 0000000..8adc78d
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/Block/DisqusTopCommentersBlock.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\disqus\Plugin\Block;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ *
+ * @Plugin(
+ *   id = "disqus_top_commenters",
+ *   admin_label = @Translation("Disqus: Top Commenters"),
+ *   module = "disqus"
+ * )
+ */
+class DisqusTopCommentersBlock extends DisqusBaseBlock {
+  protected $id = 'disqus_top_commenters';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    return array(
+      '#title' => t('Top Commenters'),
+      $this->render('top_commenters_widget')
+    );
+  }
+}
diff --git a/lib/Drupal/disqus/Plugin/views/field/DisqusCommentCount.php b/lib/Drupal/disqus/Plugin/views/field/DisqusCommentCount.php
new file mode 100644
index 0000000..445050e
--- /dev/null
+++ b/lib/Drupal/disqus/Plugin/views/field/DisqusCommentCount.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\disqus\Plugin\views\field;
+
+use Drupal\views\Plugin\views\field\FieldPluginBase;
+use Drupal\Component\Annotation\PluginID;
+use Drupal\views\ResultRow;
+
+/**
+ * Field handler to present the number of Disqus comments on a node.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @PluginID("disqus_comment_count")
+ */
+class DisqusCommentCount extends FieldPluginBase {
+  /**
+   * {@inheritdoc}
+   */
+  function render(ResultRow $values) {
+    // Ensure Disqus comments are available on the node user has access to edit this node.
+    $node = node_load($values->nid);
+
+    if (!user_access('view disqus comments') || isset($node->disqus)) {
+      reutrn;
+    }
+
+    // Extract the Disqus values.
+    $disqus = $node->disqus;
+
+    // Build a renderable array for the link.
+    $content = array(
+      '#theme' => 'link',
+      '#text' => t('Comments'),
+      '#path' => $disqus['identifier'],
+      '#options' => array(
+        'fragment' => 'disqus_thread',
+        'attributes' => array(
+          // Identify the node for Disqus with the unique identifier:
+          // http://docs.disqus.com/developers/universal/#comment-count
+          'data-disqus-identifier' => $disqus['identifier'],
+        ),
+        'html' => FALSE,
+      ),
+    );
+
+    /**
+     * This attaches disqus.js, which will look for the DOM variable
+     * disqusComments which is set below. When found, the disqus javascript
+     * api replaces the html element with the attribute:
+     * "data-disqus-identifier" and replaces the element with the number of
+     * comments on the node.
+     */
+    $content['#attached'] = array(
+      'js' => array(
+        array('data' => drupal_get_path('module', 'disqus') . '/disqus.js'),
+        array(
+          'data' => array('disqusComments' => $disqus['domain']),
+          'type' => 'setting',
+        ),
+      ),
+    );
+
+    return drupal_render($content);
+  }
+}
