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