4 * Generates the appropriate mod_rewrite rules in the htaccess file.
5 * @note If a .htaccess.in prototype file is present in the directory,
6 * it will be used as the "base" htaccess to determine the new
7 * one, and local changes to .htaccess will ALWAYS be overwritten
8 * @todo Create an HTAccess object to edit
13 $xc = XHTMLCompiler
::getInstance();
15 $identifier_begin = '# BEGIN xhtml-compiler/main.php mod_rewrite';
16 $identifier_end = '# END xhtml-compiler/main.php mod_rewrite';
17 $identifier_here = '# HERE xhtml-compiler/main.php mod_rewrite';
19 if (file_exists('.htaccess')) {
22 $files_to_check = array(
23 XHTMLCOMPILER
. '/config.php',
24 XHTMLCOMPILER
. '/config.default.php',
25 XHTMLCOMPILER
. '/config.smoketest.php',
26 XHTMLCOMPILER
. '/conf/config.php',
27 XHTMLCOMPILER
. '/htaccess.php',
32 $mtime_htaccess = filemtime('.htaccess');
34 $no_changes_needed = true;
35 foreach ($files_to_check as $file) {
36 if (file_exists($file) && filemtime($file) > $mtime_htaccess) {
37 $no_changes_needed = false;
42 if ($no_changes_needed) {
43 throw new XHTMLCompiler_Exception(503, false,
44 'No changes detected in relevant files');
47 if (!file_exists('.htaccess.in')) {
48 $contents = file_get_contents('.htaccess');
50 $contents = file_get_contents('.htaccess.in');
53 // do writeability check
56 strpos($contents, $identifier_begin) === false ||
57 strpos($contents, $identifier_end) === false
59 strpos($contents, $identifier_here) === false
61 throw new XHTMLCompiler_Exception(503, false,
62 'Pre-existing htaccess not configured to accept new rules');
65 // replace old rules with new set
68 preg_quote($identifier_begin, '/') .
70 preg_quote($identifier_end, '/') .
73 $contents = preg_replace($regex, $identifier_here, $contents);
76 $contents = $identifier_here;
79 // build the new htaccess
81 $n[] = $identifier_begin;
82 $n[] = 'DirectoryIndex index.html';
83 $n[] = 'Options -Multiviews';
84 $n[] = 'RewriteEngine on';
85 $n[] = 'RewriteBase ' . $xc->getConf('web_path') . '/';
87 // create permanent redirects
88 if (file_exists('redirects.txt')) {
89 $redirects = explode("\n", file_get_contents('redirects.txt'));
90 foreach ($redirects as $redirect) {
91 $redirect = trim($redirect) . ' ';
92 if ($redirect === ' ') continue;
93 if ($redirect[0] === '#') continue;
94 list($src, $dest, $p) = explode(' ', $redirect);
95 if ($p !== '[P]') $src = '^' . preg_quote($src) . '$';
96 // We use rewrite to prevent the appending of ?f= querystring
97 $n[] = 'RewriteRule ' . $src . ' ' . $dest . ' [R=permanent,L]';
102 $directory_index = $xc->getConf('directory_index');
103 $indexed_dirs = $xc->getConf('indexed_dirs');
104 $allowed_dirs = $xc->getConf('allowed_dirs');
105 foreach ($allowed_dirs as $dir => $recursive) {
108 $r = "([^/]+/)*"; // escaped slashes not necessary
111 $slash = (!$len ||
$dir[$len-1] === '/') ?
'' : '/';
112 $dir_exp = preg_quote($dir) . $slash . $r;
113 $big_exp[$dir] = $dir_exp;
115 $full_dir_exp = implode('|', $big_exp);
117 // prefer the extension-less URL
118 $n[] = 'RewriteCond %{REQUEST_URI} \.html$';
119 $n[] = "RewriteRule ^(($full_dir_exp)[^/]+)\.html$ \$1 [NS,R=301]";
121 foreach ($allowed_dirs as $dir => $recursive) {
122 if (is_array($indexed_dirs)) {
123 $intercept = isset($indexed_dirs[$dir]) ?
$indexed_dirs[$dir] : true;
125 $intercept = $indexed_dirs;
127 if (is_string($directory_index) && $intercept) {
128 // setup index rewrite
129 $n[] = "RewriteRule ^({$big_exp[$dir]})$ \$1$directory_index";
133 // allow pretty extension-less URLs for HTML pages
134 // this could be generalized for other URLs
135 $n[] = 'RewriteCond %{REQUEST_FILENAME} !-f';
136 $n[] = 'RewriteCond %{REQUEST_FILENAME}.html -f';
137 $n[] = 'RewriteCond %{HTTP_ACCEPT} text/html';
138 $n[] = "RewriteRule ^(($full_dir_exp)[^/]+)$ \$1.html [N]";
140 // basic redirection if it doesn't exist
141 $n[] = 'RewriteCond %{REQUEST_FILENAME} !-f [OR]';
142 $n[] = 'RewriteCond %{QUERY_STRING} purge=1 [OR]';
143 $n[] = 'RewriteCond %{HTTP_COOKIE} purgatory=1';
144 $n[] = "RewriteRule ^(($full_dir_exp)[^/]+\.html)$ xhtml-compiler/main.php?f=\$1 [L,QSA]";
146 // if purge is set, also handle directories
147 $n[] = 'RewriteCond %{QUERY_STRING} purge=1';
148 $n[] = "RewriteRule ^($full_dir_exp)$ xhtml-compiler/main.php?f=\$1 [L,QSA]";
150 // add application/xhtml+xml if the browser supports it
151 $n[] = 'RewriteCond %{HTTP_ACCEPT} application/xhtml\\+xml';
152 $n[] = "RewriteRule ^(($full_dir_exp)[^/]+\.html)$ - \"[T=application/xhtml+xml,L]\"";
154 // xc-deps are forbidden to outside world
155 $n[] = '<Files ~ "\.xc-deps$">';
156 $n[] = ' Order allow,deny';
157 $n[] = ' Deny from all';
160 // errors.log is forbidden to outside world. In theory, this will occur only
161 // in xhtml-compiler/, but it won't hurt to deny it everywhere.
162 $n[] = '<Files errors.log>';
163 $n[] = ' Order allow,deny';
164 $n[] = ' Deny from all';
168 $n[] = 'AddType application/rss+xml rss';
169 $n[] = 'AddCharset UTF-8 .rss';
170 $n[] = '<IfModule mod_headers.c>';
171 $n[] = ' <Files ~ "\.rss$">';
172 $n[] = ' Header append Cache-Control "no-cache, must-revalidate"';
174 $n[] = '</IfModule>';
176 // set UTF-8 for HTML pages
177 $n[] = 'AddCharset UTF-8 .html';
179 $n[] = $identifier_end;
181 $contents = str_replace($identifier_here, implode($n, PHP_EOL
), $contents);
183 file_put_contents('.htaccess', $contents);
184 chmod('.htaccess', 0644);
187 echo "Okay: New .htaccess file successfully written\n";
191 ?
><h1
>200: Okay
</h1
>New <tt
>.htaccess
</tt
> file successfully written