-================================================================================
- dpolakovic.space
- website files for dpolakovic.space and all it's subdomains
-================================================================================
-
-AUTHOR : David Polakovic
-EMAIL : email@dpolakovic.space
-TXT VERSION : 1.0.0
-LAST UPDATED: 2026-02-24
-LICENSE : GPLv3, with exception of /Git-server/Gtiweb folder
-
---------------------------------------------------------------------------------
-TABLE OF CONTENTS
---------------------------------------------------------------------------------
-
- 1. Overview
- 2. Project structure
- 3. Requirements
- 4. Installation
- 5. Usage
- 6. Reference
- 7. Configuration
- 8. Examples
- 9. Troubleshooting
- 10. License
-
---------------------------------------------------------------------------------
-1. OVERVIEW
---------------------------------------------------------------------------------
-
- This file describes everything I host on my server.
-
---------------------------------------------------------------------------------
-2. PROJECT STRUCTURE
---------------------------------------------------------------------------------
-
- dpolakovic-space
- ├── Git-server ## files from git.dpolakovic.space
- │ ├── Coffee ## RFC 2324 implementation
- │ │ ├── coffee-counter.js # counts served coffees from 48h ago
- │ │ └── coffee-server.php # custom http methods definitions
- │ ├── Gitweb ## gitweb (front page) files
- │ │ ├── static
- │ │ │ ├── gitweb.css
- │ │ │ └── gitweb.js
- │ │ ├── banner.html # contains JS for clicky images
- │ │ ├── footer.html # contains JS which counts coffees
- │ │ ├── gitweb.cgi # front page logic customization
- │ │ └── indextext.html # contains styles for front page
- │ └── Stuff ## other pages present on git server
- │ ├── coffee-server.html
- │ ├── contributors-guide.html
- │ ├── dot.png
- │ ├── server-rules.html
- │ ├── stuff.css
- │ └── stuff.js
- │
- ├── Root-domain ## files from www.dpolakovic.space
- │ ├── blogs ## contains all blog pages
- │ ├── error
- │ │ └── error.php # custom http status code handler
- │ ├── php
- │ │ ├── config.php # logic file included in all pages
- │ │ └── leaderboard.php # logic file for leaderboard of CTF
- │ ├── Pictures
- │ ├── Styles
- │ │ ├── FONT_LICENSE.txt
- │ │ ├── NotoSerif-Bold.ttf
- │ │ ├── NotoSerif-Italic.ttf
- │ │ ├── NotoSerif-Regular.ttf
- │ │ └── styles.css
- │ ├── brew.php # implementation of RFC 2324
- │ ├── clicky-images.js
- │ └── example-page.php
- │
- ├── Tor-site ## files from Tor site
- │ ├── Dead-drops ## encrypted messages from dead drop
- │ ├── php
- │ │ ├── captcha.php # captcha for dead drops
- │ │ ├── config.php # config for all pages
- │ │ └── dd2.php # dead drop logic
- │ ├── Pictures
- │ ├── Styles
- │ │ ├── FONT_LICENSE.txt
- │ │ ├── NotoSerif-Bold.ttf
- │ │ ├── NotoSerif-Italic.ttf
- │ │ ├── NotoSerif-Regular.ttf
- │ │ └── styles.css
- │ ├── dead-drop.php # dead drop front page
- │ └── example-page.php
- │
- ├── LICENSE.txt
- └── README.txt
-
-
---------------------------------------------------------------------------------
-3. REQUIREMENTS
---------------------------------------------------------------------------------
-
- - web server (tested on Apache)
- - PHP (any version)
- - git (any version < 3.0.0)
- - gitweb (any version)
-
---------------------------------------------------------------------------------
-4. INSTALLATION
---------------------------------------------------------------------------------
-
- 1. Clone or download this repository.
-
- 2. Copy any file you want into your server.
-
---------------------------------------------------------------------------------
-5. USAGE
---------------------------------------------------------------------------------
-
- -
-
---------------------------------------------------------------------------------
-6. REFERENCE
---------------------------------------------------------------------------------
-
- Short references are written in project structure.
-
---------------------------------------------------------------------------------
-7. CONFIGURATION
---------------------------------------------------------------------------------
-
- -
-
---------------------------------------------------------------------------------
-8. EXAMPLES
---------------------------------------------------------------------------------
-
- -
-
---------------------------------------------------------------------------------
-9. TROUBLESHOOTING
---------------------------------------------------------------------------------
-
- Problem : Coffee counter shows error.
- Solution: Restart the web server. This generates new json files and usually
- solves this
-
- Problem : Dead drops don't show contents.
- Solution: Check permissions and ownership on Dead-drops/ folder. It should
- be apache:apache
-
---------------------------------------------------------------------------------
-10. LICENSE
---------------------------------------------------------------------------------
-
- This project is licensed under the GPLv3 License.
- See the LICENSE.txt file for full details.
-
- Copyright (c) 2026 David Polakovic
-
-================================================================================
+\r
+dpolakovic.space\r
+website files and configs for dpolakovic.space, git server and the onion site\r
+\r
+AUTHOR : David Polakovic\r
+EMAIL : email@dpolakovic.space\r
+VERSION : 1.3.0\r
+LICENSE : various, see section LICENSE for more details\r
+\r
+OVERVIEW\r
+\r
+ This repository contains a collection of html, php, javascript files \r
+ which I host these domains:\r
+ - https://www.dpolakovic.space\r
+ - https://git.dpolakovic.space\r
+ - and .onion site\r
+ \r
+ I do not "git" content of sites, only example pages. Exceptions \r
+ are files in /Git-server/Stuff/* since changes on them are good to track \r
+ for existing and potential git contributors.\r
+\r
+\r
+PROJECT STRUCTURE (tree --dirsfirst)\r
+\r
+ ├── Git-server\r
+ │ ├── Coffee\r
+ │ │ ├── coffee-counter.js\r
+ │ │ └── coffee-server.php\r
+ │ ├── Gitweb\r
+ │ │ ├── static\r
+ │ │ │ ├── gitweb.css\r
+ │ │ │ └── gitweb.js\r
+ │ │ ├── banner.html\r
+ │ │ ├── footer.html\r
+ │ │ ├── gitweb.cgi\r
+ │ │ └── indextext.html\r
+ │ └── Stuff\r
+ │ ├── coffee-server.html\r
+ │ ├── contributors-guide.html\r
+ │ ├── dot.png\r
+ │ ├── server-rules.html\r
+ │ ├── stuff.css\r
+ │ └── stuff.js\r
+ │ \r
+ ├── Root-domain\r
+ │ ├── blogs\r
+ │ ├── error\r
+ │ │ └── error.php\r
+ │ ├── php\r
+ │ │ ├── config.php\r
+ │ │ └── leaderboard.php\r
+ │ ├── Pictures\r
+ │ ├── Styles\r
+ │ │ ├── FONT_LICENSE.txt\r
+ │ │ ├── NotoSerif-Bold.ttf\r
+ │ │ ├── NotoSerif-Italic.ttf\r
+ │ │ ├── NotoSerif-Regular.ttf\r
+ │ │ └── styles.css\r
+ │ ├── brew.php\r
+ │ ├── clicky-images.js\r
+ │ ├── optional-get-time.js\r
+ │ └── example-page.php\r
+ │ \r
+ ├── Scripts\r
+ │ └── Rsser\r
+ │ └── rsser.pl\r
+ │ \r
+ ├── Tor-site\r
+ │ ├── Dead-drops\r
+ │ ├── php\r
+ │ │ ├── captcha.php\r
+ │ │ ├── config.php\r
+ │ │ └── dd2.php\r
+ │ ├── Pictures\r
+ │ ├── Styles\r
+ │ │ ├── FONT_LICENSE.txt\r
+ │ │ ├── NotoSerif-Bold.ttf\r
+ │ │ ├── NotoSerif-Italic.ttf\r
+ │ │ ├── NotoSerif-Regular.ttf\r
+ │ │ └── styles.css\r
+ │ ├── dead-drop.php\r
+ │ └── example-page.php\r
+ │ \r
+ ├── DOC_LICENSE.txt\r
+ ├── LICENSE.txt\r
+ ├── README.txt\r
+ └── TO_DO.txt\r
+\r
+\r
+\r
+\r
+REQUIREMENTS\r
+\r
+ In order to roll out these files on your setup you would need\r
+ - RHEL 9 or RHEL 9 clone\r
+ - apache server (any version)\r
+ - php (any version > 2.0.0)\r
+ - git (any version < 3.0.0)\r
+ - gitweb (any version)\r
+ - highlight (any version)\r
+\r
+ The httpd and RHEL in general are optional, but other distros and/or server\r
+ would require some config changes from you.\r
+\r
+\r
+\r
+COMPONENT REFERENCE\r
+\r
+ In this repo I also bundled the rsser.pl which is script I use to transle html \r
+ formated text from my blogs into rss formated text. Then I paste the output \r
+ to the rss.xml so users fetching my rss can read the blog in full length in \r
+ their favorite reader.\r
+\r
+ rsser.pl\r
+ Purpose : converts HTML formatted text into CSS 2.0 format.\r
+ Usage : perl scripts/example_script.pl --input data.csv\r
+ Options : --help, --verbose, --output DIR\r
+\r
+\r
+\r
+CONFIGURATION\r
+\r
+\r
+ Don't forget to change the API token in Tor-site/php/dd2.php .\r
+\r
+\r
+\r
+KNOWN ISSUES\r
+\r
+ Problem : Coffee counter shows error.\r
+ Solution: Restart the web server. This generates new .json files and usually \r
+ solves this\r
+ \r
+ Problem : Dead drops don't show contents.\r
+ Solution: Check permissions and ownership on Dead-drops/ folder. It should \r
+ be apache:apache\r
+\r
+\r
+\r
+LICENSE\r
+\r
+\r
+ This project uses various licenses. Check which files uses which one.\r
+ - files located in /Git-server are licensed under GPLv2\r
+ - files located in /Root-domain and /Tor-site are licensed under GPLv3\r
+\r
+\r
+ See the LICENSE.txt in file for full details.\r
+\r
+ Copyright (c) 2022-2026 David Polakovic
\ No newline at end of file
<?php
-// Debuging stuff...
// error_reporting(E_ALL);
// ini_set('display_errors', 1);
-// Helper function for serverStatus();
function isWebsiteOnline($url) {
$domain = parse_url($url, PHP_URL_HOST);
$port = 80;
}
}
-// Show if git server is down or running
function serverStatus() {
$websiteUrl = "http://git.dpolakovic.space";
if (isWebsiteOnline($websiteUrl)) {
}
}
-// Pick random second sentence on index.php
+// Function for index.php to generate random second sentence
function randomSentence() {
$file = "./php/rnd.txt";
echo $lines[$randomIndex];
}
+
else { echo "this is an error message."; }
}
-// Print time of last update
function lastFileUpdate(...$files) {
$latestTimestamp = 0;
foreach ($files as $file) {
}
}
-// Counts lines...
function countFileLines($filename) {
if (!file_exists($filename)) {
echo "File not found: $filename<br>";
echo $lineCount;
}
-// Print footer on every page
function printFooter($usesJS, $usesCookies, ...$files) {
// Print JS status
echo "Copyright 2023-" . date("Y") . " David Polakovic. All publications licensed under ";
}
echo 'Server for this domain is ';
echo '<a href="https://www.rfc-editor.org/rfc/rfc1464.html">RFC 1464</a> and ';
- echo '<a href="https://www.rfc-editor.org/rfc/rfc2324">RFC 2324</a> compliant.';
+ echo '<a href="https://www.rfc-editor.org/rfc/rfc2324.html">RFC 2324</a> compliant.';
}
-// Just like footer...
+
function printNavBar(){
echo <<<HTML
-<nav class="nav-bar">
-<input type="checkbox" id="mobile-menu-toggle" class="mobile-menu-toggle">
-<label for="mobile-menu-toggle" class="mobile-menu-icon"></label>
-<ul>
-<li><a href="https://www.dpolakovic.space">Home</a></li>
-<li><a href="https://www.dpolakovic.space/noticeboard.php">Noticeboard</a></li>
-<li><a href="https://www.dpolakovic.space/blog.php">Blog</a></li>
-<li><a href="https://www.dpolakovic.space/dir.php">Web directory</a></li>
-<!-- <li class="desktop-only"><a href="">example</a></li> -->
+ <nav class="nav-bar">
+ <input type="checkbox" id="mobile-menu-toggle" class="mobile-menu-toggle">
+ <label for="mobile-menu-toggle" class="mobile-menu-icon"></label>
+ <ul>
+ <li><a href="https://www.dpolakovic.space">Home</a></li>
+ <li><a href="https://www.dpolakovic.space/blog.php">Blog</a></li>
+ <li><a href="https://www.dpolakovic.space/dir.php">Web directory</a></li>
+ <!-- <li><a href="https://www.dpolakovic.space/mars.php">Mars clock</a></li> -->
+ <li class="dropdown">
+ <a href="https://www.dpolakovic.space/quake/servers.php">Quake HvH
+ <svg class="dropdown-caret" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10" aria-hidden="true">
+ <path fill="rgb(248, 248, 248)" d="M1 3.25h8L5 7.5z"/></svg>
+ </a>
+ <ul class="dropdown-content">
+ <li><a href="https://www.dpolakovic.space/quake/servers.php">Server list</a></li>
+ <li><a href="https://www.dpolakovic.space/quake/roll-your-server.php">Roll your server</a></li>
+ <!-- <li><a href="https://www.dpolakovic.space/quake/landscape-mapping.php">Landscape mapping</a></li>
+ <li><a href="https://www.dpolakovic.space/quake/ezquake.php">ezQuake</a></li>
+ <li><a href="https://www.dpolakovic.space/quake/fte.php">FTE client</a></li>
+ <li><a href="https://www.dpolakovic.space/quake/vanilla.php">Original client</a></li> -->
+ </ul>
+ </li>
+ <li style="display:flex; align-items:center;">
+ <a href=" http://apebkcjcj7da3f7hfpkbp4csylu53fqrbfcw6tb7n4os3efyjbsa4cyd.onion/ ">More</a>
+ <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" style="margin-left:-15px; margin-top:4px; flex-shrink:0;">
+ <path fill="rgb(248, 248, 248)" d="M5.002 1.01h7v7l-2-2-3 2v-1l3-2.25 1 1V2.01h-3.75l1 1-2.25 3h-1l2-3z"/>
+ <path fill="rgb(248, 248, 248)" d="M7.002 3.01h-5v8h8v-5h-1v4h-6v-6h4z"/>
+ <path fill="rgb(248, 248, 248)" d="M4.082 5.51c0-.621.621-.621.621-.621q2.796.932 3.728 3.728s0 .621-.62.621C6.566 7.374 5.945 6.753 4.081 5.51z"/>
+ </svg>
+ </li>
+ <!-- <li class="desktop-only"><a href="">example</a></li> -->
<li>
HTML;
echo serverStatus();
HTML;
}
-// Checks file size ( used in javascript blog)
+
function getFileSize($filename) {
if (file_exists($filename)) {
$sizeInBytes = filesize($filename);
}
}
-// Cconverts RSS file into blog page
+// Function to convert RSS file into blog page
function printBlog2($rss_file) {
// Get the selected category from URL parameter (default to 'all')
$selected_category = isset($_GET['category']) ? $_GET['category'] : 'all';
}
}
-// Checks current TorBrowser version
+
function getTorBrowserVersion() {
$cacheFile = sys_get_temp_dir() . '/tor_browser_version_cache.txt';
$cacheTime = 3600; // 1 hour in seconds
echo $output;
}
+
+function qw_status_cached(string $host, int $port, int $maxAge = 30): ?array {
+ $cacheFile = sys_get_temp_dir() . "/qwstatus_{$host}_{$port}.json";
+
+ // If we have a fresh cached result, use it — don't hit the game server.
+ if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $maxAge) {
+ $cached = json_decode(file_get_contents($cacheFile), true);
+ if ($cached !== null) return $cached;
+ }
+
+ // Cache stale or missing — query the server once, then store it.
+ $status = qw_status($host, $port);
+ if ($status !== null) {
+ file_put_contents($cacheFile, json_encode($status));
+ }
+ return $status;
+}
+
+
+function qw_status(string $host, int $port = 27500, float $timeout = 2.0): ?array {
+ $sock = @stream_socket_client(
+ "udp://{$host}:{$port}",
+ $errno, $errstr,
+ $timeout
+ );
+ if (!$sock) {
+ return null;
+ }
+ stream_set_timeout($sock, (int)$timeout);
+
+ // status 23 = serverinfo + players + spectators + spec frag marker
+ fwrite($sock, "\xFF\xFF\xFF\xFFstatus 23\n");
+
+ $response = fread($sock, 8192);
+ fclose($sock);
+
+ if (!$response || strlen($response) < 5) {
+ return null;
+ }
+
+ // Strip 4-byte 0xFF header + 1-byte response type ('n')
+ $payload = substr($response, 5);
+ $lines = explode("\n", $payload);
+
+ // First line: serverinfo key/value string
+ $infoLine = ltrim(array_shift($lines), '\\');
+ $parts = explode('\\', $infoLine);
+ $info = [];
+ for ($i = 0; $i + 1 < count($parts); $i += 2) {
+ $info[$parts[$i]] = $parts[$i + 1];
+ }
+
+ // Remaining lines: players AND spectators
+ $players = [];
+ $spectators = [];
+ foreach ($lines as $line) {
+ $line = trim($line);
+ if ($line === '') continue;
+
+ // Standard format:
+ // id frags time ping "name" "skin" topcolor bottomcolor
+ // Spectator format (mvdsv with flag 4+8):
+ // id "S" time ping "\s\name" "skin" topcolor bottomcolor
+ if (preg_match(
+ '/^(\d+)\s+(-?\d+|"S")\s+(\d+)\s+(\d+)\s+"([^"]*)"\s+"([^"]*)"\s+(\d+)\s+(\d+)/',
+ $line, $m
+ )) {
+ $isSpec = ($m[2] === '"S"') || str_starts_with($m[5], '\\s\\');
+ $name = $m[5];
+ if (str_starts_with($name, '\\s\\')) {
+ $name = substr($name, 3); // strip the \s\ prefix
+ }
+
+ $entry = [
+ 'id' => (int)$m[1],
+ 'frags' => $isSpec ? null : (int)$m[2],
+ 'time' => (int)$m[3],
+ 'ping' => (int)$m[4],
+ 'name' => $name,
+ 'skin' => $m[6],
+ 'top' => (int)$m[7],
+ 'bottom' => (int)$m[8],
+ ];
+
+ if ($isSpec) {
+ $spectators[] = $entry;
+ } else {
+ $players[] = $entry;
+ }
+ }
+ }
+
+ return [
+ 'info' => $info,
+ 'players' => $players,
+ 'spectators' => $spectators,
+ ];
+}
+
+
+function draw_dynamic_table($room = 'mvdsv')
+{
+ /* ---------------------------------------------------------------------
+ 1. YOUR DATA — edit only this block.
+ Two rooms. Keys are the UTC start time of the slot:
+ "00:00", "02:00", "04:00" ... "22:00". Leave a slot out if it's free.
+ ------------------------------------------------------------------- */
+ $reservations = array(
+ 'mvdsv' => array(
+ 'Monday' => array(),
+ 'Tuesday' => array('16:00' => 'upkeep'),
+ 'Wednesday' => array(),
+ 'Thursday' => array(),
+ 'Friday' => array('04:00' => 'deauth pckt'),
+ 'Saturday' => array(),
+ 'Sunday' => array(),
+ ),
+ 'fte' => array(
+ 'Monday' => array(),
+ 'Tuesday' => array('16:00' => 'upkeep'),
+ 'Wednesday' => array(),
+ 'Thursday' => array(),
+ 'Friday' => array(),
+ 'Saturday' => array(),
+ 'Sunday' => array(),
+ ),
+ );
+
+ /* ---------------------------------------------------------------------
+ 2. Machinery — no need to touch anything below.
+ ------------------------------------------------------------------- */
+
+ if (!isset($reservations[$room])) {
+ echo '<!-- draw_dynamic_table: unknown room -->';
+ return;
+ }
+ $book = $reservations[$room];
+
+ $days = array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
+ $slot_minutes = 120; // 2 hours per row
+ $slot_count = 1440 / $slot_minutes; // 12 rows
+
+ /* The visitor's UTC offset in minutes, planted by optional-get-time.js.
+ No cookie (or a junk one) means no JavaScript: fall back to UTC+0. */
+ $offset = 0;
+ if (isset($_COOKIE['tzo']) && preg_match('/^-?\d{1,4}$/', $_COOKIE['tzo'])) {
+ $candidate = (int) $_COOKIE['tzo'];
+ if ($candidate >= -720 && $candidate <= 840) { // UTC-12 .. UTC+14
+ $offset = $candidate;
+ }
+ }
+
+ $wrap_day = function ($minutes) {
+ return (($minutes % 1440) + 1440) % 1440;
+ };
+
+ $clock = function ($minutes) use ($wrap_day) {
+ $m = $wrap_day($minutes);
+ return sprintf('%02d:%02d', intdiv($m, 60), $m % 60);
+ };
+
+ $zone_label = function ($minutes) {
+ $sign = $minutes < 0 ? '-' : '+';
+ $abs = abs($minutes);
+ $h = intdiv($abs, 60);
+ $m = $abs % 60;
+ return 'UTC' . $sign . $h . ($m ? sprintf(':%02d', $m) : '');
+ };
+
+ /* Each UTC row, translated into the visitor's day. day_shift says whether
+ that UTC slot lands on the previous, same, or next local day. Rows keep
+ their UTC alignment (so no booking gets split across two cells) and are
+ sorted by local start time. */
+ $rows = array();
+ for ($slot = 0; $slot < $slot_count; $slot++) {
+ $utc_start = $slot * $slot_minutes;
+ $shifted = $utc_start + $offset;
+ $rows[] = array(
+ 'utc_start' => $utc_start,
+ 'local_start' => $wrap_day($shifted),
+ 'day_shift' => (int) floor($shifted / 1440),
+ );
+ }
+ usort($rows, function ($a, $b) {
+ return $a['local_start'] - $b['local_start'];
+ });
+
+ /* "Now" in the visitor's zone, so today's column can be marked. */
+ $now_shifted = ((int) gmdate('G')) * 60 + ((int) gmdate('i')) + $offset;
+ $now_minutes = $wrap_day($now_shifted);
+ $now_day = ((((int) gmdate('N')) - 1) + (int) floor($now_shifted / 1440) + 7) % 7;
+
+ /* ---- output ------------------------------------------------------- */
+
+ $time_column_width = '8.5em';
+
+ echo '<table class="reservation-table" style="table-layout:fixed;width:100%">';
+
+ /* Fixed layout: the time column gets a set width, the seven day columns
+ split the rest equally. */
+ echo '<colgroup><col style="width:' . $time_column_width . '">'
+ . str_repeat('<col>', count($days))
+ . '</colgroup>';
+
+ /* Header: timezone in the corner, then the seven days. */
+ echo '<thead><tr>';
+ echo '<th class="rt-zone" scope="col" style="text-align:center">'
+ . htmlspecialchars($zone_label($offset), ENT_QUOTES, 'UTF-8')
+ . '</th>';
+ foreach ($days as $index => $day) {
+ echo '<th class="rt-day' . ($index === $now_day ? ' rt-today' : '') . '" scope="col">'
+ . htmlspecialchars($day, ENT_QUOTES, 'UTF-8')
+ . '</th>';
+ }
+ echo '</tr></thead>';
+
+ /* Body: one row per two-hour slot. */
+ echo '<tbody>';
+ foreach ($rows as $row) {
+ echo '<tr>';
+
+ echo '<td class="rt-time" style="text-align:center;white-space:nowrap">'
+ . htmlspecialchars(
+ $clock($row['local_start']) . ' to ' . $clock($row['local_start'] + $slot_minutes - 5),
+ ENT_QUOTES, 'UTF-8')
+ . '</td>';
+
+ $is_now_row = ($now_minutes >= $row['local_start'])
+ && ($now_minutes < $row['local_start'] + $slot_minutes);
+
+ foreach ($days as $index => $day) {
+ $utc_day = $days[($index - $row['day_shift'] + 7) % 7];
+ $key = $clock($row['utc_start']);
+ $name = isset($book[$utc_day][$key]) ? $book[$utc_day][$key] : '';
+
+ $classes = array('rt-slot', $name !== '' ? 'rt-booked' : 'rt-open');
+ if ($index === $now_day) {
+ $classes[] = 'rt-today';
+ if ($is_now_row) {
+ $classes[] = 'rt-now';
+ }
+ }
+
+ $safe = htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
+ echo '<td class="' . implode(' ', $classes) . '"'
+ . ' style="overflow:hidden;white-space:nowrap;text-overflow:ellipsis"'
+ . ($name !== '' ? ' title="' . $safe . '"' : '')
+ . '>' . $safe . '</td>';
+ }
+
+ echo '</tr>';
+ }
+ echo '</tbody></table>';
+}
+
+
?>
\ No newline at end of file