xref: /web-php/include/layout.inc (revision 555ac83c)
1<?php
2$_SERVER['STATIC_ROOT'] = $MYSITE;
3$_SERVER['MYSITE'] = $MYSITE;
4
5// Use class names instead of colors
6ini_set('highlight.comment', 'comment');
7ini_set('highlight.default', 'default');
8ini_set('highlight.keyword', 'keyword');
9ini_set('highlight.string',  'string');
10ini_set('highlight.html',    'html');
11
12// Highlight PHP code
13function highlight_php($code, $return = false)
14{
15    $highlighted = highlight_string($code, true);
16
17    // Use this ugly hack for now to avoid code snippets with bad syntax screwing up the highlighter
18    if (strstr($highlighted, "include/layout.inc</b>")) {
19        $highlighted = '<span class="html">' . nl2br(htmlentities($code, ENT_HTML5), false) . "</span>";
20    }
21
22    // Fix output to use CSS classes and wrap well
23    $highlighted = '<div class="phpcode">' . strtr(
24        $highlighted,
25        [
26            '&nbsp;' => ' ',
27            "\n" => '',
28
29            '<span style="color: ' => '<span class="',
30        ],
31    ) . '</div>';
32
33    if ($return) { return $highlighted; }
34    echo $highlighted;
35    return null;
36}
37
38// Same as highlight_php() but does not require '<?php' in $code
39function highlight_php_trimmed($code, $return = false)
40{
41    $code = "<?php\n" . $code;
42    $highlighted_code = highlight_php($code, true);
43    $highlighted_code = preg_replace("!&lt;\?php(<br />)+!", '', $highlighted_code, 1);
44
45    if ($return) { return $highlighted_code; }
46    echo $highlighted_code;
47    return null;
48}
49
50// Resize the image using the output of make_image()
51function resize_image($img, $width = 1, $height = 1)
52{
53    // Drop width and height values from image if available
54    $str = preg_replace('!width=\"([0-9]+?)\"!i',  '', $img);
55    $str = preg_replace('!height=\"([0-9]+?)\"!i', '', $str);
56
57    // Return image with new width and height added
58    return preg_replace(
59        '!/?>$!',
60        sprintf(' height="%s" width="%s">', $height, $width),
61        $str,
62    );
63}
64
65// Return an <img> tag for a given image file available on the server
66function make_image($file, $alt = false, $align = false, $extras = false,
67                    $dir = '/images', $addsize = false)
68{
69    // If no / was provided at the start of $dir, add it
70    $webdir = $_SERVER['MYSITE'] . ($dir[0] == '/' ? '' : '/') . $dir;
71
72    // Get width and height values if possible
73    if ($addsize && ($size = @getimagesize($_SERVER['DOCUMENT_ROOT'] . "$dir/$file"))) {
74        $sizeparams = ' ' . trim($size[3]);
75    } else {
76        $sizeparams = '';
77    }
78
79    // Convert right or left alignment to CSS float,
80    // but leave other alignments intact (for now)
81    if (in_array($align, ["right", "left"], true)) {
82        $align = ' style="float: ' . $align . ';"';
83    } elseif ($align) {
84        $align = ' align="' . $align . '"';
85    } else {
86        $align = '';
87    }
88
89    // Return with image built up
90    return sprintf('<img src="%s/%s" alt="%s"%s%s%s>',
91        $webdir,
92        $file,
93        ($alt ?: ''),
94        $sizeparams,
95        $align,
96        ($extras ? ' ' . $extras : ''),
97    );
98}
99
100// Print an <img> tag out for a given file
101function print_image($file, $alt = false, $align = false, $extras = false,
102                     $dir = '/images'): void
103{
104    echo make_image($file, $alt, $align, $extras, $dir);
105}
106
107// Shortcut to usual news image printing (right floating
108// image from the news dir with an alt and an URL)
109function news_image($URL, $image, $alt, $print = true)
110{
111    $str = "<a href=\"$URL\">" . make_image("news/$image", $alt, "right") . "</a>";
112    if ($print) {
113        echo $str;
114    }
115    return $str;
116}
117
118// Return HTML code for a submit button image
119function make_submit($file, $alt = false, $align = false, $extras = false,
120                     $dir = '/images', $border = 0)
121{
122    // Get an image without size info and convert the
123    // border attribute to use CSS, as border="" is not
124    // supported on <input> elements in [X]HTML
125    $img = make_image($file, $alt, $align, $extras, $dir, false);
126    $img = str_replace(
127        "border=\"$border\"",
128        "style=\"border: {$border}px;\"",
129        $img,
130    );
131
132    // Return with ready input image
133    return '<input type="image"' . substr($img, 4);
134}
135
136// Return a hiperlink to something within the site
137function make_link(string $url, string $linktext = ''): string
138{
139    return sprintf("<a href=\"%s\">%s</a>", $url, $linktext ?: $url);
140}
141
142// make_popup_link()
143// return a hyperlink to something, within the site, that pops up a new window
144//
145function make_popup_link($url, $linktext = false, $target = false, $windowprops = "", $extras = false) {
146    return sprintf("<a href=\"%s\" target=\"%s\" onclick=\"window.open('%s','%s','%s');return false;\"%s>%s</a>",
147        htmlspecialchars($url, ENT_QUOTES | ENT_IGNORE),
148        ($target ?: "_new"),
149        htmlspecialchars($url, ENT_QUOTES | ENT_IGNORE),
150        ($target ?: "_new"),
151                $windowprops,
152        ($extras ? ' ' . $extras : ''),
153        ($linktext ?: $url),
154    );
155}
156
157// Print a link for a downloadable file (including filesize)
158function download_link($file, $title): void
159{
160    $download_link = "/distributions/" . $file;
161
162    // Print out the download link
163    echo make_link($download_link, $title);
164
165    // We have a full path or a relative to the distributions dir
166    if ($tmp = strrchr($file, "/")) {
167        $local_file = substr($tmp, 1, strlen($tmp));
168    } else {
169        $local_file = "distributions/$file";
170    }
171
172    if (@file_exists($local_file . ".asc")) {
173        echo " ";
174        $sig_link = "/distributions/$file.asc";
175        echo make_link($sig_link, "(sig)");
176    }
177
178    // Try to get the size of the file
179    $size = @filesize($local_file);
180
181    // Print out size in bytes (if size is
182    // less then 1Kb, or else in Kb)
183    if ($size) {
184        echo ' [';
185        if ($size < 1024) {
186            echo number_format($size) . 'b';
187        } else {
188            echo number_format($size / 1024) . 'Kb';
189        }
190        echo ']';
191    }
192}
193
194function clean($var) {
195  return htmlspecialchars($var, ENT_QUOTES);
196}
197
198// Clean out the content of one user note for printing to HTML
199function clean_note($text)
200{
201    // Highlight PHP source
202    $text = highlight_php(trim($text), true);
203
204    // Turn urls into links
205    return preg_replace(
206        '!((mailto:|(https?|ftp|nntp|news)://).*?)(\s|<|\)|"|\\\\|\'|$)!',
207        '<a href="\1" rel="nofollow" target="_blank">\1</a>\4',
208        $text,
209    );
210}
211
212function display_errors($errors): void
213{
214    echo '<div class="errors">';
215    if (count($errors) > 1) {
216        echo "You need to do the following before your submission will be accepted:<ul>";
217        foreach ($errors as $error) {
218            echo "<li>$error</li>\n";
219        }
220        echo "</ul>";
221    }
222    else {
223        echo $errors[0];
224    }
225    echo '</div>';
226}
227
228// Displays an event. Used in event submission
229// previews and event information displays
230function display_event($event, $include_date = 1): void
231{
232    global $COUNTRIES;
233    // Current month (int)($_GET['cm'] ?: 0)
234    global $cm;
235    // Current year (int)($_GET['cy'] ?: 0)
236    global $cy;
237
238    // Weekday names array
239    for ($i = 1; $i <= 7; $i++) {
240        $days[$i] = date('l', mktime(12, 0, 0, 4, $i, 2012));
241    }
242
243    // Recurring possibilities
244    $re = [
245        1 => 'First',
246        2 => 'Second',
247        3 => 'Third',
248        4 => 'Fourth',
249        -1 => 'Last',
250        -2 => '2nd Last',
251        -3 => '3rd Last',
252    ];
253
254    if (!isset($event['start']) && isset($event['sday'])) {
255        $sday = mktime(12,0,0,$event['smonth'],$event['sday'],$event['syear']);
256    } else {
257        $sday = (isset($event['start']) && !empty($event['start'])) ? strtotime($event['start']) : 0;
258    }
259
260    if (!isset($event['end']) && isset($event['eday'])) {
261        $eday = mktime(12,0,0,$event['emonth'],$event['eday'],$event['eyear']);
262    } else {
263        $eday = (isset($event['end']) && !empty($event['end'])) ? strtotime($event['end']) : 0;
264    }
265?>
266<table border="0" cellspacing="0" cellpadding="3" width="100%" class="vevent">
267 <tr bgcolor="#dddddd"><td>
268<?php
269
270    // Print out date if needed
271    if ($include_date && (isset($event['start']))) {
272        echo "<b>", date("F j, Y", $sday), "</b>\n";
273    }
274
275    // Print link in case we have one
276    if ($event['url']) { echo '<a href="', htmlentities($event['url'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'),'" class="url">'; }
277    // Print event description
278    echo "<b class='summary'>", stripslashes(htmlentities($event['sdesc'], ENT_QUOTES | ENT_IGNORE, 'UTF-8')), "</b>";
279    // End link
280    if ($event['url']) { echo "</a>"; }
281
282    // Print extra date info for recurring and multiday events
283    switch ($event['type']) {
284        case 2:
285        case 'multi':
286            $dtend = date("Y-m-d", strtotime("+1 day", $eday));
287            echo " (<abbr class='dtstart'>", date("Y-m-d",$sday), "</abbr> to <abbr class='dtend' title='$dtend'>", date("Y-m-d",$eday), "</abbr>)";
288            break;
289        case 3:
290        case 'recur':
291            $days = $re[$event['recur']] . " " . $days[$event['recur_day']];
292            if (!$cm || $cy) {
293                $cm = date("m");
294                $cy = date("Y");
295            }
296            $month = date("M", mktime(0, 0, 0, $cm, 1, $cy));
297            $dtstart = date("Y-m-d", strtotime($days . ' 0st' . $month . ' ' . $cy));
298            echo ' (Every <abbr class="dtstart" title="' . $dtstart . '">', $days, "</abbr> of the month)";
299            break;
300    }
301
302    // Event category
303    if (isset($event['category']) && $event['category']) {
304        $cat = ["unknown", "User Group Event", "Conference", "Training"];
305        echo ' [' . $cat[$event['category']] . '] ';
306    }
307
308    // Print out country information
309    echo ' (<span class="location">' , $COUNTRIES[$event['country']] , '</span>)';
310?>
311 </td></tr>
312 <tr bgcolor="#eeeeee" class="description"><td>
313<?php
314
315    // Print long description
316    echo preg_replace("/\r?\n\r?\n/", "<br><br>", trim(htmlentities($event['ldesc'],ENT_QUOTES | ENT_IGNORE, 'UTF-8')));
317    // If we have an URL, print it out
318    if ($event['url']) {
319        echo '<br><br><b>URL:</b> ',
320             '<a href="', htmlentities($event['url'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'), '">',
321             htmlentities($event['url'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'), '</a>';
322    }
323?>
324 </td></tr>
325</table>
326<?php
327}
328
329// Print news links for archives
330function news_archive_sidebar(): void
331{
332    global $SIDEBAR_DATA;
333    $SIDEBAR_DATA = '
334<h3 class="announcements">Archives by year</h3>
335
336';
337    for ($i = date("Y"); $i >= 1998; $i--) {
338        $pagename = "archive/$i.php";
339        $classname = ($pagename == $_SERVER['BASE_PAGE'] ? " active" : '');
340        $SIDEBAR_DATA .= "<p class='panel{$classname}'><a href=\"/{$pagename}\">{$i}</a></p>\n";
341    }
342}
343
344// Print news
345function print_news($news, $dog, $max = 5, $onlyyear = null, $return = false) {
346    $retval = [];
347    $count = 0;
348    $news = $news ?: []; // default to empty array (if no news)
349    foreach ($news as $item) {
350        $ok = false;
351
352        // Only print entries in the provided s/dog/cat/ egory
353        // If $dog is null, everything matches
354        foreach ($item["category"] as $category) {
355            if (null === $dog || in_array($category["term"], (array)$dog, true)) {
356                $ok = true;
357                $count++;
358                break;
359            }
360        }
361        if ($count > $max) {
362            break;
363        }
364        if ($ok === false) {
365            continue;
366        }
367
368        $image = "";
369        if (isset($item["newsImage"])) {
370            $image = news_image($item["newsImage"]["link"], $item["newsImage"]["content"], $item["newsImage"]["title"], false);
371        }
372
373        $id = parse_url($item["id"]);
374        $id = $id["fragment"];
375
376        // Find the permlink
377        foreach ($item["link"] as $link) {
378            if ($link["rel"] === "via") {
379                $permlink = $link["href"];
380                break;
381            }
382        }
383        if (!isset($permlink)) {
384            $permlink = "#" . $id;
385        }
386
387        $published = substr($item["published"], 0, 10);
388        $nixtimestamp = strtotime($published);
389        $newsdate = date("d M Y", $nixtimestamp);
390        if ($onlyyear && date("Y", $nixtimestamp) != $onlyyear) {
391            $count--;
392            continue;
393        }
394
395        if ($return) {
396            $retval[] = [
397                "title" => $item["title"],
398                "id" => $id,
399                "permlink" => $permlink,
400                "date" => $newsdate,
401            ];
402            continue;
403        }
404
405        echo <<<EOT
406<article class="newsItem">
407  <header>
408    <div class="newsImage">{$image}</div>
409    <h2 class="newstitle"><a id="{$id}" href="{$permlink}" rel="bookmark" class="bookmark">{$item["title"]}</a></h2>
410  </header>
411  <time class="newsdate" datetime="{$item["published"]}">{$newsdate}</time>
412  <div class="newscontent">
413    {$item["content"]}
414  </div>
415</article>
416
417EOT;
418    }
419
420    return $retval;
421}
422
423function site_header(string $title = 'Hypertext Preprocessor', array $config = []): void
424{
425    global $MYSITE;
426
427    $defaults = [
428        "lang" => myphpnet_language(),
429        "current" => "",
430        "meta-navigation" => [],
431        'classes' => '',
432        'layout_span' => 9,
433        "cache" => false,
434        "headsup" => "",
435    ];
436
437    $config = array_merge($defaults, $config);
438
439    $config["headsup"] = get_news_changes();
440
441    $lang = language_convert($config["lang"]);
442    $curr = $config["current"];
443    $classes = $config['classes'];
444
445    if (isset($_COOKIE["MD"]) || isset($_GET["MD"])) {
446        $classes .= "markdown-content";
447        $config["css_overwrite"] = ["/styles/i-love-markdown.css"];
448    }
449
450    // shorturl; http://wiki.snaplog.com/short_url
451    if (isset($_SERVER['BASE_PAGE']) && $shortname = get_shortname($_SERVER["BASE_PAGE"])) {
452        $shorturl = "https://www.php.net/" . $shortname;
453    }
454
455    require __DIR__ . "/header.inc";
456}
457function site_footer(array $config = []): void
458{
459    require __DIR__ . "/footer.inc";
460}
461
462function get_news_changes()
463{
464    include __DIR__ . "/pregen-news.inc";
465    $date = date_create($NEWS_ENTRIES[0]["updated"]);
466    if (isset($_COOKIE["LAST_NEWS"]) && $_COOKIE["LAST_NEWS"] >= $date->getTimestamp()) {
467        return false;
468    }
469
470    /* It is a bug when this happens.. but I don't know where it is coming from */
471    if (!isset($_SERVER["BASE_PAGE"])) {
472        return false;
473    }
474    if ($_SERVER["BASE_PAGE"] == "index.php") {
475        return false;
476    }
477
478    $date->modify("+1 week");
479    if ($date->getTimestamp() > $_SERVER["REQUEST_TIME"]) {
480        $link = preg_replace('~^(http://php.net/|https://www.php.net/)~', '/', $NEWS_ENTRIES[0]["link"][0]["href"]);
481        $title = $NEWS_ENTRIES[0]["title"];
482        return "<a href='{$link}'>{$title}</a>";
483    }
484    return false;
485}
486
487function doc_toc($lang): void {
488    $file = __DIR__ . "/../manual/$lang/toc/index.inc";
489    if (!file_exists($file)) {
490        $lang = "en"; // Fallback on english if the translation doesn't exist
491        $file = __DIR__ . "/../manual/en/toc/index.inc";
492    }
493    require __DIR__ . "/../manual/$lang/toc/index.inc";
494
495    echo "<dl>\n";
496    doc_toc_list($lang, $TOC, "getting-started");
497    doc_toc_list($lang, $TOC, "langref");
498    echo "</dl>\n";
499
500    echo "<dl>\n";
501    doc_toc_list($lang, $TOC, "security");
502    doc_toc_list($lang, $TOC, "features");
503    echo "</dl>\n";
504
505    echo "<dl>\n";
506    doc_toc_list($lang, $TOC, "funcref");
507    echo "</dl>\n";
508
509    echo "<dl>\n";
510    echo "<dt>Keyboard Shortcuts</dt>";
511    echo "<dt>?</dt>\n";
512    echo "<dd>This help</dd>\n";
513    echo "<dt>j</dt>\n";
514    echo "<dd>Next menu item</dd>\n";
515    echo "<dt>k</dt>\n";
516    echo "<dd>Previous menu item</dd>\n";
517    echo "<dt>g p</dt>\n";
518    echo "<dd>Previous man page</dd>\n";
519    echo "<dt>g n</dt>\n";
520    echo "<dd>Next man page</dd>\n";
521    echo "<dt>G</dt>\n";
522    echo "<dd>Scroll to bottom</dd>\n";
523    echo "<dt>g g</dt>\n";
524    echo "<dd>Scroll to top</dd>\n";
525    echo "<dt>g h</dt>\n";
526    echo "<dd>Goto homepage</dd>\n";
527    echo "<dt>g s</dt>\n";
528    echo "<dd>Goto search<br>(current page)</dd>\n";
529    echo "<dt>/</dt>\n";
530    echo "<dd>Focus search box</dd>\n";
531    echo "</dl>";
532
533}
534function doc_toc_list($lang, $index, $file): void {
535    include __DIR__ . "/../manual/$lang/toc/$file.inc";
536
537    doc_toc_title($lang, $index, $file);
538    foreach ($TOC as $entry) {
539        echo "\t<dd><a href='/manual/$lang/{$entry[0]}'>{$entry[1]}</a></dd>\n";
540    }
541}
542function doc_toc_title($lang, $index, $file, $elm = "dt"): void {
543    foreach ($index as $entry) {
544        if ($entry[0] == "$file.php") {
545            $link = $entry[0];
546            $title = $entry[1];
547            break;
548        }
549    }
550    echo "<$elm><a href='/manual/$lang/$link'>$title</a></$elm>\n";
551}
552