From 331c0c997256e02db6b76798572b1f22d8e2731f Mon Sep 17 00:00:00 2001
From: Colan Schwartz <colan@58704.no-reply.drupal.org>
Date: Mon, 9 May 2016 15:37:25 -0400
Subject: [PATCH] Issue #2720439 by colan: Added site autocompletion & ID
 retrieval without loading.

---
 site/hosting_site.module | 105 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 89 insertions(+), 16 deletions(-)

diff --git a/site/hosting_site.module b/site/hosting_site.module
index 3e137fe..770f98a 100644
--- a/site/hosting_site.module
+++ b/site/hosting_site.module
@@ -69,6 +69,13 @@ function hosting_site_menu() {
     'access arguments' => array('access content'),
   );
 
+  // Provide autocomplete functionality.
+  $items['hosting/sites/autocomplete'] = array(
+    'title' => 'Autocomplete for sites',
+    'page callback' => 'hosting_site_autocomplete_sites',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
 
   $items['node/%node/goto_site'] = array(
     'page callback' => 'hosting_site_goto',
@@ -115,6 +122,38 @@ function _hosting_site_form_check() {
 }
 
 /**
+ * Retrieve autocomplete suggestions for site names.
+ *
+ * @param $string
+ *   The typed string so far, as typed in by the user.
+ *
+ * @return
+ *   A list of matching sites in JSON format.
+ */
+function hosting_site_autocomplete_sites($string) {
+
+  // Fetch the list of matching active sites. Don't use EntityFieldQuery here
+  // as we'd have to load the nodes afterwards to get the field data. db_query()
+  // is much faster as we can get away with only one DB query overall.
+  $query = db_select('node', 'n');
+  $query->join('hosting_site', 's', 's.nid = n.nid');
+  $results = $query->fields('n', array('nid', 'title'))
+    ->condition('n.type', 'site')
+    ->condition('n.title', '%' . db_like($string) . '%', 'LIKE')
+    ->condition('s.status', HOSTING_SITE_DELETED, '!=')
+    ->execute();
+
+  // Save each result to the list.
+  $matches = array();
+  foreach ($results as $row) {
+    $matches[$row->title] = check_plain($row->title);
+  }
+
+  // Return the results as a JSON list.
+  drupal_json_output($matches);
+}
+
+/**
  * Menu wildcard loader callback.
  *
  * Loads a hosting_site node.
@@ -473,9 +512,25 @@ function hosting_get_sites_by_status($platform, $status) {
 }
 
 /**
- * Retrieve a node based on the url
- */
-function hosting_get_site_by_url($url) {
+ * Retrieves a site node based on the URL.
+ *
+ * @param string $url
+ *   The URL of the site.
+ * @param boolean $loaded_object
+ *   Determines if a loaded site should be returned, or just the node ID.
+ *   Defaults to TRUE.
+ * @return
+ *   If $loaded_object is TRUE, a fully loaded site node object.
+ *   If $loaded_object is FALSE, the node ID of the site.
+ *   In either case, if the site can't be found, FALSE is returned.
+ * @todo
+ *   Remove the db_query() and have only one EntityFieldQuery. The two
+ *   different cases will be handled by adding different conditions/tags to it.
+ *   A new tag will have to be added to match the title for aliases. These tags
+ *   won't be necessary in Drupal 8 as JOINs are natively supported. See
+ *   https://www.drupal.org/node/1882418 for details.
+ */
+function hosting_get_site_by_url($url, $loaded_object = TRUE) {
   // If the Aliases feature is enabled, try and get the site by its alias too
   if (hosting_feature('alias')) {
     $nid = db_query("SELECT n.nid
@@ -495,26 +550,44 @@ function hosting_get_site_by_url($url) {
                     ))->fetchField();
   }
   else {
-    $nid = db_query("SELECT n.nid
-                       FROM {node} n
-                       JOIN {hosting_site} h
-                         ON n.nid = h.nid
-                      WHERE n.title = :title
-                        AND n.type = :type
-                        AND NOT (h.status = :status)",
-                    array(
-                      ':title' => $url,
-                      ':type' => 'site',
-                      ':status' => HOSTING_SITE_DELETED,
-                    ))->fetchField();
+    // Get the list of node IDs whose title matches the name.
+    $query = new EntityFieldQuery();
+    $entities = $query->entityCondition('entity_type', 'node')
+      ->propertyCondition('type', 'site')
+      ->propertyCondition('title', $url)
+      ->addTag('site_not_deleted')
+      ->range(0, 1)
+      ->execute();
+
+    // Get the ID of the single node, if there is one.
+    if (!empty($entities['node'])) {
+      // Fetch the node ID from the list and return it.
+      $nids = array_keys($entities['node']);
+      $nid = array_shift($nids);
+    }
+    else {
+      $nid = FALSE;
+    }
   }
   if ($nid) {
-    return node_load($nid);
+    return $loaded_object ? node_load($nid) : $nid;
   }
   return FALSE;
 }
 
 /**
+ * Implements hook_query_TAG_alter() for the 'site_not_deleted' tag.
+ *
+ * Ensures that only non-deleted sites are returned in the query.
+ *
+ * @see hosting_get_site_by_url()
+ */
+function hosting_site_query_site_not_deleted_alter(QueryAlterableInterface $query) {
+  $query->join('hosting_site', 'site', 'site.nid = node.nid');
+  $query->condition('site.status', HOSTING_SITE_DELETED, '!=');
+}
+
+/**
  * Define the status types of a site
  */
 function _hosting_site_status($node) {
-- 
2.5.0

