Dotclear

source: themes/ductile/_public.php @ 3531:f2de7e462b78

Revision 3531:f2de7e462b78, 17.7 KB checked in by franck <carnet.franck.paul@…>, 9 years ago (diff)

Using theme\<theme_name> namespace for _public.php and _prepend.php, in order to simplify theme copy and hack

RevLine 
[345]1<?php
[3531]2namespace themes\ductile;
3
[345]4# -- BEGIN LICENSE BLOCK ---------------------------------------
[631]5# This file is part of Ductile, a theme for Dotclear
[345]6#
[631]7# Copyright (c) 2011 - Association Dotclear
[345]8# Licensed under the GPL version 2.0 license.
9# See LICENSE file or
10# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11#
12# -- END LICENSE BLOCK -----------------------------------------
13
14if (!defined('DC_RC_PATH')) { return; }
15
[3531]16\l10n::set(dirname(__FILE__).'/locales/'.$_lang.'/main');
[603]17
[419]18# Behaviors
[3531]19$core->addBehavior('publicHeadContent',array(__NAMESPACE__.'\tplDuctileTheme','publicHeadContent'));
20$core->addBehavior('publicInsideFooter',array(__NAMESPACE__.'\tplDuctileTheme','publicInsideFooter'));
[419]21
[563]22# Templates
[3531]23$core->tpl->addValue('ductileEntriesList',array(__NAMESPACE__.'\tplDuctileTheme','ductileEntriesList'));
24$core->tpl->addBlock('EntryIfContentIsCut',array(__NAMESPACE__.'\tplDuctileTheme','EntryIfContentIsCut'));
25$core->tpl->addValue('ductileNbEntryPerPage',array(__NAMESPACE__.'\tplDuctileTheme','ductileNbEntryPerPage'));
26$core->tpl->addValue('ductileLogoSrc',array(__NAMESPACE__.'\tplDuctileTheme','ductileLogoSrc'));
27$core->tpl->addBlock('IfPreviewIsNotMandatory',array(__NAMESPACE__.'\tplDuctileTheme','IfPreviewIsNotMandatory'));
[563]28
[345]29class tplDuctileTheme
30{
[583]31     public static function ductileNbEntryPerPage($attr)
32     {
[3531]33          return '<?php '.__NAMESPACE__.'\tplDuctileTheme::ductileNbEntryPerPageHelper(); ?>';
[828]34     }
[2566]35
[828]36     public static function ductileNbEntryPerPageHelper()
37     {
38          global $_ctx;
[2566]39
[2581]40          $nb_other = $nb_first = 0;
41
[828]42          $s = $GLOBALS['core']->blog->settings->themes->get($GLOBALS['core']->blog->settings->system->theme.'_entries_counts');
[583]43          if ($s !== null) {
44               $s = @unserialize($s);
45               if (is_array($s)) {
[2581]46                    switch ($GLOBALS['core']->url->type) {
47                         case 'default':
48                         case 'default-page':
49                              if (isset($s['default'])) {
50                                   $nb_first = $nb_other = (integer) $s['default'];
51                              }
52                              if (isset($s['default-page'])) {
53                                   $nb_other = (integer) $s['default-page'];
54                              }
55                              break;
56                         default:
57                              if (isset($s[$GLOBALS['core']->url->type])) {
58                                   // Nb de billets par page défini par la config du thème
59                                   $nb_first = $nb_other = (integer) $s[$GLOBALS['core']->url->type];
60                              }
61                              break;
[583]62                    }
63               }
64          }
65
[2581]66          if ($nb_other == 0) {
[583]67               if (!empty($attr['nb'])) {
68                    // Nb de billets par page défini par défaut dans le template
[2581]69                    $nb_other = $nb_first = (integer) $attr['nb'];
[583]70               }
71          }
72
[2581]73          if ($nb_other > 0) {
74               $_ctx->nb_entry_per_page = $nb_other;
75          }
76          if ($nb_first > 0) {
77               $_ctx->nb_entry_first_page = $nb_first;
78          }
[583]79     }
[2566]80
[574]81     public static function EntryIfContentIsCut($attr,$content)
82     {
83          global $core;
[2566]84
[619]85          if (empty($attr['cut_string']) || !empty($attr['full'])) {
[574]86               return '';
87          }
[2566]88
[574]89          $urls = '0';
90          if (!empty($attr['absolute_urls'])) {
91               $urls = '1';
92          }
[619]93
[574]94          $short = $core->tpl->getFilters($attr);
[619]95          $cut = $attr['cut_string'];
[574]96          $attr['cut_string'] = 0;
97          $full = $core->tpl->getFilters($attr);
[619]98          $attr['cut_string'] = $cut;
[574]99
100          return '<?php if (strlen('.sprintf($full,'$_ctx->posts->getContent('.$urls.')').') > '.
101               'strlen('.sprintf($short,'$_ctx->posts->getContent('.$urls.')').')) : ?>'.
102               $content.
103               '<?php endif; ?>';
[2566]104     }
105
[563]106     public static function ductileEntriesList($attr)
107     {
108          global $core;
[2566]109
[830]110          $tpl_path = dirname(__FILE__).'/tpl/';
111          $list_types = array('title','short','full');
112
113          // Get all _entry-*.html in tpl folder of theme
[3531]114          $list_types_templates = \files::scandir($tpl_path);
[830]115          if (is_array($list_types_templates)) {
116               foreach ($list_types_templates as $v) {
117                    if (preg_match('/^_entry\-(.*)\.html$/',$v,$m)) {
118                         if (isset($m[1])) {
119                              if (!in_array($m[1],$list_types)) {
120                                   // template not already in full list
121                                   $list_types[] = $m[1];
122                              }
123                         }
124                    }
125               }
126          }
127
[563]128          $default = isset($attr['default']) ? trim($attr['default']) : 'short';
[830]129          $ret = '<?php '."\n".
[3531]130               'switch ('.__NAMESPACE__.'\tplDuctileTheme::ductileEntriesListHelper(\''.$default.'\')) {'."\n";
[830]131
132          foreach ($list_types as $v) {
133               $ret .= ' case \''.$v.'\':'."\n".
134                    '?>'."\n".
135                              $core->tpl->includeFile(array('src' => '_entry-'.$v.'.html'))."\n".
136                    '<?php '."\n".
137                    '         break;'."\n";
138          }
139
140          $ret .= '}'."\n".
141               '?>';
[2566]142
[830]143          return $ret;
[828]144     }
[2566]145
[828]146     public static function ductileEntriesListHelper($default)
147     {
148          $s = $GLOBALS['core']->blog->settings->themes->get($GLOBALS['core']->blog->settings->system->theme.'_entries_lists');
[563]149          if ($s !== null) {
150               $s = @unserialize($s);
151               if (is_array($s)) {
[828]152                    if (isset($s[$GLOBALS['core']->url->type])) {
153                         $model = $s[$GLOBALS['core']->url->type];
154                         return $model;
[563]155                    }
156               }
157          }
[828]158          return $default;
[563]159     }
160
[810]161     public static function ductileLogoSrc($attr)
162     {
[3531]163          return '<?php echo '.__NAMESPACE__.'\tplDuctileTheme::ductileLogoSrcHelper(); ?>';
[828]164     }
[810]165
[828]166     public static function ductileLogoSrcHelper()
167     {
[2716]168          $img_url = $GLOBALS['core']->blog->settings->system->themes_url.'/'.$GLOBALS['core']->blog->settings->system->theme.'/img/logo.png';
169
[810]170          $s = $GLOBALS['core']->blog->settings->themes->get($GLOBALS['core']->blog->settings->system->theme.'_style');
171          if ($s === null) {
[2716]172               // no settings yet, return default logo
173               return $img_url;
[810]174          }
175          $s = @unserialize($s);
176          if (!is_array($s)) {
[2716]177               // settings error, return default logo
178               return $img_url;
[810]179          }
[2566]180
[810]181          if (isset($s['logo_src'])) {
182               if ($s['logo_src'] !== null) {
183                    if ($s['logo_src'] != '') {
184                         if ((substr($s['logo_src'],0,1) == '/') || (parse_url($s['logo_src'],PHP_URL_SCHEME) != '')) {
185                              // absolute URL
186                              $img_url = $s['logo_src'];
187                         } else {
188                              // relative URL (base = img folder of ductile theme)
[828]189                              $img_url = $GLOBALS['core']->blog->settings->system->themes_url.'/'.$GLOBALS['core']->blog->settings->system->theme.'/img/'.$s['logo_src'];
[810]190                         }
191                    }
192               }
193          }
[2566]194
[810]195          return $img_url;
196     }
197
[1096]198     public static function IfPreviewIsNotMandatory($attr,$content)
199     {
200          $s = $GLOBALS['core']->blog->settings->themes->get($GLOBALS['core']->blog->settings->system->theme.'_style');
201          if ($s !== null) {
202               $s = @unserialize($s);
203               if (is_array($s)) {
204                    if (isset($s['preview_not_mandatory'])) {
205                         if ($s['preview_not_mandatory']) {
206                              return $content;
207                         }
208                    }
209               }
210          }
211          return '';
212     }
213
[429]214     public static function publicInsideFooter($core)
[419]215     {
216          $res = '';
217          $default = false;
[429]218          $img_url = $core->blog->settings->system->themes_url.'/'.$core->blog->settings->system->theme.'/img/';
[419]219
[429]220          $s = $core->blog->settings->themes->get($core->blog->settings->system->theme.'_stickers');
[419]221
222          if ($s === null) {
223               $default = true;
224          } else {
225               $s = @unserialize($s);
226               if (!is_array($s)) {
227                    $default = true;
228               } else {
[3531]229                    $s = array_filter($s,'self::cleanStickers');
[419]230                    if (count($s) == 0) {
231                         $default = true;
232                    } else {
233                         $count = 1;
234                         foreach ($s as $sticker) {
235                              $res .= self::setSticker($count,($count == count($s)),$sticker['label'],$sticker['url'],$img_url.$sticker['image']);
236                              $count++;
237                         }
238                    }
239               }
240          }
241
242          if ($default || $res == '') {
[776]243               $res = self::setSticker(1,true,__('Subscribe'),$core->blog->url.
244                    $core->url->getURLFor('feed','atom'),$img_url.'sticker-feed.png');
[419]245          }
246
247          if ($res != '') {
248               $res = '<ul id="stickers">'."\n".$res.'</ul>'."\n";
[429]249               echo $res;
[419]250          }
251     }
[2566]252
[419]253     protected static function cleanStickers($s)
254     {
255          if (is_array($s)) {
256               if (isset($s['label']) && isset($s['url']) && isset($s['image'])) {
257                    if ($s['label'] != null && $s['url'] != null && $s['image'] != null) {
258                         return true;
259                    }
260               }
261          }
262          return false;
263     }
[2566]264
[419]265     protected static function setSticker($position,$last,$label,$url,$image)
266     {
267          return '<li id="sticker'.$position.'"'.($last ? ' class="last"' : '').'>'."\n".
268               '<a href="'.$url.'">'."\n".
269               '<img alt="" src="'.$image.'" />'."\n".
270               '<span>'.$label.'</span>'."\n".
271               '</a>'."\n".
272               '</li>'."\n";
273     }
274
[345]275     public static function publicHeadContent($core)
276     {
[2566]277          echo
[357]278               '<style type="text/css">'."\n".
[421]279               '/* '.__('Additionnal style directives').' */'."\n".
[357]280               self::ductileStyleHelper().
281               "</style>\n";
[2566]282
[592]283          echo
284               '<script type="text/javascript" src="'.
285               $core->blog->settings->system->themes_url.'/'.$core->blog->settings->system->theme.
286               '/ductile.js"></script>'."\n";
[1081]287
288          echo self::ductileWebfontHelper();
289     }
290
291     public static function ductileWebfontHelper()
292     {
293          $s = $GLOBALS['core']->blog->settings->themes->get($GLOBALS['core']->blog->settings->system->theme.'_style');
294
295          if ($s === null) {
296               return;
297          }
298
299          $s = @unserialize($s);
300          if (!is_array($s)) {
301               return;
302          }
303
304          $ret = '';
305          $css = array();
306          $uri = array();
307          if (!isset($s['body_font']) || ($s['body_font'] == '')) {
308               // See if webfont defined for main font
309               if (isset($s['body_webfont_api']) && isset($s['body_webfont_family']) && isset($s['body_webfont_url'])) {
310                    $uri[] = $s['body_webfont_url'];
311                    switch ($s['body_webfont_api']) {
312                         case 'js':
313                              $ret .= sprintf('<script type="text/javascript" src="%s"></script>',$s['body_webfont_url'])."\n";
314                              break;
315                         case 'css':
316                              $ret .= sprintf('<link type="text/css" href="%s" rel="stylesheet" />',$s['body_webfont_url'])."\n";
317                              break;
318                    }
319                    # Main font
320                    $selectors = 'body, .supranav li a span, #comments.me, a.comment-number';
[3531]321                    \dcThemeConfig::prop($css,$selectors,'font-family',$s['body_webfont_family']);
[1081]322               }
323          }
324          if (!isset($s['alternate_font']) || ($s['alternate_font'] == '')) {
325               // See if webfont defined for secondary font
326               if (isset($s['alternate_webfont_api']) && isset($s['alternate_webfont_family']) && isset($s['alternate_webfont_url'])) {
327                    if (!in_array($s['alternate_webfont_url'], $uri)) {
328                         switch ($s['alternate_webfont_api']) {
329                              case 'js':
330                                   $ret .= sprintf('<script type="text/javascript" src="%s"></script>',$s['alternate_webfont_url'])."\n";
331                                   break;
332                              case 'css':
333                                   $ret .= sprintf('<link type="text/css" href="%s" rel="stylesheet" />',$s['alternate_webfont_url'])."\n";
334                                   break;
335                         }
336                    }
337                    # Secondary font
338                    $selectors = '#blogdesc, .supranav, #content-info, #subcategories, #comments-feed, #sidebar h2, #sidebar h3, #footer';
[3531]339                    \dcThemeConfig::prop($css,$selectors,'font-family',$s['alternate_webfont_family']);
[1081]340               }
341          }
342          # Style directives
343          $res = '';
344          foreach ($css as $selector => $values) {
345               $res .= $selector." {\n";
346               foreach ($values as $k => $v) {
347                    $res .= $k.':'.$v.";\n";
348               }
349               $res .= "}\n";
350          }
351          if ($res != '') {
352               $ret .= '<style type="text/css">'."\n".$res.'</style>'."\n";
353          }
354
355          return $ret;
[357]356     }
[2566]357
[357]358     public static function ductileStyleHelper()
359     {
[394]360          $s = $GLOBALS['core']->blog->settings->themes->get($GLOBALS['core']->blog->settings->system->theme.'_style');
[357]361
362          if ($s === null) {
363               return;
364          }
365
366          $s = @unserialize($s);
367          if (!is_array($s)) {
368               return;
369          }
370
371          $css = array();
372
373          # Properties
[2566]374
[389]375          # Blog description
376          $selectors = '#blogdesc';
[3531]377          if (isset($s['subtitle_hidden'])) \dcThemeConfig::prop($css,$selectors,'display',($s['subtitle_hidden'] ? 'none' : null));
[357]378
[376]379          # Main font
[626]380          $selectors = 'body, .supranav li a span, #comments.me, a.comment-number';
[3531]381          if (isset($s['body_font'])) \dcThemeConfig::prop($css,$selectors,'font-family',self::fontDef($s['body_font']));
[376]382
[378]383          # Secondary font
[674]384          $selectors = '#blogdesc, .supranav, #content-info, #subcategories, #comments-feed, #sidebar h2, #sidebar h3, #footer';
[3531]385          if (isset($s['alternate_font'])) \dcThemeConfig::prop($css,$selectors,'font-family',self::fontDef($s['alternate_font']));
[2566]386
[378]387          # Inside posts links font weight
[387]388          $selectors = '.post-excerpt a, .post-content a';
[3531]389          if (isset($s['post_link_w'])) \dcThemeConfig::prop($css,$selectors,'font-weight',($s['post_link_w'] ? 'bold' : 'normal'));
[376]390
[378]391          # Inside posts links colors (normal, visited)
[387]392          $selectors = '.post-excerpt a:link, .post-excerpt a:visited, .post-content a:link, .post-content a:visited';
[3531]393          if (isset($s['post_link_v_c'])) \dcThemeConfig::prop($css,$selectors,'color',$s['post_link_v_c']);
[378]394
395          # Inside posts links colors (hover, active, focus)
[387]396          $selectors = '.post-excerpt a:hover, .post-excerpt a:active, .post-excerpt a:focus, .post-content a:hover, .post-content a:active, .post-content a:focus';
[3531]397          if (isset($s['post_link_f_c'])) \dcThemeConfig::prop($css,$selectors,'color',$s['post_link_f_c']);
[357]398
[376]399          # Style directives
[357]400          $res = '';
401          foreach ($css as $selector => $values) {
402               $res .= $selector." {\n";
403               foreach ($values as $k => $v) {
404                    $res .= $k.':'.$v.";\n";
405               }
406               $res .= "}\n";
407          }
408
[387]409          # Large screens
410          $css_large = array();
411
412          # Blog title font weight
413          $selectors = 'h1, h1 a:link, h1 a:visited, h1 a:hover, h1 a:visited, h1 a:focus';
[3531]414          if (isset($s['blog_title_w'])) \dcThemeConfig::prop($css_large,$selectors,'font-weight',($s['blog_title_w'] ? 'bold' : 'normal'));
[2566]415
[387]416          # Blog title font size
417          $selectors = 'h1';
[3531]418          if (isset($s['blog_title_s'])) \dcThemeConfig::prop($css_large,$selectors,'font-size',$s['blog_title_s']);
[2566]419
[387]420          # Blog title color
421          $selectors = 'h1 a:link, h1 a:visited, h1 a:hover, h1 a:visited, h1 a:focus';
[3531]422          if (isset($s['blog_title_c'])) \dcThemeConfig::prop($css_large,$selectors,'color',$s['blog_title_c']);
[387]423
424          # Post title font weight
425          $selectors = 'h2.post-title, h2.post-title a:link, h2.post-title a:visited, h2.post-title a:hover, h2.post-title a:visited, h2.post-title a:focus';
[3531]426          if (isset($s['post_title_w'])) \dcThemeConfig::prop($css_large,$selectors,'font-weight',($s['post_title_w'] ? 'bold' : 'normal'));
[2566]427
[387]428          # Post title font size
429          $selectors = 'h2.post-title';
[3531]430          if (isset($s['post_title_s'])) \dcThemeConfig::prop($css_large,$selectors,'font-size',$s['post_title_s']);
[2566]431
[387]432          # Post title color
433          $selectors = 'h2.post-title a:link, h2.post-title a:visited, h2.post-title a:hover, h2.post-title a:visited, h2.post-title a:focus';
[3531]434          if (isset($s['post_title_c'])) \dcThemeConfig::prop($css_large,$selectors,'color',$s['post_title_c']);
[387]435
[515]436          # Simple title color (title without link)
437          $selectors = '#content-info h2, .post-title, .post h3, .post h4, .post h5, .post h6, .arch-block h3';
[3531]438          if (isset($s['post_simple_title_c'])) \dcThemeConfig::prop($css_large,$selectors,'color',$s['post_simple_title_c']);
[515]439
[387]440          # Style directives for large screens
441          if (count($css_large)) {
442               $res .= '@media only screen and (min-width: 481px) {'."\n";
443               foreach ($css_large as $selector => $values) {
444                    $res .= $selector." {\n";
445                    foreach ($values as $k => $v) {
446                         $res .= $k.':'.$v.";\n";
447                    }
448                    $res .= "}\n";
449               }
450               $res .= "}\n";
451          }
452
453          # Small screens
454          $css_small = array();
455
456          # Blog title font weight
457          $selectors = 'h1, h1 a:link, h1 a:visited, h1 a:hover, h1 a:visited, h1 a:focus';
[3531]458          if (isset($s['blog_title_w_m'])) \dcThemeConfig::prop($css_small,$selectors,'font-weight',($s['blog_title_w_m'] ? 'bold' : 'normal'));
[2566]459
[387]460          # Blog title font size
461          $selectors = 'h1';
[3531]462          if (isset($s['blog_title_s_m'])) \dcThemeConfig::prop($css_small,$selectors,'font-size',$s['blog_title_s_m']);
[2566]463
[387]464          # Blog title color
465          $selectors = 'h1 a:link, h1 a:visited, h1 a:hover, h1 a:visited, h1 a:focus';
[3531]466          if (isset($s['blog_title_c_m'])) \dcThemeConfig::prop($css_small,$selectors,'color',$s['blog_title_c_m']);
[387]467
468          # Post title font weight
469          $selectors = 'h2.post-title, h2.post-title a:link, h2.post-title a:visited, h2.post-title a:hover, h2.post-title a:visited, h2.post-title a:focus';
[3531]470          if (isset($s['post_title_w_m'])) \dcThemeConfig::prop($css_small,$selectors,'font-weight',($s['post_title_w_m'] ? 'bold' : 'normal'));
[2566]471
[387]472          # Post title font size
473          $selectors = 'h2.post-title';
[3531]474          if (isset($s['post_title_s_m'])) \dcThemeConfig::prop($css_small,$selectors,'font-size',$s['post_title_s_m']);
[2566]475
[387]476          # Post title color
477          $selectors = 'h2.post-title a:link, h2.post-title a:visited, h2.post-title a:hover, h2.post-title a:visited, h2.post-title a:focus';
[3531]478          if (isset($s['post_title_c_m'])) \dcThemeConfig::prop($css_small,$selectors,'color',$s['post_title_c_m']);
[387]479
480          # Style directives for small screens
481          if (count($css_small)) {
482               $res .= '@media only screen and (max-width: 480px) {'."\n";
483               foreach ($css_small as $selector => $values) {
484                    $res .= $selector." {\n";
485                    foreach ($values as $k => $v) {
486                         $res .= $k.':'.$v.";\n";
487                    }
488                    $res .= "}\n";
489               }
490               $res .= "}\n";
491          }
[2566]492
[357]493          return $res;
494     }
495
[376]496     protected static $fonts = array(
[377]497          // Theme standard
[376]498          'Ductile body' => '"Century Schoolbook", "Century Schoolbook L", Georgia, serif',
499          'Ductile alternate' => '"Franklin gothic medium", "arial narrow", "DejaVu Sans Condensed", "helvetica neue", helvetica, sans-serif',
[377]500
501          // Serif families
[376]502          'Times New Roman' => 'Cambria, "Hoefler Text", Utopia, "Liberation Serif", "Nimbus Roman No9 L Regular", Times, "Times New Roman", serif',
[625]503          'Georgia' => 'Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif',
[376]504          'Garamond' => '"Palatino Linotype", Palatino, Palladio, "URW Palladio L", "Book Antiqua", Baskerville, "Bookman Old Style", "Bitstream Charter", "Nimbus Roman No9 L", Garamond, "Apple Garamond", "ITC Garamond Narrow", "New Century Schoolbook", "Century Schoolbook", "Century Schoolbook L", Georgia, serif',
[377]505
506          // Sans-serif families
[376]507          'Helvetica/Arial' => 'Frutiger, "Frutiger Linotype", Univers, Calibri, "Gill Sans", "Gill Sans MT", "Myriad Pro", Myriad, "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif',
508          'Verdana' => 'Corbel, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", "DejaVu Sans", "Bitstream Vera Sans", "Liberation Sans", Verdana, "Verdana Ref", sans-serif',
509          'Trebuchet MS' => '"Segoe UI", Candara, "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, "Verdana Ref", sans-serif',
[377]510
511          // Cursive families
[376]512          'Impact' => 'Impact, Haettenschweiler, "Franklin Gothic Bold", Charcoal, "Helvetica Inserat", "Bitstream Vera Sans Bold", "Arial Black", sans-serif',
[377]513
514          // Monospace families
[376]515          'Monospace' => 'Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace'
516     );
517
518     protected static function fontDef($c)
519     {
520          return isset(self::$fonts[$c]) ? self::$fonts[$c] : null;
521     }
[345]522}
Note: See TracBrowser for help on using the repository browser.

Sites map