function tableOfContents($zones = null, $options = array())
 {
     $defOpt = array('group' => '<ul>%items%</ul>', 'item' => '<li><a href="%anchor%">%title%</a></li>', 'titleGroup' => '<ul>%groups%</ul>', 'title' => '<li><p>%title%</p>%items%</li>');
     $opt = array_merge($defOpt, $options);
     if (empty($zones)) {
         $zones = array_keys($this->boxes_by_zone);
     }
     if (!is_array($zones)) {
         $zones = array($zones);
     }
     $named = !set::numeric(array_keys($zones));
     $out = '';
     foreach ($zones as $key => $id) {
         $links = '';
         if (!empty($this->boxes_by_zone[$id])) {
             foreach ($this->boxes_by_zone[$id] as $box) {
                 if (!empty($box['NewsletterBox']['data']['title'])) {
                     $anchor = '#' . strtolower(Inflector::slug($box['NewsletterBox']['data']['title'])) . '_' . $box['NewsletterBox']['id'];
                     $replace = array('%anchor%' => $anchor, '%title%' => $box['NewsletterBox']['data']['title']);
                     $links .= str_replace(array_keys($replace), array_values($replace), $opt['item']);
                 }
             }
             if (!empty($links)) {
                 $links = str_replace('%items%', $links, $opt['group']);
             }
         }
         if (!empty($links) && $named) {
             $replace = array('%title%' => $key, '%items%' => $links);
             $out .= str_replace(array_keys($replace), array_values($replace), $opt['title']);
         } else {
             $out .= $links;
         }
     }
     if (!empty($out) && $named) {
         $out = str_replace('%groups%', $out, $opt['titleGroup']);
     }
     return $out;
 }