18c2e8227SGreg Roach<?php 28c2e8227SGreg Roach/** 38c2e8227SGreg Roach * webtrees: online genealogy 4369c0ce6SGreg Roach * Copyright (C) 2016 webtrees development team 58c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify 68c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by 78c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or 88c2e8227SGreg Roach * (at your option) any later version. 98c2e8227SGreg Roach * This program is distributed in the hope that it will be useful, 108c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 118c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 128c2e8227SGreg Roach * GNU General Public License for more details. 138c2e8227SGreg Roach * You should have received a copy of the GNU General Public License 148c2e8227SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 158c2e8227SGreg Roach */ 1676692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module; 1776692c8bSGreg Roach 180e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth; 190e62c4b8SGreg Roachuse Fisharebest\Webtrees\Filter; 203d7a8a4cSGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsDate; 213d7a8a4cSGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsEdit; 220e62c4b8SGreg Roachuse Fisharebest\Webtrees\GedcomTag; 230e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 240e62c4b8SGreg Roachuse Fisharebest\Webtrees\Module; 250e62c4b8SGreg Roachuse Fisharebest\Webtrees\Site; 260e62c4b8SGreg Roachuse Fisharebest\Webtrees\Stats; 270e62c4b8SGreg Roachuse Fisharebest\Webtrees\Theme; 280e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 298c2e8227SGreg Roach 308c2e8227SGreg Roach/** 318c2e8227SGreg Roach * Class HtmlBlockModule 328c2e8227SGreg Roach */ 33e2a378d3SGreg Roachclass HtmlBlockModule extends AbstractModule implements ModuleBlockInterface { 348c2e8227SGreg Roach /** {@inheritdoc} */ 358c2e8227SGreg Roach public function getTitle() { 368c2e8227SGreg Roach return /* I18N: Name of a module */ I18N::translate('HTML'); 378c2e8227SGreg Roach } 388c2e8227SGreg Roach 398c2e8227SGreg Roach /** {@inheritdoc} */ 408c2e8227SGreg Roach public function getDescription() { 418c2e8227SGreg Roach return /* I18N: Description of the “HTML” module */ I18N::translate('Add your own text and graphics.'); 428c2e8227SGreg Roach } 438c2e8227SGreg Roach 4476692c8bSGreg Roach /** 4576692c8bSGreg Roach * Generate the HTML content of this block. 4676692c8bSGreg Roach * 4776692c8bSGreg Roach * @param int $block_id 4876692c8bSGreg Roach * @param bool $template 49727f238cSGreg Roach * @param string[] $cfg 5076692c8bSGreg Roach * 5176692c8bSGreg Roach * @return string 5276692c8bSGreg Roach */ 5376692c8bSGreg Roach public function getBlock($block_id, $template = true, $cfg = array()) { 548c2e8227SGreg Roach global $ctype, $WT_TREE; 558c2e8227SGreg Roach 56e2a378d3SGreg Roach $title = $this->getBlockSetting($block_id, 'title'); 57e2a378d3SGreg Roach $html = $this->getBlockSetting($block_id, 'html'); 58e2a378d3SGreg Roach $gedcom = $this->getBlockSetting($block_id, 'gedcom'); 59e2a378d3SGreg Roach $show_timestamp = $this->getBlockSetting($block_id, 'show_timestamp', '0'); 60e2a378d3SGreg Roach $languages = $this->getBlockSetting($block_id, 'languages'); 618c2e8227SGreg Roach 628c2e8227SGreg Roach // Only show this block for certain languages 638c2e8227SGreg Roach if ($languages && !in_array(WT_LOCALE, explode(',', $languages))) { 648c2e8227SGreg Roach return ''; 658c2e8227SGreg Roach } 668c2e8227SGreg Roach 678c2e8227SGreg Roach /* 688c2e8227SGreg Roach * Select GEDCOM 698c2e8227SGreg Roach */ 708c2e8227SGreg Roach switch ($gedcom) { 718c2e8227SGreg Roach case '__current__': 728c2e8227SGreg Roach $stats = new Stats($WT_TREE); 738c2e8227SGreg Roach break; 748c2e8227SGreg Roach case '__default__': 75ef2fd529SGreg Roach $tree = Tree::findByName(Site::getPreference('DEFAULT_GEDCOM')); 76ef2fd529SGreg Roach if ($tree) { 77ef2fd529SGreg Roach $stats = new Stats($tree); 788c2e8227SGreg Roach } else { 798c2e8227SGreg Roach $stats = new Stats($WT_TREE); 808c2e8227SGreg Roach } 818c2e8227SGreg Roach break; 828c2e8227SGreg Roach default: 83ef2fd529SGreg Roach $tree = Tree::findByName($gedcom); 84ef2fd529SGreg Roach if ($tree) { 85ef2fd529SGreg Roach $stats = new Stats($tree); 868c2e8227SGreg Roach } else { 878c2e8227SGreg Roach $stats = new Stats($WT_TREE); 888c2e8227SGreg Roach } 898c2e8227SGreg Roach break; 908c2e8227SGreg Roach } 918c2e8227SGreg Roach 928c2e8227SGreg Roach /* 938c2e8227SGreg Roach * Retrieve text, process embedded variables 948c2e8227SGreg Roach */ 958c2e8227SGreg Roach if (strpos($title, '#') !== false || strpos($html, '#') !== false) { 968c2e8227SGreg Roach $title = $stats->embedTags($title); 978c2e8227SGreg Roach $html = $stats->embedTags($html); 988c2e8227SGreg Roach } 998c2e8227SGreg Roach 1008c2e8227SGreg Roach /* 1018c2e8227SGreg Roach * Start Of Output 1028c2e8227SGreg Roach */ 1038c2e8227SGreg Roach $id = $this->getName() . $block_id; 1048c2e8227SGreg Roach $class = $this->getName() . '_block'; 1054b9ff166SGreg Roach if ($ctype === 'gedcom' && Auth::isManager($WT_TREE) || $ctype === 'user' && Auth::check()) { 106e9ac0a13SGreg Roach $title = '<a class="icon-admin" title="' . I18N::translate('Configure') . '" href="block_edit.php?block_id=' . $block_id . '&ged=' . $WT_TREE->getNameHtml() . '&ctype=' . $ctype . '"></a>' . $title; 1078c2e8227SGreg Roach } 1088c2e8227SGreg Roach 1098c2e8227SGreg Roach $content = $html; 1108c2e8227SGreg Roach 1118c2e8227SGreg Roach if ($show_timestamp) { 1123d7a8a4cSGreg Roach $content .= '<br>' . FunctionsDate::formatTimestamp($this->getBlockSetting($block_id, 'timestamp', WT_TIMESTAMP) + WT_TIMESTAMP_OFFSET); 1138c2e8227SGreg Roach } 1148c2e8227SGreg Roach 1158c2e8227SGreg Roach if ($template) { 1168c2e8227SGreg Roach return Theme::theme()->formatBlock($id, $title, $class, $content); 1178c2e8227SGreg Roach } else { 1188c2e8227SGreg Roach return $content; 1198c2e8227SGreg Roach } 1208c2e8227SGreg Roach } 1218c2e8227SGreg Roach 1228c2e8227SGreg Roach /** {@inheritdoc} */ 1238c2e8227SGreg Roach public function loadAjax() { 1248c2e8227SGreg Roach return false; 1258c2e8227SGreg Roach } 1268c2e8227SGreg Roach 1278c2e8227SGreg Roach /** {@inheritdoc} */ 1288c2e8227SGreg Roach public function isUserBlock() { 1298c2e8227SGreg Roach return true; 1308c2e8227SGreg Roach } 1318c2e8227SGreg Roach 1328c2e8227SGreg Roach /** {@inheritdoc} */ 1338c2e8227SGreg Roach public function isGedcomBlock() { 1348c2e8227SGreg Roach return true; 1358c2e8227SGreg Roach } 1368c2e8227SGreg Roach 13776692c8bSGreg Roach /** 13876692c8bSGreg Roach * An HTML form to edit block settings 13976692c8bSGreg Roach * 14076692c8bSGreg Roach * @param int $block_id 14176692c8bSGreg Roach */ 1428c2e8227SGreg Roach public function configureBlock($block_id) { 1434b9ff166SGreg Roach global $WT_TREE; 1444b9ff166SGreg Roach 1458c2e8227SGreg Roach if (Filter::postBool('save') && Filter::checkCsrf()) { 146c999a340SGreg Roach $languages = Filter::postArray('lang'); 147e2a378d3SGreg Roach $this->setBlockSetting($block_id, 'gedcom', Filter::post('gedcom')); 148e2a378d3SGreg Roach $this->setBlockSetting($block_id, 'title', Filter::post('title')); 149e2a378d3SGreg Roach $this->setBlockSetting($block_id, 'html', Filter::post('html')); 150e2a378d3SGreg Roach $this->setBlockSetting($block_id, 'show_timestamp', Filter::postBool('show_timestamp')); 151e2a378d3SGreg Roach $this->setBlockSetting($block_id, 'timestamp', Filter::post('timestamp')); 152e2a378d3SGreg Roach $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 1538c2e8227SGreg Roach } 1548c2e8227SGreg Roach 1558c2e8227SGreg Roach $templates = array( 1568c2e8227SGreg Roach I18N::translate('Keyword examples') => 1578c2e8227SGreg Roach '#getAllTagsTable#', 1588c2e8227SGreg Roach 1598c2e8227SGreg Roach I18N::translate('Narrative description') => 1608c2e8227SGreg Roach /* I18N: do not translate the #keywords# */ I18N::translate('This family tree was last updated on #gedcomUpdated#. There are #totalSurnames# surnames in this family tree. The earliest recorded event is the #firstEventType# of #firstEventName# in #firstEventYear#. The most recent event is the #lastEventType# of #lastEventName# in #lastEventYear#.<br><br>If you have any comments or feedback please contact #contactWebmaster#.'), 1618c2e8227SGreg Roach 1628c2e8227SGreg Roach I18N::translate('Statistics') => 1638c2e8227SGreg Roach '<div class="gedcom_stats"> 1648c2e8227SGreg Roach <span style="font-weight: bold;"><a href="index.php?command=gedcom">#gedcomTitle#</a></span><br> 1658c2e8227SGreg Roach ' . I18N::translate('This family tree was last updated on %s.', '#gedcomUpdated#') . ' 1668c2e8227SGreg Roach <table id="keywords"> 1678c2e8227SGreg Roach <tr> 168*a86dd8b1SGreg Roach <td class="width20"> 1698c2e8227SGreg Roach <table cellspacing="1" cellpadding="0"> 1708c2e8227SGreg Roach <tr> 1718c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Individuals') . '</td> 172*a86dd8b1SGreg Roach <td class="facts_value"><a href="indilist.php?surname_sublist=no">#totalIndividuals#</a></td> 1738c2e8227SGreg Roach </tr> 1748c2e8227SGreg Roach <tr> 1758c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Males') . '</td> 176*a86dd8b1SGreg Roach <td class="facts_value">#totalSexMales#<br>#totalSexMalesPercentage#</td> 1778c2e8227SGreg Roach </tr> 1788c2e8227SGreg Roach <tr> 1798c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Females') . '</td> 180*a86dd8b1SGreg Roach <td class="facts_value">#totalSexFemales#<br>#totalSexFemalesPercentage#</td> 1818c2e8227SGreg Roach </tr> 1828c2e8227SGreg Roach <tr> 1838c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Total surnames') . '</td> 184*a86dd8b1SGreg Roach <td class="facts_value"><a href="indilist.php?show_all=yes&surname_sublist=yes&ged=' . $WT_TREE->getNameUrl() . '">#totalSurnames#</a></td> 1858c2e8227SGreg Roach </tr> 1868c2e8227SGreg Roach <tr> 1878c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Families') . '</td> 188*a86dd8b1SGreg Roach <td class="facts_value"><a href="famlist.php?ged=' . $WT_TREE->getNameUrl() . '">#totalFamilies#</a></td> 1898c2e8227SGreg Roach </tr> 1908c2e8227SGreg Roach <tr> 1918c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Sources') . '</td> 192*a86dd8b1SGreg Roach <td class="facts_value"><a href="sourcelist.php?ged=' . $WT_TREE->getNameUrl() . '">#totalSources#</a></td> 1938c2e8227SGreg Roach </tr> 1948c2e8227SGreg Roach <tr> 1958c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Media objects') . '</td> 196*a86dd8b1SGreg Roach <td class="facts_value"><a href="medialist.php?ged=' . $WT_TREE->getNameUrl() . '">#totalMedia#</a></td> 1978c2e8227SGreg Roach </tr> 1988c2e8227SGreg Roach <tr> 1998c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Repositories') . '</td> 200*a86dd8b1SGreg Roach <td class="facts_value"><a href="repolist.php?ged=' . $WT_TREE->getNameUrl() . '">#totalRepositories#</a></td> 2018c2e8227SGreg Roach </tr> 2028c2e8227SGreg Roach <tr> 2038c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Total events') . '</td> 204*a86dd8b1SGreg Roach <td class="facts_value">#totalEvents#</td> 2058c2e8227SGreg Roach </tr> 2068c2e8227SGreg Roach <tr> 2078c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Total users') . '</td> 208*a86dd8b1SGreg Roach <td class="facts_value">#totalUsers#</td> 2098c2e8227SGreg Roach </tr> 2108c2e8227SGreg Roach </table> 2118c2e8227SGreg Roach </td> 2128c2e8227SGreg Roach <td><br></td> 213*a86dd8b1SGreg Roach <td> 2148c2e8227SGreg Roach <table cellspacing="1" cellpadding="0" border="0"> 2158c2e8227SGreg Roach <tr> 2168c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Earliest birth year') . '</td> 217*a86dd8b1SGreg Roach <td class="facts_value">#firstBirthYear#</td> 2188c2e8227SGreg Roach <td class="facts_value">#firstBirth#</td> 2198c2e8227SGreg Roach </tr> 2208c2e8227SGreg Roach <tr> 2218c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Latest birth year') . '</td> 222*a86dd8b1SGreg Roach <td class="facts_value">#lastBirthYear#</td> 2238c2e8227SGreg Roach <td class="facts_value">#lastBirth#</td> 2248c2e8227SGreg Roach </tr> 2258c2e8227SGreg Roach <tr> 2268c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Earliest death year') . '</td> 227*a86dd8b1SGreg Roach <td class="facts_value">#firstDeathYear#</td> 2288c2e8227SGreg Roach <td class="facts_value">#firstDeath#</td> 2298c2e8227SGreg Roach </tr> 2308c2e8227SGreg Roach <tr> 2318c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Latest death year') . '</td> 232*a86dd8b1SGreg Roach <td class="facts_value">#lastDeathYear#</td> 2338c2e8227SGreg Roach <td class="facts_value">#lastDeath#</td> 2348c2e8227SGreg Roach </tr> 2358c2e8227SGreg Roach <tr> 2368c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Individual who lived the longest') . '</td> 237*a86dd8b1SGreg Roach <td class="facts_value">#longestLifeAge#</td> 2388c2e8227SGreg Roach <td class="facts_value">#longestLife#</td> 2398c2e8227SGreg Roach </tr> 2408c2e8227SGreg Roach <tr> 2418c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Average age at death') . '</td> 242*a86dd8b1SGreg Roach <td class="facts_value">#averageLifespan#</td> 2438c2e8227SGreg Roach <td class="facts_value"></td> 2448c2e8227SGreg Roach </tr> 2458c2e8227SGreg Roach <tr> 2468c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Family with the most children') . '</td> 247*a86dd8b1SGreg Roach <td class="facts_value">#largestFamilySize#</td> 2488c2e8227SGreg Roach <td class="facts_value">#largestFamily#</td> 2498c2e8227SGreg Roach </tr> 2508c2e8227SGreg Roach <tr> 2518c2e8227SGreg Roach <td class="facts_label">' . I18N::translate('Average number of children per family') . '</td> 252*a86dd8b1SGreg Roach <td class="facts_value">#averageChildren#</td> 2538c2e8227SGreg Roach <td class="facts_value"></td> 2548c2e8227SGreg Roach </tr> 2558c2e8227SGreg Roach </table> 2568c2e8227SGreg Roach </td> 2578c2e8227SGreg Roach </tr> 2588c2e8227SGreg Roach </table><br> 2598c2e8227SGreg Roach <span style="font-weight: bold;">' . I18N::translate('Most common surnames') . '</span><br> 2608c2e8227SGreg Roach #commonSurnames# 261cbc1590aSGreg Roach </div>', 2628c2e8227SGreg Roach ); 2638c2e8227SGreg Roach 264e2a378d3SGreg Roach $title = $this->getBlockSetting($block_id, 'title'); 265e2a378d3SGreg Roach $html = $this->getBlockSetting($block_id, 'html'); 266e2a378d3SGreg Roach $gedcom = $this->getBlockSetting($block_id, 'gedcom'); 267e2a378d3SGreg Roach $show_timestamp = $this->getBlockSetting($block_id, 'show_timestamp', '0'); 268e2a378d3SGreg Roach $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); 2698c2e8227SGreg Roach 2708c2e8227SGreg Roach echo '<tr><td class="descriptionbox wrap">', 271764a01d9SGreg Roach GedcomTag::getLabel('TITL'), 2728c2e8227SGreg Roach '</td><td class="optionbox"><input type="text" name="title" size="30" value="', Filter::escapeHtml($title), '"></td></tr>'; 2738c2e8227SGreg Roach 2748c2e8227SGreg Roach // templates 2758c2e8227SGreg Roach echo '<tr><td class="descriptionbox wrap">', 2768c2e8227SGreg Roach I18N::translate('Templates'), 2778c2e8227SGreg Roach '</td><td class="optionbox wrap">'; 2788c2e8227SGreg Roach // The CK editor needs lots of help to load/save data :-( 2798c2e8227SGreg Roach if (Module::getModuleByName('ckeditor')) { 2808c2e8227SGreg Roach $ckeditor_onchange = 'CKEDITOR.instances.html.setData(document.block.html.value);'; 2818c2e8227SGreg Roach } else { 2828c2e8227SGreg Roach $ckeditor_onchange = ''; 2838c2e8227SGreg Roach } 2848c2e8227SGreg Roach echo '<select name="template" onchange="document.block.html.value=document.block.template.options[document.block.template.selectedIndex].value;', $ckeditor_onchange, '">'; 2858c2e8227SGreg Roach echo '<option value="', Filter::escapeHtml($html), '">', I18N::translate('Custom'), '</option>'; 2868c2e8227SGreg Roach foreach ($templates as $title => $template) { 2878c2e8227SGreg Roach echo '<option value="', Filter::escapeHtml($template), '">', $title, '</option>'; 2888c2e8227SGreg Roach } 2898c2e8227SGreg Roach echo '</select>'; 2908c2e8227SGreg Roach if (!$html) { 2918c2e8227SGreg Roach echo '<p>', I18N::translate('To assist you in getting started with this block, we have created several standard templates. When you select one of these templates, the text area will contain a copy that you can then alter to suit your site’s requirements.'), '</p>'; 2928c2e8227SGreg Roach } 2938c2e8227SGreg Roach echo '</td></tr>'; 2948c2e8227SGreg Roach 2958c2e8227SGreg Roach if (count(Tree::getAll()) > 1) { 2968c2e8227SGreg Roach if ($gedcom == '__current__') {$sel_current = 'selected'; } else {$sel_current = ''; } 2978c2e8227SGreg Roach if ($gedcom == '__default__') {$sel_default = 'selected'; } else {$sel_default = ''; } 2988c2e8227SGreg Roach echo '<tr><td class="descriptionbox wrap">', 2998c2e8227SGreg Roach I18N::translate('Family tree'), 3008c2e8227SGreg Roach '</td><td class="optionbox">', 3018c2e8227SGreg Roach '<select name="gedcom">', 3028c2e8227SGreg Roach '<option value="__current__" ', $sel_current, '>', I18N::translate('Current'), '</option>', 3038c2e8227SGreg Roach '<option value="__default__" ', $sel_default, '>', I18N::translate('Default'), '</option>'; 3048c2e8227SGreg Roach foreach (Tree::getAll() as $tree) { 3058c2e8227SGreg Roach if ($tree->getName() === $gedcom) {$sel = 'selected'; } else {$sel = ''; } 306a77340e1SGreg Roach echo '<option value="', $tree->getNameHtml(), '" ', $sel, '>', $tree->getTitleHtml(), '</option>'; 3078c2e8227SGreg Roach } 3088c2e8227SGreg Roach echo '</select>'; 3098c2e8227SGreg Roach echo '</td></tr>'; 3108c2e8227SGreg Roach } 3118c2e8227SGreg Roach 3128c2e8227SGreg Roach // html 3138c2e8227SGreg Roach echo '<tr><td colspan="2" class="descriptionbox">', 3148c2e8227SGreg Roach I18N::translate('Content'); 3158c2e8227SGreg Roach if (!$html) { 3168c2e8227SGreg Roach echo '<p>', I18N::translate('As well as using the toolbar to apply HTML formatting, you can insert database fields which are updated automatically. These special fields are marked with <b>#</b> characters. For example <b>#totalFamilies#</b> will be replaced with the actual number of families in the database. Advanced users may wish to apply CSS classes to their text, so that the formatting matches the currently selected theme.'), '</p>'; 3178c2e8227SGreg Roach } 3188c2e8227SGreg Roach echo 3198c2e8227SGreg Roach '</td></tr><tr>', 3208c2e8227SGreg Roach '<td colspan="2" class="optionbox">'; 3218c2e8227SGreg Roach echo '<textarea name="html" class="html-edit" rows="10" style="width:98%;">', Filter::escapeHtml($html), '</textarea>'; 3228c2e8227SGreg Roach echo '</td></tr>'; 3238c2e8227SGreg Roach 3248c2e8227SGreg Roach echo '<tr><td class="descriptionbox wrap">'; 3258c2e8227SGreg Roach echo I18N::translate('Show the date and time of update'); 3268c2e8227SGreg Roach echo '</td><td class="optionbox">'; 3273d7a8a4cSGreg Roach echo FunctionsEdit::editFieldYesNo('show_timestamp', $show_timestamp); 3288c2e8227SGreg Roach echo '<input type="hidden" name="timestamp" value="', WT_TIMESTAMP, '">'; 3298c2e8227SGreg Roach echo '</td></tr>'; 3308c2e8227SGreg Roach 3318c2e8227SGreg Roach echo '<tr><td class="descriptionbox wrap">'; 3328c2e8227SGreg Roach echo I18N::translate('Show this block for which languages?'); 3338c2e8227SGreg Roach echo '</td><td class="optionbox">'; 3343d7a8a4cSGreg Roach echo FunctionsEdit::editLanguageCheckboxes('lang', $languages); 3358c2e8227SGreg Roach echo '</td></tr>'; 3368c2e8227SGreg Roach } 3378c2e8227SGreg Roach} 338