]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/Theme.php
new PrevNext plugin with two buttons
[SourceForge/phpwiki.git] / lib / Theme.php
1 <?php rcs_id('$Id: Theme.php,v 1.51 2002-08-24 13:18:56 rurban Exp $');
2
3 require_once('lib/HtmlElement.php');
4
5
6 /**
7  * Make a link to a wiki page (in this wiki).
8  *
9  * This is a convenience function.
10  *
11  * @param $page_or_rev mixed
12  * Can be:<dl>
13  * <dt>A string</dt><dd>The page to link to.</dd>
14  * <dt>A WikiDB_Page object</dt><dd>The page to link to.</dd>
15  * <dt>A WikiDB_PageRevision object</dt><dd>A specific version of the page to link to.</dd>
16  * </dl>
17  *
18  * @param $type string
19  * One of:<dl>
20  * <dt>'unknown'</dt><dd>Make link appropriate for a non-existant page.</dd>
21  * <dt>'known'</dt><dd>Make link appropriate for an existing page.</dd>
22  * <dt>'auto'</dt><dd>Either 'unknown' or 'known' as appropriate.</dd>
23  * <dt>'button'</dt><dd>Make a button-style link.</dd>
24  * </dl>
25  * Unless $type of of the latter form, the link will be of class 'wiki', 'wikiunknown',
26  * 'named-wiki', or 'named-wikiunknown', as appropriate.
27  *
28  * @param $label mixed (string or XmlContent object)
29  * Label for the link.  If not given, defaults to the page name.
30  * (Label is ignored for $type == 'button'.)
31  */
32 function WikiLink ($page_or_rev, $type = 'known', $label = false) {
33     global $Theme;
34
35     if ($type == 'button') {
36         return $Theme->makeLinkButton($page_or_rev, $label);
37     }
38
39     $version = false;
40
41     if (isa($page_or_rev, 'WikiDB_PageRevision')) {
42         $version = $page_or_rev->getVersion();
43         $page = $page_or_rev->getPage();
44         $pagename = $page->getName();
45         $exists = true;
46     }
47     elseif (isa($page_or_rev, 'WikiDB_Page')) {
48         $page = $page_or_rev;
49         $pagename = $page->getName();
50     }
51     else {
52         $pagename = $page_or_rev;
53         // "/SubPage" relative link
54         if (substr($pagename,0,1) == SUBPAGE_SEPARATOR) { // relative link to page below
55             global $request;
56             $page = $request->getArg('pagename');
57             $label = substr($page_or_rev,1);
58             $pagename = $page . $pagename;
59         }
60     }
61
62     if ($type == 'auto') {
63         if (isset($page)) {
64             $current = $page->getCurrentRevision();
65             $exists = ! $current->hasDefaultContents();
66         }
67         else {
68             global $request;
69             $dbi = $request->getDbh();
70             $exists = $dbi->isWikiPage($pagename);
71         }
72     }
73     elseif ($type == 'unknown') {
74         $exists = false;
75     }
76     else {
77         $exists = true;
78     }
79     // Todo: test external ImageLinks http://some/images/next.gif
80     if (is_string($page_or_rev) 
81         and !$label 
82         and strchr(substr($page_or_rev,1), SUBPAGE_SEPARATOR)) {
83         $pages = explode(SUBPAGE_SEPARATOR,$pagename);
84         $last_page = array_pop($pages); // deletes last element from array as side-effect
85         $link = HTML::span($Theme->linkExistingWikiWord($pages[0], $pages[0] . SUBPAGE_SEPARATOR));
86         $first_pages = $pages[0] . SUBPAGE_SEPARATOR;
87         array_shift($pages);
88         foreach ($pages as $page)  {
89             $link->pushContent($Theme->linkExistingWikiWord($first_pages . $page, $page . SUBPAGE_SEPARATOR));
90             $first_pages .= $page . SUBPAGE_SEPARATOR;
91         }
92         $label = $last_page;
93         if ($exists) {
94             $link->pushContent($Theme->linkExistingWikiWord($pagename, $label, $version));
95             return $link;
96         }
97         else {
98             $link->pushContent($Theme->linkUnknownWikiWord($pagename, $label));
99             return $link;       
100         }
101     }
102     elseif ($exists) {
103         return $Theme->linkExistingWikiWord($pagename, $label, $version);
104     }
105     else {
106         return $Theme->linkUnknownWikiWord($pagename, $label);
107     }
108 }
109
110
111
112 /**
113  * Make a button.
114  *
115  * This is a convenience function.
116  *
117  * @param $action string
118  * One of <dl>
119  * <dt>[action]</dt><dd>Perform action (e.g. 'edit') on the selected page.</dd>
120  * <dt>[ActionPage]</dt><dd>Run the actionpage (e.g. 'BackLinks') on the selected page.</dd>
121  * <dt>'submit:'[name]</dt><dd>Make a form submission button with the given name.
122  *      ([name] can be blank for a nameless submit button.)</dd>
123  * <dt>a hash</dt><dd>Query args for the action. E.g.<pre>
124  *      array('action' => 'diff', 'previous' => 'author')
125  * </pre></dd>
126  * </dl>
127  *
128  * @param $label string
129  * A label for the button.  If ommited, a suitable default (based on the valued of $action)
130  * will be picked.
131  *
132  * @param $page_or_rev mixed
133  * Which page (& version) to perform the action on.
134  * Can be one of:<dl>
135  * <dt>A string</dt><dd>The pagename.</dd>
136  * <dt>A WikiDB_Page object</dt><dd>The page.</dd>
137  * <dt>A WikiDB_PageRevision object</dt><dd>A specific version of the page.</dd>
138  * </dl>
139  * ($Page_or_rev is ignored for submit buttons.)
140  */
141 function Button ($action, $label = false, $page_or_rev = false) {
142     global $Theme;
143
144     if (!is_array($action) && preg_match('/submit:(.*)/A', $action, $m))
145         return $Theme->makeSubmitButton($label, $m[1], $class = $page_or_rev);
146     else
147         return $Theme->makeActionButton($action, $label, $page_or_rev);
148 }
149
150
151
152
153 class Theme {
154     var $HTML_DUMP_SUFFIX = '';
155     function Theme ($theme_name = 'default') {
156         $this->_name = $theme_name;
157         $themes_dir = defined('PHPWIKI_DIR') ? PHPWIKI_DIR . "/themes" : "themes";
158
159         $this->_path  = defined('PHPWIKI_DIR') ? PHPWIKI_DIR . "/" : "";
160         $this->_theme = "themes/$theme_name";
161
162         if ($theme_name != 'default')
163             $this->_default_theme = new Theme;
164     }
165
166     function file ($file) {
167         return $this->_path . "$this->_theme/$file";
168     }
169
170     function _findFile ($file, $missing_okay = false) {
171         if (file_exists($this->_path . "$this->_theme/$file"))
172             return "$this->_theme/$file";
173
174         // FIXME: this is a short-term hack.  Delete this after all files
175         // get moved into themes/...
176         if (file_exists($this->_path . $file))
177             return $file;
178
179
180         if (isset($this->_default_theme)) {
181             return $this->_default_theme->_findFile($file, $missing_okay);
182         }
183         else if (!$missing_okay) {
184             trigger_error("$file: not found", E_USER_NOTICE);
185         }
186         return false;
187     }
188
189     function _findData ($file, $missing_okay = false) {
190         $path = $this->_findFile($file, $missing_okay);
191         if (!$path)
192             return false;
193
194         if (defined('DATA_PATH'))
195             return DATA_PATH . "/$path";
196         return $path;
197     }
198
199     ////////////////////////////////////////////////////////////////
200     //
201     // Date and Time formatting
202     //
203     ////////////////////////////////////////////////////////////////
204
205     // Note:  Windows' implemetation of strftime does not include certain
206         // format specifiers, such as %e (for date without leading zeros).  In
207         // general, see:
208         // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_strftime.2c_.wcsftime.asp
209         // As a result, we have to use %d, and strip out leading zeros ourselves.
210
211     var $_dateFormat = "%B %d, %Y";
212     var $_timeFormat = "%I:%M %p";
213
214     var $_showModTime = true;
215
216     /**
217      * Set format string used for dates.
218      *
219      * @param $fs string Format string for dates.
220      *
221      * @param $show_mod_time bool If true (default) then times
222      * are included in the messages generated by getLastModifiedMessage(),
223      * otherwise, only the date of last modification will be shown.
224      */
225     function setDateFormat ($fs, $show_mod_time = true) {
226         $this->_dateFormat = $fs;
227         $this->_showModTime = $show_mod_time;
228     }
229
230     /**
231      * Set format string used for times.
232      *
233      * @param $fs string Format string for times.
234      */
235     function setTimeFormat ($fs) {
236         $this->_timeFormat = $fs;
237     }
238
239     /**
240      * Format a date.
241      *
242      * Any time zone offset specified in the users preferences is
243      * taken into account by this method.
244      *
245      * @param $time_t integer Unix-style time.
246      *
247      * @return string The date.
248      */
249     function formatDate ($time_t) {
250         global $request;
251         
252         $offset_time = $time_t + 3600 * $request->getPref('timeOffset');
253         // strip leading zeros from date elements (ie space followed by zero)
254         return preg_replace('/ 0/', ' ', 
255                             strftime($this->_dateFormat, $offset_time));
256     }
257
258     /**
259      * Format a date.
260      *
261      * Any time zone offset specified in the users preferences is
262      * taken into account by this method.
263      *
264      * @param $time_t integer Unix-style time.
265      *
266      * @return string The time.
267      */
268     function formatTime ($time_t) {
269         //FIXME: make 24-hour mode configurable?
270         global $request;
271         $offset_time = $time_t + 3600 * $request->getPref('timeOffset');
272         return preg_replace('/^0/', ' ',
273                             strtolower(strftime($this->_timeFormat, $offset_time)));
274     }
275
276     /**
277      * Format a date and time.
278      *
279      * Any time zone offset specified in the users preferences is
280      * taken into account by this method.
281      *
282      * @param $time_t integer Unix-style time.
283      *
284      * @return string The date and time.
285      */
286     function formatDateTime ($time_t) {
287         return $this->formatDate($time_t) . ' ' . $this->formatTime($time_t);
288     }
289
290     /**
291      * Format a (possibly relative) date.
292      *
293      * If enabled in the users preferences, this method might
294      * return a relative day (e.g. 'Today', 'Yesterday').
295      *
296      * Any time zone offset specified in the users preferences is
297      * taken into account by this method.
298      *
299      * @param $time_t integer Unix-style time.
300      *
301      * @return string The day.
302      */
303     function getDay ($time_t) {
304         global $request;
305         
306         if ($request->getPref('relativeDates') && ($date = $this->_relativeDay($time_t))) {
307             return ucfirst($date);
308         }
309         return $this->formatDate($time_t);
310     }
311     
312     /**
313      * Format the "last modified" message for a page revision.
314      *
315      * @param $revision object A WikiDB_PageRevision object.
316      *
317      * @param $show_version bool Should the page version number
318      * be included in the message.  (If this argument is omitted,
319      * then the version number will be shown only iff the revision
320      * is not the current one.
321      *
322      * @return string The "last modified" message.
323      */
324     function getLastModifiedMessage ($revision, $show_version = 'auto') {
325         global $request;
326
327         $mtime = $revision->get('mtime');
328         
329         if ($show_version == 'auto')
330             $show_version = !$revision->isCurrent();
331             
332         if ($request->getPref('relativeDates') && ($date = $this->_relativeDay($mtime))) {
333             if ($this->_showModTime)
334                 $date =  sprintf(_("%s at %s"),
335                                  $date, $this->formatTime($mtime));
336             
337             if ($show_version)
338                 return fmt("Version %s, saved %s.", $revision->getVersion(), $date);
339             else
340                 return fmt("Last edited %s.", $date);
341         }
342
343         if ($this->_showModTime)
344             $date = $this->formatDateTime($mtime);
345         else
346             $date = $this->formatDate($mtime);
347         
348         if ($show_version)
349             return fmt("Version %s, saved on %s.", $revision->getVersion(), $date);
350         else
351             return fmt("Last edited on %s.", $date);
352     }
353     
354     function _relativeDay ($time_t) {
355         global $request;
356         $offset = 3600 * $request->getPref('timeOffset');
357
358         $now = time() + $offset;
359         $today = localtime($now, true);
360         $time = localtime($time_t + $offset, true);
361
362         if ($time['tm_yday'] == $today['tm_yday'] && $time['tm_year'] == $today['tm_year'])
363             return _("today");
364         
365         // Note that due to daylight savings chages (and leap seconds), $now minus
366         // 24 hours is not guaranteed to be yesterday.
367         $yesterday = localtime($now - (12 + $today['tm_hour']) * 3600, true);
368         if ($time['tm_yday'] == $yesterday['tm_yday'] && $time['tm_year'] == $yesterday['tm_year'])
369             return _("yesterday");
370
371         return false;
372     }
373
374     ////////////////////////////////////////////////////////////////
375     //
376     // Hooks for other formatting
377     //
378     ////////////////////////////////////////////////////////////////
379
380     //FIXME: PHP 4.1 Warnings
381     //lib/Theme.php:84: Notice[8]: The call_user_method() function is deprecated,
382     //use the call_user_func variety with the array(&$obj, "method") syntax instead
383
384     function getFormatter ($type, $format) {
385         $method = strtolower("get${type}Formatter");
386         if (method_exists($this, $method))
387             return $this->{$method}($format);
388         return false;
389     }
390
391     ////////////////////////////////////////////////////////////////
392     //
393     // Links
394     //
395     ////////////////////////////////////////////////////////////////
396
397     var $_autosplitWikiWords = false;
398
399     function setAutosplitWikiWords($autosplit=false) {
400         $this->_autosplitWikiWords = $autosplit ? true : false;
401     }
402
403     function maybeSplitWikiWord ($wikiword) {
404         if ($this->_autosplitWikiWords)
405             return split_pagename($wikiword);
406         else
407             return $wikiword;
408     }
409
410     function linkExistingWikiWord($wikiword, $linktext = '', $version = false) {
411         if ($version !== false)
412             $url = WikiURL($wikiword, array('version' => $version));
413         else
414             $url = WikiURL($wikiword);
415
416         // Extra steps for dumping page to an html file.
417         if ($this->HTML_DUMP_SUFFIX) {
418             // urlencode for pagenames with accented letters
419             $url = rawurlencode($url);
420             $url = preg_replace('/^\./', '%2e', $url);
421             $url .= $this->HTML_DUMP_SUFFIX;
422         }
423         $link = HTML::a(array('href' => $url));
424
425         if (!empty($linktext)) {
426             $link->pushContent($linktext);
427             $link->setAttr('class', 'named-wiki');
428             $link->setAttr('title', $this->maybeSplitWikiWord($wikiword));
429         }
430         else {
431             $link->pushContent($this->maybeSplitWikiWord($wikiword));
432             $link->setAttr('class', 'wiki');
433         }
434         return $link;
435     }
436
437     function linkUnknownWikiWord($wikiword, $linktext = '') {
438         $url = WikiURL($wikiword, array('action' => 'edit'));
439         //$link = HTML::span(HTML::a(array('href' => $url), '?'));
440         $button = $this->makeButton('?', $url);
441         $button->addTooltip(sprintf(_("Edit: %s"), $wikiword));
442         $link = HTML::span($button);
443
444
445         if (!empty($linktext)) {
446             $link->pushContent(HTML::u($linktext));
447             $link->setAttr('class', 'named-wikiunknown');
448         }
449         else {
450             $link->pushContent(HTML::u($this->maybeSplitWikiWord($wikiword)));
451             $link->setAttr('class', 'wikiunknown');
452         }
453
454         return $link;
455     }
456
457     ////////////////////////////////////////////////////////////////
458     //
459     // Images and Icons
460     //
461     ////////////////////////////////////////////////////////////////
462
463     /**
464      *
465      * (To disable an image, alias the image to <code>false</code>.
466      */
467     function addImageAlias ($alias, $image_name) {
468         $this->_imageAliases[$alias] = $image_name;
469     }
470
471     function getImageURL ($image) {
472         $aliases = &$this->_imageAliases;
473
474         if (isset($aliases[$image])) {
475             $image = $aliases[$image];
476             if (!$image)
477                 return false;
478         }
479
480         // If not extension, default to .png.
481         if (!preg_match('/\.\w+$/', $image))
482             $image .= '.png';
483
484         // FIXME: this should probably be made to fall back
485         //        automatically to .gif, .jpg.
486         //        Also try .gif before .png if browser doesn't like png.
487
488         $path = $this->_findData("images/$image", 'missing okay');
489         if (!$path) // search explicit images/ or button/ links also
490             return $this->_findData("$image", 'missing okay');
491         else 
492             return $path;       
493     }
494
495     function setLinkIcon($proto, $image = false) {
496         if (!$image)
497             $image = $proto;
498
499         $this->_linkIcons[$proto] = $image;
500     }
501
502     function getLinkIconURL ($proto) {
503         $icons = &$this->_linkIcons;
504         if (!empty($icons[$proto]))
505             return $this->getImageURL($icons[$proto]);
506         elseif (!empty($icons['*']))
507             return $this->getImageURL($icons['*']);
508         return false;
509     }
510
511     function addButtonAlias ($text, $alias = false) {
512         $aliases = &$this->_buttonAliases;
513
514         if (is_array($text))
515             $aliases = array_merge($aliases, $text);
516         elseif ($alias === false)
517             unset($aliases[$text]);
518         else
519             $aliases[$text] = $alias;
520     }
521
522     function getButtonURL ($text) {
523         $aliases = &$this->_buttonAliases;
524         if (isset($aliases[$text]))
525             $text = $aliases[$text];
526
527         $qtext = urlencode($text);
528         $url = $this->_findButton("$qtext.png");
529         if ($url && strstr($url, '%')) {
530             $url = preg_replace('|([^/]+)$|e', 'urlencode("\\1")', $url);
531         }
532         if (!$url) {// Jeff complained about png not supported everywhere. This is not PC
533             $url = $this->_findButton("$qtext.gif");
534             if ($url && strstr($url, '%')) {
535                 $url = preg_replace('|([^/]+)$|e', 'urlencode("\\1")', $url);
536             }
537         }
538         return $url;
539     }
540
541     function _findButton ($button_file) {
542         if (!isset($this->_button_path))
543             $this->_button_path = $this->_getButtonPath();
544
545         foreach ($this->_button_path as $dir) {
546             $path = "$this->_theme/$dir/$button_file";
547             if (file_exists($this->_path . $path))
548                 return defined('DATA_PATH') ? DATA_PATH . "/$path" : $path;
549         }
550         return false;
551     }
552
553     function _getButtonPath () {
554         $button_dir = $this->file("buttons");
555         if (!file_exists($button_dir) || !is_dir($button_dir))
556             return array();
557
558         $path = array('buttons');
559
560         $dir = dir($button_dir);
561         while (($subdir = $dir->read()) !== false) {
562             if ($subdir[0] == '.')
563                 continue;
564             if (is_dir("$button_dir/$subdir"))
565                 $path[] = "buttons/$subdir";
566         }
567         $dir->close();
568
569         return $path;
570     }
571
572     ////////////////////////////////////////////////////////////////
573     //
574     // Button style
575     //
576     ////////////////////////////////////////////////////////////////
577
578     function makeButton ($text, $url, $class = false) {
579         // FIXME: don't always try for image button?
580
581         // Special case: URLs like 'submit:preview' generate form
582         // submission buttons.
583         if (preg_match('/^submit:(.*)$/', $url, $m))
584             return $this->makeSubmitButton($text, $m[1], $class);
585
586         $imgurl = $this->getButtonURL($text);
587         if ($imgurl)
588             return new ImageButton($text, $url, $class, $imgurl);
589         else
590             return new Button($text, $url, $class);
591     }
592
593     function makeSubmitButton ($text, $name, $class = false) {
594         $imgurl = $this->getButtonURL($text);
595
596         if ($imgurl)
597             return new SubmitImageButton($text, $name, $class, $imgurl);
598         else
599             return new SubmitButton($text, $name, $class);
600     }
601
602     /**
603      * Make button to perform action.
604      *
605      * This constructs a button which performs an action on the
606      * currently selected version of the current page.
607      * (Or anotherpage or version, if you want...)
608      *
609      * @param $action string The action to perform (e.g. 'edit', 'lock').
610      * This can also be the name of an "action page" like 'LikePages'.
611      * Alternatively you can give a hash of query args to be applied
612      * to the page.
613      *
614      * @param $label string Textual label for the button.  If left empty,
615      * a suitable name will be guessed.
616      *
617      * @param $page_or_rev mixed  The page to link to.  This can be
618      * given as a string (the page name), a WikiDB_Page object, or as
619      * WikiDB_PageRevision object.  If given as a WikiDB_PageRevision
620      * object, the button will link to a specific version of the
621      * designated page, otherwise the button links to the most recent
622      * version of the page.
623      *
624      * @return object A Button object.
625      */
626     function makeActionButton ($action, $label = false, $page_or_rev = false) {
627         extract($this->_get_name_and_rev($page_or_rev));
628
629         if (is_array($action)) {
630             $attr = $action;
631             $action = isset($attr['action']) ? $attr['action'] : 'browse';
632         }
633         else
634             $attr['action'] = $action;
635
636         $class = is_safe_action($action) ? 'wikiaction' : 'wikiadmin';
637         if (!$label)
638             $label = $this->_labelForAction($action);
639
640         if ($version)
641             $attr['version'] = $version;
642
643         if ($action == 'browse')
644             unset($attr['action']);
645
646         return $this->makeButton($label, WikiURL($pagename, $attr), $class);
647     }
648
649     /**
650      * Make a "button" which links to a wiki-page.
651      *
652      * These are really just regular WikiLinks, possibly
653      * disguised (e.g. behind an image button) by the theme.
654      *
655      * This method should probably only be used for links
656      * which appear in page navigation bars, or similar places.
657      *
658      * Use linkExistingWikiWord, or LinkWikiWord for normal links.
659      *
660      * @param $page_or_rev mixed The page to link to.  This can be
661      * given as a string (the page name), a WikiDB_Page object, or as
662      * WikiDB_PageRevision object.  If given as a WikiDB_PageRevision
663      * object, the button will link to a specific version of the
664      * designated page, otherwise the button links to the most recent
665      * version of the page.
666      *
667      * @return object A Button object.
668      */
669     function makeLinkButton ($page_or_rev, $label = false) {
670         extract($this->_get_name_and_rev($page_or_rev));
671
672         $args = $version ? array('version' => $version) : false;
673
674         return $this->makeButton($label ? $label : $pagename, WikiURL($pagename, $args), 'wiki');
675     }
676
677     function _get_name_and_rev ($page_or_rev) {
678         $version = false;
679
680         if (empty($page_or_rev)) {
681             global $request;
682             $pagename = $request->getArg("pagename");
683             $version = $request->getArg("version");
684         }
685         elseif (is_object($page_or_rev)) {
686             if (isa($page_or_rev, 'WikiDB_PageRevision')) {
687                 $rev = $page_or_rev;
688                 $page = $rev->getPage();
689                 $version = $rev->getVersion();
690             }
691             else {
692                 $page = $page_or_rev;
693             }
694             $pagename = $page->getName();
695         }
696         else {
697             $pagename = (string) $page_or_rev;
698         }
699         return compact('pagename', 'version');
700     }
701
702     function _labelForAction ($action) {
703         switch ($action) {
704             case 'edit':   return _("Edit");
705             case 'diff':   return _("Diff");
706             case 'logout': return _("Sign Out");
707             case 'login':  return _("Sign In");
708             case 'lock':   return _("Lock Page");
709             case 'unlock': return _("Unlock Page");
710             case 'remove': return _("Remove Page");
711             default:
712                 // I don't think the rest of these actually get used.
713                 // 'setprefs'
714                 // 'upload' 'dumpserial' 'loadfile' 'zip'
715                 // 'save' 'browse'
716                 return ucfirst($action);
717         }
718     }
719
720     //----------------------------------------------------------------
721     var $_buttonSeparator = "\n | ";
722
723     function setButtonSeparator($separator) {
724         $this->_buttonSeparator = $separator;
725     }
726
727     function getButtonSeparator() {
728         return $this->_buttonSeparator;
729     }
730
731
732     ////////////////////////////////////////////////////////////////
733     //
734     // CSS
735     //
736     ////////////////////////////////////////////////////////////////
737
738     function _CSSlink($title, $css_file, $media, $is_alt = false) {
739         $link = HTML::link(array('rel'     => $is_alt ? 'alternate stylesheet' : 'stylesheet',
740                                  'title'   => $title,
741                                  'type'    => 'text/css',
742                                  'charset' => CHARSET,
743                                  'href'    => $this->_findData($css_file)));
744         if ($media)
745             $link->setAttr('media', $media);
746         return $link;
747     }
748
749     function setDefaultCSS ($title, $css_file, $media = false) {
750         if (isset($this->_defaultCSS)) {
751             $oldtitle = $this->_defaultCSS->_attr['title'];
752             $error = sprintf("'%s' -> '%s'", $oldtitle, $title);
753             trigger_error(sprintf(_("Redefinition of %s: %s"), "'default CSS'", $error),
754                           E_USER_NOTICE);
755         }
756         if (isset($this->_alternateCSS))
757             unset($this->_alternateCSS[$title]);
758         $this->_defaultCSS = $this->_CSSlink($title, $css_file, $media);
759     }
760
761     function addAlternateCSS ($title, $css_file, $media = false) {
762         $this->_alternateCSS[$title] = $this->_CSSlink($title, $css_file, $media, true);
763     }
764
765     /**
766         * @return string HTML for CSS.
767      */
768     function getCSS () {
769         $css = HTML($this->_defaultCSS);
770         if (!empty($this->_alternateCSS))
771             $css->pushContent($this->_alternateCSS);
772         return $css;
773     }
774
775     function findTemplate ($name) {
776         return $this->_path . $this->_findFile("templates/$name.tmpl");
777     }
778 };
779
780
781 /**
782  * A class representing a clickable "button".
783  *
784  * In it's simplest (default) form, a "button" is just a link associated
785  * with some sort of wiki-action.
786  */
787 class Button extends HtmlElement {
788     /** Constructor
789      *
790      * @param $text string The text for the button.
791      * @param $url string The url (href) for the button.
792      * @param $class string The CSS class for the button.
793      */
794     function Button ($text, $url, $class = false) {
795         $this->HtmlElement('a', array('href' => $url));
796         if ($class)
797             $this->setAttr('class', $class);
798         $this->pushContent($text);
799     }
800
801 };
802
803
804 /**
805  * A clickable image button.
806  */
807 class ImageButton extends Button {
808     /** Constructor
809      *
810      * @param $text string The text for the button.
811      * @param $url string The url (href) for the button.
812      * @param $class string The CSS class for the button.
813      * @param $img_url string URL for button's image.
814      * @param $img_attr array Additional attributes for the &lt;img&gt; tag.
815      */
816     function ImageButton ($text, $url, $class, $img_url, $img_attr = false) {
817         $this->HtmlElement('a', array('href' => $url));
818         if ($class)
819             $this->setAttr('class', $class);
820
821         if (!is_array($img_attr))
822             $img_attr = array();
823         $img_attr['src'] = $img_url;
824         $img_attr['alt'] = $text;
825         $img_attr['class'] = 'wiki-button';
826         $img_attr['border'] = 0;
827         $this->pushContent(HTML::img($img_attr));
828     }
829 };
830
831 /**
832  * A class representing a form <samp>submit</samp> button.
833  */
834 class SubmitButton extends HtmlElement {
835     /** Constructor
836      *
837      * @param $text string The text for the button.
838      * @param $name string The name of the form field.
839      * @param $class string The CSS class for the button.
840      */
841     function SubmitButton ($text, $name = false, $class = false) {
842         $this->HtmlElement('input', array('type' => 'submit',
843                                           'value' => $text));
844         if ($name)
845             $this->setAttr('name', $name);
846         if ($class)
847             $this->setAttr('class', $class);
848     }
849
850 };
851
852
853 /**
854  * A class representing an image form <samp>submit</samp> button.
855  */
856 class SubmitImageButton extends SubmitButton {
857     /** Constructor
858      *
859      * @param $text string The text for the button.
860      * @param $name string The name of the form field.
861      * @param $class string The CSS class for the button.
862      * @param $img_url string URL for button's image.
863      * @param $img_attr array Additional attributes for the &lt;img&gt; tag.
864      */
865     function SubmitImageButton ($text, $name = false, $class = false, $img_url) {
866         $this->HtmlElement('input', array('type'  => 'image',
867                                           'src'   => $img_url,
868                                           'value' => $text,
869                                           'alt'   => $text));
870         if ($name)
871             $this->setAttr('name', $name);
872         if ($class)
873             $this->setAttr('class', $class);
874     }
875
876 };
877
878
879 // (c-file-style: "gnu")
880 // Local Variables:
881 // mode: php
882 // tab-width: 8
883 // c-basic-offset: 4
884 // c-hanging-comment-ender-p: nil
885 // indent-tabs-mode: nil
886 // End:
887 ?>