Index: google_cse_adv.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/google_cse_adv/google_cse_adv.module,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 google_cse_adv.module
--- google_cse_adv.module	15 Sep 2010 17:18:12 -0000	1.1.2.2
+++ google_cse_adv.module	23 Sep 2010 00:53:40 -0000
@@ -252,6 +252,13 @@ function google_cse_adv_response_results
 
   if (isset($xml->RES->R)) {
     foreach($xml->RES->R as $result) {
+
+      // Process subscribed links/promotions
+      if (isset($result->SL_RESULTS)) {
+        google_cse_adv_result_subscribed_links($result);
+        continue; // to next result.
+      }
+
       // Markup is escaped in title - we have to strip it (and strip_tags is not enough protection for XSS)
 	  $title = strip_tags((string)$result->T);
       $title = function_exists('htmlspecialchars_decode') ? htmlspecialchars_decode($title, ENT_QUOTES) : $title;
@@ -271,6 +278,48 @@ function google_cse_adv_response_results
   return $results;
 }
 
+/**
+ * Extract/return the promotions/subscribed links from a result.
+ *
+ * @param $result
+ *   XML object for an <R> element
+ * @return Either:
+ *   a) All stored values, if $result is NULL.
+ *   b) Nothing, if processing a result.
+ */
+function google_cse_adv_result_subscribed_links($result = NULL) {
+  static $subscribed_links = array();
+
+  if (is_null($result)) {
+    return $subscribed_links;
+  }
+
+  foreach ($result->SL_RESULTS->SL_MAIN as $sl) {
+    // Subscribed link titles are XML objects and may have child
+    // nodes, so convert to XML (XHTML) in order to process them.
+    $subscribed_link = array(
+      'xml' => $sl,
+      'title' => check_plain(strip_tags($sl->T->asXML())),
+      'href' => check_url((string)$sl->U),
+      'body' => array(),
+    );
+    if (isset($sl->BODY_LINE)) {
+      foreach ($sl->BODY_LINE as $body) {
+        if (isset($body->BLOCK)) {
+          foreach ($body->BLOCK as $block) {
+            $subscribed_link['body'][] = array(
+              'xml' => $block,
+              'title' => check_plain(strip_tags($block->T->asXML())),
+              'href' => check_url((string)$block->U),
+            );
+          }
+        }
+      }
+    }
+    $subscribed_links[] = $subscribed_link;
+  }
+}
+
 function google_cse_adv_response_filters($response) {
   $xml = simplexml_load_string($response);
   $results = array();
@@ -364,7 +413,10 @@ function google_cse_adv_block($op = 'lis
         ),
         1 => array(
           'info' => t('Google CSE Advanced suggestion')
-        )
+        ),
+        2 => array(
+          'info' => t('Google CSE Advanced subscribed links / promotions')
+        ),
       );
 
     case 'view';
@@ -374,6 +426,9 @@ function google_cse_adv_block($op = 'lis
 
         case 1:
           return google_cse_adv_block_suggestion();
+
+        case 2:
+          return google_cse_adv_block_subscribed_links();
       }
   }
 }
@@ -383,16 +438,9 @@ function google_cse_adv_block_filters() 
     'subject' => t('Filters'),
     'content' => ''
   );
-  $keys = search_get_keys();
-
-  // Execute only if there is something to be found (keys)
-  // Note: search_get_keys() can return result even if there
-  // was no search request so we have to check if we are on
-  // the search page.
-  if (user_access('search content') && $keys
-      && arg(0) == 'search' && arg(1) == 'google_cse_adv') {
+  if (google_cse_adv_block_access()) {
     $page = isset($_GET['page']) ? $_GET['page'] : 0;
-    $response = google_cse_adv_execute($keys, $page);
+    $response = google_cse_adv_execute(search_get_keys(), $page);
     $block['content'] = google_cse_adv_response_filters($response);
   }
   return $block;
@@ -403,22 +451,46 @@ function google_cse_adv_block_suggestion
     'subject' => t('Spelling Suggestion'),
     'content' => ''
   );
-  $keys = search_get_keys();
-
-  // Execute only if there is something to be found (keys)
-  // Note: search_get_keys() can return result even if there
-  // was no search request so we have to check if we are on
-  // the search page.
-  if (user_access('search content') && $keys
-      && arg(0) == 'search' && arg(1) == 'google_cse_adv') {
+  if (google_cse_adv_block_access()) {
     $page = isset($_GET['page']) ? $_GET['page'] : 0;
-    $response = google_cse_adv_execute($keys, $page);
+    $response = google_cse_adv_execute(search_get_keys(), $page);
     $block['content'] = google_cse_adv_response_suggestion($response);
   }
   return $block;
 }
 
 /**
+ * Block view for Subscribed links / Promotions
+ */
+function google_cse_adv_block_subscribed_links() {
+  $block = array(
+    'subject' => t('Subscribed links / Promotions'),
+    'content' => ''
+  );
+  if (google_cse_adv_block_access()) {
+    $page = isset($_GET['page']) ? $_GET['page'] : 0;
+    $response = google_cse_adv_execute(search_get_keys(), $page);
+    $data = google_cse_adv_result_subscribed_links();
+    $block['content'] = theme('google_cse_adv_subscribed_links', $data);
+  }
+  return $block;
+}
+
+/**
+ * Helper for block functions, so that we execute block code
+ * only if there is something to be found (keys).
+ * Note: search_get_keys() can return a result even if there
+ * was no search request so we have to check if we are on
+ * the search page.
+ */
+function google_cse_adv_block_access() {
+  return arg(0) == 'search'
+    && arg(1) == 'google_cse_adv'
+    && search_get_keys()
+    && user_access('search content');
+}
+
+/**
  * Returns "hl" language param for search request.
  *
  * @return string
@@ -532,6 +604,9 @@ function google_cse_adv_theme($existing,
     'google_cse_adv_suggestion' => array(
       'arguments' => array('suggestions' => NULL)
     ),
+    'google_cse_adv_subscribed_links' => array(
+      'arguments' => array('links' => NULL),
+    ),
   );
 }
 
@@ -553,3 +628,37 @@ function theme_google_cse_adv_suggestion
   $output .= theme('item_list', $items);
   return $output;
 }
+
+/**
+ * Theme a set of subscribed links or promotions.
+ *
+ * Both cases are combined within the same XML sub-tree, the format of
+ * which allows for more content than the Promotions editor provides
+ * for. In particular, the 'description' field of a promotion is
+ * returned as the title (with no associated URL) for one of
+ * potentially many links.
+ *
+ * @param array $links
+ *   Subscribed links / Promotions.
+ * @return string
+ */
+function theme_google_cse_adv_subscribed_links($links = NULL) {
+  if (empty($links)) {
+    return '';
+  }
+  $items = array();
+
+  foreach ($links as $link) {
+    $item = array('data' => l($link['title'], $link['href']));
+    foreach ($link['body'] as $body_link) {
+      $child = $body_link['href']
+        ? l($body_link['title'], $body_link['href'])
+        : '<span>' . $body_link['title'] . '</span>';
+      $item['children'][] = $child;
+    }
+    $item['children'][] = l($link['href'], $link['href']);
+    $items[] = $item;
+  }
+  $attributes = array('class' => 'subscribed-links');
+  return theme('item_list', $items, NULL, 'ul', $attributes);
+}
