Fix bug with entities inside header text.
[xhtml-compiler.git] / functions.php
blobb56e03a7f7b14c2f368ca9cb5c242ae5a4db5071
1 <?php
3 /**
4 * Convenience function that outputs an HTTP status code
5 * @param $code integer HTTP status code
6 * @return string HTTP status code's name
7 */
8 function set_response_code($code) {
9 $php = XHTMLCompiler::getPHPWrapper();
10 $code_descriptions = array( // could be factored out
11 200 => 'Okay',
12 304 => 'Not Modified',
13 401 => 'Unauthorized',
14 403 => 'Forbidden',
15 404 => 'Not Found',
16 500 => 'Internal Server Error',
17 503 => 'Service Unavailable',
19 $code = (int) $code; // enforce integer
20 $text = (string) $code;
21 if (isset($code_descriptions[$code])) {
22 $text .= ' ' . $code_descriptions[$code];
24 if (isset($_SERVER['SERVER_PROTOCOL'])) {
25 $php->header($_SERVER['SERVER_PROTOCOL'] . ' ' . $text, true, $code);
26 $php->header('Status: ' . $text);
28 return $text;
31 /**
32 * Determines the requested page from server environment files
33 * @return string page name
35 function get_page_from_server() {
36 $php = XHTMLCompiler::getPHPWrapper();
37 // load up page from environment variables, if using ErrorDocument impl.
38 $page = $php->getRequestURI();
39 $self = dirname($php->getPHPSelf());
40 $root = strlen(substr($self, 0, strrpos($self, '/')));
41 $page = substr($page, $root + 1); // remove leading slash and root
42 return $page;
45 /**
46 * Determines the requested page from get parameter
47 * @return string page name
49 function get_page_from_get() {
50 $php = XHTMLCompiler::getPHPWrapper();
51 return $php->getGVal('f');
54 /**
55 * Takes a page name and appends index filename if necessary
56 * @param $page string page name
57 * @param $directory_index string name of file to use as directory index
59 function normalize_index($page, $directory_index) {
60 $php = XHTMLCompiler::getPHPWrapper();
61 if ($page == '') $page = $directory_index;
62 if ($php->isDir($page)) {
63 if ($page[strlen($page)-1] !== '/') $page .= '/';
64 $page .= $directory_index;
66 return $page;
69 /**
70 * Determines whether or not an .html file was generated by us
71 * @param $page filename of page, must exist
73 function is_created_by_us($page) {
74 $contents = file_get_contents($page);
75 return (strpos($contents, '<!-- generated by XHTML Compiler -->') !== false);
78 /**
79 * Exception handler, prints a pretty HTTP error message
80 * @param $e The uncaught exception
81 * @todo Augment debug mode to have stack traces
82 * @todo Make more friendly to command-line
84 function xhtmlcompiler_exception_handler(Exception $e) {
85 $xc = XHTMLCompiler::getInstance();
86 $php = XHTMLCompiler::getPHPWrapper();
88 $error_xsl = $xc->getConf('error_xsl');
90 // extract information out of exception
91 if ($e instanceof XHTMLCompiler_Exception) {
92 $code = $e->getCode();
93 $title = $e->getMessage();
94 $details = $e->getDetails();
95 } else {
96 $code = 500;
97 $title = false;
98 $details = $e->getMessage();
101 // send appropriate response code
102 $default = set_response_code($code);
104 // munge the title
105 if (!$title) $title = $default;
106 else $title = $code . ' ' . $title;
108 // build a error document
109 $page = new DOMDocument('1.0', 'utf-8');
110 $error = $page->createElement('error'); $page->appendChild($error);
111 $error->appendChild($page->createElement('code', $code));
112 $error->appendChild($page->createElement('title', $title));
113 $error->appendChild($page->createElement('base', $xc->getConf('web_path')));
115 // details (in HTML)
116 if ($details) {
117 $html = $page->createDocumentFragment();
118 $html->appendXML($details);
119 $details_dom = $page->createElement('details');
120 $details_dom->appendChild($html);
121 $error->appendChild($details_dom);
124 // debug
125 if ($xc->getConf('debug')) {
126 $debug = $page->createElement('debug');
127 $debug->appendChild($page->createElement('base-dir', $base = dirname(__FILE__)));
128 $debug->appendChild($page->createElement('file', shortenFile($e->getFile(), $base)));
129 $debug->appendChild($page->createElement('line', $e->getLine()));
130 $error->appendChild($debug);
133 // generate and output html
134 $xslt_processor = new ConfigDoc_HTMLXSLTProcessor();
135 $xslt_processor->importStylesheet(dirname(__FILE__) . '/../' . $error_xsl);
136 $html = $xslt_processor->transformToHTML($page);
137 $php->paint($html);
142 /** Returns short filename based on a base directory */
143 function shortenFile($file, $base) {
144 if (strpos($file, $base) === 0) {
145 $file = str_replace('\\', '/', substr($file, strlen($base)));
146 if ($file === '') $file = '.';
147 if ($file[0] === '/') $file = substr($file, 1);
149 return $file;
153 * Retrieves the last $limit log entries.
154 * @param $repos_url Repository URL of item to get logs for
155 * @param $limit Integer limit of items
157 function svn_log_limit($repos_url, $limit, $page = null) {
158 $limit = (int) $limit;
159 if ($limit <= 0) return array();
160 // attempt the cache
161 $cache_filename = 'xhtml-compiler/cache/svn/log/' . md5($repos_url) . '.ser';
162 $cached_rev = false;
163 if ($page && file_exists($cache_filename)) {
164 $logs = unserialize(file_get_contents($cache_filename));
165 $cached_rev = !empty($logs[0]) ? $logs[0]['rev'] : false;
166 // determine current revision number
167 $rev = $page->getSVNRevision();
168 if ($cached_rev == $rev && count($logs) == $limit) return $logs;
170 // -q flag used to prevent server from sending log messages
171 // most recent is retrieved first
172 $output = shell_exec("svn log -q --limit $limit $repos_url");
173 preg_match_all('/^r(\d+) /m', $output, $matches);
174 $ret = array();
175 foreach ($matches[1] as $rev) {
176 // reuse previously cached entries
177 if ($rev <= $cached_rev) {
178 $ret[] = array_shift($logs);
179 continue;
181 $log = svn_log($repos_url, (int) $rev);
182 $ret[] = $log[0]; // log is only one item long
184 // save to cache
185 file_put_contents($cache_filename, serialize($ret));
186 return $ret;
190 * Checks for errors, and sends a notification email if necessary
192 function check_errors() {
193 $xc = XHTMLCompiler::getInstance();
194 $error_log = $xc->getConf('error_log');
195 $error_mute = $xc->getConf('error_mute');
196 if (file_exists($error_log) && filesize($error_log) > 0) {
197 if (file_exists($error_mute) && filesize($error_mute) == 0) {
198 $error_text = wordwrap(file_get_contents($error_log), 70, "\r\n");
199 mail($xc->getConf('admin_email'), 'Errors in XHTML Compiler',
200 "Some errors occurred in the log. No further messages\r\n".
201 "will be sent until the mute file [1] is deleted. The\r\n".
202 "error log file [2] will be blanked after resolving any\r\n".
203 "issues.\r\n\r\n[1] $error_mute\r\n[2] $error_log\r\n\r\n$error_text\r\n");
204 file_put_contents($error_mute, '1');
205 } elseif (!file_exists($error_mute) && file_exists($error_log)) {
206 $tentative_new_name = "$error_log." . date('Ymd');
207 $i = 0;
208 $new_name = $tentative_new_name;
209 while (file_exists($tentative_new_name) && file_exists($new_name)) {
210 $new_name = "$tentative_new_name-$i";
211 $i++;
213 copy($error_log, $new_name);
214 file_put_contents($error_log, '');
215 file_put_contents($error_mute, '');