No changes between revisions
/WebSVN/include/auth.inc |
---|
1,242 → 1,242 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// auth.inc |
// |
// Handle reading and interpretation of an SVN auth file |
require_once("include/accessfile.inc"); |
define("UNDEFINED", 0); |
define("ALLOW", 1); |
define("DENY", 2); |
class Authentication |
{ |
var $rights; |
var $user; |
var $usersGroups = array(); |
// {{{ __construct |
function Authentication($accessfile) |
{ |
$this->rights = new IniFile(); |
$this->rights->readIniFile($accessfile); |
$this->setUsername(""); |
$this->identifyGroups(); |
} |
// }}} |
// {{{ setUsername($username) |
// |
// Set the username if it is given, or |
// get the user of the current http session |
function setUsername($username) |
{ |
if ($username == "") // Use the current user |
{ |
$this->user = @$_SERVER["REMOTE_USER"]; |
} |
else |
{ |
$this->user = $username; |
} |
} |
// }}} |
// {{{ identifyGroups() |
// |
// Checks to see which groups the user belongs to |
function identifyGroups() |
{ |
$this->usersGroups[] = "*"; |
if (is_array($this->rights->getValues("groups"))) |
{ |
foreach ($this->rights->getValues("groups") as $group => $names) |
{ |
if (in_array(strtolower($this->user), preg_split('/\s*,\s*/', $names))) |
$this->usersGroups[] = "@" . $group; |
} |
} |
} |
// }}} |
// {{{ inList |
// |
// Check if the user is in the given list and return their read status |
// if they are (UNDEFINED, ALLOW or DENY) |
function inList($accessors, $user) |
{ |
$output = UNDEFINED; |
foreach($accessors As $key => $rights) |
{ |
$keymatch = false; |
if (in_array($key, $this->usersGroups) || !strcmp($key, strtolower($user))) |
$keymatch = true; |
if ($keymatch) |
{ |
if (strpos($rights, "r") !== false) |
return ALLOW; |
else |
$output = DENY; |
} |
} |
return $output; |
} |
// }}} |
// {{{ hasReadAccess |
// |
// Returns true if the user has read access to the given path |
function hasReadAccess($repos, $path, $checkSubFolders = false) |
{ |
$access = UNDEFINED; |
if ($path{0} != "/") |
$path = "/$path"; |
// If were told to, we should check sub folders of the path to see if there's |
// a read access below this level. This is used to display the folders needed |
// to get to the folder to which read access is granted. |
if ($checkSubFolders) |
{ |
$sections = $this->rights->getSections(); |
foreach($sections As $section => $accessers) |
{ |
$qualified = $repos.":".$path; |
$len = strlen($qualified); |
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
if ($access != ALLOW) |
{ |
$len = strlen($path); |
if ($len < strlen($section) && strncmp($section, $path, strlen($path)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
} |
if ($access == ALLOW) |
break; |
} |
} |
// If we still don't have access, check each subpath of the path until we find an |
// access level... |
if ($access != ALLOW) |
{ |
$access = UNDEFINED; |
do |
{ |
$accessers = $this->rights->getValues($repos.":".$path); |
if (!empty($accessers)) |
$access = $this->inList($accessers, $this->user); |
if ($access == UNDEFINED) |
{ |
$accessers = $this->rights->getValues($path); |
if (!empty($accessers)) |
$access = $this->inList($accessers, $this->user); |
} |
// If we've not got a match, remove the sub directory and start again |
if ($access == UNDEFINED) |
{ |
if ($path == "/") break; |
$path = substr($path, 0, strrpos(substr($path, 0, -1), "/") + 1); |
} |
} while ($access == UNDEFINED && $path != ""); |
} |
return $access == ALLOW; |
} |
// }}} |
// {{{ hasUnrestrictedReadAccess |
// |
// Returns true if the user has read access to the given path and too |
// all subfolders |
function hasUnrestrictedReadAccess($repos, $path) |
{ |
// First make sure that we have full read access at this level |
if (!$this->hasReadAccess($repos, $path, false)) |
return false; |
// Now check to see if there is a sub folder that's protected |
$sections = $this->rights->getSections(); |
foreach($sections As $section => $accessers) |
{ |
$qualified = $repos.":".$path; |
$len = strlen($qualified); |
$access = UNDEFINED; |
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
if ($access != DENY) |
{ |
$len = strlen($path); |
if ($len < strlen($section) && strncmp($section, $path, strlen($qualified)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
} |
if ($access == DENY) |
return false; |
} |
return true; |
} |
// }}} |
} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// auth.inc |
// |
// Handle reading and interpretation of an SVN auth file |
require_once("include/accessfile.inc"); |
define("UNDEFINED", 0); |
define("ALLOW", 1); |
define("DENY", 2); |
class Authentication |
{ |
var $rights; |
var $user; |
var $usersGroups = array(); |
// {{{ __construct |
function Authentication($accessfile) |
{ |
$this->rights = new IniFile(); |
$this->rights->readIniFile($accessfile); |
$this->setUsername(""); |
$this->identifyGroups(); |
} |
// }}} |
// {{{ setUsername($username) |
// |
// Set the username if it is given, or |
// get the user of the current http session |
function setUsername($username) |
{ |
if ($username == "") // Use the current user |
{ |
$this->user = @$_SERVER["REMOTE_USER"]; |
} |
else |
{ |
$this->user = $username; |
} |
} |
// }}} |
// {{{ identifyGroups() |
// |
// Checks to see which groups the user belongs to |
function identifyGroups() |
{ |
$this->usersGroups[] = "*"; |
if (is_array($this->rights->getValues("groups"))) |
{ |
foreach ($this->rights->getValues("groups") as $group => $names) |
{ |
if (in_array(strtolower($this->user), preg_split('/\s*,\s*/', $names))) |
$this->usersGroups[] = "@" . $group; |
} |
} |
} |
// }}} |
// {{{ inList |
// |
// Check if the user is in the given list and return their read status |
// if they are (UNDEFINED, ALLOW or DENY) |
function inList($accessors, $user) |
{ |
$output = UNDEFINED; |
foreach($accessors As $key => $rights) |
{ |
$keymatch = false; |
if (in_array($key, $this->usersGroups) || !strcmp($key, strtolower($user))) |
$keymatch = true; |
if ($keymatch) |
{ |
if (strpos($rights, "r") !== false) |
return ALLOW; |
else |
$output = DENY; |
} |
} |
return $output; |
} |
// }}} |
// {{{ hasReadAccess |
// |
// Returns true if the user has read access to the given path |
function hasReadAccess($repos, $path, $checkSubFolders = false) |
{ |
$access = UNDEFINED; |
if ($path{0} != "/") |
$path = "/$path"; |
// If were told to, we should check sub folders of the path to see if there's |
// a read access below this level. This is used to display the folders needed |
// to get to the folder to which read access is granted. |
if ($checkSubFolders) |
{ |
$sections = $this->rights->getSections(); |
foreach($sections As $section => $accessers) |
{ |
$qualified = $repos.":".$path; |
$len = strlen($qualified); |
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
if ($access != ALLOW) |
{ |
$len = strlen($path); |
if ($len < strlen($section) && strncmp($section, $path, strlen($path)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
} |
if ($access == ALLOW) |
break; |
} |
} |
// If we still don't have access, check each subpath of the path until we find an |
// access level... |
if ($access != ALLOW) |
{ |
$access = UNDEFINED; |
do |
{ |
$accessers = $this->rights->getValues($repos.":".$path); |
if (!empty($accessers)) |
$access = $this->inList($accessers, $this->user); |
if ($access == UNDEFINED) |
{ |
$accessers = $this->rights->getValues($path); |
if (!empty($accessers)) |
$access = $this->inList($accessers, $this->user); |
} |
// If we've not got a match, remove the sub directory and start again |
if ($access == UNDEFINED) |
{ |
if ($path == "/") break; |
$path = substr($path, 0, strrpos(substr($path, 0, -1), "/") + 1); |
} |
} while ($access == UNDEFINED && $path != ""); |
} |
return $access == ALLOW; |
} |
// }}} |
// {{{ hasUnrestrictedReadAccess |
// |
// Returns true if the user has read access to the given path and too |
// all subfolders |
function hasUnrestrictedReadAccess($repos, $path) |
{ |
// First make sure that we have full read access at this level |
if (!$this->hasReadAccess($repos, $path, false)) |
return false; |
// Now check to see if there is a sub folder that's protected |
$sections = $this->rights->getSections(); |
foreach($sections As $section => $accessers) |
{ |
$qualified = $repos.":".$path; |
$len = strlen($qualified); |
$access = UNDEFINED; |
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
if ($access != DENY) |
{ |
$len = strlen($path); |
if ($len < strlen($section) && strncmp($section, $path, strlen($qualified)) == 0) |
{ |
$access = $this->inList($accessers, $this->user); |
} |
} |
if ($access == DENY) |
return false; |
} |
return true; |
} |
// }}} |
} |
?> |
/WebSVN/include/bugtraq.inc |
---|
1,390 → 1,390 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// bugtraq.inc |
// |
// Functions for accessing the bugtraq properties and replacing issue IDs |
// with URLs. |
// |
// For more information about bugtraq, see |
// http://svn.collab.net/repos/tortoisesvn/trunk/doc/issuetrackers.txt |
class Bugtraq |
{ |
// {{{ Properties |
var $msgstring; |
var $urlstring; |
var $logregex; |
var $append; |
var $firstPart; |
var $firstPartLen; |
var $lastPart; |
var $lastPartLen; |
var $propsfound = false; |
// }}} |
// {{{ __construct($rep, $svnrep, $path) |
function Bugtraq($rep, $svnrep, $path) |
{ |
global $config; |
if ($rep->getBugtraq()) |
{ |
$pos = strrpos($path, "/"); |
$parent = substr($path, 0, $pos + 1); |
$this->append = true; |
$enoughdata = false; |
while(!$enoughdata && (strpos($parent, "/") !== false)) |
{ |
if (empty($this->msgstring)) $this->msgstring = $svnrep->getProperty($parent, 'bugtraq:message'); |
if (empty($this->logregex)) $this->logregex = $svnrep->getProperty($parent, 'bugtraq:logregex'); |
if (empty($this->urlstring)) $this->urlstring = $svnrep->getProperty($parent, 'bugtraq:url'); |
if (empty($this->append)) $this->append = ($svnrep->getProperty($parent, 'bugtraq:append') == "true"); |
$parent = substr($parent, 0, -1); // Remove the trailing slash |
$pos = strrpos($parent, "/"); // Find the last trailing slash |
$parent = substr($parent, 0, $pos + 1); // Find the previous parent directory |
$enoughdata = ((!empty($this->msgstring) || !empty($this->logregex)) && !empty($this->urlstring)); |
} |
$this->msgstring = trim(@$this->msgstring); |
$this->urlstring = trim(@$this->urlstring); |
if ($enoughdata && !empty($this->msgstring)) |
$this->initPartInfo(); |
if ($enoughdata) |
$this->propsfound = true; |
} |
} |
// }}} |
// {{{ initPartInfo() |
function initPartInfo() |
{ |
if (($bugidpos = strpos($this->msgstring, "%BUGID%")) !== false && strpos($this->urlstring, "%BUGID%") !== false) |
{ |
// Get the textual parts of the message string for comparison purposes |
$this->firstPart = substr($this->msgstring, 0, $bugidpos); |
$this->firstPartLen = strlen($this->firstPart); |
$this->lastPart = substr($this->msgstring, $bugidpos + 7); |
$this->lastPartLen = strlen($this->lastPart); |
} |
} |
// }}} |
// {{{ replaceIDs($message) |
function replaceIDs($message) |
{ |
if ($this->propsfound) |
{ |
// First we search for the message string |
$logmsg = ""; |
$message = rtrim($message); |
if ($this->append) |
{ |
// Just compare the last line |
if (($offset = strrpos($message, "\n")) !== false) |
{ |
$logmsg = substr($message, 0, $offset + 1); |
$bugLine = substr($message, $offset + 1); |
} |
else |
$bugLine = $message; |
} |
else |
{ |
if (($offset = strpos($message, "\n")) !== false) |
{ |
$bugLine = substr($message, 0, $offset); |
$logmsg = substr($message, $offset); |
} |
else |
$bugLine = $message; |
} |
// Make sure that our line really is an issue tracker message |
if (((strncmp($bugLine, $this->firstPart, $this->firstPartLen) == 0)) && |
strcmp(substr($bugLine, -$this->lastPartLen, $this->lastPartLen), $this->lastPart) == 0) |
{ |
// Get the issues list |
if ($this->lastPartLen > 0) |
$issues = substr($bugLine, $this->firstPartLen, -$this->lastPartLen); |
else |
$issues = substr($bugLine, $this->firstPartLen); |
// Add each reference to the first part of the line |
$line = $this->firstPart; |
while ($pos = strpos($issues, ",")) |
{ |
$issue = trim(substr($issues, 0, $pos)); |
$issues = substr($issues, $pos + 1); |
$line .= "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">$issue</a>, "; |
} |
$line .= "<a href=\"".str_replace("%BUGID%", trim($issues), $this->urlstring)."\">".trim($issues)."</a>".$this->lastPart; |
if ($this->append) |
$message = $logmsg.$line; |
else |
$message = $line.$logmsg; |
} |
// Now replace all other instances of bug IDs that match the regex |
if ($this->logregex) |
{ |
$message = rtrim($message); |
$line = ""; |
$allissues = ""; |
$lines = split("\n", $this->logregex); |
$regex_all = "~".$lines[0]."~"; |
$regex_single = @$lines[1]; |
if (empty($regex_single)) |
{ |
// If the property only contains one line, then the pattern is only designed |
// to find one issue number at a time. e.g. [Ii]ssue #?(\d+). In this case |
// we need to replace the matched issue ID with the link. |
if ($numMatches = preg_match_all($regex_all, $message, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) |
{ |
$addedOffset = 0; |
for ($match = 0; $match < $numMatches; $match++) |
{ |
$issue = $matches[$match][1][0]; |
$issueOffset = $matches[$match][1][1]; |
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>"; |
$message = substr_replace($message, $issueLink, $issueOffset + $addedOffset, strlen($issue)); |
$addedOffset += strlen($issueLink) - strlen($issue); |
} |
} |
} |
else |
{ |
// It the property contains two lines, then the first is a pattern for extracting |
// multiple issue numbers, and the second is a pattern extracting each issue |
// number from the multiple match. e.g. [Ii]ssue #?(\d+)(,? ?#?(\d+))+ and (\d+) |
while (preg_match($regex_all, $message, $matches, PREG_OFFSET_CAPTURE)) |
{ |
$completeMatch = $matches[0][0]; |
$completeMatchOffset = $matches[0][1]; |
$replacement = $completeMatch; |
if ($numMatches = preg_match_all("~".$regex_single."~", $replacement, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) |
{ |
$addedOffset = 0; |
for ($match = 0; $match < $numMatches; $match++) |
{ |
$issue = $matches[$match][1][0]; |
$issueOffset = $matches[$match][1][1]; |
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>"; |
$replacement = substr_replace($replacement, $issueLink, $issueOffset + $addedOffset, strlen($issue)); |
$addedOffset += strlen($issueLink) - strlen($issue); |
} |
} |
$message = substr_replace($message, $replacement, $completeMatchOffset, strlen($completeMatch)); |
} |
} |
} |
} |
return $message; |
} |
// }}} |
} |
// The BugtraqTestable class is a derived class that is used to test the matching |
// abilities of the Bugtraq class. In particular, it allows for the initialisation of the |
// class without the need for a repository. |
class BugtraqTestable extends Bugtraq |
{ |
// {{{ __construct() |
function BugtraqTestable() |
{ |
// This constructor serves to assure that the parent constructor is not |
// called. |
} |
// }}} |
// {{{ setUpVars($message, $url, $regex, $append) |
function setUpVars($message, $url, $regex, $append) |
{ |
$this->msgstring = $message; |
$this->urlstring = $url; |
$this->logregex = $regex; |
$this->append = $append; |
$this->propsfound = true; |
$this->initPartInfo(); |
} |
// }}} |
// {{{ setMessage($message) |
function setMessage($message) |
{ |
$this->msgstring = $message; |
} |
// }}} |
// {{{ setUrl($url) |
function setUrl($url) |
{ |
$this->urlstring = $url; |
} |
// }}} |
// {{{ setRegex($regex) |
function setRegEx($regex) |
{ |
$this->logregex = $regex; |
} |
// }}} |
// {{{ setAppend($append) |
function setAppend($append) |
{ |
$this->append = $append; |
} |
// }}} |
// {{{ printVars() |
function printVars() |
{ |
echo "msgstring = ".$this->msgstring."\n"; |
echo "urlstring = ".$this->urlstring."\n"; |
echo "logregex = ".$this->logregex."\n"; |
echo "append = ".$this->append."\n"; |
echo "firstPart = ".$this->firstPart."\n"; |
echo "firstPartLen = ".$this->firstPartLen."\n"; |
echo "lastPart = ".$this->lastPart."\n"; |
echo "lastPartLen = ".$this->lastPartLen."\n"; |
} |
// }}} |
} |
// {{{ test_bugtraq() |
function test_bugtraq() |
{ |
$tester = new BugtraqTestable; |
$tester->setUpVars("BugID: %BUGID%", |
"http://bugtracker/?id=%BUGID%", |
"[Ii]ssue #?(\d+)", |
true); |
//$tester->printVars(); |
$res = $tester->replaceIDs("BugID: 789\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789"); |
echo nl2br($res)."<p>"; |
$res = $tester->replaceIDs("BugID: 789, 101112\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789, 101112"); |
echo nl2br($res)."<p>"; |
$tester->setAppend(false); |
$res = $tester->replaceIDs("BugID: 789\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789"); |
echo nl2br($res)."<p>"; |
$res = $tester->replaceIDs("BugID: 789, 101112\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789, 101112"); |
echo nl2br($res)."<p>"; |
$tester->setUpVars("BugID: %BUGID%", |
"http://bugtracker/?id=%BUGID%", |
"[Ii]ssues?:?(\s*(,|and)?\s*#\d+)+\n(\d+)", |
true); |
$res = $tester->replaceIDs("BugID: 789, 101112\n". |
"This is a test message that refers to issue #123 and\n". |
"issues #456, #654 and #321.\n". |
"BugID: 789, 101112"); |
echo nl2br($res)."<p>"; |
$tester->setUpVars("Test: %BUGID%", |
"http://bugtracker/?id=%BUGID%", |
"\s*[Cc]ases*\s*[IDs]*\s*[#: ]+((\d+[ ,:;#]*)+)\n(\d+)", |
true); |
$res = $tester->replaceIDs("Cosmetic change\n". |
"CaseIDs: 48"); |
echo nl2br($res)."<p>"; |
} |
// }}} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// bugtraq.inc |
// |
// Functions for accessing the bugtraq properties and replacing issue IDs |
// with URLs. |
// |
// For more information about bugtraq, see |
// http://svn.collab.net/repos/tortoisesvn/trunk/doc/issuetrackers.txt |
class Bugtraq |
{ |
// {{{ Properties |
var $msgstring; |
var $urlstring; |
var $logregex; |
var $append; |
var $firstPart; |
var $firstPartLen; |
var $lastPart; |
var $lastPartLen; |
var $propsfound = false; |
// }}} |
// {{{ __construct($rep, $svnrep, $path) |
function Bugtraq($rep, $svnrep, $path) |
{ |
global $config; |
if ($rep->getBugtraq()) |
{ |
$pos = strrpos($path, "/"); |
$parent = substr($path, 0, $pos + 1); |
$this->append = true; |
$enoughdata = false; |
while(!$enoughdata && (strpos($parent, "/") !== false)) |
{ |
if (empty($this->msgstring)) $this->msgstring = $svnrep->getProperty($parent, 'bugtraq:message'); |
if (empty($this->logregex)) $this->logregex = $svnrep->getProperty($parent, 'bugtraq:logregex'); |
if (empty($this->urlstring)) $this->urlstring = $svnrep->getProperty($parent, 'bugtraq:url'); |
if (empty($this->append)) $this->append = ($svnrep->getProperty($parent, 'bugtraq:append') == "true"); |
$parent = substr($parent, 0, -1); // Remove the trailing slash |
$pos = strrpos($parent, "/"); // Find the last trailing slash |
$parent = substr($parent, 0, $pos + 1); // Find the previous parent directory |
$enoughdata = ((!empty($this->msgstring) || !empty($this->logregex)) && !empty($this->urlstring)); |
} |
$this->msgstring = trim(@$this->msgstring); |
$this->urlstring = trim(@$this->urlstring); |
if ($enoughdata && !empty($this->msgstring)) |
$this->initPartInfo(); |
if ($enoughdata) |
$this->propsfound = true; |
} |
} |
// }}} |
// {{{ initPartInfo() |
function initPartInfo() |
{ |
if (($bugidpos = strpos($this->msgstring, "%BUGID%")) !== false && strpos($this->urlstring, "%BUGID%") !== false) |
{ |
// Get the textual parts of the message string for comparison purposes |
$this->firstPart = substr($this->msgstring, 0, $bugidpos); |
$this->firstPartLen = strlen($this->firstPart); |
$this->lastPart = substr($this->msgstring, $bugidpos + 7); |
$this->lastPartLen = strlen($this->lastPart); |
} |
} |
// }}} |
// {{{ replaceIDs($message) |
function replaceIDs($message) |
{ |
if ($this->propsfound) |
{ |
// First we search for the message string |
$logmsg = ""; |
$message = rtrim($message); |
if ($this->append) |
{ |
// Just compare the last line |
if (($offset = strrpos($message, "\n")) !== false) |
{ |
$logmsg = substr($message, 0, $offset + 1); |
$bugLine = substr($message, $offset + 1); |
} |
else |
$bugLine = $message; |
} |
else |
{ |
if (($offset = strpos($message, "\n")) !== false) |
{ |
$bugLine = substr($message, 0, $offset); |
$logmsg = substr($message, $offset); |
} |
else |
$bugLine = $message; |
} |
// Make sure that our line really is an issue tracker message |
if (((strncmp($bugLine, $this->firstPart, $this->firstPartLen) == 0)) && |
strcmp(substr($bugLine, -$this->lastPartLen, $this->lastPartLen), $this->lastPart) == 0) |
{ |
// Get the issues list |
if ($this->lastPartLen > 0) |
$issues = substr($bugLine, $this->firstPartLen, -$this->lastPartLen); |
else |
$issues = substr($bugLine, $this->firstPartLen); |
// Add each reference to the first part of the line |
$line = $this->firstPart; |
while ($pos = strpos($issues, ",")) |
{ |
$issue = trim(substr($issues, 0, $pos)); |
$issues = substr($issues, $pos + 1); |
$line .= "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">$issue</a>, "; |
} |
$line .= "<a href=\"".str_replace("%BUGID%", trim($issues), $this->urlstring)."\">".trim($issues)."</a>".$this->lastPart; |
if ($this->append) |
$message = $logmsg.$line; |
else |
$message = $line.$logmsg; |
} |
// Now replace all other instances of bug IDs that match the regex |
if ($this->logregex) |
{ |
$message = rtrim($message); |
$line = ""; |
$allissues = ""; |
$lines = split("\n", $this->logregex); |
$regex_all = "~".$lines[0]."~"; |
$regex_single = @$lines[1]; |
if (empty($regex_single)) |
{ |
// If the property only contains one line, then the pattern is only designed |
// to find one issue number at a time. e.g. [Ii]ssue #?(\d+). In this case |
// we need to replace the matched issue ID with the link. |
if ($numMatches = preg_match_all($regex_all, $message, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) |
{ |
$addedOffset = 0; |
for ($match = 0; $match < $numMatches; $match++) |
{ |
$issue = $matches[$match][1][0]; |
$issueOffset = $matches[$match][1][1]; |
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>"; |
$message = substr_replace($message, $issueLink, $issueOffset + $addedOffset, strlen($issue)); |
$addedOffset += strlen($issueLink) - strlen($issue); |
} |
} |
} |
else |
{ |
// It the property contains two lines, then the first is a pattern for extracting |
// multiple issue numbers, and the second is a pattern extracting each issue |
// number from the multiple match. e.g. [Ii]ssue #?(\d+)(,? ?#?(\d+))+ and (\d+) |
while (preg_match($regex_all, $message, $matches, PREG_OFFSET_CAPTURE)) |
{ |
$completeMatch = $matches[0][0]; |
$completeMatchOffset = $matches[0][1]; |
$replacement = $completeMatch; |
if ($numMatches = preg_match_all("~".$regex_single."~", $replacement, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) |
{ |
$addedOffset = 0; |
for ($match = 0; $match < $numMatches; $match++) |
{ |
$issue = $matches[$match][1][0]; |
$issueOffset = $matches[$match][1][1]; |
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>"; |
$replacement = substr_replace($replacement, $issueLink, $issueOffset + $addedOffset, strlen($issue)); |
$addedOffset += strlen($issueLink) - strlen($issue); |
} |
} |
$message = substr_replace($message, $replacement, $completeMatchOffset, strlen($completeMatch)); |
} |
} |
} |
} |
return $message; |
} |
// }}} |
} |
// The BugtraqTestable class is a derived class that is used to test the matching |
// abilities of the Bugtraq class. In particular, it allows for the initialisation of the |
// class without the need for a repository. |
class BugtraqTestable extends Bugtraq |
{ |
// {{{ __construct() |
function BugtraqTestable() |
{ |
// This constructor serves to assure that the parent constructor is not |
// called. |
} |
// }}} |
// {{{ setUpVars($message, $url, $regex, $append) |
function setUpVars($message, $url, $regex, $append) |
{ |
$this->msgstring = $message; |
$this->urlstring = $url; |
$this->logregex = $regex; |
$this->append = $append; |
$this->propsfound = true; |
$this->initPartInfo(); |
} |
// }}} |
// {{{ setMessage($message) |
function setMessage($message) |
{ |
$this->msgstring = $message; |
} |
// }}} |
// {{{ setUrl($url) |
function setUrl($url) |
{ |
$this->urlstring = $url; |
} |
// }}} |
// {{{ setRegex($regex) |
function setRegEx($regex) |
{ |
$this->logregex = $regex; |
} |
// }}} |
// {{{ setAppend($append) |
function setAppend($append) |
{ |
$this->append = $append; |
} |
// }}} |
// {{{ printVars() |
function printVars() |
{ |
echo "msgstring = ".$this->msgstring."\n"; |
echo "urlstring = ".$this->urlstring."\n"; |
echo "logregex = ".$this->logregex."\n"; |
echo "append = ".$this->append."\n"; |
echo "firstPart = ".$this->firstPart."\n"; |
echo "firstPartLen = ".$this->firstPartLen."\n"; |
echo "lastPart = ".$this->lastPart."\n"; |
echo "lastPartLen = ".$this->lastPartLen."\n"; |
} |
// }}} |
} |
// {{{ test_bugtraq() |
function test_bugtraq() |
{ |
$tester = new BugtraqTestable; |
$tester->setUpVars("BugID: %BUGID%", |
"http://bugtracker/?id=%BUGID%", |
"[Ii]ssue #?(\d+)", |
true); |
//$tester->printVars(); |
$res = $tester->replaceIDs("BugID: 789\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789"); |
echo nl2br($res)."<p>"; |
$res = $tester->replaceIDs("BugID: 789, 101112\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789, 101112"); |
echo nl2br($res)."<p>"; |
$tester->setAppend(false); |
$res = $tester->replaceIDs("BugID: 789\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789"); |
echo nl2br($res)."<p>"; |
$res = $tester->replaceIDs("BugID: 789, 101112\n". |
"This is a test message that refers to issue #123 and\n". |
"issue #456.\n". |
"BugID: 789, 101112"); |
echo nl2br($res)."<p>"; |
$tester->setUpVars("BugID: %BUGID%", |
"http://bugtracker/?id=%BUGID%", |
"[Ii]ssues?:?(\s*(,|and)?\s*#\d+)+\n(\d+)", |
true); |
$res = $tester->replaceIDs("BugID: 789, 101112\n". |
"This is a test message that refers to issue #123 and\n". |
"issues #456, #654 and #321.\n". |
"BugID: 789, 101112"); |
echo nl2br($res)."<p>"; |
$tester->setUpVars("Test: %BUGID%", |
"http://bugtracker/?id=%BUGID%", |
"\s*[Cc]ases*\s*[IDs]*\s*[#: ]+((\d+[ ,:;#]*)+)\n(\d+)", |
true); |
$res = $tester->replaceIDs("Cosmetic change\n". |
"CaseIDs: 48"); |
echo nl2br($res)."<p>"; |
} |
// }}} |
?> |
/WebSVN/include/command.inc |
---|
1,171 → 1,171 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// command.inc |
// |
// External command handling |
// {{{ replaceEntities |
// |
// Replace character codes with HTML entities for display purposes. |
// This routine assumes that the character encoding of the string is |
// that of the local system (i.e., it's a string returned from a command |
// line command). |
function replaceEntities($str, $rep) |
{ |
global $config; |
// Ideally, we'd do this: |
// |
// $str = htmlentities($str, ENT_COMPAT, $config->inputEnc); |
// |
// However, htmlentities is very limited in it's ability to process |
// character encodings. We have to rely on something more powerful. |
if (version_compare(phpversion(), "4.1.0", "<")) |
{ |
// In this case, we can't do any better than assume that the |
// input encoding is ISO-8859-1. |
$str = htmlentities($str); |
} |
else |
{ |
$str = toOutputEncoding($str, $rep->getContentEncoding()); |
// $str is now encoded as UTF-8. |
$str = htmlentities($str, ENT_COMPAT, $config->outputEnc); |
} |
return $str; |
} |
// }}} |
// {{{ toOutputEncoding |
function toOutputEncoding($str, $inputEncoding = "") |
{ |
global $config; |
if (empty($inputEncoding)) |
$inputEncoding = $config->inputEnc; |
// Try to convert the messages based on the locale information |
if ($config->inputEnc && $config->outputEnc) |
{ |
if (function_exists("iconv")) |
{ |
$output = @iconv($inputEncoding, $config->outputEnc, $str); |
if (!empty($output)) |
$str = $output; |
} |
} |
return $str; |
} |
// }}} |
// {{{ quoteCommand |
function quoteCommand($cmd, $redirecterr) |
{ |
global $config; |
if ($redirecterr) |
$cmd .= " 2>&1"; |
// On Windows machines, the whole line needs quotes round it so that it's |
// passed to cmd.exe correctly |
if ($config->serverIsWindows) |
$cmd = "\"$cmd\""; |
return $cmd; |
} |
// }}} |
// {{{ runCommand |
function runCommand($cmd, $mayReturnNothing = false) |
{ |
global $lang; |
$output = array (); |
$err = false; |
$c = quoteCommand($cmd, false); |
// Try to run the command normally |
if ($handle = popen($c, 'r')) |
{ |
$firstline = true; |
while (!feof($handle)) |
{ |
$line = fgets($handle); |
if ($firstline && empty($line) && !$mayReturnNothing) |
{ |
$err = true; |
} |
$firstline = false; |
$output[] = toOutputEncoding(rtrim($line)); |
} |
pclose($handle); |
if (!$err) |
return $output; |
} |
echo '<p>',$lang['BADCMD'],': <code>',$cmd,'</code></p>'; |
// Rerun the command, this time grabbing the error information |
$c = quoteCommand($cmd, true); |
$output = toOutputEncoding(shell_exec($c)); |
if (!empty($output)) |
echo '<p>',nl2br($output),'</p>'; |
exit; |
} |
// }}} |
// {{{ quote |
// |
// Quote a string to send to the command line |
function quote($str) |
{ |
global $config; |
if ($config->serverIsWindows) |
return "\"$str\""; |
else |
return escapeshellarg($str); |
} |
// }}} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// command.inc |
// |
// External command handling |
// {{{ replaceEntities |
// |
// Replace character codes with HTML entities for display purposes. |
// This routine assumes that the character encoding of the string is |
// that of the local system (i.e., it's a string returned from a command |
// line command). |
function replaceEntities($str, $rep) |
{ |
global $config; |
// Ideally, we'd do this: |
// |
// $str = htmlentities($str, ENT_COMPAT, $config->inputEnc); |
// |
// However, htmlentities is very limited in it's ability to process |
// character encodings. We have to rely on something more powerful. |
if (version_compare(phpversion(), "4.1.0", "<")) |
{ |
// In this case, we can't do any better than assume that the |
// input encoding is ISO-8859-1. |
$str = htmlentities($str); |
} |
else |
{ |
$str = toOutputEncoding($str, $rep->getContentEncoding()); |
// $str is now encoded as UTF-8. |
$str = htmlentities($str, ENT_COMPAT, $config->outputEnc); |
} |
return $str; |
} |
// }}} |
// {{{ toOutputEncoding |
function toOutputEncoding($str, $inputEncoding = "") |
{ |
global $config; |
if (empty($inputEncoding)) |
$inputEncoding = $config->inputEnc; |
// Try to convert the messages based on the locale information |
if ($config->inputEnc && $config->outputEnc) |
{ |
if (function_exists("iconv")) |
{ |
$output = @iconv($inputEncoding, $config->outputEnc, $str); |
if (!empty($output)) |
$str = $output; |
} |
} |
return $str; |
} |
// }}} |
// {{{ quoteCommand |
function quoteCommand($cmd, $redirecterr) |
{ |
global $config; |
if ($redirecterr) |
$cmd .= " 2>&1"; |
// On Windows machines, the whole line needs quotes round it so that it's |
// passed to cmd.exe correctly |
if ($config->serverIsWindows) |
$cmd = "\"$cmd\""; |
return $cmd; |
} |
// }}} |
// {{{ runCommand |
function runCommand($cmd, $mayReturnNothing = false) |
{ |
global $lang; |
$output = array (); |
$err = false; |
$c = quoteCommand($cmd, false); |
// Try to run the command normally |
if ($handle = popen($c, 'r')) |
{ |
$firstline = true; |
while (!feof($handle)) |
{ |
$line = fgets($handle); |
if ($firstline && empty($line) && !$mayReturnNothing) |
{ |
$err = true; |
} |
$firstline = false; |
$output[] = toOutputEncoding(rtrim($line)); |
} |
pclose($handle); |
if (!$err) |
return $output; |
} |
echo '<p>',$lang['BADCMD'],': <code>',$cmd,'</code></p>'; |
// Rerun the command, this time grabbing the error information |
$c = quoteCommand($cmd, true); |
$output = toOutputEncoding(shell_exec($c)); |
if (!empty($output)) |
echo '<p>',nl2br($output),'</p>'; |
exit; |
} |
// }}} |
// {{{ quote |
// |
// Quote a string to send to the command line |
function quote($str) |
{ |
global $config; |
if ($config->serverIsWindows) |
return "\"$str\""; |
else |
return escapeshellarg($str); |
} |
// }}} |
?> |
/WebSVN/include/configclass.inc |
---|
1,1251 → 1,1269 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// configclass.inc4 |
// |
// General class for handling configuration options |
require_once("include/command.inc"); |
require_once("include/auth.inc"); |
require_once("include/version.inc"); |
// Auxillary functions used to sort repositories by name/group |
// {{{ cmpReps($a, $b) |
function cmpReps($a, $b) |
{ |
// First, sort by group |
$g = strcasecmp($a->group, $b->group); |
if ($g) |
return $g; |
// Same group? Sort by name |
return strcasecmp($a->name, $b->name); |
} |
// }}} |
// {{{ cmpGroups($a, $b) |
function cmpGroups($a, $b) |
{ |
$g = strcasecmp($a->group, $b->group); |
if ($g) |
return $g; |
return 0; |
} |
// }}} |
// {{{ mergesort(&$array, [$cmp_function]) |
function mergesort(&$array, $cmp_function = 'strcmp') |
{ |
// Arrays of size < 2 require no action |
if (count($array) < 2) |
return; |
// Split the array in half |
$halfway = count($array) / 2; |
$array1 = array_slice($array, 0, $halfway); |
$array2 = array_slice($array, $halfway); |
// Recurse to sort the two halves |
mergesort($array1, $cmp_function); |
mergesort($array2, $cmp_function); |
// If all of $array1 is <= all of $array2, just append them. |
if (call_user_func($cmp_function, end($array1), $array2[0]) < 1) |
{ |
$array = array_merge($array1, $array2); |
return; |
} |
// Merge the two sorted arrays into a single sorted array |
$array = array(); |
$ptr1 = $ptr2 = 0; |
while ($ptr1 < count($array1) && $ptr2 < count($array2)) |
{ |
if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1) |
{ |
$array[] = $array1[$ptr1++]; |
} |
else |
{ |
$array[] = $array2[$ptr2++]; |
} |
} |
// Merge the remainder |
while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++]; |
while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++]; |
return; |
} |
// }}} |
// A Repository configuration class |
Class Repository |
{ |
// {{{ Properties |
var $name; |
var $svnName; |
var $path; |
var $group; |
var $username; |
var $password; |
// Local configuration options must start off unset |
var $allowDownload; |
var $minDownloadLevel; |
var $allowedExceptions = array(); |
var $disallowedExceptions = array(); |
var $rss; |
var $spaces; |
var $ignoreSvnMimeTypes; |
var $ignoreWebSVNContentTypes; |
var $bugtraq; |
var $auth; |
var $contentEnc; |
var $templatePath; |
// }}} |
// {{{ __construct($name, $svnName, $path, [$group, [$username, [$password]]]) |
function Repository($name, $svnName, $path, $group = NULL, $username = NULL, $password = NULL) |
{ |
$this->name = $name; |
$this->svnName = $svnName; |
$this->path = $path; |
$this->group = $group; |
$this->username = $username; |
$this->password = $password; |
} |
// }}} |
// {{{ getDisplayName() |
function getDisplayName() |
{ |
if(!empty($this->group)) |
return $this->group.".".$this->name; |
else |
return $this->name; |
} |
// }}} |
// {{{ svnParams |
function svnParams() |
{ |
if (!empty($this->username)) |
return " --username ".$this->username." --password ".$this->password." "; |
else |
return " "; |
} |
// }}} |
// Local configuration accessors |
// {{{ RSS Feed |
function hideRSS() |
{ |
$this->rss = false; |
} |
function showRSS() |
{ |
$this->rss = true; |
} |
function getHideRSS() |
{ |
global $config; |
if (isset($this->rss)) |
return $this->rss; |
return $config->getHideRSS(); |
} |
// }}} |
// {{{ Download |
function allowDownload() |
{ |
$this->allowDownload = true; |
} |
function disallowDownload() |
{ |
$this->allowDownload = false; |
} |
function getAllowDownload() |
{ |
global $config; |
if (isset($this->allowDownload)) |
return $this->allowDownload; |
return $config->getAllowDownload(); |
} |
function setMinDownloadLevel($level) |
{ |
$this->minDownloadLevel = $level; |
} |
function getMinDownloadLevel() |
{ |
global $config; |
if (isset($this->minDownloadLevel)) |
return $this->minDownloadLevel; |
return $config->getMinDownloadLevel(); |
} |
function addAllowedDownloadException($path) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
$this->allowedExceptions[] = $path; |
} |
function addDisallowedDownloadException($path) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
$this->disallowedExceptions[] = $path; |
} |
function isDownloadAllowed($path) |
{ |
global $config; |
// Check global download option |
if (!$this->getAllowDownload()) |
return false; |
// Check with access module |
if (!$this->hasUnrestrictedReadAccess($path)) |
return false; |
$subs = explode("/", $path); |
$level = count($subs) - 2; |
if ($level >= $this->getMinDownloadLevel()) |
{ |
// Level OK, search for disallowed exceptions |
if ($config->findException($path, $this->disallowedExceptions)) |
return false; |
if ($config->findException($path, $config->disallowedExceptions)) |
return false; |
return true; |
} |
else |
{ |
// Level not OK, search for disallowed exceptions |
if ($config->findException($path, $this->allowedExceptions)) |
return true; |
if ($config->findException($path, $config->allowedExceptions)) |
return true; |
return false; |
} |
} |
// }}} |
// {{{ Templates |
function setTemplatePath($path) |
{ |
$lastchar = substr($path, -1, 1); |
if (!($lastchar == DIRECTORY_SEPARATOR || |
$lastchar == '/' || |
$lastchar == '\\')) |
$path .= DIRECTORY_SEPARATOR; |
$this->templatePath = $path; |
} |
function getTemplatePath() |
{ |
global $config; |
if (!empty($this->templatePath)) |
return $this->templatePath; |
else |
return $config->getTemplatePath(); |
} |
// }}} |
// {{{ Tab expansion |
function expandTabsBy($sp) |
{ |
$this->spaces = $sp; |
} |
function getExpandTabsBy() |
{ |
global $config; |
if (isset($this->spaces)) |
return $this->spaces; |
return $config->getExpandTabsBy(); |
} |
// }}} |
// {{{ MIME-Type Handing |
function ignoreSvnMimeTypes() |
{ |
$this->ignoreSvnMimeTypes = true; |
} |
function useSvnMimeTypes() |
{ |
$this->ignoreSvnMimeTypes = false; |
} |
function getIgnoreSvnMimeTypes() |
{ |
global $config; |
if (isset($this->ignoreSvnMimeTypes)) |
return $this->ignoreSvnMimeTypes; |
return $config->getIgnoreSvnMimeTypes(); |
} |
function ignoreWebSVNContentTypes() |
{ |
$this->ignoreWebSVNContentTypes = true; |
} |
function useWebSVNContentTypes() |
{ |
$this->ignoreWebSVNContentTypes = false; |
} |
function getIgnoreWebSVNContentTypes() |
{ |
global $config; |
if (isset($this->ignoreWebSVNContentTypes)) |
return $this->ignoreWebSVNContentTypes; |
return $config->getIgnoreWebSVNContentTypes(); |
} |
// }}} |
// {{{ Issue Tracking |
function useBugtraqProperties() |
{ |
$this->bugtraq = true; |
} |
function ignoreBugtraqProperties() |
{ |
$this->bugtraq = false; |
} |
function getBugtraq() |
{ |
global $config; |
if (isset($this->bugtraq)) |
return $this->bugtraq; |
return $config->getBugtraq(); |
} |
// }}} |
// {{{ Encodings |
function setContentEncoding($contentEnc) |
{ |
$this->contentEnc = $contentEnc; |
} |
function getContentEncoding() |
{ |
global $config; |
if (isset($this->contentEnc)) |
return $this->contentEnc; |
return $config->getContentEncoding(); |
} |
// }}} |
// {{{ Authentication |
function useAuthenticationFile($file) |
{ |
if (is_readable($file)) |
$this->auth =& new Authentication($file); |
else |
die('Unable to read authentication file "'.$file.'"'); |
} |
function hasReadAccess($path, $checkSubFolders = false) |
{ |
global $config; |
$a = null; |
if (isset($this->auth)) |
$a =& $this->auth; |
else |
$a =& $config->getAuth(); |
if (!empty($a)) |
return $a->hasReadAccess($this->svnName, $path, $checkSubFolders); |
// No auth file - free access... |
return true; |
} |
function hasUnrestrictedReadAccess($path) |
{ |
global $config; |
$a = null; |
if (isset($this->auth)) |
$a =& $this->auth; |
else |
$a =& $config->getAuth(); |
if (!empty($a)) |
return $a->hasUnrestrictedReadAccess($this->svnName, $path); |
// No auth file - free access... |
return true; |
} |
// }}} |
} |
// The general configuration class |
Class Config |
{ |
// {{{ Properties |
// Tool path locations |
var $svnlook = "svnlook"; |
var $svn = "svn --non-interactive --config-dir /tmp"; |
var $svn_noparams = "svn --config-dir /tmp"; |
var $diff = "diff"; |
var $enscript ="enscript"; |
var $sed = "sed"; |
var $gzip = "gzip"; |
var $tar = "tar"; |
// Other configuration items |
var $treeView = true; |
var $flatIndex = true; |
var $openTree = false; |
var $serverIsWindows = false; |
var $cacheResults = false; |
var $multiViews = false; |
var $useEnscript = false; |
var $allowDownload = false; |
var $minDownloadLevel = 0; |
var $allowedExceptions = array(); |
var $disallowedExceptions = array(); |
var $rss = true; |
var $spaces = 8; |
var $bugtraq = false; |
var $auth = ""; |
var $templatePath = "./templates/Standard/"; |
var $phpCompatPath = './include/PHP/Compat.php'; |
var $ignoreSvnMimeTypes = false; |
var $ignoreWebSVNContentTypes = false; |
var $subversionMajorVersion = ""; |
var $subversionMinorVersion = ""; |
// Default character encodings |
var $inputEnc = ""; // Encoding of output returned from command line |
var $contentEnc = ""; // Encoding of repository content |
var $outputEnc = "UTF-8"; // Encoding of web page. Now forced to UTF-8 |
var $quote = "'"; |
var $_repositories; |
// }}} |
// {{{ __construct() |
function Config() |
{ |
} |
// }}} |
// {{{ Repository configuration |
function addRepository($name, $url, $group = NULL, $username = NULL, $password = NULL) |
{ |
$url = str_replace(DIRECTORY_SEPARATOR, "/", $url); |
if ($url{strlen($url) - 1} == "/") |
$url = substr($url, 0, -1); |
$svnName = substr($url, strrpos($url, "/") + 1); |
$this->_repositories[] = new Repository($name, $svnName, $url, $group, $username, $password); |
} |
function getRepositories() |
{ |
return $this->_repositories; |
} |
function &findRepository($name) |
{ |
foreach ($this->_repositories as $index => $rep) |
{ |
if (strcmp($rep->getDisplayName(), $name) == 0) |
{ |
$repref =& $this->_repositories[$index]; |
return $repref; |
} |
} |
print "ERROR: Unable to find repository '$name'"; |
exit; |
} |
// }}} |
// {{{ setServerIsWindows |
// |
// The server is running on Windows |
function setServerIsWindows() |
{ |
$this->serverIsWindows = true; |
// Try to set the input encoding intelligently |
$cp = 0; |
if ($cp = @shell_exec("CHCP")) |
{ |
$cp = trim(substr($cp, strpos($cp, ":") + 1)); |
settype($cp, "integer"); |
} |
// Use the most sensible default value if that failed |
if ($cp == 0) $cp = 850; |
// We assume, as a default, that the encoding of the repository contents is |
// in iso-8859-1, to be compatible with compilers and the like. |
$this->setInputEncoding("CP$cp", "iso-8859-1"); |
// On Windows machines, use double quotes around command line parameters |
$this->quote = '"'; |
} |
// }}} |
// {{{ Caching |
// setCachingOn |
// |
// Set result caching on |
function setCachingOn() |
{ |
$this->cacheResults = true; |
} |
function isCachingOn() |
{ |
return $this->cacheResults; |
} |
// }}} |
// {{{ MultiViews |
// useMultiViews |
// |
// Use MultiViews to access the repository |
function useMultiViews() |
{ |
$this->multiViews = true; |
} |
function getUseMultiViews() |
{ |
return $this->multiViews; |
} |
// }}} |
// {{{ Enscript |
// useEnscript |
// |
// Use Enscript to colourise listings |
function useEnscript() |
{ |
$this->useEnscript = true; |
} |
function getUseEnscript() |
{ |
return $this->useEnscript; |
} |
// }}} |
// {{{ RSS |
// offerRSS |
// |
// Use Enscript to colourise listings |
function hideRSS($myrep = 0) |
{ |
if (empty($myrep)) |
$this->rss = false; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->hideRSS(); |
} |
} |
function getHideRSS() |
{ |
return $this->rss; |
} |
// }}} |
// {{{ Downloads |
// allowDownload |
// |
// Allow download of tarballs |
function allowDownload($myrep = 0) |
{ |
if (empty($myrep)) |
$this->allowDownload = true; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->allowDownload(); |
} |
} |
function disallowDownload($myrep = 0) |
{ |
if (empty($myrep)) |
$this->allowDownload = false; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->disallowDownload(); |
} |
} |
function getAllowDownload() |
{ |
return $this->allowDownload; |
} |
function setMinDownloadLevel($level, $myrep = 0) |
{ |
if (empty($myrep)) |
$this->minDownloadLevel = $level; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->setMinDownloadLevel($level); |
} |
} |
function getMinDownloadLevel() |
{ |
return $this->minDownloadLevel; |
} |
function addAllowedDownloadException($path, $myrep = 0) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
if (empty($myrep)) |
$this->allowedExceptions[] = $path; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->addAllowedDownloadException($path); |
} |
} |
function addDisallowedDownloadException($path, $myrep = 0) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
if (empty($myrep)) |
$this->disallowedExceptions[] = $path; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->addDisallowedDownloadException($path); |
} |
} |
function findException($path, $exceptions) |
{ |
foreach ($exceptions As $key => $exc) |
{ |
if (strncmp($exc, $path, strlen($exc)) == 0) |
return true; |
} |
return false; |
} |
// }}} |
// {{{ getURL |
// |
// Get the URL to a path name based on the current config |
function getURL($rep, $path, $op) |
{ |
$base = $_SERVER["SCRIPT_NAME"]; |
if ($this->multiViews) |
{ |
// Remove the .php |
if (eregi(".php$", $base)) |
{ |
// Remove the .php |
$base = substr($base, 0, -4); |
} |
if ($path && $path{0} != "/") $path = "/".$path; |
$url = $base; |
if (is_object($rep)) |
{ |
$url .= "/".$rep->getDisplayName().str_replace('%2F', '/', urlencode($path)); |
if ($op != "dir" && $op != "file") |
$url .= "?op=$op&"; |
else |
$url .= "?"; |
} |
return $url; |
} |
else |
{ |
switch ($op) |
{ |
case "dir": |
$fname = "listing.php"; |
break; |
case "file": |
$fname = "filedetails.php"; |
break; |
case "log": |
$fname = "log.php"; |
break; |
case "diff": |
$fname = "diff.php"; |
break; |
case "blame": |
$fname = "blame.php"; |
break; |
case "form": |
$fname = "form.php"; |
break; |
case "rss": |
$fname = "rss.php"; |
break; |
case "dl": |
$fname = "dl.php"; |
break; |
case "comp": |
$fname = "comp.php"; |
break; |
} |
if ($rep == -1) |
return $fname."?path=".urlencode($path)."&"; |
else |
return $fname."?repname=".urlencode($rep->getDisplayName())."&path=".urlencode($path)."&"; |
} |
} |
// }}} |
// {{{ Paths and Commands |
// setPath |
// |
// Set the location of the given path |
function setPath(&$var, $path, $name, $params = "") |
{ |
$lastchar = substr($path, -1, 1); |
$isDir = ($lastchar == DIRECTORY_SEPARATOR || |
$lastchar == "/" || |
$lastchar == "\\"); |
if (!$isDir) |
{ |
$path .= DIRECTORY_SEPARATOR; |
} |
// On a windows machine we need to put spaces around the entire command |
// to allow for spaces in the path |
if ($this->serverIsWindows) |
$var = "\"$path$name\""; |
else |
$var = "$path$name"; |
$var .= " ".$params; |
} |
// setSVNCommandPath |
// |
// Define the location of the svn and svnlook commands |
function setSVNCommandPath($path) |
{ |
$this->setPath($this->svn, $path, "svn", "--non-interactive --config-dir /tmp"); |
$this->setPath($this->svn_noparams, $path, "svn", " --config-dir /tmp"); |
$this->setPath($this->svnlook, $path, "svnlook"); |
} |
function getSvnCommand() |
{ |
return $this->svn; |
} |
function getCleanSvnCommand() |
{ |
return $this->svn_noparams; |
} |
function getSvnlookCommand() |
{ |
return $this->svnlook; |
} |
// setDiffPath |
// |
// Define the location of the diff command |
function setDiffPath($path) |
{ |
$this->setPath($this->diff, $path, "diff"); |
} |
function getDiffCommand() |
{ |
return $this->diff; |
} |
// setEnscriptPath |
// |
// Define the location of the enscript command |
function setEnscriptPath($path) |
{ |
$this->setPath($this->enscript, $path, "enscript"); |
} |
function getEnscriptCommand() |
{ |
return $this->enscript; |
} |
// setSedPath |
// |
// Define the location of the sed command |
function setSedPath($path) |
{ |
$this->setPath($this->sed, $path, "sed"); |
} |
function getSedCommand() |
{ |
return $this->sed; |
} |
// setTarPath |
// |
// Define the location of the tar command |
function setTarPath($path) |
{ |
$this->setPath($this->tar, $path, "tar"); |
} |
function getTarCommand() |
{ |
return $this->tar; |
} |
// setGzipPath |
// |
// Define the location of the GZip command |
function setGzipPath($path) |
{ |
$this->setPath($this->gzip, $path, "gzip"); |
} |
function getGzipCommand() |
{ |
return $this->gzip; |
} |
// Templates |
function setTemplatePath($path, $myrep = 0) |
{ |
if (empty($myrep)) |
{ |
$lastchar = substr($path, -1, 1); |
if (!($lastchar == DIRECTORY_SEPARATOR || |
$lastchar == '/' || |
$lastchar == '\\')) |
$path .= DIRECTORY_SEPARATOR; |
$this->templatePath = $path; |
} |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->setTemplatePath($path); |
} |
} |
function getTemplatePath() |
{ |
return $this->templatePath; |
} |
// PHP Compat file (from PEAR) |
function setPHPCompatPath($path) |
{ |
$this->setPath($this->phpCompatPath, $path, 'Compat.php'); |
} |
function getPHPCompatFile() |
{ |
return trim($this->phpCompatPath); |
} |
// }}} |
// {{{ parentPath |
// |
// Automatically set up the repositories based on a parent path |
function parentPath($path, $group = NULL) |
{ |
if ($handle = @opendir($path)) |
{ |
// For each file... |
while (false !== ($file = readdir($handle))) |
{ |
// That's also a non hidden directory |
if (is_dir($path.DIRECTORY_SEPARATOR.$file) && $file{0} != ".") |
{ |
// And that contains a db directory (in an attempt to not include |
// non svn repositories. |
if (is_dir($path.DIRECTORY_SEPARATOR.$file.DIRECTORY_SEPARATOR."db")) |
{ |
// We add the repository to the list |
$this->addRepository($file, "file:///".$path.DIRECTORY_SEPARATOR.$file, $group); |
} |
} |
} |
closedir($handle); |
} |
// Sort the repositories into alphabetical order |
if (!empty($this->_repositories)) |
usort($this->_repositories, "cmpReps"); |
} |
// }}} |
// {{{ Encoding functions |
function setInputEncoding($systemEnc) |
{ |
$this->inputEnc = $systemEnc; |
if (!isset($this->contentEnc)) |
$this->contentEnc = $systemEnc; |
} |
function getInputEncoding() |
{ |
return $this->inputEnc; |
} |
function setContentEncoding($contentEnc, $myrep = 0) |
{ |
if (empty($myrep)) |
$this->contentEnc = $contentEnc; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->setContentEncoding($contentEnc); |
} |
} |
function getContentEncoding() |
{ |
return $this->contentEnc; |
} |
// }}} |
// {{{ Tab expansion functions |
function expandTabsBy($sp, $myrep = 0) |
{ |
if (empty($myrep)) |
$this->spaces = $sp; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->expandTabsBy($sp); |
} |
} |
function getExpandTabsBy() |
{ |
return $this->spaces; |
} |
// }}} |
// {{{ Misc settings |
function ignoreSvnMimeTypes() |
{ |
$this->ignoreSvnMimeTypes = true; |
} |
function getIgnoreSvnMimeTypes() |
{ |
return $this->ignoreSvnMimeTypes; |
} |
function ignoreWebSVNContentTypes() |
{ |
$this->ignoreWebSVNContentTypes = true; |
} |
function getIgnoreWebSVNContentTypes() |
{ |
return $this->ignoreWebSVNContentTypes; |
} |
function useBugtraqProperties($myrep = 0) |
{ |
if (empty($myrep)) |
$this->bugtraq = true; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->useBugtraqProperties(); |
} |
} |
function getBugtraq() |
{ |
return $this->bugtraq; |
} |
function useAuthenticationFile($file, $myrep = 0) |
{ |
if (empty($myrep)) |
{ |
if (is_readable($file)) |
$this->auth = new Authentication($file); |
else |
{ |
echo "Unable to read authentication file '$file'"; |
exit; |
} |
} |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->useAuthenticationFile($file); |
} |
} |
function &getAuth() |
{ |
return $this->auth; |
} |
function useTreeView() |
{ |
$this->treeView = true; |
} |
function getUseTreeView() |
{ |
return $this->treeView; |
} |
function useFlatView() |
{ |
$this->treeView = false; |
} |
function useTreeIndex($open) |
{ |
$this->flatIndex = false; |
$this->openTree = $open; |
} |
function getUseFlatIndex() |
{ |
return $this->flatIndex; |
} |
function getOpenTree() |
{ |
return $this->openTree; |
} |
// setSubversionMajorVersion |
// |
// Set subversion major version |
function setSubversionMajorVersion($subversionMajorVersion) |
{ |
$this->subversionMajorVersion = $subversionMajorVersion; |
} |
function getSubversionMajorVersion() |
{ |
return $this->subversionMajorVersion; |
} |
// setSubversionMinorVersion |
// |
// Set subversion minor version |
function setSubversionMinorVersion($subversionMinorVersion) |
{ |
$this->subversionMinorVersion = $subversionMinorVersion; |
} |
function getSubversionMinorVersion() |
{ |
return $this->subversionMinorVersion; |
} |
// }}} |
// {{{ Sort the repostories |
// |
// This function sorts the repositories by group name. The contents of the |
// group are left in there original order, which will either be sorted if the |
// group was added using the parentPath function, or defined for the order in |
// which the repositories were included in the user's config file. |
// |
// Note that as of PHP 4.0.6 the usort command no longer preserves the order |
// of items that are considered equal (in our case, part of the same group). |
// The mergesort function preserves this order. |
function sortByGroup() |
{ |
if (!empty($this->_repositories)) |
mergesort($this->_repositories, "cmpGroups"); |
} |
// }}} |
} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// configclass.inc4 |
// |
// General class for handling configuration options |
require_once("include/command.inc"); |
require_once("include/auth.inc"); |
require_once("include/version.inc"); |
// Auxillary functions used to sort repositories by name/group |
// {{{ cmpReps($a, $b) |
function cmpReps($a, $b) |
{ |
// First, sort by group |
$g = strcasecmp($a->group, $b->group); |
if ($g) |
return $g; |
// Same group? Sort by name |
return strcasecmp($a->name, $b->name); |
} |
// }}} |
// {{{ cmpGroups($a, $b) |
function cmpGroups($a, $b) |
{ |
$g = strcasecmp($a->group, $b->group); |
if ($g) |
return $g; |
return 0; |
} |
// }}} |
// {{{ mergesort(&$array, [$cmp_function]) |
function mergesort(&$array, $cmp_function = 'strcmp') |
{ |
// Arrays of size < 2 require no action |
if (count($array) < 2) |
return; |
// Split the array in half |
$halfway = count($array) / 2; |
$array1 = array_slice($array, 0, $halfway); |
$array2 = array_slice($array, $halfway); |
// Recurse to sort the two halves |
mergesort($array1, $cmp_function); |
mergesort($array2, $cmp_function); |
// If all of $array1 is <= all of $array2, just append them. |
if (call_user_func($cmp_function, end($array1), $array2[0]) < 1) |
{ |
$array = array_merge($array1, $array2); |
return; |
} |
// Merge the two sorted arrays into a single sorted array |
$array = array(); |
$ptr1 = $ptr2 = 0; |
while ($ptr1 < count($array1) && $ptr2 < count($array2)) |
{ |
if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1) |
{ |
$array[] = $array1[$ptr1++]; |
} |
else |
{ |
$array[] = $array2[$ptr2++]; |
} |
} |
// Merge the remainder |
while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++]; |
while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++]; |
return; |
} |
// }}} |
// A Repository configuration class |
Class Repository |
{ |
// {{{ Properties |
var $name; |
var $svnName; |
var $path; |
var $group; |
var $username; |
var $password; |
// Local configuration options must start off unset |
var $allowDownload; |
var $minDownloadLevel; |
var $allowedExceptions = array(); |
var $disallowedExceptions = array(); |
var $rss; |
var $spaces; |
var $ignoreSvnMimeTypes; |
var $ignoreWebSVNContentTypes; |
var $bugtraq; |
var $auth; |
var $contentEnc; |
var $templatePath; |
// }}} |
// {{{ __construct($name, $svnName, $path, [$group, [$username, [$password]]]) |
function Repository($name, $svnName, $path, $group = NULL, $username = NULL, $password = NULL) |
{ |
$this->name = $name; |
$this->svnName = $svnName; |
$this->path = $path; |
$this->group = $group; |
$this->username = $username; |
$this->password = $password; |
} |
// }}} |
// {{{ getDisplayName() |
function getDisplayName() |
{ |
if(!empty($this->group)) |
return $this->group.".".$this->name; |
else |
return $this->name; |
} |
// }}} |
// {{{ svnParams |
function svnParams() |
{ |
if (!empty($this->username)) |
return " --username ".$this->username." --password ".$this->password." "; |
else |
return " "; |
} |
// }}} |
// Local configuration accessors |
// {{{ RSS Feed |
function hideRSS() |
{ |
$this->rss = false; |
} |
function showRSS() |
{ |
$this->rss = true; |
} |
function getHideRSS() |
{ |
global $config; |
if (isset($this->rss)) |
return $this->rss; |
return $config->getHideRSS(); |
} |
// }}} |
// {{{ Download |
function allowDownload() |
{ |
$this->allowDownload = true; |
} |
function disallowDownload() |
{ |
$this->allowDownload = false; |
} |
function getAllowDownload() |
{ |
global $config; |
if (isset($this->allowDownload)) |
return $this->allowDownload; |
return $config->getAllowDownload(); |
} |
function setMinDownloadLevel($level) |
{ |
$this->minDownloadLevel = $level; |
} |
function getMinDownloadLevel() |
{ |
global $config; |
if (isset($this->minDownloadLevel)) |
return $this->minDownloadLevel; |
return $config->getMinDownloadLevel(); |
} |
function addAllowedDownloadException($path) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
$this->allowedExceptions[] = $path; |
} |
function addDisallowedDownloadException($path) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
$this->disallowedExceptions[] = $path; |
} |
function isDownloadAllowed($path) |
{ |
global $config; |
// Check global download option |
if (!$this->getAllowDownload()) |
return false; |
// Check with access module |
if (!$this->hasUnrestrictedReadAccess($path)) |
return false; |
$subs = explode("/", $path); |
$level = count($subs) - 2; |
if ($level >= $this->getMinDownloadLevel()) |
{ |
// Level OK, search for disallowed exceptions |
if ($config->findException($path, $this->disallowedExceptions)) |
return false; |
if ($config->findException($path, $config->disallowedExceptions)) |
return false; |
return true; |
} |
else |
{ |
// Level not OK, search for disallowed exceptions |
if ($config->findException($path, $this->allowedExceptions)) |
return true; |
if ($config->findException($path, $config->allowedExceptions)) |
return true; |
return false; |
} |
} |
// }}} |
// {{{ Templates |
function setTemplatePath($path) |
{ |
$lastchar = substr($path, -1, 1); |
if (!($lastchar == DIRECTORY_SEPARATOR || |
$lastchar == '/' || |
$lastchar == '\\')) |
$path .= DIRECTORY_SEPARATOR; |
$this->templatePath = $path; |
} |
function getTemplatePath() |
{ |
global $config; |
if (!empty($this->templatePath)) |
return $this->templatePath; |
else |
return $config->getTemplatePath(); |
} |
// }}} |
// {{{ Tab expansion |
function expandTabsBy($sp) |
{ |
$this->spaces = $sp; |
} |
function getExpandTabsBy() |
{ |
global $config; |
if (isset($this->spaces)) |
return $this->spaces; |
return $config->getExpandTabsBy(); |
} |
// }}} |
// {{{ MIME-Type Handing |
function ignoreSvnMimeTypes() |
{ |
$this->ignoreSvnMimeTypes = true; |
} |
function useSvnMimeTypes() |
{ |
$this->ignoreSvnMimeTypes = false; |
} |
function getIgnoreSvnMimeTypes() |
{ |
global $config; |
if (isset($this->ignoreSvnMimeTypes)) |
return $this->ignoreSvnMimeTypes; |
return $config->getIgnoreSvnMimeTypes(); |
} |
function ignoreWebSVNContentTypes() |
{ |
$this->ignoreWebSVNContentTypes = true; |
} |
function useWebSVNContentTypes() |
{ |
$this->ignoreWebSVNContentTypes = false; |
} |
function getIgnoreWebSVNContentTypes() |
{ |
global $config; |
if (isset($this->ignoreWebSVNContentTypes)) |
return $this->ignoreWebSVNContentTypes; |
return $config->getIgnoreWebSVNContentTypes(); |
} |
// }}} |
// {{{ Issue Tracking |
function useBugtraqProperties() |
{ |
$this->bugtraq = true; |
} |
function ignoreBugtraqProperties() |
{ |
$this->bugtraq = false; |
} |
function getBugtraq() |
{ |
global $config; |
if (isset($this->bugtraq)) |
return $this->bugtraq; |
return $config->getBugtraq(); |
} |
// }}} |
// {{{ Encodings |
function setContentEncoding($contentEnc) |
{ |
$this->contentEnc = $contentEnc; |
} |
function getContentEncoding() |
{ |
global $config; |
if (isset($this->contentEnc)) |
return $this->contentEnc; |
return $config->getContentEncoding(); |
} |
// }}} |
// {{{ Authentication |
function useAuthenticationFile($file) |
{ |
if (is_readable($file)) |
$this->auth =& new Authentication($file); |
else |
die('Unable to read authentication file "'.$file.'"'); |
} |
function hasReadAccess($path, $checkSubFolders = false) |
{ |
global $config; |
$a = null; |
if (isset($this->auth)) |
$a =& $this->auth; |
else |
$a =& $config->getAuth(); |
if (!empty($a)) |
return $a->hasReadAccess($this->svnName, $path, $checkSubFolders); |
// No auth file - free access... |
return true; |
} |
function hasUnrestrictedReadAccess($path) |
{ |
global $config; |
$a = null; |
if (isset($this->auth)) |
$a =& $this->auth; |
else |
$a =& $config->getAuth(); |
if (!empty($a)) |
return $a->hasUnrestrictedReadAccess($this->svnName, $path); |
// No auth file - free access... |
return true; |
} |
// }}} |
} |
// The general configuration class |
Class Config |
{ |
// {{{ Properties |
// Tool path locations |
var $svnlook = "svnlook"; |
var $svn = "svn --non-interactive --config-dir /tmp"; |
var $svn_noparams = "svn --config-dir /tmp"; |
var $diff = "diff"; |
var $enscript ="enscript"; |
var $sed = "sed"; |
var $gzip = "gzip"; |
var $tar = "tar"; |
// Other configuration items |
var $treeView = true; |
var $flatIndex = true; |
var $openTree = false; |
var $serverIsWindows = false; |
var $phpCompatEnabled = false; |
var $cacheResults = false; |
var $multiViews = false; |
var $useEnscript = false; |
var $allowDownload = false; |
var $minDownloadLevel = 0; |
var $allowedExceptions = array(); |
var $disallowedExceptions = array(); |
var $rss = true; |
var $spaces = 8; |
var $bugtraq = false; |
var $auth = ""; |
var $templatePath = "./templates/Standard/"; |
var $phpCompatPath = './include/PHP/Compat.php'; |
var $ignoreSvnMimeTypes = false; |
var $ignoreWebSVNContentTypes = false; |
var $subversionMajorVersion = ""; |
var $subversionMinorVersion = ""; |
// Default character encodings |
var $inputEnc = ""; // Encoding of output returned from command line |
var $contentEnc = ""; // Encoding of repository content |
var $outputEnc = "UTF-8"; // Encoding of web page. Now forced to UTF-8 |
var $quote = "'"; |
var $pathSeparator = ":"; |
var $_repositories; |
// }}} |
// {{{ __construct() |
function Config() |
{ |
} |
// }}} |
// {{{ Repository configuration |
function addRepository($name, $url, $group = NULL, $username = NULL, $password = NULL) |
{ |
$url = str_replace(DIRECTORY_SEPARATOR, "/", $url); |
if ($url{strlen($url) - 1} == "/") |
$url = substr($url, 0, -1); |
$svnName = substr($url, strrpos($url, "/") + 1); |
$this->_repositories[] = new Repository($name, $svnName, $url, $group, $username, $password); |
} |
function getRepositories() |
{ |
return $this->_repositories; |
} |
function &findRepository($name) |
{ |
foreach ($this->_repositories as $index => $rep) |
{ |
if (strcmp($rep->getDisplayName(), $name) == 0) |
{ |
$repref =& $this->_repositories[$index]; |
return $repref; |
} |
} |
print "ERROR: Unable to find repository '$name'"; |
exit; |
} |
// }}} |
// {{{ setServerIsWindows |
// |
// The server is running on Windows |
function setServerIsWindows() |
{ |
$this->serverIsWindows = true; |
// Try to set the input encoding intelligently |
$cp = 0; |
if ($cp = @shell_exec("CHCP")) |
{ |
$cp = trim(substr($cp, strpos($cp, ":") + 1)); |
settype($cp, "integer"); |
} |
// Use the most sensible default value if that failed |
if ($cp == 0) $cp = 850; |
// We assume, as a default, that the encoding of the repository contents is |
// in iso-8859-1, to be compatible with compilers and the like. |
$this->setInputEncoding("CP$cp", "iso-8859-1"); |
// On Windows machines, use double quotes around command line parameters |
$this->quote = '"'; |
// On Windows, semicolon separates path entries in a list rather than colon. |
$this->pathSeparator = ";"; |
} |
// }}} |
// {{{ setPHPCompatEnabled |
// |
// Used for PHP4 |
function setPHPCompatEnabled() { |
$this->phpCompatEnabled = true; |
} |
function isPHPCompatEnabled() { |
return $this->phpCompatEnabled; |
} |
// }}} |
// {{{ Caching |
// setCachingOn |
// |
// Set result caching on |
function setCachingOn() |
{ |
$this->cacheResults = true; |
} |
function isCachingOn() |
{ |
return $this->cacheResults; |
} |
// }}} |
// {{{ MultiViews |
// useMultiViews |
// |
// Use MultiViews to access the repository |
function useMultiViews() |
{ |
$this->multiViews = true; |
} |
function getUseMultiViews() |
{ |
return $this->multiViews; |
} |
// }}} |
// {{{ Enscript |
// useEnscript |
// |
// Use Enscript to colourise listings |
function useEnscript() |
{ |
$this->useEnscript = true; |
} |
function getUseEnscript() |
{ |
return $this->useEnscript; |
} |
// }}} |
// {{{ RSS |
// offerRSS |
// |
// Use Enscript to colourise listings |
function hideRSS($myrep = 0) |
{ |
if (empty($myrep)) |
$this->rss = false; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->hideRSS(); |
} |
} |
function getHideRSS() |
{ |
return $this->rss; |
} |
// }}} |
// {{{ Downloads |
// allowDownload |
// |
// Allow download of tarballs |
function allowDownload($myrep = 0) |
{ |
if (empty($myrep)) |
$this->allowDownload = true; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->allowDownload(); |
} |
} |
function disallowDownload($myrep = 0) |
{ |
if (empty($myrep)) |
$this->allowDownload = false; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->disallowDownload(); |
} |
} |
function getAllowDownload() |
{ |
return $this->allowDownload; |
} |
function setMinDownloadLevel($level, $myrep = 0) |
{ |
if (empty($myrep)) |
$this->minDownloadLevel = $level; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->setMinDownloadLevel($level); |
} |
} |
function getMinDownloadLevel() |
{ |
return $this->minDownloadLevel; |
} |
function addAllowedDownloadException($path, $myrep = 0) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
if (empty($myrep)) |
$this->allowedExceptions[] = $path; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->addAllowedDownloadException($path); |
} |
} |
function addDisallowedDownloadException($path, $myrep = 0) |
{ |
if ($path{strlen($path) - 1} != "/") |
$path .= "/"; |
if (empty($myrep)) |
$this->disallowedExceptions[] = $path; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->addDisallowedDownloadException($path); |
} |
} |
function findException($path, $exceptions) |
{ |
foreach ($exceptions As $key => $exc) |
{ |
if (strncmp($exc, $path, strlen($exc)) == 0) |
return true; |
} |
return false; |
} |
// }}} |
// {{{ getURL |
// |
// Get the URL to a path name based on the current config |
function getURL($rep, $path, $op) |
{ |
$base = $_SERVER["SCRIPT_NAME"]; |
if ($this->multiViews) |
{ |
// Remove the .php |
if (eregi(".php$", $base)) |
{ |
// Remove the .php |
$base = substr($base, 0, -4); |
} |
if ($path && $path{0} != "/") $path = "/".$path; |
$url = $base; |
if (is_object($rep)) |
{ |
$url .= "/".$rep->getDisplayName().str_replace('%2F', '/', urlencode($path)); |
if ($op != "dir" && $op != "file") |
$url .= "?op=$op&"; |
else |
$url .= "?"; |
} |
return $url; |
} |
else |
{ |
switch ($op) |
{ |
case "dir": |
$fname = "listing.php"; |
break; |
case "file": |
$fname = "filedetails.php"; |
break; |
case "log": |
$fname = "log.php"; |
break; |
case "diff": |
$fname = "diff.php"; |
break; |
case "blame": |
$fname = "blame.php"; |
break; |
case "form": |
$fname = "form.php"; |
break; |
case "rss": |
$fname = "rss.php"; |
break; |
case "dl": |
$fname = "dl.php"; |
break; |
case "comp": |
$fname = "comp.php"; |
break; |
} |
if ($rep == -1) |
return $fname."?path=".urlencode($path)."&"; |
else |
return $fname."?repname=".urlencode($rep->getDisplayName())."&path=".urlencode($path)."&"; |
} |
} |
// }}} |
// {{{ Paths and Commands |
// setPath |
// |
// Set the location of the given path |
function setPath(&$var, $path, $name, $params = "") |
{ |
$lastchar = substr($path, -1, 1); |
$isDir = ($lastchar == DIRECTORY_SEPARATOR || |
$lastchar == "/" || |
$lastchar == "\\"); |
if (!$isDir) |
{ |
$path .= DIRECTORY_SEPARATOR; |
} |
// On a windows machine we need to put spaces around the entire command |
// to allow for spaces in the path |
if ($this->serverIsWindows) |
$var = "\"$path$name\""; |
else |
$var = "$path$name"; |
$var .= " ".$params; |
} |
// setSVNCommandPath |
// |
// Define the location of the svn and svnlook commands |
function setSVNCommandPath($path) |
{ |
$this->setPath($this->svn, $path, "svn", "--non-interactive --config-dir /tmp"); |
$this->setPath($this->svn_noparams, $path, "svn", " --config-dir /tmp"); |
$this->setPath($this->svnlook, $path, "svnlook"); |
} |
function getSvnCommand() |
{ |
return $this->svn; |
} |
function getCleanSvnCommand() |
{ |
return $this->svn_noparams; |
} |
function getSvnlookCommand() |
{ |
return $this->svnlook; |
} |
// setDiffPath |
// |
// Define the location of the diff command |
function setDiffPath($path) |
{ |
$this->setPath($this->diff, $path, "diff"); |
} |
function getDiffCommand() |
{ |
return $this->diff; |
} |
// setEnscriptPath |
// |
// Define the location of the enscript command |
function setEnscriptPath($path) |
{ |
$this->setPath($this->enscript, $path, "enscript"); |
} |
function getEnscriptCommand() |
{ |
return $this->enscript; |
} |
// setSedPath |
// |
// Define the location of the sed command |
function setSedPath($path) |
{ |
$this->setPath($this->sed, $path, "sed"); |
} |
function getSedCommand() |
{ |
return $this->sed; |
} |
// setTarPath |
// |
// Define the location of the tar command |
function setTarPath($path) |
{ |
$this->setPath($this->tar, $path, "tar"); |
} |
function getTarCommand() |
{ |
return $this->tar; |
} |
// setGzipPath |
// |
// Define the location of the GZip command |
function setGzipPath($path) |
{ |
$this->setPath($this->gzip, $path, "gzip"); |
} |
function getGzipCommand() |
{ |
return $this->gzip; |
} |
// Templates |
function setTemplatePath($path, $myrep = 0) |
{ |
if (empty($myrep)) |
{ |
$lastchar = substr($path, -1, 1); |
if (!($lastchar == DIRECTORY_SEPARATOR || |
$lastchar == '/' || |
$lastchar == '\\')) |
$path .= DIRECTORY_SEPARATOR; |
$this->templatePath = $path; |
} |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->setTemplatePath($path); |
} |
} |
function getTemplatePath() |
{ |
return $this->templatePath; |
} |
// PHP Compat file (from PEAR) |
function setPHPCompatPath($path) |
{ |
$this->setPath($this->phpCompatPath, $path, 'Compat.php'); |
} |
function getPHPCompatFile() |
{ |
return trim($this->phpCompatPath); |
} |
// }}} |
// {{{ parentPath |
// |
// Automatically set up the repositories based on a parent path |
function parentPath($path, $group = NULL) |
{ |
if ($handle = @opendir($path)) |
{ |
// For each file... |
while (false !== ($file = readdir($handle))) |
{ |
// That's also a non hidden directory |
if (is_dir($path.DIRECTORY_SEPARATOR.$file) && $file{0} != ".") |
{ |
// And that contains a db directory (in an attempt to not include |
// non svn repositories. |
if (is_dir($path.DIRECTORY_SEPARATOR.$file.DIRECTORY_SEPARATOR."db")) |
{ |
// We add the repository to the list |
$this->addRepository($file, "file:///".$path.DIRECTORY_SEPARATOR.$file, $group); |
} |
} |
} |
closedir($handle); |
} |
// Sort the repositories into alphabetical order |
if (!empty($this->_repositories)) |
usort($this->_repositories, "cmpReps"); |
} |
// }}} |
// {{{ Encoding functions |
function setInputEncoding($systemEnc) |
{ |
$this->inputEnc = $systemEnc; |
if (!isset($this->contentEnc)) |
$this->contentEnc = $systemEnc; |
} |
function getInputEncoding() |
{ |
return $this->inputEnc; |
} |
function setContentEncoding($contentEnc, $myrep = 0) |
{ |
if (empty($myrep)) |
$this->contentEnc = $contentEnc; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->setContentEncoding($contentEnc); |
} |
} |
function getContentEncoding() |
{ |
return $this->contentEnc; |
} |
// }}} |
// {{{ Tab expansion functions |
function expandTabsBy($sp, $myrep = 0) |
{ |
if (empty($myrep)) |
$this->spaces = $sp; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->expandTabsBy($sp); |
} |
} |
function getExpandTabsBy() |
{ |
return $this->spaces; |
} |
// }}} |
// {{{ Misc settings |
function ignoreSvnMimeTypes() |
{ |
$this->ignoreSvnMimeTypes = true; |
} |
function getIgnoreSvnMimeTypes() |
{ |
return $this->ignoreSvnMimeTypes; |
} |
function ignoreWebSVNContentTypes() |
{ |
$this->ignoreWebSVNContentTypes = true; |
} |
function getIgnoreWebSVNContentTypes() |
{ |
return $this->ignoreWebSVNContentTypes; |
} |
function useBugtraqProperties($myrep = 0) |
{ |
if (empty($myrep)) |
$this->bugtraq = true; |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->useBugtraqProperties(); |
} |
} |
function getBugtraq() |
{ |
return $this->bugtraq; |
} |
function useAuthenticationFile($file, $myrep = 0) |
{ |
if (empty($myrep)) |
{ |
if (is_readable($file)) |
$this->auth = new Authentication($file); |
else |
{ |
echo "Unable to read authentication file '$file'"; |
exit; |
} |
} |
else |
{ |
$repo =& $this->findRepository($myrep); |
$repo->useAuthenticationFile($file); |
} |
} |
function &getAuth() |
{ |
return $this->auth; |
} |
function useTreeView() |
{ |
$this->treeView = true; |
} |
function getUseTreeView() |
{ |
return $this->treeView; |
} |
function useFlatView() |
{ |
$this->treeView = false; |
} |
function useTreeIndex($open) |
{ |
$this->flatIndex = false; |
$this->openTree = $open; |
} |
function getUseFlatIndex() |
{ |
return $this->flatIndex; |
} |
function getOpenTree() |
{ |
return $this->openTree; |
} |
// setSubversionMajorVersion |
// |
// Set subversion major version |
function setSubversionMajorVersion($subversionMajorVersion) |
{ |
$this->subversionMajorVersion = $subversionMajorVersion; |
} |
function getSubversionMajorVersion() |
{ |
return $this->subversionMajorVersion; |
} |
// setSubversionMinorVersion |
// |
// Set subversion minor version |
function setSubversionMinorVersion($subversionMinorVersion) |
{ |
$this->subversionMinorVersion = $subversionMinorVersion; |
} |
function getSubversionMinorVersion() |
{ |
return $this->subversionMinorVersion; |
} |
// }}} |
// {{{ Sort the repostories |
// |
// This function sorts the repositories by group name. The contents of the |
// group are left in there original order, which will either be sorted if the |
// group was added using the parentPath function, or defined for the order in |
// which the repositories were included in the user's config file. |
// |
// Note that as of PHP 4.0.6 the usort command no longer preserves the order |
// of items that are considered equal (in our case, part of the same group). |
// The mergesort function preserves this order. |
function sortByGroup() |
{ |
if (!empty($this->_repositories)) |
mergesort($this->_repositories, "cmpGroups"); |
} |
// }}} |
} |
?> |
/WebSVN/include/distconfig.inc |
---|
1,375 → 1,376 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// config.inc |
// |
// Configuration parameters |
// --- FOLLOW THE INSTRUCTIONS BELOW TO CONFIGURE YOUR SETUP --- |
// {{{ PLATFORM CONFIGURATION --- |
// Uncomment the next line if your running a windows server |
// |
// $config->setServerIsWindows(); |
// Configure these lines if your commands aren't on your path. |
// |
// $config->setSVNCommandPath('Path/to/svn and svnlook/ e.g. c:\\program files\\subversion\\bin'); |
// $config->setDiffPath('Path/to/diff/command/'); |
// For syntax colouring, if option enabled... |
// $config->setEnscriptPath('Path/to/enscript/command/'); |
// $config->setSedPath('Path/to/sed/command/'); |
// For delivered tarballs, if option enabled... |
// $config->setTarPath('Path/to/tar/command/'); |
// For delivered GZIP'd files and tarballs, if option enabled... |
// $config->setGZipPath('Path/to/gzip/command/'); |
// }}} |
// {{{ REPOSITORY SETUP --- |
// There are 2 methods for defining the repositiories available on the system. Either you list |
// them by hand, in which case you can give each one the name of your choice, or you use the |
// parent path function, in which case the name of the directory is used as the repository name. |
// |
// In all cases, you may optionally supply a group name to the repositories. This is useful in the |
// case that you need to separate your projects. Grouped Repositories are referred to using the |
// convention GroupName.RepositoryName |
// |
// Performance is much better on local repositories (e.g. accessed by file:///). However, you |
// can also provide an interface onto a remote repository. In this case you should supply the |
// username and password needed to access it. |
// |
// To configure the repositories by hand, copy the appropriate line below, uncomment it and |
// replace the name and URL of your repository. |
// Local repositories (without and with optional group): |
// |
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)'); |
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)', 'group'); |
// |
// Remote repositories (without and with optional group): |
// |
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', NULL, 'username', 'password'); |
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', 'group', 'username', 'password'); |
// |
// To use the parent path method, uncomment the newt line and and replace the path with your one. You |
// can call the function several times if you have several parent paths. Note that in this case the |
// path is a filesystem path |
// |
// $config->parentPath('Path/to/parent (e.g. c:\\svn)'); |
// }}} |
// {{{ LOOK AND FEEL --- |
// |
// Uncomment ONLY the template file that you want. |
$config->setTemplatePath("$locwebsvnreal/templates/Standard/"); |
// $config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/"); |
// $config->setTemplatePath("$locwebsvnreal/templates/Zinn/"); |
// You may also specify a per repository template file by uncommenting and changing the following |
// line as necessary. Use the convention "groupname.myrep" if your repository is in a group. |
// $config->setTemplatePath('$locwebsvnreal/templates/Standard/', 'myrep'); // Access file for myrep |
// The index page containing the projects may either be displayed as a flat view (the default), |
// where grouped repositories are displayed as "GroupName.RepName" or as a tree view. |
// In the case of a tree view, you may choose whether the entire tree is open by default. |
// $config->useTreeIndex(false); // Tree index, closed by default |
// $config->useTreeIndex(true); // Tree index, open by default |
// By default, WebSVN displays a tree view onto the current directory. You can however |
// choose to display a flat view of the current directory only, which may make the display |
// load faster. Uncomment this line if you want that. |
// $config->useFlatView(); |
// }}} |
// {{{ LANGUAGE SETUP --- |
// WebSVN uses the iconv module to convert messages from your system's character set to the |
// UTF-8 output encoding. If you find that your log messages aren't displayed correctly then |
// you'll need to change the value here. |
// |
// You may also specify the character encoding of the repository contents if different from |
// the system encoding. This is typically the case for windows users, whereby the command |
// line returns, for example, CP850 encoded strings, whereas the source files are encoded |
// as iso-8859-1 by Windows based text editors. When display text file, WebSVN will convert |
// them from the content encoding to the output encoding (UTF-8). |
// |
// WebSVN does its best to automate all this, so only use the following if it doesn't work |
// "out of the box". Uncomment and change one of the examples below. |
// |
// $config->setInputEncoding('CP850'); // Encoding of result returned by svn command line, etc. |
// $config->setContentEncoding('iso-8859-1'); // Content encoding of all your repositories // repositories |
// You may also specify a content encoding on a per repository basis. Uncomment and copy this |
// line as necessary. |
// |
// $config->setContentEncoding('iso-8859-1', 'MyEnc'); |
// Note for Windows users: To enable iconv you'll need to enable the extension in your php.ini file |
// AND copy iconv.dll (not php_iconv.dll) to your Windows system folder. In most cases the correct |
// encoding is set when you call $config->setServerIsWindows();. |
// Note for *nix users. You'll need to have iconv compiled into your binary. The default input and |
// output encodings are taken from your locale informations. Override these if they aren't correct. |
// Uncomment the default language. If you want English then don't do anything here. |
// |
// include 'languages/catalan.inc'; |
// include 'languages/danish.inc'; |
// include 'languages/dutch.inc'; |
// include 'languages/finnish.inc'; |
// include 'languages/french.inc'; |
// include 'languages/german.inc'; |
// include 'languages/japanese.inc'; |
// include 'languages/korean.inc'; |
// include 'languages/norwegian.inc'; |
// include 'languages/polish.inc'; |
// include 'languages/portuguese.inc'; |
// include 'languages/russian.inc'; |
// include 'languages/schinese.inc'; |
// include 'languages/slovenian.inc'; |
// include 'languages/spanish.inc'; |
// include 'languages/swedish.inc'; |
// include 'languages/tchinese.inc'; |
// include 'languages/turkish.inc'; |
// }}} |
// {{{ MULTIVIEWS --- |
// Uncomment this line if you want to use MultiView to access the repository by, for example: |
// |
// http://servername/wsvn/repname/path/in/repository |
// |
// Note: The websvn directory will need to have Multiviews turned on in Apache, and you'll need to configure |
// wsvn.php |
// $config->useMultiViews(); |
// }}} |
// {{{ ACCESS RIGHTS --- |
// Uncomment this line if you want to use your Subversion access file to control access |
// rights via WebSVN. For this to work, you'll need to set up the same Apache based authentication |
// to the WebSVN (or wsvn) directory as you have for Subversion itself. More information can be |
// found in install.txt |
// $config->useAuthenticationFile('/path/to/accessfile'); // Global access file |
// You may also specify a per repository access file by uncommenting and copying the following |
// line as necessary. Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->useAuthenticationFile('/path/to/accessfile', 'myrep'); // Access file for myrep |
// }}} |
// {{{ FILE CONTENT --- |
// |
// You may wish certain file types to be GZIP'd and delieved to the user when clicked apon. |
// This is useful for binary files and the like that don't display well in a browser window! |
// Copy, uncomment and modify this line for each extension to which this rule should apply. |
// (Don't forget the . before the extension. You don't need an index between the []'s). |
// If you'd rather that the files were delivered uncompressed with the associated MIME type, |
// then read below. |
// |
// $zipped[] = '.dll'; |
// Subversion controlled files have an svn:mime-type property that can |
// be set on a file indicating its mime type. By default binary files |
// are set to the generic appcliation/octet-stream, and other files |
// don't have it set at all. WebSVN also has a built-in list of |
// associations from file extension to MIME content type. (You can |
// view this list in setup.inc). |
// |
// Determining the content-type: By default, if the svn:mime-type |
// property exists and is different from application/octet-stream, it |
// is used. Otherwise, if the built-in list has a contentType entry |
// for the extension of the file, that is used. Otherwise, if the |
// svn:mime-type property exists has the generic binary value of |
// application/octet-stream, the file will be served as a binary |
// file. Otherwise, the file will be brought up as ASCII text in the |
// browser window (although this text may optionally be colourised. |
// See below). |
// |
// Uncomment this if you want to ignore any svn:mime-type property on your |
// files. |
// |
// $config->ignoreSvnMimeTypes(); |
// |
// Uncomment this if you want skip WebSVN's custom mime-type handling |
// |
// $config->ignoreWebSVNContentTypes(); |
// |
// Following the examples below, you can add new associations, modify |
// the default ones or even delete them entirely (to show them in |
// ASCII via WebSVN). |
// $contentType['.c'] = 'plain/text'; // Create a new association |
// $contentType['.doc'] = 'plain/text'; // Modify an existing one |
// unset($contentType['.m'] // Remove a default association |
// }}} |
// {{{ TARBALLS --- |
// You need tar and gzip installed on your system. Set the paths above if necessary |
// |
// Uncomment the line below to offer a tarball download option across all your |
// repositories. |
// |
// $config->allowDownload(); |
// |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->allowDownload('myrep'); // Specifically allow downloading for 'myrep' |
// $config->disallowDownload('myrep'); // Specifically disallow downloading for 'myrep' |
// You can also choose the minimum directory level from which you'll allow downloading. |
// A value of zero will allow downloading from the root. 1 will allow downloding of directories |
// in the root, etc. |
// |
// If your project is arranged with trunk, tags and branches at the root level, then a value of 2 |
// would allow the downloading of directories within branches/tags while disallowing the download |
// of the entire branches or tags directories. This would also stop downloading of the trunk, but |
// see after for path exceptions. |
// |
// Change the line below to set the download level across all your repositories. |
$config->setMinDownloadLevel(2); |
// To change the level for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->setMinDownloadLevel(2, 'myrep'); |
// Finally, you may add or remove certain directories (and their contents) either globally |
// or on a per repository basis. Uncomment and copy the following lines as necessary. Note |
// that the these are searched in the order than you give them until a match is made (with the |
// exception that all the per repository exceptions are tested before the global ones). This means |
// that you must disallow /a/b/c/ before you allow /a/b/ otherwise the allowed match on /a/b/ will |
// stop any further searching, thereby allowing downloads on /a/b/c/. |
// Global exceptions possibilties: |
// |
// $config->addAllowedDownloadException('/path/to/allowed/directory/'); |
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/'); |
// |
// Per repository exception possibilties: |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// |
// $config->addAllowedDownloadException('/path/to/allowed/directory/', 'myrep'); |
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/', 'myrep'); |
// }}} |
// {{{ COLOURISATION --- |
// Uncomment this line if you want to use Enscript to colourise your file listings |
// |
// You'll need Enscript version 1.6 or higher AND Sed installed to use this feature. |
// Set the path above. |
// |
// $config->useEnscript(); |
// Enscript need to be told what the contents of a file are so that it can be colourised |
// correctly. WebSVN includes a predefined list of mappings from file extension to Enscript |
// file type (viewable in setup.inc). |
// |
// Here you should add and other extensions not already listed or redefine the default ones. eg: |
// |
// $extEnscript['.pas'] = 'pascal'; |
// |
// Note that extensions are case sensitive. |
// }}} |
// {{{ RSSFEED --- |
// Uncomment this line if you wish to hide the RSS feed links across all repositories |
// |
// $config->hideRSS(); |
// |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->hideRSS('myrep'); // Specifically hide RSS links for 'myrep' |
// $config->showRSS('myrep'); // Specifically show RSS links for 'myrep' |
// }}} |
// {{{ BUGTRAQ --- |
// Uncomment this line if you wish to use bugtraq: properties to show links to your BugTracker |
// from the log messages. |
// |
// $config->useBugtraqProperties(); |
// |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->useBugtraqProperties('myrep'); // Specifically use bugtraq properties for 'myrep' |
// $config->ignoreBugtraqProperties('myrep'); // Specifically ignore bugtraq properties for 'myrep' |
// }}} |
// {{{ MISCELLANEOUS --- |
// Comment out this if you don't have the right to use it. Be warned that you may need it however! |
set_time_limit(0); |
// Comment this line to turn off caching of repo information. This will slow down your browsing. |
$config->setCachingOn(); |
// Number of spaces to expand tabs to in diff/listing view across all repositories |
$config->expandTabsBy(8); |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->expandTabsBy(3, 'myrep'); // Expand Tabs by 3 for repository 'myrep' |
// For installations without PHP5, a copy of PEAR's PHP Compat library is included. |
// If you have your own version of Compat you wish to use, go ahead and specify here. |
// $config->setPHPCompatPath('/usr/share/php/PHP/'); |
// }}} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// config.inc |
// |
// Configuration parameters |
// --- FOLLOW THE INSTRUCTIONS BELOW TO CONFIGURE YOUR SETUP --- |
// {{{ PLATFORM CONFIGURATION --- |
// Uncomment the next line if your running a windows server |
// |
// $config->setServerIsWindows(); |
// Configure these lines if your commands aren't on your path. |
// |
// $config->setSVNCommandPath('Path/to/svn and svnlook/ e.g. c:\\program files\\subversion\\bin'); |
// $config->setDiffPath('Path/to/diff/command/'); |
// For syntax colouring, if option enabled... |
// $config->setEnscriptPath('Path/to/enscript/command/'); |
// $config->setSedPath('Path/to/sed/command/'); |
// For delivered tarballs, if option enabled... |
// $config->setTarPath('Path/to/tar/command/'); |
// For delivered GZIP'd files and tarballs, if option enabled... |
// $config->setGZipPath('Path/to/gzip/command/'); |
// }}} |
// {{{ REPOSITORY SETUP --- |
// There are 2 methods for defining the repositiories available on the system. Either you list |
// them by hand, in which case you can give each one the name of your choice, or you use the |
// parent path function, in which case the name of the directory is used as the repository name. |
// |
// In all cases, you may optionally supply a group name to the repositories. This is useful in the |
// case that you need to separate your projects. Grouped Repositories are referred to using the |
// convention GroupName.RepositoryName |
// |
// Performance is much better on local repositories (e.g. accessed by file:///). However, you |
// can also provide an interface onto a remote repository. In this case you should supply the |
// username and password needed to access it. |
// |
// To configure the repositories by hand, copy the appropriate line below, uncomment it and |
// replace the name and URL of your repository. |
// Local repositories (without and with optional group): |
// |
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)'); |
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)', 'group'); |
// |
// Remote repositories (without and with optional group): |
// |
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', NULL, 'username', 'password'); |
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', 'group', 'username', 'password'); |
// |
// To use the parent path method, uncomment the newt line and and replace the path with your one. You |
// can call the function several times if you have several parent paths. Note that in this case the |
// path is a filesystem path |
// |
// $config->parentPath('Path/to/parent (e.g. c:\\svn)'); |
// }}} |
// {{{ LOOK AND FEEL --- |
// |
// Uncomment ONLY the template file that you want. |
$config->setTemplatePath("$locwebsvnreal/templates/Standard/"); |
// $config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/"); |
// $config->setTemplatePath("$locwebsvnreal/templates/Zinn/"); |
// You may also specify a per repository template file by uncommenting and changing the following |
// line as necessary. Use the convention "groupname.myrep" if your repository is in a group. |
// $config->setTemplatePath('$locwebsvnreal/templates/Standard/', 'myrep'); // Access file for myrep |
// The index page containing the projects may either be displayed as a flat view (the default), |
// where grouped repositories are displayed as "GroupName.RepName" or as a tree view. |
// In the case of a tree view, you may choose whether the entire tree is open by default. |
// $config->useTreeIndex(false); // Tree index, closed by default |
// $config->useTreeIndex(true); // Tree index, open by default |
// By default, WebSVN displays a tree view onto the current directory. You can however |
// choose to display a flat view of the current directory only, which may make the display |
// load faster. Uncomment this line if you want that. |
// $config->useFlatView(); |
// }}} |
// {{{ LANGUAGE SETUP --- |
// WebSVN uses the iconv module to convert messages from your system's character set to the |
// UTF-8 output encoding. If you find that your log messages aren't displayed correctly then |
// you'll need to change the value here. |
// |
// You may also specify the character encoding of the repository contents if different from |
// the system encoding. This is typically the case for windows users, whereby the command |
// line returns, for example, CP850 encoded strings, whereas the source files are encoded |
// as iso-8859-1 by Windows based text editors. When display text file, WebSVN will convert |
// them from the content encoding to the output encoding (UTF-8). |
// |
// WebSVN does its best to automate all this, so only use the following if it doesn't work |
// "out of the box". Uncomment and change one of the examples below. |
// |
// $config->setInputEncoding('CP850'); // Encoding of result returned by svn command line, etc. |
// $config->setContentEncoding('iso-8859-1'); // Content encoding of all your repositories // repositories |
// You may also specify a content encoding on a per repository basis. Uncomment and copy this |
// line as necessary. |
// |
// $config->setContentEncoding('iso-8859-1', 'MyEnc'); |
// Note for Windows users: To enable iconv you'll need to enable the extension in your php.ini file |
// AND copy iconv.dll (not php_iconv.dll) to your Windows system folder. In most cases the correct |
// encoding is set when you call $config->setServerIsWindows();. |
// Note for *nix users. You'll need to have iconv compiled into your binary. The default input and |
// output encodings are taken from your locale informations. Override these if they aren't correct. |
// Uncomment the default language. If you want English then don't do anything here. |
// |
// include 'languages/catalan.inc'; |
// include 'languages/czech.inc'; |
// include 'languages/danish.inc'; |
// include 'languages/dutch.inc'; |
// include 'languages/finnish.inc'; |
// include 'languages/french.inc'; |
// include 'languages/german.inc'; |
// include 'languages/japanese.inc'; |
// include 'languages/korean.inc'; |
// include 'languages/norwegian.inc'; |
// include 'languages/polish.inc'; |
// include 'languages/portuguese.inc'; |
// include 'languages/russian.inc'; |
// include 'languages/schinese.inc'; |
// include 'languages/slovenian.inc'; |
// include 'languages/spanish.inc'; |
// include 'languages/swedish.inc'; |
// include 'languages/tchinese.inc'; |
// include 'languages/turkish.inc'; |
// }}} |
// {{{ MULTIVIEWS --- |
// Uncomment this line if you want to use MultiView to access the repository by, for example: |
// |
// http://servername/wsvn/repname/path/in/repository |
// |
// Note: The websvn directory will need to have Multiviews turned on in Apache, and you'll need to configure |
// wsvn.php |
// $config->useMultiViews(); |
// }}} |
// {{{ ACCESS RIGHTS --- |
// Uncomment this line if you want to use your Subversion access file to control access |
// rights via WebSVN. For this to work, you'll need to set up the same Apache based authentication |
// to the WebSVN (or wsvn) directory as you have for Subversion itself. More information can be |
// found in install.txt |
// $config->useAuthenticationFile('/path/to/accessfile'); // Global access file |
// You may also specify a per repository access file by uncommenting and copying the following |
// line as necessary. Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->useAuthenticationFile('/path/to/accessfile', 'myrep'); // Access file for myrep |
// }}} |
// {{{ FILE CONTENT --- |
// |
// You may wish certain file types to be GZIP'd and delieved to the user when clicked apon. |
// This is useful for binary files and the like that don't display well in a browser window! |
// Copy, uncomment and modify this line for each extension to which this rule should apply. |
// (Don't forget the . before the extension. You don't need an index between the []'s). |
// If you'd rather that the files were delivered uncompressed with the associated MIME type, |
// then read below. |
// |
// $zipped[] = '.dll'; |
// Subversion controlled files have an svn:mime-type property that can |
// be set on a file indicating its mime type. By default binary files |
// are set to the generic appcliation/octet-stream, and other files |
// don't have it set at all. WebSVN also has a built-in list of |
// associations from file extension to MIME content type. (You can |
// view this list in setup.inc). |
// |
// Determining the content-type: By default, if the svn:mime-type |
// property exists and is different from application/octet-stream, it |
// is used. Otherwise, if the built-in list has a contentType entry |
// for the extension of the file, that is used. Otherwise, if the |
// svn:mime-type property exists has the generic binary value of |
// application/octet-stream, the file will be served as a binary |
// file. Otherwise, the file will be brought up as ASCII text in the |
// browser window (although this text may optionally be colourised. |
// See below). |
// |
// Uncomment this if you want to ignore any svn:mime-type property on your |
// files. |
// |
// $config->ignoreSvnMimeTypes(); |
// |
// Uncomment this if you want skip WebSVN's custom mime-type handling |
// |
// $config->ignoreWebSVNContentTypes(); |
// |
// Following the examples below, you can add new associations, modify |
// the default ones or even delete them entirely (to show them in |
// ASCII via WebSVN). |
// $contentType['.c'] = 'plain/text'; // Create a new association |
// $contentType['.doc'] = 'plain/text'; // Modify an existing one |
// unset($contentType['.m'] // Remove a default association |
// }}} |
// {{{ TARBALLS --- |
// You need tar and gzip installed on your system. Set the paths above if necessary |
// |
// Uncomment the line below to offer a tarball download option across all your |
// repositories. |
// |
// $config->allowDownload(); |
// |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->allowDownload('myrep'); // Specifically allow downloading for 'myrep' |
// $config->disallowDownload('myrep'); // Specifically disallow downloading for 'myrep' |
// You can also choose the minimum directory level from which you'll allow downloading. |
// A value of zero will allow downloading from the root. 1 will allow downloding of directories |
// in the root, etc. |
// |
// If your project is arranged with trunk, tags and branches at the root level, then a value of 2 |
// would allow the downloading of directories within branches/tags while disallowing the download |
// of the entire branches or tags directories. This would also stop downloading of the trunk, but |
// see after for path exceptions. |
// |
// Change the line below to set the download level across all your repositories. |
$config->setMinDownloadLevel(2); |
// To change the level for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->setMinDownloadLevel(2, 'myrep'); |
// Finally, you may add or remove certain directories (and their contents) either globally |
// or on a per repository basis. Uncomment and copy the following lines as necessary. Note |
// that the these are searched in the order than you give them until a match is made (with the |
// exception that all the per repository exceptions are tested before the global ones). This means |
// that you must disallow /a/b/c/ before you allow /a/b/ otherwise the allowed match on /a/b/ will |
// stop any further searching, thereby allowing downloads on /a/b/c/. |
// Global exceptions possibilties: |
// |
// $config->addAllowedDownloadException('/path/to/allowed/directory/'); |
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/'); |
// |
// Per repository exception possibilties: |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// |
// $config->addAllowedDownloadException('/path/to/allowed/directory/', 'myrep'); |
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/', 'myrep'); |
// }}} |
// {{{ COLOURISATION --- |
// Uncomment this line if you want to use Enscript to colourise your file listings |
// |
// You'll need Enscript version 1.6 or higher AND Sed installed to use this feature. |
// Set the path above. |
// |
// $config->useEnscript(); |
// Enscript need to be told what the contents of a file are so that it can be colourised |
// correctly. WebSVN includes a predefined list of mappings from file extension to Enscript |
// file type (viewable in setup.inc). |
// |
// Here you should add and other extensions not already listed or redefine the default ones. eg: |
// |
// $extEnscript['.pas'] = 'pascal'; |
// |
// Note that extensions are case sensitive. |
// }}} |
// {{{ RSSFEED --- |
// Uncomment this line if you wish to hide the RSS feed links across all repositories |
// |
// $config->hideRSS(); |
// |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->hideRSS('myrep'); // Specifically hide RSS links for 'myrep' |
// $config->showRSS('myrep'); // Specifically show RSS links for 'myrep' |
// }}} |
// {{{ BUGTRAQ --- |
// Uncomment this line if you wish to use bugtraq: properties to show links to your BugTracker |
// from the log messages. |
// |
// $config->useBugtraqProperties(); |
// |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->useBugtraqProperties('myrep'); // Specifically use bugtraq properties for 'myrep' |
// $config->ignoreBugtraqProperties('myrep'); // Specifically ignore bugtraq properties for 'myrep' |
// }}} |
// {{{ MISCELLANEOUS --- |
// Comment out this if you don't have the right to use it. Be warned that you may need it however! |
set_time_limit(0); |
// Comment this line to turn off caching of repo information. This will slow down your browsing. |
$config->setCachingOn(); |
// Number of spaces to expand tabs to in diff/listing view across all repositories |
$config->expandTabsBy(8); |
// To change the global option for individual repositories, uncomment and replicate |
// the required line below (replacing 'myrep' for the name of the repository to be changed). |
// Use the convention 'groupname.myrep' if your repository is in a group. |
// $config->expandTabsBy(3, 'myrep'); // Expand Tabs by 3 for repository 'myrep' |
// For installations without PHP5, a copy of PEAR's PHP Compat library is included. |
// If you have your own version of Compat you wish to use, go ahead and specify here. |
// $config->setPHPCompatPath('/usr/share/php/PHP/'); |
// }}} |
?> |
/WebSVN/include/feedcreator.class.php |
---|
1,1153 → 1,1153 |
<?php |
# vim:et:ts=4:sts=4:sw=4:fdm=marker: |
# {{{ Info |
/*************************************************************************** |
FeedCreator class v1.6 |
originally (c) Kai Blankenhorn |
www.bitfolge.de |
kaib@bitfolge.de |
v1.3 work by Scott Reynen (scott@randomchaos.com) and Kai Blankenhorn |
v1.5 OPML support by Dirk Clemens |
This program is free software; you can redistribute it and/or |
modify it under the terms of the GNU General Public License |
as published by the Free Software Foundation; either version 2 |
of the License, or (at your option) any later version. |
This program is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details: <http://www.gnu.org/licenses/gpl.txt> |
**************************************************************************** |
Changelog: |
Modifications for WebSVN: |
The main description link wasn't put through htmlspecialcharacters |
Output encoding now defined by $config |
Remove hardcoded time zone |
v1.6 05-10-04 |
added stylesheet to RSS 1.0 feeds |
fixed generator comment (thanks Kevin L. Papendick and Tanguy Pruvot) |
fixed RFC822 date bug (thanks Tanguy Pruvot) |
added TimeZone customization for RFC8601 (thanks Tanguy Pruvot) |
fixed Content-type could be empty (thanks Tanguy Pruvot) |
fixed author/creator in RSS1.0 (thanks Tanguy Pruvot) |
v1.6 beta 02-28-04 |
added Atom 0.3 support (not all features, though) |
improved OPML 1.0 support (hopefully - added more elements) |
added support for arbitrary additional elements (use with caution) |
code beautification :-) |
considered beta due to some internal changes |
v1.5.1 01-27-04 |
fixed some RSS 1.0 glitches (thanks to Stéphane Vanpoperynghe) |
fixed some inconsistencies between documentation and code (thanks to Timothy Martin) |
v1.5 01-06-04 |
added support for OPML 1.0 |
added more documentation |
v1.4 11-11-03 |
optional feed saving and caching |
improved documentation |
minor improvements |
v1.3 10-02-03 |
renamed to FeedCreator, as it not only creates RSS anymore |
added support for mbox |
tentative support for echo/necho/atom/pie/??? |
v1.2 07-20-03 |
intelligent auto-truncating of RSS 0.91 attributes |
don't create some attributes when they're not set |
documentation improved |
fixed a real and a possible bug with date conversions |
code cleanup |
v1.1 06-29-03 |
added images to feeds |
now includes most RSS 0.91 attributes |
added RSS 2.0 feeds |
v1.0 06-24-03 |
initial release |
***************************************************************************/ |
/*** GENERAL USAGE ********************************************************* |
include("feedcreator.class.php"); |
$rss = new UniversalFeedCreator(); |
$rss->useCached(); // use cached version if age<1 hour |
$rss->title = "PHP news"; |
$rss->description = "daily news from the PHP scripting world"; |
$rss->link = "http://www.dailyphp.net/news"; |
$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"]; |
$image = new FeedImage(); |
$image->title = "dailyphp.net logo"; |
$image->url = "http://www.dailyphp.net/images/logo.gif"; |
$image->link = "http://www.dailyphp.net"; |
$image->description = "Feed provided by dailyphp.net. Click to visit."; |
$rss->image = $image; |
// get your news items from somewhere, e.g. your database: |
mysql_select_db($dbHost, $dbUser, $dbPass); |
$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC"); |
while ($data = mysql_fetch_object($res)) { |
$item = new FeedItem(); |
$item->title = $data->title; |
$item->link = $data->url; |
$item->description = $data->short; |
$item->date = $data->newsdate; |
$item->source = "http://www.dailyphp.net"; |
$item->author = "John Doe"; |
$rss->addItem($item); |
} |
// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated), |
// MBOX, OPML, ATOM0.3 |
echo $rss->saveFeed("RSS1.0", "news/feed.xml"); |
# }}} |
*************************************************************************** |
* A little setup * |
**************************************************************************/ |
/** |
* Version string. |
**/ |
define("FEEDCREATOR_VERSION", "FeedCreator 1.6"); |
/** |
* A FeedItem is a part of a FeedCreator feed. |
* |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
* @since 1.3 |
*/ |
class FeedItem { |
# {{{ Properties |
/** |
* Mandatory attributes of an item. |
*/ |
var $title, $description, $link; |
/** |
* Optional attributes of an item. |
*/ |
var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator; |
/** |
* Publishing date of an item. May be in one of the following formats: |
* |
* RFC 822: |
* "Mon, 20 Jan 03 18:05:41 +0400" |
* "20 Jan 03 18:05:41 +0000" |
* |
* ISO 8601: |
* "2003-01-20T18:05:41+04:00" |
* |
* Unix: |
* 1043082341 |
*/ |
var $date; |
/** |
* Any additional elements to include as an assiciated array. All $key => $value pairs |
* will be included unencoded in the feed item in the form |
* <$key>$value</$key> |
* Again: No encoding will be used! This means you can invalidate or enhance the feed |
* if $value contains markup. This may be abused to embed tags not implemented by |
* the FeedCreator class used. |
*/ |
var $additionalElements = Array(); |
// on hold |
// var $source; |
# }}} |
} |
/** |
* An FeedImage may be added to a FeedCreator feed. |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
* @since 1.3 |
*/ |
class FeedImage { |
# {{{ Properties |
/** |
* Mandatory attributes of an image. |
*/ |
var $title, $url, $link; |
/** |
* Optional attributes of an image. |
*/ |
var $width, $height, $description; |
# }}} |
} |
/** |
* UniversalFeedCreator lets you choose during runtime which |
* format to build. |
* For general usage of a feed class, see the FeedCreator class |
* below or the example above. |
* |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class UniversalFeedCreator extends FeedCreator { |
var $_feed; |
# {{{ _setFormat |
function _setFormat($format) { |
switch (strtoupper($format)) { |
case "2.0": |
// fall through |
case "RSS2.0": |
$this->_feed = new RSSCreator20(); |
break; |
case "1.0": |
// fall through |
case "RSS1.0": |
$this->_feed = new RSSCreator10(); |
break; |
case "0.91": |
// fall through |
case "RSS0.91": |
$this->_feed = new RSSCreator091(); |
break; |
case "PIE0.1": |
$this->_feed = new PIECreator01(); |
break; |
case "MBOX": |
$this->_feed = new MBOXCreator(); |
break; |
case "OPML": |
$this->_feed = new OPMLCreator(); |
break; |
case "ATOM0.3": |
$this->_feed = new AtomCreator03(); |
break; |
default: |
$this->_feed = new RSSCreator091(); |
break; |
} |
$vars = get_object_vars($this); |
foreach ($vars as $key => $value) { |
if ($key!="feed") { |
$this->_feed->{$key} = $this->{$key}; |
} |
} |
} |
# }}} |
# {{{ createFeed |
/** |
* Creates a syndication feed based on the items previously added. |
* |
* @see FeedCreator::addItem() |
* @param string format format the feed should comply to. Valid values are: |
* "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML". |
* @return string the contents of the feed. |
*/ |
function createFeed($format = "RSS0.91") { |
$this->_setFormat($format); |
return $this->_feed->createFeed(); |
} |
# }}} |
# {{{ saveFeed |
/** |
* Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect |
* header may be sent to redirect the use to the newly created file. |
* @since 1.4 |
* |
* @param string format format the feed should comply to. Valid values are: |
* "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3". |
* @param string filename optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). |
* @param boolean displayContents optional send the content of the file or not. If true, the file will be sent in the body of the response. |
*/ |
function saveFeed($format="RSS0.91", $filename="", $displayContents=true) { |
$this->_setFormat($format); |
$this->_feed->saveFeed($filename, $displayContents); |
} |
# }}} |
} |
/** |
* FeedCreator is the abstract base implementation for concrete |
* implementations that implement a specific format of syndication. |
* |
* @abstract |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
* @since 1.4 |
*/ |
class FeedCreator { |
# {{{ Properties |
/** |
* Mandatory attributes of a feed. |
*/ |
var $title, $description, $link; |
/** |
* Optional attributes of a feed. |
*/ |
var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays; |
/** |
* @access private |
*/ |
var $items = Array(); |
/** |
* This feed's MIME content type. |
* @since 1.4 |
* @access private |
*/ |
var $contentType = "text/xml"; |
/** |
* Any additional elements to include as an assiciated array. All $key => $value pairs |
* will be included unencoded in the feed in the form |
* <$key>$value</$key> |
* Again: No encoding will be used! This means you can invalidate or enhance the feed |
* if $value contains markup. This may be abused to embed tags not implemented by |
* the FeedCreator class used. |
*/ |
var $additionalElements = Array(); |
# }}} |
# {{{ addItem |
/** |
* Adds an FeedItem to the feed. |
* |
* @param object FeedItem $item The FeedItem to add to the feed. |
* @access public |
*/ |
function addItem($item) { |
$this->items[] = $item; |
} |
# }}} |
# {{{ iTrunc |
/** |
* Truncates a string to a certain length at the most sensible point. |
* First, if there's a '.' character near the end of the string, the string is truncated after this character. |
* If there is no '.', the string is truncated after the last ' ' character. |
* If the string is truncated, " ..." is appended. |
* If the string is already shorter than $length, it is returned unchanged. |
* |
* @static |
* @param string string A string to be truncated. |
* @param int length the maximum length the string should be truncated to |
* @return string the truncated string |
*/ |
function iTrunc($string, $length) { |
if (strlen($string)<=$length) { |
return $string; |
} |
$pos = strrpos($string,"."); |
if ($pos>=$length-4) { |
$string = substr($string,0,$length-4); |
$pos = strrpos($string,"."); |
} |
if ($pos>=$length*0.4) { |
return substr($string,0,$pos+1)." ..."; |
} |
$pos = strrpos($string," "); |
if ($pos>=$length-4) { |
$string = substr($string,0,$length-4); |
$pos = strrpos($string," "); |
} |
if ($pos>=$length*0.4) { |
return substr($string,0,$pos)." ..."; |
} |
return substr($string,0,$length-4)." ..."; |
} |
# }}} |
# {{{ _createGeneratorComment |
/** |
* Creates a comment indicating the generator of this feed. |
* The format of this comment seems to be recognized by |
* Syndic8.com. |
*/ |
function _createGeneratorComment() { |
return "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\n"; |
} |
# }}} |
# {{{ _createAdditionalElements |
/** |
* Creates a string containing all additional elements specified in |
* $additionalElements. |
* @param elements array an associative array containing key => value pairs |
* @param indentString string a string that will be inserted before every generated line |
* @return string the XML tags corresponding to $additionalElements |
*/ |
function _createAdditionalElements($elements, $indentString="") { |
$ae = ""; |
if (is_array($elements)) { |
foreach($elements AS $key => $value) { |
$ae.= $indentString."<$key>$value</$key>\n"; |
} |
} |
return $ae; |
} |
# }}} |
# {{{ createFeed |
/** |
* Builds the feed's text. |
* @abstract |
* @return string the feed's complete text |
*/ |
function createFeed() { |
} |
# }}} |
# {{{ _generateFilename |
/** |
* Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml. |
* For example: |
* |
* echo $_SERVER["PHP_SELF"]."\n"; |
* echo FeedCreator::_generateFilename(); |
* |
* would produce: |
* |
* /rss/latestnews.php |
* latestnews.xml |
* |
* @return string the feed cache filename |
* @since 1.4 |
* @access private |
*/ |
function _generateFilename() { |
$fileInfo = pathinfo($_SERVER["PHP_SELF"]); |
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml"; |
} |
# }}} |
# {{{ _redirect |
/** |
* @since 1.4 |
* @access private |
*/ |
function _redirect($filename) { |
// attention, heavily-commented-out-area |
// maybe use this in addition to file time checking |
//Header("Expires: ".date("r",time()+$this->_timeout)); |
/* no caching at all, doesn't seem to work as good: |
Header("Cache-Control: no-cache"); |
Header("Pragma: no-cache"); |
*/ |
// HTTP redirect, some feed readers' simple HTTP implementations don't follow it |
//Header("Location: ".$filename); |
Header("Content-Type: ".$this->contentType."; filename=".basename($filename)); |
Header("Content-Disposition: inline; filename=".basename($filename)); |
readfile($filename, "r"); |
die(); |
} |
# }}} |
# {{{ useCached |
/** |
* Turns on caching and checks if there is a recent version of this feed in the cache. |
* If there is, an HTTP redirect header is sent. |
* To effectively use caching, you should create the FeedCreator object and call this method |
* before anything else, especially before you do the time consuming task to build the feed |
* (web fetching, for example). |
* @since 1.4 |
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). |
* @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour) |
*/ |
function useCached($filename="", $timeout=3600) { |
$this->_timeout = $timeout; |
if ($filename=="") { |
$filename = $this->_generateFilename(); |
} |
if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) { |
$this->_redirect($filename); |
} |
} |
# }}} |
# {{{ saveFeed |
/** |
* Saves this feed as a file on the local disk. After the file is saved, a redirect |
* header may be sent to redirect the user to the newly created file. |
* @since 1.4 |
* |
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). |
* @param redirect boolean optional send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file. |
*/ |
function saveFeed($filename="", $displayContents=true) { |
if ($filename=="") { |
$filename = $this->_generateFilename(); |
} |
$feedFile = fopen($filename, "w+"); |
if ($feedFile) { |
fputs($feedFile,$this->createFeed()); |
fclose($feedFile); |
if ($displayContents) { |
$this->_redirect($filename); |
} |
} else { |
echo "<br /><b>Error creating feed file, please check write permissions.</b><br />"; |
} |
} |
# }}} |
} |
/** |
* FeedDate is an internal class that stores a date for a feed or feed item. |
* Usually, you won't need to use this. |
*/ |
class FeedDate { |
var $unix; |
# {{{ __construct |
/** |
* Creates a new instance of FeedDate representing a given date. |
* Accepts RFC 822, ISO 8601 date formats as well as unix time stamps. |
* @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used. |
*/ |
function FeedDate($dateString="") { |
if ($dateString=="") $dateString = date("r"); |
if (is_integer($dateString)) { |
$this->unix = $dateString; |
return; |
} |
if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) { |
$months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12); |
$this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]); |
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') { |
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60; |
} else { |
if (strlen($matches[7])==1) { |
$oneHour = 3600; |
$ord = ord($matches[7]); |
if ($ord < ord("M")) { |
$tzOffset = (ord("A") - $ord - 1) * $oneHour; |
} elseif ($ord >= ord("M") AND $matches[7]!="Z") { |
$tzOffset = ($ord - ord("M")) * $oneHour; |
} elseif ($matches[7]=="Z") { |
$tzOffset = 0; |
} |
} |
switch ($matches[7]) { |
case "UT": |
case "GMT": $tzOffset = 0; |
} |
} |
$this->unix += $tzOffset; |
return; |
} |
if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) { |
$this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]); |
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') { |
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60; |
} else { |
if ($matches[7]=="Z") { |
$tzOffset = 0; |
} |
} |
$this->unix += $tzOffset; |
return; |
} |
$this->unix = 0; |
} |
# }}} |
# {{{ rfc822 |
/** |
* Gets the date stored in this FeedDate as an RFC 822 date. |
* |
* @return a date in RFC 822 format |
*/ |
function rfc822() { |
return gmdate("r",$this->unix); |
} |
# }}} |
# {{{ iso8601 |
/** |
* Gets the date stored in this FeedDate as an ISO 8601 date. |
* |
* @return a date in ISO 8601 format |
*/ |
function iso8601() { |
$date = gmdate("Y-m-d\TH:i:sO",$this->unix); |
$date = substr($date,0,22) . ':' . substr($date,-2); |
return $date; |
} |
# }}} |
# {{{ unix |
/** |
* Gets the date stored in this FeedDate as unix time stamp. |
* |
* @return a date as a unix time stamp |
*/ |
function unix() { |
return $this->unix; |
} |
# }}} |
} |
/** |
* RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0. |
* |
* @see http://www.purl.org/rss/1.0/ |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class RSSCreator10 extends FeedCreator { |
# {{{ createFeed |
/** |
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0. |
* The feed will contain all items previously added in the same order. |
* @return string the feed's complete text |
*/ |
function createFeed() { |
global $config; |
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n"; |
$feed.= "<?xml-stylesheet href=\"http://www.w3.org/2000/08/w3c-synd/style.css\" type=\"text/css\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<rdf:RDF\n"; |
$feed.= " xmlns=\"http://purl.org/rss/1.0/\"\n"; |
$feed.= " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"; |
$feed.= " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n"; |
$feed.= " <channel rdf:about=\"".htmlspecialchars($this->syndicationURL)."\">\n"; |
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n"; |
$feed.= " <description>".htmlspecialchars($this->description)."</description>\n"; |
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n"; |
if ($this->image!=null) { |
$feed.= " <image rdf:resource=\"".$this->image->url."\" />\n"; |
} |
$now = new FeedDate(); |
$feed.= " <dc:date>".htmlspecialchars($now->iso8601())."</dc:date>\n"; |
$feed.= " <items>\n"; |
$feed.= " <rdf:Seq>\n"; |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <rdf:li rdf:resource=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n"; |
} |
$feed.= " </rdf:Seq>\n"; |
$feed.= " </items>\n"; |
$feed.= " </channel>\n"; |
if ($this->image!=null) { |
$feed.= " <image rdf:about=\"".$this->image->url."\">\n"; |
$feed.= " <title>".$this->image->title."</title>\n"; |
$feed.= " <link>".$this->image->link."</link>\n"; |
$feed.= " <url>".$this->image->url."</url>\n"; |
$feed.= " </image>\n"; |
} |
//$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <item rdf:about=\"".htmlspecialchars($this->items[$i]->link)."\">\n"; |
//$feed.= " <dc:type>Posting</dc:type>\n"; |
$feed.= " <dc:format>text/html</dc:format>\n"; |
if ($this->items[$i]->date!=null) { |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <dc:date>".htmlspecialchars($itemDate->iso8601())."</dc:date>\n"; |
} |
if ($this->items[$i]->source!="") { |
$feed.= " <dc:source>".htmlspecialchars($this->items[$i]->source)."</dc:source>\n"; |
} |
if ($this->items[$i]->author!="") { |
$feed.= " <dc:creator>".htmlspecialchars($this->items[$i]->author)."</dc:creator>\n"; |
} |
$feed.= " <title>".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."</title>\n"; |
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n"; |
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n"; |
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
$feed.= " </item>\n"; |
} |
$feed.= "</rdf:RDF>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3. |
* |
* @see http://my.netscape.com/publish/formats/rss-spec-0.91.html |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class RSSCreator091 extends FeedCreator { |
/** |
* Stores this RSS feed's version number. |
* @access private |
*/ |
var $RSSVersion; |
# {{{ __construct |
function RSSCreator091() { |
$this->_setRSSVersion("0.91"); |
$this->contentType = "application/rss+xml"; |
} |
# }}} |
# {{{ _setRSSVersion |
/** |
* Sets this RSS feed's version number. |
* @access private |
*/ |
function _setRSSVersion($version) { |
$this->RSSVersion = $version; |
} |
# }}} |
# {{{ createFeed |
/** |
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0. |
* The feed will contain all items previously added in the same order. |
* @return string the feed's complete text |
*/ |
function createFeed() { |
global $config; |
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<rss version=\"".$this->RSSVersion."\">\n"; |
$feed.= " <channel>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n"; |
$feed.= " <description>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</description>\n"; |
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n"; |
$now = new FeedDate(); |
$feed.= " <lastBuildDate>".htmlspecialchars($now->rfc822())."</lastBuildDate>\n"; |
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n"; |
if ($this->image!=null) { |
$feed.= " <image>\n"; |
$feed.= " <url>".$this->image->url."</url>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."</title>\n"; |
$feed.= " <link>".$this->image->link."</link>\n"; |
if ($this->image->width!="") { |
$feed.= " <width>".$this->image->width."</width>\n"; |
} |
if ($this->image->height!="") { |
$feed.= " <height>".$this->image->height."</height>\n"; |
} |
if ($this->image->description!="") { |
$feed.= " <description>".htmlspecialchars($this->image->description)."</description>\n"; |
} |
$feed.= " </image>\n"; |
} |
if ($this->language!="") { |
$feed.= " <language>".$this->language."</language>\n"; |
} |
if ($this->copyright!="") { |
$feed.= " <copyright>".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."</copyright>\n"; |
} |
if ($this->editor!="") { |
$feed.= " <managingEditor>".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."</managingEditor>\n"; |
} |
if ($this->webmaster!="") { |
$feed.= " <webMaster>".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."</webMaster>\n"; |
} |
if ($this->pubDate!="") { |
$pubDate = new FeedDate($this->pubDate); |
$feed.= " <pubDate>".htmlspecialchars($pubDate->rfc822())."</pubDate>\n"; |
} |
if ($this->category!="") { |
$feed.= " <category>".htmlspecialchars($this->category)."</category>\n"; |
} |
if ($this->docs!="") { |
$feed.= " <docs>".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."</docs>\n"; |
} |
if ($this->ttl!="") { |
$feed.= " <ttl>".htmlspecialchars($this->ttl)."</ttl>\n"; |
} |
if ($this->rating!="") { |
$feed.= " <rating>".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."</rating>\n"; |
} |
if ($this->skipHours!="") { |
$feed.= " <skipHours>".htmlspecialchars($this->skipHours)."</skipHours>\n"; |
} |
if ($this->skipDays!="") { |
$feed.= " <skipDays>".htmlspecialchars($this->skipDays)."</skipDays>\n"; |
} |
$feed.= $this->_createAdditionalElements($this->additionalElements, " "); |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <item>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n"; |
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n"; |
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n"; |
if ($this->items[$i]->author!="") { |
$feed.= " <author>".htmlspecialchars($this->items[$i]->author)."</author>\n"; |
} |
/* |
// on hold |
if ($this->items[$i]->source!="") { |
$feed.= " <source>".htmlspecialchars($this->items[$i]->source)."</source>\n"; |
} |
*/ |
if ($this->items[$i]->category!="") { |
$feed.= " <category>".htmlspecialchars($this->items[$i]->category)."</category>\n"; |
} |
if ($this->items[$i]->comments!="") { |
$feed.= " <comments>".$this->items[$i]->comments."</comments>\n"; |
} |
if ($this->items[$i]->date!="") { |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <pubDate>".htmlspecialchars($itemDate->rfc822())."</pubDate>\n"; |
} |
if ($this->items[$i]->guid!="") { |
$feed.= " <guid>".$this->items[$i]->guid."</guid>\n"; |
} |
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
$feed.= " </item>\n"; |
} |
$feed.= " </channel>\n"; |
$feed.= "</rss>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0. |
* |
* @see http://backend.userland.com/rss |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class RSSCreator20 extends RSSCreator091 { |
# {{{ __construct |
function RSSCreator20() { |
parent::_setRSSVersion("2.0"); |
} |
# }}} |
} |
/** |
* PIECreator01 is a FeedCreator that implements the emerging PIE specification, |
* as in http://intertwingly.net/wiki/pie/Syntax. |
* |
* @deprecated |
* @since 1.3 |
* @author Scott Reynen <scott@randomchaos.com> and Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class PIECreator01 extends FeedCreator { |
# {{{ createFeed |
function createFeed() { |
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
$feed.= "<feed version=\"0.1\" xmlns=\"http://example.com/newformat#\">\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n"; |
$feed.= " <subtitle>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</subtitle>\n"; |
$feed.= " <link>".$this->link."</link>\n"; |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <entry>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n"; |
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n"; |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n"; |
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n"; |
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n"; |
$feed.= " <id>".$this->items[$i]->guid."</id>\n"; |
if ($this->items[$i]->author!="") { |
$feed.= " <author>\n"; |
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n"; |
if ($this->items[$i]->authorEmail!="") { |
$feed.= " <email>".$this->items[$i]->authorEmail."</email>\n"; |
} |
$feed.=" </author>\n"; |
} |
$feed.= " <content type=\"text/html\" xml:lang=\"en-us\">\n"; |
$feed.= " <div xmlns=\"http://www.w3.org/1999/xhtml\">".$this->items[$i]->description."</div>\n"; |
$feed.= " </content>\n"; |
$feed.= " </entry>\n"; |
} |
$feed.= "</feed>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* AtomCreator03 is a FeedCreator that implements the atom specification, |
* as in http://www.intertwingly.net/wiki/pie/FrontPage. |
* Please note that just by using AtomCreator03 you won't automatically |
* produce valid atom files. For example, you have to specify either an editor |
* for the feed or an author for every single feed item. |
* |
* Some elements have not been implemented yet. These are (incomplete list): |
* author URL, item author's email and URL, item contents, alternate links, |
* other link content types than text/html. Some of them may be created with |
* AtomCreator03::additionalElements. |
* |
* @see FeedCreator#additionalElements |
* @since 1.6 |
* @author Kai Blankenhorn <kaib@bitfolge.de>, Scott Reynen <scott@randomchaos.com> |
*/ |
class AtomCreator03 extends FeedCreator { |
# {{{ __construct |
function AtomCreator03() { |
$this->contentType = "application/atom+xml"; |
} |
# }}} |
# {{{ createFeed |
function createFeed() { |
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<feed version=\"0.3\" xmlns=\"http://purl.org/atom/ns#\""; |
if ($this->language!="") { |
$feed.= " xml:lang:\"".$this->language."\""; |
} |
$feed.= ">\n"; |
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n"; |
$feed.= " <tagline>".htmlspecialchars($this->description)."</tagline>\n"; |
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n"; |
$feed.= " <id>".$this->link."</id>\n"; |
$now = new FeedDate(); |
$feed.= " <modified>".htmlspecialchars($now->iso8601())."</modified>\n"; |
if ($this->editor!="") { |
$feed.= " <author>\n"; |
$feed.= " <name>".$this->editor."</name>\n"; |
if ($this->editorEmail!="") { |
$feed.= " <email>".$this->editorEmail."</email>\n"; |
} |
$feed.= " </author>\n"; |
} |
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n"; |
$feed.= $this->_createAdditionalElements($this->additionalElements, " "); |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <entry>\n"; |
$feed.= " <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n"; |
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n"; |
if ($this->items[$i]->date=="") { |
$this->items[$i]->date = time(); |
} |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n"; |
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n"; |
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n"; |
$feed.= " <id>".$this->items[$i]->link."</id>\n"; |
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
if ($this->items[$i]->author!="") { |
$feed.= " <author>\n"; |
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n"; |
$feed.= " </author>\n"; |
} |
if ($this->items[$i]->description!="") { |
$feed.= " <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n"; |
} |
$feed.= " </entry>\n"; |
} |
$feed.= "</feed>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* MBOXCreator is a FeedCreator that implements the mbox format |
* as described in http://www.qmail.org/man/man5/mbox.html |
* |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class MBOXCreator extends FeedCreator { |
# {{{ __construct |
function MBOXCreator() { |
$this->contentType = "text/plain"; |
} |
# }}} |
# {{{ qp_enc |
function qp_enc($input = "", $line_max = 76) { |
$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); |
$lines = preg_split("/(?:\r\n|\r|\n)/", $input); |
$eol = "\r\n"; |
$escape = "="; |
$output = ""; |
while( list(, $line) = each($lines) ) { |
//$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary |
$linlen = strlen($line); |
$newline = ""; |
for($i = 0; $i < $linlen; $i++) { |
$c = substr($line, $i, 1); |
$dec = ord($c); |
if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only |
$c = "=20"; |
} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required |
$h2 = floor($dec/16); $h1 = floor($dec%16); |
$c = $escape.$hex["$h2"].$hex["$h1"]; |
} |
if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted |
$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay |
$newline = ""; |
} |
$newline .= $c; |
} // end of for |
$output .= $newline.$eol; |
} |
return trim($output); |
} |
# }}} |
# {{{ createFeed |
/** |
* Builds the MBOX contents. |
* @return string the feed's complete text |
*/ |
function createFeed() { |
global $config; |
for ($i=0;$i<count($this->items);$i++) { |
if ($this->items[$i]->author!="") { |
$from = $this->items[$i]->author; |
} else { |
$from = $this->title; |
} |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n"; |
$feed.= "Content-Type: text/plain;\n"; |
$feed.= " charset=\"".$config->outputEnc."\"\n"; |
$feed.= "Content-Transfer-Encoding: quoted-printable\n"; |
$feed.= "Content-Type: text/plain\n"; |
$feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n"; |
$feed.= "Date: ".$itemDate->rfc822()."\n"; |
$feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n"; |
$feed.= "\n"; |
$body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description)); |
$feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body); |
$feed.= "\n"; |
$feed.= "\n"; |
} |
return $feed; |
} |
# }}} |
# {{{ _generateFilename |
/** |
* Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types. |
* @return string the feed cache filename |
* @since 1.4 |
* @access private |
*/ |
function _generateFilename() { |
$fileInfo = pathinfo($_SERVER["PHP_SELF"]); |
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox"; |
} |
# }}} |
} |
/** |
* OPMLCreator is a FeedCreator that implements OPML 1.0. |
* |
* @see http://opml.scripting.com/spec |
* @author Dirk Clemens, Kai Blankenhorn |
* @since 1.5 |
*/ |
class OPMLCreator extends FeedCreator { |
# {{{ createFeed |
function createFeed() { |
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<opml xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"; |
$feed.= " <head>\n"; |
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n"; |
if ($this->pubDate!="") { |
$date = new FeedDate($this->pubDate); |
$feed.= " <dateCreated>".$date->rfc822()."</dateCreated>\n"; |
} |
if ($this->lastBuildDate!="") { |
$date = new FeedDate($this->lastBuildDate); |
$feed.= " <dateModified>".$date->rfc822()."</dateModified>\n"; |
} |
if ($this->editor!="") { |
$feed.= " <ownerName>".$this->editor."</ownerName>\n"; |
} |
if ($this->editorEmail!="") { |
$feed.= " <ownerEmail>".$this->editorEmail."</ownerEmail>\n"; |
} |
$feed.= " </head>\n"; |
$feed.= " <body>\n"; |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <outline type=\"rss\" "; |
$title = htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," "))); |
$feed.= " title=\"".$title."\""; |
$feed.= " text=\"".$title."\""; |
//$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\""; |
$feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\""; |
$feed.= "/>\n"; |
} |
$feed.= " </body>\n"; |
$feed.= "</opml>\n"; |
return $feed; |
} |
# }}} |
} |
?> |
<?php |
# vim:et:ts=4:sts=4:sw=4:fdm=marker: |
# {{{ Info |
/*************************************************************************** |
FeedCreator class v1.6 |
originally (c) Kai Blankenhorn |
www.bitfolge.de |
kaib@bitfolge.de |
v1.3 work by Scott Reynen (scott@randomchaos.com) and Kai Blankenhorn |
v1.5 OPML support by Dirk Clemens |
This program is free software; you can redistribute it and/or |
modify it under the terms of the GNU General Public License |
as published by the Free Software Foundation; either version 2 |
of the License, or (at your option) any later version. |
This program is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details: <http://www.gnu.org/licenses/gpl.txt> |
**************************************************************************** |
Changelog: |
Modifications for WebSVN: |
The main description link wasn't put through htmlspecialcharacters |
Output encoding now defined by $config |
Remove hardcoded time zone |
v1.6 05-10-04 |
added stylesheet to RSS 1.0 feeds |
fixed generator comment (thanks Kevin L. Papendick and Tanguy Pruvot) |
fixed RFC822 date bug (thanks Tanguy Pruvot) |
added TimeZone customization for RFC8601 (thanks Tanguy Pruvot) |
fixed Content-type could be empty (thanks Tanguy Pruvot) |
fixed author/creator in RSS1.0 (thanks Tanguy Pruvot) |
v1.6 beta 02-28-04 |
added Atom 0.3 support (not all features, though) |
improved OPML 1.0 support (hopefully - added more elements) |
added support for arbitrary additional elements (use with caution) |
code beautification :-) |
considered beta due to some internal changes |
v1.5.1 01-27-04 |
fixed some RSS 1.0 glitches (thanks to Stéphane Vanpoperynghe) |
fixed some inconsistencies between documentation and code (thanks to Timothy Martin) |
v1.5 01-06-04 |
added support for OPML 1.0 |
added more documentation |
v1.4 11-11-03 |
optional feed saving and caching |
improved documentation |
minor improvements |
v1.3 10-02-03 |
renamed to FeedCreator, as it not only creates RSS anymore |
added support for mbox |
tentative support for echo/necho/atom/pie/??? |
v1.2 07-20-03 |
intelligent auto-truncating of RSS 0.91 attributes |
don't create some attributes when they're not set |
documentation improved |
fixed a real and a possible bug with date conversions |
code cleanup |
v1.1 06-29-03 |
added images to feeds |
now includes most RSS 0.91 attributes |
added RSS 2.0 feeds |
v1.0 06-24-03 |
initial release |
***************************************************************************/ |
/*** GENERAL USAGE ********************************************************* |
include("feedcreator.class.php"); |
$rss = new UniversalFeedCreator(); |
$rss->useCached(); // use cached version if age<1 hour |
$rss->title = "PHP news"; |
$rss->description = "daily news from the PHP scripting world"; |
$rss->link = "http://www.dailyphp.net/news"; |
$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"]; |
$image = new FeedImage(); |
$image->title = "dailyphp.net logo"; |
$image->url = "http://www.dailyphp.net/images/logo.gif"; |
$image->link = "http://www.dailyphp.net"; |
$image->description = "Feed provided by dailyphp.net. Click to visit."; |
$rss->image = $image; |
// get your news items from somewhere, e.g. your database: |
mysql_select_db($dbHost, $dbUser, $dbPass); |
$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC"); |
while ($data = mysql_fetch_object($res)) { |
$item = new FeedItem(); |
$item->title = $data->title; |
$item->link = $data->url; |
$item->description = $data->short; |
$item->date = $data->newsdate; |
$item->source = "http://www.dailyphp.net"; |
$item->author = "John Doe"; |
$rss->addItem($item); |
} |
// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated), |
// MBOX, OPML, ATOM0.3 |
echo $rss->saveFeed("RSS1.0", "news/feed.xml"); |
# }}} |
*************************************************************************** |
* A little setup * |
**************************************************************************/ |
/** |
* Version string. |
**/ |
define("FEEDCREATOR_VERSION", "FeedCreator 1.6"); |
/** |
* A FeedItem is a part of a FeedCreator feed. |
* |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
* @since 1.3 |
*/ |
class FeedItem { |
# {{{ Properties |
/** |
* Mandatory attributes of an item. |
*/ |
var $title, $description, $link; |
/** |
* Optional attributes of an item. |
*/ |
var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator; |
/** |
* Publishing date of an item. May be in one of the following formats: |
* |
* RFC 822: |
* "Mon, 20 Jan 03 18:05:41 +0400" |
* "20 Jan 03 18:05:41 +0000" |
* |
* ISO 8601: |
* "2003-01-20T18:05:41+04:00" |
* |
* Unix: |
* 1043082341 |
*/ |
var $date; |
/** |
* Any additional elements to include as an assiciated array. All $key => $value pairs |
* will be included unencoded in the feed item in the form |
* <$key>$value</$key> |
* Again: No encoding will be used! This means you can invalidate or enhance the feed |
* if $value contains markup. This may be abused to embed tags not implemented by |
* the FeedCreator class used. |
*/ |
var $additionalElements = Array(); |
// on hold |
// var $source; |
# }}} |
} |
/** |
* An FeedImage may be added to a FeedCreator feed. |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
* @since 1.3 |
*/ |
class FeedImage { |
# {{{ Properties |
/** |
* Mandatory attributes of an image. |
*/ |
var $title, $url, $link; |
/** |
* Optional attributes of an image. |
*/ |
var $width, $height, $description; |
# }}} |
} |
/** |
* UniversalFeedCreator lets you choose during runtime which |
* format to build. |
* For general usage of a feed class, see the FeedCreator class |
* below or the example above. |
* |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class UniversalFeedCreator extends FeedCreator { |
var $_feed; |
# {{{ _setFormat |
function _setFormat($format) { |
switch (strtoupper($format)) { |
case "2.0": |
// fall through |
case "RSS2.0": |
$this->_feed = new RSSCreator20(); |
break; |
case "1.0": |
// fall through |
case "RSS1.0": |
$this->_feed = new RSSCreator10(); |
break; |
case "0.91": |
// fall through |
case "RSS0.91": |
$this->_feed = new RSSCreator091(); |
break; |
case "PIE0.1": |
$this->_feed = new PIECreator01(); |
break; |
case "MBOX": |
$this->_feed = new MBOXCreator(); |
break; |
case "OPML": |
$this->_feed = new OPMLCreator(); |
break; |
case "ATOM0.3": |
$this->_feed = new AtomCreator03(); |
break; |
default: |
$this->_feed = new RSSCreator091(); |
break; |
} |
$vars = get_object_vars($this); |
foreach ($vars as $key => $value) { |
if ($key!="feed") { |
$this->_feed->{$key} = $this->{$key}; |
} |
} |
} |
# }}} |
# {{{ createFeed |
/** |
* Creates a syndication feed based on the items previously added. |
* |
* @see FeedCreator::addItem() |
* @param string format format the feed should comply to. Valid values are: |
* "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML". |
* @return string the contents of the feed. |
*/ |
function createFeed($format = "RSS0.91") { |
$this->_setFormat($format); |
return $this->_feed->createFeed(); |
} |
# }}} |
# {{{ saveFeed |
/** |
* Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect |
* header may be sent to redirect the use to the newly created file. |
* @since 1.4 |
* |
* @param string format format the feed should comply to. Valid values are: |
* "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3". |
* @param string filename optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). |
* @param boolean displayContents optional send the content of the file or not. If true, the file will be sent in the body of the response. |
*/ |
function saveFeed($format="RSS0.91", $filename="", $displayContents=true) { |
$this->_setFormat($format); |
$this->_feed->saveFeed($filename, $displayContents); |
} |
# }}} |
} |
/** |
* FeedCreator is the abstract base implementation for concrete |
* implementations that implement a specific format of syndication. |
* |
* @abstract |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
* @since 1.4 |
*/ |
class FeedCreator { |
# {{{ Properties |
/** |
* Mandatory attributes of a feed. |
*/ |
var $title, $description, $link; |
/** |
* Optional attributes of a feed. |
*/ |
var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays; |
/** |
* @access private |
*/ |
var $items = Array(); |
/** |
* This feed's MIME content type. |
* @since 1.4 |
* @access private |
*/ |
var $contentType = "text/xml"; |
/** |
* Any additional elements to include as an assiciated array. All $key => $value pairs |
* will be included unencoded in the feed in the form |
* <$key>$value</$key> |
* Again: No encoding will be used! This means you can invalidate or enhance the feed |
* if $value contains markup. This may be abused to embed tags not implemented by |
* the FeedCreator class used. |
*/ |
var $additionalElements = Array(); |
# }}} |
# {{{ addItem |
/** |
* Adds an FeedItem to the feed. |
* |
* @param object FeedItem $item The FeedItem to add to the feed. |
* @access public |
*/ |
function addItem($item) { |
$this->items[] = $item; |
} |
# }}} |
# {{{ iTrunc |
/** |
* Truncates a string to a certain length at the most sensible point. |
* First, if there's a '.' character near the end of the string, the string is truncated after this character. |
* If there is no '.', the string is truncated after the last ' ' character. |
* If the string is truncated, " ..." is appended. |
* If the string is already shorter than $length, it is returned unchanged. |
* |
* @static |
* @param string string A string to be truncated. |
* @param int length the maximum length the string should be truncated to |
* @return string the truncated string |
*/ |
function iTrunc($string, $length) { |
if (strlen($string)<=$length) { |
return $string; |
} |
$pos = strrpos($string,"."); |
if ($pos>=$length-4) { |
$string = substr($string,0,$length-4); |
$pos = strrpos($string,"."); |
} |
if ($pos>=$length*0.4) { |
return substr($string,0,$pos+1)." ..."; |
} |
$pos = strrpos($string," "); |
if ($pos>=$length-4) { |
$string = substr($string,0,$length-4); |
$pos = strrpos($string," "); |
} |
if ($pos>=$length*0.4) { |
return substr($string,0,$pos)." ..."; |
} |
return substr($string,0,$length-4)." ..."; |
} |
# }}} |
# {{{ _createGeneratorComment |
/** |
* Creates a comment indicating the generator of this feed. |
* The format of this comment seems to be recognized by |
* Syndic8.com. |
*/ |
function _createGeneratorComment() { |
return "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\n"; |
} |
# }}} |
# {{{ _createAdditionalElements |
/** |
* Creates a string containing all additional elements specified in |
* $additionalElements. |
* @param elements array an associative array containing key => value pairs |
* @param indentString string a string that will be inserted before every generated line |
* @return string the XML tags corresponding to $additionalElements |
*/ |
function _createAdditionalElements($elements, $indentString="") { |
$ae = ""; |
if (is_array($elements)) { |
foreach($elements AS $key => $value) { |
$ae.= $indentString."<$key>$value</$key>\n"; |
} |
} |
return $ae; |
} |
# }}} |
# {{{ createFeed |
/** |
* Builds the feed's text. |
* @abstract |
* @return string the feed's complete text |
*/ |
function createFeed() { |
} |
# }}} |
# {{{ _generateFilename |
/** |
* Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml. |
* For example: |
* |
* echo $_SERVER["PHP_SELF"]."\n"; |
* echo FeedCreator::_generateFilename(); |
* |
* would produce: |
* |
* /rss/latestnews.php |
* latestnews.xml |
* |
* @return string the feed cache filename |
* @since 1.4 |
* @access private |
*/ |
function _generateFilename() { |
$fileInfo = pathinfo($_SERVER["PHP_SELF"]); |
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml"; |
} |
# }}} |
# {{{ _redirect |
/** |
* @since 1.4 |
* @access private |
*/ |
function _redirect($filename) { |
// attention, heavily-commented-out-area |
// maybe use this in addition to file time checking |
//Header("Expires: ".date("r",time()+$this->_timeout)); |
/* no caching at all, doesn't seem to work as good: |
Header("Cache-Control: no-cache"); |
Header("Pragma: no-cache"); |
*/ |
// HTTP redirect, some feed readers' simple HTTP implementations don't follow it |
//Header("Location: ".$filename); |
Header("Content-Type: ".$this->contentType."; filename=".basename($filename)); |
Header("Content-Disposition: inline; filename=".basename($filename)); |
readfile($filename, "r"); |
die(); |
} |
# }}} |
# {{{ useCached |
/** |
* Turns on caching and checks if there is a recent version of this feed in the cache. |
* If there is, an HTTP redirect header is sent. |
* To effectively use caching, you should create the FeedCreator object and call this method |
* before anything else, especially before you do the time consuming task to build the feed |
* (web fetching, for example). |
* @since 1.4 |
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). |
* @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour) |
*/ |
function useCached($filename="", $timeout=3600) { |
$this->_timeout = $timeout; |
if ($filename=="") { |
$filename = $this->_generateFilename(); |
} |
if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) { |
$this->_redirect($filename); |
} |
} |
# }}} |
# {{{ saveFeed |
/** |
* Saves this feed as a file on the local disk. After the file is saved, a redirect |
* header may be sent to redirect the user to the newly created file. |
* @since 1.4 |
* |
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). |
* @param redirect boolean optional send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file. |
*/ |
function saveFeed($filename="", $displayContents=true) { |
if ($filename=="") { |
$filename = $this->_generateFilename(); |
} |
$feedFile = fopen($filename, "w+"); |
if ($feedFile) { |
fputs($feedFile,$this->createFeed()); |
fclose($feedFile); |
if ($displayContents) { |
$this->_redirect($filename); |
} |
} else { |
echo "<br /><b>Error creating feed file, please check write permissions.</b><br />"; |
} |
} |
# }}} |
} |
/** |
* FeedDate is an internal class that stores a date for a feed or feed item. |
* Usually, you won't need to use this. |
*/ |
class FeedDate { |
var $unix; |
# {{{ __construct |
/** |
* Creates a new instance of FeedDate representing a given date. |
* Accepts RFC 822, ISO 8601 date formats as well as unix time stamps. |
* @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used. |
*/ |
function FeedDate($dateString="") { |
if ($dateString=="") $dateString = date("r"); |
if (is_integer($dateString)) { |
$this->unix = $dateString; |
return; |
} |
if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) { |
$months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12); |
$this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]); |
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') { |
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60; |
} else { |
if (strlen($matches[7])==1) { |
$oneHour = 3600; |
$ord = ord($matches[7]); |
if ($ord < ord("M")) { |
$tzOffset = (ord("A") - $ord - 1) * $oneHour; |
} elseif ($ord >= ord("M") AND $matches[7]!="Z") { |
$tzOffset = ($ord - ord("M")) * $oneHour; |
} elseif ($matches[7]=="Z") { |
$tzOffset = 0; |
} |
} |
switch ($matches[7]) { |
case "UT": |
case "GMT": $tzOffset = 0; |
} |
} |
$this->unix += $tzOffset; |
return; |
} |
if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) { |
$this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]); |
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') { |
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60; |
} else { |
if ($matches[7]=="Z") { |
$tzOffset = 0; |
} |
} |
$this->unix += $tzOffset; |
return; |
} |
$this->unix = 0; |
} |
# }}} |
# {{{ rfc822 |
/** |
* Gets the date stored in this FeedDate as an RFC 822 date. |
* |
* @return a date in RFC 822 format |
*/ |
function rfc822() { |
return gmdate("r",$this->unix); |
} |
# }}} |
# {{{ iso8601 |
/** |
* Gets the date stored in this FeedDate as an ISO 8601 date. |
* |
* @return a date in ISO 8601 format |
*/ |
function iso8601() { |
$date = gmdate("Y-m-d\TH:i:sO",$this->unix); |
$date = substr($date,0,22) . ':' . substr($date,-2); |
return $date; |
} |
# }}} |
# {{{ unix |
/** |
* Gets the date stored in this FeedDate as unix time stamp. |
* |
* @return a date as a unix time stamp |
*/ |
function unix() { |
return $this->unix; |
} |
# }}} |
} |
/** |
* RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0. |
* |
* @see http://www.purl.org/rss/1.0/ |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class RSSCreator10 extends FeedCreator { |
# {{{ createFeed |
/** |
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0. |
* The feed will contain all items previously added in the same order. |
* @return string the feed's complete text |
*/ |
function createFeed() { |
global $config; |
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n"; |
$feed.= "<?xml-stylesheet href=\"http://www.w3.org/2000/08/w3c-synd/style.css\" type=\"text/css\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<rdf:RDF\n"; |
$feed.= " xmlns=\"http://purl.org/rss/1.0/\"\n"; |
$feed.= " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"; |
$feed.= " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n"; |
$feed.= " <channel rdf:about=\"".htmlspecialchars($this->syndicationURL)."\">\n"; |
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n"; |
$feed.= " <description>".htmlspecialchars($this->description)."</description>\n"; |
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n"; |
if ($this->image!=null) { |
$feed.= " <image rdf:resource=\"".$this->image->url."\" />\n"; |
} |
$now = new FeedDate(); |
$feed.= " <dc:date>".htmlspecialchars($now->iso8601())."</dc:date>\n"; |
$feed.= " <items>\n"; |
$feed.= " <rdf:Seq>\n"; |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <rdf:li rdf:resource=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n"; |
} |
$feed.= " </rdf:Seq>\n"; |
$feed.= " </items>\n"; |
$feed.= " </channel>\n"; |
if ($this->image!=null) { |
$feed.= " <image rdf:about=\"".$this->image->url."\">\n"; |
$feed.= " <title>".$this->image->title."</title>\n"; |
$feed.= " <link>".$this->image->link."</link>\n"; |
$feed.= " <url>".$this->image->url."</url>\n"; |
$feed.= " </image>\n"; |
} |
//$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <item rdf:about=\"".htmlspecialchars($this->items[$i]->link)."\">\n"; |
//$feed.= " <dc:type>Posting</dc:type>\n"; |
$feed.= " <dc:format>text/html</dc:format>\n"; |
if ($this->items[$i]->date!=null) { |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <dc:date>".htmlspecialchars($itemDate->iso8601())."</dc:date>\n"; |
} |
if ($this->items[$i]->source!="") { |
$feed.= " <dc:source>".htmlspecialchars($this->items[$i]->source)."</dc:source>\n"; |
} |
if ($this->items[$i]->author!="") { |
$feed.= " <dc:creator>".htmlspecialchars($this->items[$i]->author)."</dc:creator>\n"; |
} |
$feed.= " <title>".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."</title>\n"; |
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n"; |
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n"; |
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
$feed.= " </item>\n"; |
} |
$feed.= "</rdf:RDF>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3. |
* |
* @see http://my.netscape.com/publish/formats/rss-spec-0.91.html |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class RSSCreator091 extends FeedCreator { |
/** |
* Stores this RSS feed's version number. |
* @access private |
*/ |
var $RSSVersion; |
# {{{ __construct |
function RSSCreator091() { |
$this->_setRSSVersion("0.91"); |
$this->contentType = "application/rss+xml"; |
} |
# }}} |
# {{{ _setRSSVersion |
/** |
* Sets this RSS feed's version number. |
* @access private |
*/ |
function _setRSSVersion($version) { |
$this->RSSVersion = $version; |
} |
# }}} |
# {{{ createFeed |
/** |
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0. |
* The feed will contain all items previously added in the same order. |
* @return string the feed's complete text |
*/ |
function createFeed() { |
global $config; |
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<rss version=\"".$this->RSSVersion."\">\n"; |
$feed.= " <channel>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n"; |
$feed.= " <description>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</description>\n"; |
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n"; |
$now = new FeedDate(); |
$feed.= " <lastBuildDate>".htmlspecialchars($now->rfc822())."</lastBuildDate>\n"; |
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n"; |
if ($this->image!=null) { |
$feed.= " <image>\n"; |
$feed.= " <url>".$this->image->url."</url>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."</title>\n"; |
$feed.= " <link>".$this->image->link."</link>\n"; |
if ($this->image->width!="") { |
$feed.= " <width>".$this->image->width."</width>\n"; |
} |
if ($this->image->height!="") { |
$feed.= " <height>".$this->image->height."</height>\n"; |
} |
if ($this->image->description!="") { |
$feed.= " <description>".htmlspecialchars($this->image->description)."</description>\n"; |
} |
$feed.= " </image>\n"; |
} |
if ($this->language!="") { |
$feed.= " <language>".$this->language."</language>\n"; |
} |
if ($this->copyright!="") { |
$feed.= " <copyright>".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."</copyright>\n"; |
} |
if ($this->editor!="") { |
$feed.= " <managingEditor>".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."</managingEditor>\n"; |
} |
if ($this->webmaster!="") { |
$feed.= " <webMaster>".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."</webMaster>\n"; |
} |
if ($this->pubDate!="") { |
$pubDate = new FeedDate($this->pubDate); |
$feed.= " <pubDate>".htmlspecialchars($pubDate->rfc822())."</pubDate>\n"; |
} |
if ($this->category!="") { |
$feed.= " <category>".htmlspecialchars($this->category)."</category>\n"; |
} |
if ($this->docs!="") { |
$feed.= " <docs>".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."</docs>\n"; |
} |
if ($this->ttl!="") { |
$feed.= " <ttl>".htmlspecialchars($this->ttl)."</ttl>\n"; |
} |
if ($this->rating!="") { |
$feed.= " <rating>".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."</rating>\n"; |
} |
if ($this->skipHours!="") { |
$feed.= " <skipHours>".htmlspecialchars($this->skipHours)."</skipHours>\n"; |
} |
if ($this->skipDays!="") { |
$feed.= " <skipDays>".htmlspecialchars($this->skipDays)."</skipDays>\n"; |
} |
$feed.= $this->_createAdditionalElements($this->additionalElements, " "); |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <item>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n"; |
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n"; |
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n"; |
if ($this->items[$i]->author!="") { |
$feed.= " <author>".htmlspecialchars($this->items[$i]->author)."</author>\n"; |
} |
/* |
// on hold |
if ($this->items[$i]->source!="") { |
$feed.= " <source>".htmlspecialchars($this->items[$i]->source)."</source>\n"; |
} |
*/ |
if ($this->items[$i]->category!="") { |
$feed.= " <category>".htmlspecialchars($this->items[$i]->category)."</category>\n"; |
} |
if ($this->items[$i]->comments!="") { |
$feed.= " <comments>".$this->items[$i]->comments."</comments>\n"; |
} |
if ($this->items[$i]->date!="") { |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <pubDate>".htmlspecialchars($itemDate->rfc822())."</pubDate>\n"; |
} |
if ($this->items[$i]->guid!="") { |
$feed.= " <guid>".$this->items[$i]->guid."</guid>\n"; |
} |
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
$feed.= " </item>\n"; |
} |
$feed.= " </channel>\n"; |
$feed.= "</rss>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0. |
* |
* @see http://backend.userland.com/rss |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class RSSCreator20 extends RSSCreator091 { |
# {{{ __construct |
function RSSCreator20() { |
parent::_setRSSVersion("2.0"); |
} |
# }}} |
} |
/** |
* PIECreator01 is a FeedCreator that implements the emerging PIE specification, |
* as in http://intertwingly.net/wiki/pie/Syntax. |
* |
* @deprecated |
* @since 1.3 |
* @author Scott Reynen <scott@randomchaos.com> and Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class PIECreator01 extends FeedCreator { |
# {{{ createFeed |
function createFeed() { |
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
$feed.= "<feed version=\"0.1\" xmlns=\"http://example.com/newformat#\">\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n"; |
$feed.= " <subtitle>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</subtitle>\n"; |
$feed.= " <link>".$this->link."</link>\n"; |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <entry>\n"; |
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n"; |
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n"; |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n"; |
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n"; |
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n"; |
$feed.= " <id>".$this->items[$i]->guid."</id>\n"; |
if ($this->items[$i]->author!="") { |
$feed.= " <author>\n"; |
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n"; |
if ($this->items[$i]->authorEmail!="") { |
$feed.= " <email>".$this->items[$i]->authorEmail."</email>\n"; |
} |
$feed.=" </author>\n"; |
} |
$feed.= " <content type=\"text/html\" xml:lang=\"en-us\">\n"; |
$feed.= " <div xmlns=\"http://www.w3.org/1999/xhtml\">".$this->items[$i]->description."</div>\n"; |
$feed.= " </content>\n"; |
$feed.= " </entry>\n"; |
} |
$feed.= "</feed>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* AtomCreator03 is a FeedCreator that implements the atom specification, |
* as in http://www.intertwingly.net/wiki/pie/FrontPage. |
* Please note that just by using AtomCreator03 you won't automatically |
* produce valid atom files. For example, you have to specify either an editor |
* for the feed or an author for every single feed item. |
* |
* Some elements have not been implemented yet. These are (incomplete list): |
* author URL, item author's email and URL, item contents, alternate links, |
* other link content types than text/html. Some of them may be created with |
* AtomCreator03::additionalElements. |
* |
* @see FeedCreator#additionalElements |
* @since 1.6 |
* @author Kai Blankenhorn <kaib@bitfolge.de>, Scott Reynen <scott@randomchaos.com> |
*/ |
class AtomCreator03 extends FeedCreator { |
# {{{ __construct |
function AtomCreator03() { |
$this->contentType = "application/atom+xml"; |
} |
# }}} |
# {{{ createFeed |
function createFeed() { |
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<feed version=\"0.3\" xmlns=\"http://purl.org/atom/ns#\""; |
if ($this->language!="") { |
$feed.= " xml:lang:\"".$this->language."\""; |
} |
$feed.= ">\n"; |
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n"; |
$feed.= " <tagline>".htmlspecialchars($this->description)."</tagline>\n"; |
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n"; |
$feed.= " <id>".$this->link."</id>\n"; |
$now = new FeedDate(); |
$feed.= " <modified>".htmlspecialchars($now->iso8601())."</modified>\n"; |
if ($this->editor!="") { |
$feed.= " <author>\n"; |
$feed.= " <name>".$this->editor."</name>\n"; |
if ($this->editorEmail!="") { |
$feed.= " <email>".$this->editorEmail."</email>\n"; |
} |
$feed.= " </author>\n"; |
} |
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n"; |
$feed.= $this->_createAdditionalElements($this->additionalElements, " "); |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <entry>\n"; |
$feed.= " <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n"; |
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n"; |
if ($this->items[$i]->date=="") { |
$this->items[$i]->date = time(); |
} |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n"; |
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n"; |
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n"; |
$feed.= " <id>".$this->items[$i]->link."</id>\n"; |
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); |
if ($this->items[$i]->author!="") { |
$feed.= " <author>\n"; |
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n"; |
$feed.= " </author>\n"; |
} |
if ($this->items[$i]->description!="") { |
$feed.= " <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n"; |
} |
$feed.= " </entry>\n"; |
} |
$feed.= "</feed>\n"; |
return $feed; |
} |
# }}} |
} |
/** |
* MBOXCreator is a FeedCreator that implements the mbox format |
* as described in http://www.qmail.org/man/man5/mbox.html |
* |
* @since 1.3 |
* @author Kai Blankenhorn <kaib@bitfolge.de> |
*/ |
class MBOXCreator extends FeedCreator { |
# {{{ __construct |
function MBOXCreator() { |
$this->contentType = "text/plain"; |
} |
# }}} |
# {{{ qp_enc |
function qp_enc($input = "", $line_max = 76) { |
$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); |
$lines = preg_split("/(?:\r\n|\r|\n)/", $input); |
$eol = "\r\n"; |
$escape = "="; |
$output = ""; |
while( list(, $line) = each($lines) ) { |
//$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary |
$linlen = strlen($line); |
$newline = ""; |
for($i = 0; $i < $linlen; $i++) { |
$c = substr($line, $i, 1); |
$dec = ord($c); |
if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only |
$c = "=20"; |
} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required |
$h2 = floor($dec/16); $h1 = floor($dec%16); |
$c = $escape.$hex["$h2"].$hex["$h1"]; |
} |
if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted |
$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay |
$newline = ""; |
} |
$newline .= $c; |
} // end of for |
$output .= $newline.$eol; |
} |
return trim($output); |
} |
# }}} |
# {{{ createFeed |
/** |
* Builds the MBOX contents. |
* @return string the feed's complete text |
*/ |
function createFeed() { |
global $config; |
for ($i=0;$i<count($this->items);$i++) { |
if ($this->items[$i]->author!="") { |
$from = $this->items[$i]->author; |
} else { |
$from = $this->title; |
} |
$itemDate = new FeedDate($this->items[$i]->date); |
$feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n"; |
$feed.= "Content-Type: text/plain;\n"; |
$feed.= " charset=\"".$config->outputEnc."\"\n"; |
$feed.= "Content-Transfer-Encoding: quoted-printable\n"; |
$feed.= "Content-Type: text/plain\n"; |
$feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n"; |
$feed.= "Date: ".$itemDate->rfc822()."\n"; |
$feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n"; |
$feed.= "\n"; |
$body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description)); |
$feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body); |
$feed.= "\n"; |
$feed.= "\n"; |
} |
return $feed; |
} |
# }}} |
# {{{ _generateFilename |
/** |
* Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types. |
* @return string the feed cache filename |
* @since 1.4 |
* @access private |
*/ |
function _generateFilename() { |
$fileInfo = pathinfo($_SERVER["PHP_SELF"]); |
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox"; |
} |
# }}} |
} |
/** |
* OPMLCreator is a FeedCreator that implements OPML 1.0. |
* |
* @see http://opml.scripting.com/spec |
* @author Dirk Clemens, Kai Blankenhorn |
* @since 1.5 |
*/ |
class OPMLCreator extends FeedCreator { |
# {{{ createFeed |
function createFeed() { |
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
$feed.= $this->_createGeneratorComment(); |
$feed.= "<opml xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"; |
$feed.= " <head>\n"; |
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n"; |
if ($this->pubDate!="") { |
$date = new FeedDate($this->pubDate); |
$feed.= " <dateCreated>".$date->rfc822()."</dateCreated>\n"; |
} |
if ($this->lastBuildDate!="") { |
$date = new FeedDate($this->lastBuildDate); |
$feed.= " <dateModified>".$date->rfc822()."</dateModified>\n"; |
} |
if ($this->editor!="") { |
$feed.= " <ownerName>".$this->editor."</ownerName>\n"; |
} |
if ($this->editorEmail!="") { |
$feed.= " <ownerEmail>".$this->editorEmail."</ownerEmail>\n"; |
} |
$feed.= " </head>\n"; |
$feed.= " <body>\n"; |
for ($i=0;$i<count($this->items);$i++) { |
$feed.= " <outline type=\"rss\" "; |
$title = htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," "))); |
$feed.= " title=\"".$title."\""; |
$feed.= " text=\"".$title."\""; |
//$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\""; |
$feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\""; |
$feed.= "/>\n"; |
} |
$feed.= " </body>\n"; |
$feed.= "</opml>\n"; |
return $feed; |
} |
# }}} |
} |
?> |
/WebSVN/include/php5compat.inc |
---|
2,12 → 2,6 |
if (version_compare(phpversion(), '5.0.0', 'lt')) { |
# XXX: these includes shouldn't be necessary! |
require_once 'include/configclass.inc'; |
$config = new Config; |
require_once 'include/config.inc'; |
require_once($config->getPHPCompatFile()); |
// Configure necessary functions here |
$funcs = array( |
'stripos', |
16,6 → 10,13 |
// End configuration |
// JB: PHP_Compat expects to be able to find functions by including |
// 'PHP/Compat/Functions/functionname.php', but that hardcoded path |
// isn't relative to the standard include path '.' (it's instead relative |
// to './include'). So rather than hack PHP_Compat to work, just add |
// './include' to our include path. |
ini_set("include_path", ini_get("include_path").$config->pathSeparator."./include"); |
foreach ($funcs as $fn) { |
if (PHP_Compat::loadFunction($fn) != true) |
error_log('Could not load function `'.$fn.'\' as required by PHP Compat.'); |
/WebSVN/include/setup.inc |
---|
1,430 → 1,454 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// setup.inc |
// |
// Global setup |
// --- DON'T CHANGE THIS FILE --- |
// |
// User changes should be done in config.ini |
// Include the configuration class |
require_once 'include/configclass.inc'; |
require_once 'include/svnlook.inc'; |
// Define the language array |
$lang = array(); |
$langNames = array(); |
// Include a default language file. |
require 'languages/english.inc'; |
// Set up locwebsvnhttp |
// Note: we will use nothing in MultiViews mode so that the URLs use the root |
// directory by default. |
if (empty($locwebsvnhttp)) |
$locwebsvnhttp = defined('WSVN_MULTIVIEWS') ? '' : '.'; |
if (empty($locwebsvnreal)) |
$locwebsvnreal = '.'; |
$vars['locwebsvnhttp'] = $locwebsvnhttp; |
// Make sure that the input locale is set up correctly |
setlocale(LC_ALL, ''); |
// Create the config |
$config = new Config; |
// Set up the default character encodings |
if (function_exists('iconv_get_encoding')) |
{ |
$config->setInputEncoding(iconv_get_encoding('input_encoding')); |
} |
// {{{ Content-Type's |
// Set up the default content-type extension handling |
$contentType = array ( |
'.dwg' => 'application/acad', // AutoCAD Drawing files |
'.DWG' => 'application/acad', // AutoCAD Drawing files |
'.arj' => 'application/arj', // |
'.ccAD' => 'application/clariscad', // ClarisCAD files |
'.DRW' => 'application/drafting', // MATRA Prelude drafting |
'.dxf' => 'application/dxf', // DXF (AutoCAD) |
'.DXF' => 'application/dxf', // DXF (AutoCAD) |
'.xl' => 'application/excel', // Microsoft Excel |
'.unv' => 'application/i-deas', //SDRC I-DEAS files |
'.UNV' => 'application/i-deas', //SDRC I-DEAS files |
'.igs' => 'application/iges', // IGES graphics format |
'.iges' => 'application/iges', // IGES graphics format |
'.IGS' => 'application/iges', // IGES graphics format |
'.IGES' => 'application/iges', // IGES graphics format |
'.hqx' => 'application/mac-binhex40', // Macintosh BinHex format |
'.word' => 'application/msword', // Microsoft Word |
'.w6w' => 'application/msword', // Microsoft Word |
'.doc' => 'application/msword', // Microsoft Word |
'.wri' => 'application/mswrite', // Microsoft Write |
'.bin' => 'application/octet-stream', // Uninterpreted binary |
'.exe' => 'application/x-msdownload', // Windows EXE |
'.EXE' => 'application/x-msdownload', // Windows EXE |
'.oda' => 'application/oda', // |
'.pdf' => 'application/pdf', // PDF (Adobe Acrobat) |
'.ai' => 'application/postscript', // PostScript |
'.PS' => 'application/postscript', // PostScript |
'.ps' => 'application/postscript', // PostScript |
'.eps' => 'application/postscript', // PostScript |
'.prt' => 'application/pro_eng', // PTC Pro/ENGINEER |
'.PRT' => 'application/pro_eng', // PTC Pro/ENGINEER |
'.part' => 'application/pro_eng', // PTC Pro/ENGINEER |
'.rtf' => 'application/rtf', // Rich Text Format |
'.set' => 'application/set', // SET (French CAD standard) |
'.SET' => 'application/set', // SET (French CAD standard) |
'.stl' => 'application/sla', // Stereolithography |
'.STL' => 'application/sla', // Stereolithography |
'.SOL' => 'application/solids', // MATRA Prelude Solids |
'.stp' => 'application/STEP', // ISO-10303 STEP data files |
'.STP' => 'application/STEP', // ISO-10303 STEP data files |
'.step' => 'application/STEP', // ISO-10303 STEP data files |
'.STEP' => 'application/STEP', // ISO-10303 STEP data files |
'.vda' => 'application/vda', // VDA-FS Surface data |
'.VDA' => 'application/vda', // VDA-FS Surface data |
'.dir' => 'application/x-director', // Macromedia Director |
'.dcr' => 'application/x-director', // Macromedia Director |
'.dxr' => 'application/x-director', // Macromedia Director |
'.mif' => 'application/x-mif', // FrameMaker MIF Format |
'.csh' => 'application/x-csh', // C-shell script |
'.dvi' => 'application/x-dvi', // TeX DVI |
'.gz' => 'application/x-gzip', // GNU Zip |
'.gzip' => 'application/x-gzip', // GNU Zip |
'.hdf' => 'application/x-hdf', // ncSA HDF Data File |
'.latex' => 'application/x-latex', // LaTeX source |
'.nc' => 'application/x-netcdf', // Unidata netCDF |
'.cdf' => 'application/x-netcdf', // Unidata netCDF |
'.sit' => 'application/x-stuffit', // Stiffut Archive |
'.tcl' => 'application/x-tcl', // TCL script |
'.texinfo' => 'application/x-texinfo', // Texinfo (Emacs) |
'.texi' => 'application/x-texinfo', // Texinfo (Emacs) |
'.t' => 'application/x-troff', // Troff |
'.tr' => 'application/x-troff', // Troff |
'.roff' => 'application/x-troff', // Troff |
'.man' => 'application/x-troff-man', // Troff with MAN macros |
'.me' => 'application/x-troff-me', // Troff with ME macros |
'.ms' => 'application/x-troff-ms', // Troff with MS macros |
'.src' => 'application/x-wais-source', // WAIS source |
'.bcpio' => 'application/x-bcpio', // Old binary CPIO |
'.cpio' => 'application/x-cpio', // POSIX CPIO |
'.gtar' => 'application/x-gtar', // GNU tar |
'.shar' => 'application/x-shar', // Shell archive |
'.sv4cpio' => 'application/x-sv4cpio', // SVR4 CPIO |
'.sv4crc' => 'application/x-sv4crc', // SVR4 CPIO with CRC |
'.tar' => 'application/x-tar', // 4.3BSD tar format |
'.ustar' => 'application/x-ustar', // POSIX tar format |
'.hlp' => 'application/x-winhelp', // Windows Help |
'.zip' => 'application/zip', // ZIP archive |
'.au' => 'audio/basic', // Basic audio (usually m-law) |
'.snd' => 'audio/basic', // Basic audio (usually m-law) |
'.aif' => 'audio/x-aiff', // AIFF audio |
'.aiff' => 'audio/x-aiff', // AIFF audio |
'.aifc' => 'audio/x-aiff', // AIFF audio |
'.ra' => 'audio/x-pn-realaudio', // RealAudio |
'.ram' => 'audio/x-pn-realaudio', // RealAudio |
'.rpm' => 'audio/x-pn-realaudio-plugin', // RealAudio (plug-in) |
'.wav' => 'audio/x-wav', // Windows WAVE audio |
'.mp3' => 'audio/x-mp3', // MP3 files |
'.gif' => 'image/gif', // gif image |
'.ief' => 'image/ief', // Image Exchange Format |
'.jpg' => 'image/jpeg', // JPEG image |
'.JPG' => 'image/jpeg', // JPEG image |
'.JPE' => 'image/jpeg', // JPEG image |
'.jpe' => 'image/jpeg', // JPEG image |
'.JPEG' => 'image/jpeg', // JPEG image |
'.jpeg' => 'image/jpeg', // JPEG image |
'.pict' => 'image/pict', // Macintosh PICT |
'.tiff' => 'image/tiff', // TIFF image |
'.tif' => 'image/tiff', // TIFF image |
'.ras' => 'image/x-cmu-raster', // CMU raster |
'.pnm' => 'image/x-portable-anymap', // PBM Anymap format |
'.pbm' => 'image/x-portable-bitmap', // PBM Bitmap format |
'.pgm' => 'image/x-portable-graymap', // PBM Graymap format |
'.ppm' => 'image/x-portable-pixmap', // PBM Pixmap format |
'.rgb' => 'image/x-rgb', // RGB Image |
'.xbm' => 'image/x-xbitmap', // X Bitmap |
'.xpm' => 'image/x-xpixmap', // X Pixmap |
'.xwd' => 'image/x-xwindowdump', // X Windows dump (xwd) format |
'.zip' => 'multipart/x-zip', // PKZIP Archive |
'.gzip' => 'multipart/x-gzip', // GNU ZIP Archive |
'.mpeg' => 'video/mpeg', // MPEG video |
'.mpg' => 'video/mpeg', // MPEG video |
'.MPG' => 'video/mpeg', // MPEG video |
'.MPE' => 'video/mpeg', // MPEG video |
'.mpe' => 'video/mpeg', // MPEG video |
'.MPEG' => 'video/mpeg', // MPEG video |
'.mpeg' => 'video/mpeg', // MPEG video |
'.qt' => 'video/quicktime', // QuickTime Video |
'.mov' => 'video/quicktime', // QuickTime Video |
'.avi' => 'video/msvideo', // Microsoft Windows Video |
'.movie' => 'video/x-sgi-movie', // SGI Movieplayer format |
'.wrl' => 'x-world/x-vrml' // VRML Worlds |
); |
// }}} |
// {{{ Enscript file extensions |
// List of extensions recognised by enscript. |
$extEnscript = array |
( |
'.ada' => 'ada', |
'.adb' => 'ada', |
'.ads' => 'ada', |
'.awk' => 'awk', |
'.c' => 'c', |
'.c++' => 'cpp', |
'.cc' => 'cpp', |
'.cpp' => 'cpp', |
'.csh' => 'csh', |
'.cxx' => 'cpp', |
'.diff' => 'diffu', |
'.dpr' => 'delphi', |
'.el' => 'elisp', |
'.eps' => 'postscript', |
'.f' => 'fortran', |
'.for' => 'fortran', |
'.gs' => 'haskell', |
'.h' => 'c', |
'.hpp' => 'cpp', |
'.hs' => 'haskell', |
'.htm' => 'html', |
'.html' => 'html', |
'.idl' => 'idl', |
'.java' => 'java', |
'.js' => 'javascript', |
'.lgs' => 'haskell', |
'.lhs' => 'haskell', |
'.m' => 'objc', |
'.m4' => 'm4', |
'.man' => 'nroff', |
'.nr' => 'nroff', |
'.p' => 'pascal', |
'.pas' => 'delphi', |
'.patch' => 'diffu', |
'.pkg' => 'sql', |
'.pl' => 'perl', |
'.pm' => 'perl', |
'.pp' => 'pascal', |
'.ps' => 'postscript', |
'.s' => 'asm', |
'.scheme' => 'scheme', |
'.scm' => 'scheme', |
'.scr' => 'synopsys', |
'.sh' => 'sh', |
'.shtml' => 'html', |
'.sql' => 'sql', |
'.st' => 'states', |
'.syn' => 'synopsys', |
'.synth' => 'synopsys', |
'.tcl' => 'tcl', |
'.tex' => 'tex', |
'.texi' => 'tex', |
'.texinfo' => 'tex', |
'.v' => 'verilog', |
'.vba' => 'vba', |
'.vh' => 'verilog', |
'.vhd' => 'vhdl', |
'.vhdl' => 'vhdl', |
'.py' => 'python', |
// The following are handled internally by WebSVN, since there's no |
// support for them in Enscript |
'.php' => 'php', |
'.phtml' => 'php', |
'.php3' => 'php', |
'.inc' => 'php' |
); |
// }}} |
// Default 'zipped' array |
$zipped = array (); |
// Get the user's personalised config |
require_once 'config.inc'; |
// Set up the version info |
initSvnVersion($major,$minor); |
// Get the language choice as defained as the default by config.inc |
$user_defaultLang = $lang['LANGUAGENAME']; |
// Override this with the user choice if there is one, and memorise the setting |
// as a cookie (since we don't have user accounts, we can't store the setting |
// anywhere else). We try to memorise a permanant cookie and a per session cookie |
// in case the user's disabled permanant ones. |
if (!empty($_REQUEST['langchoice'])) |
{ |
$user_defaultLang = $_REQUEST['langchoice']; |
setcookie('storedlang', $_REQUEST['langchoice'], time()+(3600*24*356*10), '/'); |
setcookie('storedsesslang', $_REQUEST['langchoice']); |
} |
else // Try to read an existing cookie if there is one |
{ |
if (!empty($_COOKIE['storedlang'])) $user_defaultLang = $_COOKIE['storedlang']; |
else if (!empty($_COOKIE['storedsesslang'])) $user_defaultLang = $_COOKIE['storedsesslang']; |
} |
$user_defaultFile = ''; |
if ($handle = opendir('languages')) |
{ |
// Read the language name for each language. |
while (false !== ($file = readdir($handle))) |
{ |
if ($file{0} != '.' && !is_dir('languages'.DIRECTORY_SEPARATOR.$file)) |
{ |
$lang['LANGUAGENAME'] = ''; |
require 'languages/'.$file; |
if ($lang['LANGUAGENAME'] != '') |
{ |
$langNames[] = $lang['LANGUAGENAME']; |
if ($lang['LANGUAGENAME'] == $user_defaultLang) |
$user_defaultFile = $file; |
} |
} |
} |
closedir($handle); |
// XXX: this shouldn't be necessary |
// ^ i.e. just require english.inc, then the desired language |
// Reload english to get untranslated strings |
require 'languages/english.inc'; |
// Reload the default language |
if (!empty($user_defaultFile)) |
require 'languages/'.$user_defaultFile; |
$url = getParameterisedSelfUrl(true); |
$vars["lang_form"] = "<form action=\"$url\" method=\"post\" id=\"langform\">"; |
$vars["lang_select"] = "<select name=\"langchoice\" onchange=\"javascript:this.form.submit();\">"; |
reset($langNames); |
foreach ($langNames as $name) |
{ |
$sel = ""; |
if ($name == $user_defaultLang) $sel = "selected"; |
$vars["lang_select"] .= "<option value=\"$name\" $sel>$name</option>"; |
} |
$vars["lang_select"] .= "</select>"; |
$vars["lang_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\">"; |
$vars["lang_endform"] = "</form>"; |
} |
// Set up headers |
header("Content-type: text/html; charset=".$config->outputEnc); |
// Make sure that the user has set up a repository |
$reps = $config->getRepositories(); |
if (empty($reps[0])) |
{ |
echo $lang["SUPPLYREP"]; |
exit; |
} |
// Override the rep parameter with the repository name if it's available |
$repname = @$_REQUEST["repname"]; |
if (isset($repname)) |
{ |
$rep = $config->findRepository($repname); |
} |
else |
$rep = $reps[0]; |
// Retrieve other standard parameters |
# due to possible XSS exploit, we need to clean up path first |
$path = !empty($_REQUEST['path']) ? $_REQUEST['path'] : null; |
$vars['safepath'] = htmlentities($path); |
$rev = (int)@$_REQUEST["rev"]; |
$showchanged = (@$_REQUEST["sc"] == 1)?1:0; |
// Function to create the project selection HTML form |
function createProjectSelectionForm() |
{ |
global $config, $vars, $rep, $lang, $showchanged; |
$url = $config->getURL(-1, "", "form"); |
$vars["projects_form"] = "<form action=\"$url\" method=\"post\" id=\"projectform\">"; |
$reps = $config->getRepositories(); |
$vars["projects_select"] = "<select name=\"repname\" onchange=\"javascript:this.form.submit();\">"; |
foreach ($reps as $trep) |
{ |
if ($trep->hasReadAccess("/", true)) |
{ |
if ($rep->getDisplayName() == $trep->getDisplayName()) |
$sel = "selected"; |
else |
$sel = ""; |
$vars["projects_select"] .= "<option value=\"".$trep->getDisplayName()."\" $sel>".$trep->getDisplayName()."</option>"; |
} |
} |
$vars["projects_select"] .= "</select>"; |
$vars["projects_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\" />"; |
$vars["projects_endform"] = "<input type=\"hidden\" name=\"selectproj\" value=\"1\" /><input type=\"hidden\" name=\"op\" value=\"form\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>"; |
} |
// Create the form if we're not in MultiViews. Otherwise wsvn must create the form once the current project has |
// been found |
if (!$config->multiViews) |
{ |
createProjectSelectionForm(); |
} |
if ($rep) |
{ |
$vars["allowdownload"] = $rep->getAllowDownload(); |
$vars["repname"] = $rep->getDisplayName(); |
} |
// As of version 1.70 the output encoding is forced to be UTF-8, since this is the output |
// encoding returned by svn log --xml. This is good, since we are no longer reliant on PHP's |
// rudimentary conversions. |
$vars["charset"] = "UTF-8"; |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// setup.inc |
// |
// Global setup |
// --- DON'T CHANGE THIS FILE --- |
// |
// User changes should be done in config.ini |
// Include the configuration class |
require_once 'include/configclass.inc'; |
// Create the config |
$config = new Config; |
// Set up the default character encodings |
if (function_exists('iconv_get_encoding')) |
{ |
$config->setInputEncoding(iconv_get_encoding('input_encoding')); |
} |
// Define the language array |
$lang = array(); |
$langNames = array(); |
// Set up locwebsvnhttp |
// Note: we will use nothing in MultiViews mode so that the URLs use the root |
// directory by default. |
if (empty($locwebsvnhttp)) |
$locwebsvnhttp = defined('WSVN_MULTIVIEWS') ? '' : '.'; |
if (empty($locwebsvnreal)) |
$locwebsvnreal = '.'; |
$vars['locwebsvnhttp'] = $locwebsvnhttp; |
// Include a default language file (must go before config.inc) |
require 'languages/english.inc'; |
// Get the user's personalised config (requires the locwebsvnhttp stuff above) |
require_once 'config.inc'; |
// Load PHP_Compat if we're going to use it. This needs to be done after including config.inc (which contains |
// the setting) but before svnlook.inc (which requires util.inc, which contains PHP4-incompatible functions) |
if ($config->isPHPCompatEnabled()) { |
require_once($config->getPHPCompatFile()); |
require_once 'include/php5compat.inc'; |
} |
require_once 'include/svnlook.inc'; |
// Make sure that the input locale is set up correctly |
setlocale(LC_ALL, ''); |
// {{{ Content-Type's |
// Set up the default content-type extension handling |
$contentType = array ( |
'.dwg' => 'application/acad', // AutoCAD Drawing files |
'.DWG' => 'application/acad', // AutoCAD Drawing files |
'.arj' => 'application/arj', // |
'.ccAD' => 'application/clariscad', // ClarisCAD files |
'.DRW' => 'application/drafting', // MATRA Prelude drafting |
'.dxf' => 'application/dxf', // DXF (AutoCAD) |
'.DXF' => 'application/dxf', // DXF (AutoCAD) |
'.xl' => 'application/excel', // Microsoft Excel |
'.unv' => 'application/i-deas', //SDRC I-DEAS files |
'.UNV' => 'application/i-deas', //SDRC I-DEAS files |
'.igs' => 'application/iges', // IGES graphics format |
'.iges' => 'application/iges', // IGES graphics format |
'.IGS' => 'application/iges', // IGES graphics format |
'.IGES' => 'application/iges', // IGES graphics format |
'.hqx' => 'application/mac-binhex40', // Macintosh BinHex format |
'.word' => 'application/msword', // Microsoft Word |
'.w6w' => 'application/msword', // Microsoft Word |
'.doc' => 'application/msword', // Microsoft Word |
'.wri' => 'application/mswrite', // Microsoft Write |
'.bin' => 'application/octet-stream', // Uninterpreted binary |
'.exe' => 'application/x-msdownload', // Windows EXE |
'.EXE' => 'application/x-msdownload', // Windows EXE |
'.oda' => 'application/oda', // |
'.pdf' => 'application/pdf', // PDF (Adobe Acrobat) |
'.ai' => 'application/postscript', // PostScript |
'.PS' => 'application/postscript', // PostScript |
'.ps' => 'application/postscript', // PostScript |
'.eps' => 'application/postscript', // PostScript |
'.prt' => 'application/pro_eng', // PTC Pro/ENGINEER |
'.PRT' => 'application/pro_eng', // PTC Pro/ENGINEER |
'.part' => 'application/pro_eng', // PTC Pro/ENGINEER |
'.rtf' => 'application/rtf', // Rich Text Format |
'.set' => 'application/set', // SET (French CAD standard) |
'.SET' => 'application/set', // SET (French CAD standard) |
'.stl' => 'application/sla', // Stereolithography |
'.STL' => 'application/sla', // Stereolithography |
'.SOL' => 'application/solids', // MATRA Prelude Solids |
'.stp' => 'application/STEP', // ISO-10303 STEP data files |
'.STP' => 'application/STEP', // ISO-10303 STEP data files |
'.step' => 'application/STEP', // ISO-10303 STEP data files |
'.STEP' => 'application/STEP', // ISO-10303 STEP data files |
'.vda' => 'application/vda', // VDA-FS Surface data |
'.VDA' => 'application/vda', // VDA-FS Surface data |
'.dir' => 'application/x-director', // Macromedia Director |
'.dcr' => 'application/x-director', // Macromedia Director |
'.dxr' => 'application/x-director', // Macromedia Director |
'.mif' => 'application/x-mif', // FrameMaker MIF Format |
'.csh' => 'application/x-csh', // C-shell script |
'.dvi' => 'application/x-dvi', // TeX DVI |
'.gz' => 'application/x-gzip', // GNU Zip |
'.gzip' => 'application/x-gzip', // GNU Zip |
'.hdf' => 'application/x-hdf', // ncSA HDF Data File |
'.latex' => 'application/x-latex', // LaTeX source |
'.nc' => 'application/x-netcdf', // Unidata netCDF |
'.cdf' => 'application/x-netcdf', // Unidata netCDF |
'.sit' => 'application/x-stuffit', // Stiffut Archive |
'.tcl' => 'application/x-tcl', // TCL script |
'.texinfo' => 'application/x-texinfo', // Texinfo (Emacs) |
'.texi' => 'application/x-texinfo', // Texinfo (Emacs) |
'.t' => 'application/x-troff', // Troff |
'.tr' => 'application/x-troff', // Troff |
'.roff' => 'application/x-troff', // Troff |
'.man' => 'application/x-troff-man', // Troff with MAN macros |
'.me' => 'application/x-troff-me', // Troff with ME macros |
'.ms' => 'application/x-troff-ms', // Troff with MS macros |
'.src' => 'application/x-wais-source', // WAIS source |
'.bcpio' => 'application/x-bcpio', // Old binary CPIO |
'.cpio' => 'application/x-cpio', // POSIX CPIO |
'.gtar' => 'application/x-gtar', // GNU tar |
'.shar' => 'application/x-shar', // Shell archive |
'.sv4cpio' => 'application/x-sv4cpio', // SVR4 CPIO |
'.sv4crc' => 'application/x-sv4crc', // SVR4 CPIO with CRC |
'.tar' => 'application/x-tar', // 4.3BSD tar format |
'.ustar' => 'application/x-ustar', // POSIX tar format |
'.hlp' => 'application/x-winhelp', // Windows Help |
'.zip' => 'application/zip', // ZIP archive |
'.au' => 'audio/basic', // Basic audio (usually m-law) |
'.snd' => 'audio/basic', // Basic audio (usually m-law) |
'.aif' => 'audio/x-aiff', // AIFF audio |
'.aiff' => 'audio/x-aiff', // AIFF audio |
'.aifc' => 'audio/x-aiff', // AIFF audio |
'.ra' => 'audio/x-pn-realaudio', // RealAudio |
'.ram' => 'audio/x-pn-realaudio', // RealAudio |
'.rpm' => 'audio/x-pn-realaudio-plugin', // RealAudio (plug-in) |
'.wav' => 'audio/x-wav', // Windows WAVE audio |
'.mp3' => 'audio/x-mp3', // MP3 files |
'.gif' => 'image/gif', // gif image |
'.ief' => 'image/ief', // Image Exchange Format |
'.jpg' => 'image/jpeg', // JPEG image |
'.JPG' => 'image/jpeg', // JPEG image |
'.JPE' => 'image/jpeg', // JPEG image |
'.jpe' => 'image/jpeg', // JPEG image |
'.JPEG' => 'image/jpeg', // JPEG image |
'.jpeg' => 'image/jpeg', // JPEG image |
'.pict' => 'image/pict', // Macintosh PICT |
'.tiff' => 'image/tiff', // TIFF image |
'.tif' => 'image/tiff', // TIFF image |
'.ras' => 'image/x-cmu-raster', // CMU raster |
'.pnm' => 'image/x-portable-anymap', // PBM Anymap format |
'.pbm' => 'image/x-portable-bitmap', // PBM Bitmap format |
'.pgm' => 'image/x-portable-graymap', // PBM Graymap format |
'.ppm' => 'image/x-portable-pixmap', // PBM Pixmap format |
'.rgb' => 'image/x-rgb', // RGB Image |
'.xbm' => 'image/x-xbitmap', // X Bitmap |
'.xpm' => 'image/x-xpixmap', // X Pixmap |
'.xwd' => 'image/x-xwindowdump', // X Windows dump (xwd) format |
'.zip' => 'multipart/x-zip', // PKZIP Archive |
'.gzip' => 'multipart/x-gzip', // GNU ZIP Archive |
'.mpeg' => 'video/mpeg', // MPEG video |
'.mpg' => 'video/mpeg', // MPEG video |
'.MPG' => 'video/mpeg', // MPEG video |
'.MPE' => 'video/mpeg', // MPEG video |
'.mpe' => 'video/mpeg', // MPEG video |
'.MPEG' => 'video/mpeg', // MPEG video |
'.mpeg' => 'video/mpeg', // MPEG video |
'.qt' => 'video/quicktime', // QuickTime Video |
'.mov' => 'video/quicktime', // QuickTime Video |
'.avi' => 'video/msvideo', // Microsoft Windows Video |
'.movie' => 'video/x-sgi-movie', // SGI Movieplayer format |
'.wrl' => 'x-world/x-vrml', // VRML Worlds |
'.odt' => 'application/vnd.oasis.opendocument.text', // OpenDocument Text |
'.ott' => 'application/vnd.oasis.opendocument.text-template', // OpenDocument Text Template |
'.ods' => 'application/vnd.oasis.opendocument.spreadsheet', // OpenDocument Spreadsheet |
'.ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', // OpenDocument Spreadsheet Template |
'.odp' => 'application/vnd.oasis.opendocument.presentation', // OpenDocument Presentation |
'.otp' => 'application/vnd.oasis.opendocument.presentation-template', // OpenDocument Presentation Template |
'.odg' => 'application/vnd.oasis.opendocument.graphics', // OpenDocument Drawing |
'.otg' => 'application/vnd.oasis.opendocument.graphics-template', // OpenDocument Drawing Template |
'.odc' => 'application/vnd.oasis.opendocument.chart', // OpenDocument Chart |
'.otc' => 'application/vnd.oasis.opendocument.chart-template', // OpenDocument Chart Template |
'.odf' => 'application/vnd.oasis.opendocument.formula', // OpenDocument Formula |
'.otf' => 'application/vnd.oasis.opendocument.formula-template', // OpenDocument Formula Template |
'.odi' => 'application/vnd.oasis.opendocument.image', // OpenDocument Image |
'.oti' => 'application/vnd.oasis.opendocument.image-template', // OpenDocument Image Template |
'.odm' => 'application/vnd.oasis.opendocument.text-master', // OpenDocument Master Document |
'.oth' => 'application/vnd.oasis.opendocument.text-web', // HTML Document Template |
'.odb' => 'application/vnd.oasis.opendocument.database', // OpenDocument Database |
); |
// }}} |
// {{{ Enscript file extensions |
// List of extensions recognised by enscript. |
$extEnscript = array |
( |
'.ada' => 'ada', |
'.adb' => 'ada', |
'.ads' => 'ada', |
'.awk' => 'awk', |
'.c' => 'c', |
'.c++' => 'cpp', |
'.cc' => 'cpp', |
'.cpp' => 'cpp', |
'.csh' => 'csh', |
'.cxx' => 'cpp', |
'.diff' => 'diffu', |
'.dpr' => 'delphi', |
'.el' => 'elisp', |
'.eps' => 'postscript', |
'.f' => 'fortran', |
'.for' => 'fortran', |
'.gs' => 'haskell', |
'.h' => 'c', |
'.hpp' => 'cpp', |
'.hs' => 'haskell', |
'.htm' => 'html', |
'.html' => 'html', |
'.idl' => 'idl', |
'.java' => 'java', |
'.js' => 'javascript', |
'.lgs' => 'haskell', |
'.lhs' => 'haskell', |
'.m' => 'objc', |
'.m4' => 'm4', |
'.man' => 'nroff', |
'.nr' => 'nroff', |
'.p' => 'pascal', |
'.pas' => 'delphi', |
'.patch' => 'diffu', |
'.pkg' => 'sql', |
'.pl' => 'perl', |
'.pm' => 'perl', |
'.pp' => 'pascal', |
'.ps' => 'postscript', |
'.s' => 'asm', |
'.scheme' => 'scheme', |
'.scm' => 'scheme', |
'.scr' => 'synopsys', |
'.sh' => 'sh', |
'.shtml' => 'html', |
'.sql' => 'sql', |
'.st' => 'states', |
'.syn' => 'synopsys', |
'.synth' => 'synopsys', |
'.tcl' => 'tcl', |
'.tex' => 'tex', |
'.texi' => 'tex', |
'.texinfo' => 'tex', |
'.v' => 'verilog', |
'.vba' => 'vba', |
'.vh' => 'verilog', |
'.vhd' => 'vhdl', |
'.vhdl' => 'vhdl', |
'.py' => 'python', |
// The following are handled internally by WebSVN, since there's no |
// support for them in Enscript |
'.php' => 'php', |
'.phtml' => 'php', |
'.php3' => 'php', |
'.inc' => 'php' |
); |
// }}} |
// Default 'zipped' array |
$zipped = array (); |
// Set up the version info |
initSvnVersion($major,$minor); |
// Get the language choice as defained as the default by config.inc |
$user_defaultLang = $lang['LANGUAGENAME']; |
// Override this with the user choice if there is one, and memorise the setting |
// as a cookie (since we don't have user accounts, we can't store the setting |
// anywhere else). We try to memorise a permanant cookie and a per session cookie |
// in case the user's disabled permanant ones. |
if (!empty($_REQUEST['langchoice'])) |
{ |
$user_defaultLang = $_REQUEST['langchoice']; |
setcookie('storedlang', $_REQUEST['langchoice'], time()+(3600*24*356*10), '/'); |
setcookie('storedsesslang', $_REQUEST['langchoice']); |
} |
else // Try to read an existing cookie if there is one |
{ |
if (!empty($_COOKIE['storedlang'])) $user_defaultLang = $_COOKIE['storedlang']; |
else if (!empty($_COOKIE['storedsesslang'])) $user_defaultLang = $_COOKIE['storedsesslang']; |
} |
$user_defaultFile = ''; |
if ($handle = opendir('languages')) |
{ |
// Read the language name for each language. |
while (false !== ($file = readdir($handle))) |
{ |
if ($file{0} != '.' && !is_dir('languages'.DIRECTORY_SEPARATOR.$file)) |
{ |
$lang['LANGUAGENAME'] = ''; |
require 'languages/'.$file; |
if ($lang['LANGUAGENAME'] != '') |
{ |
$langNames[] = $lang['LANGUAGENAME']; |
if ($lang['LANGUAGENAME'] == $user_defaultLang) |
$user_defaultFile = $file; |
} |
} |
} |
closedir($handle); |
// XXX: this shouldn't be necessary |
// ^ i.e. just require english.inc, then the desired language |
// Reload english to get untranslated strings |
require 'languages/english.inc'; |
// Reload the default language |
if (!empty($user_defaultFile)) |
require 'languages/'.$user_defaultFile; |
$url = getParameterisedSelfUrl(true); |
$vars["lang_form"] = "<form action=\"$url\" method=\"post\" id=\"langform\">"; |
$vars["lang_select"] = "<select name=\"langchoice\" onchange=\"javascript:this.form.submit();\">"; |
reset($langNames); |
foreach ($langNames as $name) |
{ |
$sel = ""; |
if ($name == $user_defaultLang) $sel = "selected"; |
$vars["lang_select"] .= "<option value=\"$name\" $sel>$name</option>"; |
} |
$vars["lang_select"] .= "</select>"; |
$vars["lang_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\">"; |
$vars["lang_endform"] = "</form>"; |
} |
// Set up headers |
header("Content-type: text/html; charset=".$config->outputEnc); |
// Make sure that the user has set up a repository |
$reps = $config->getRepositories(); |
if (empty($reps[0])) |
{ |
echo $lang["SUPPLYREP"]; |
exit; |
} |
// Override the rep parameter with the repository name if it's available |
$repname = @$_REQUEST["repname"]; |
if (isset($repname)) |
{ |
$rep = $config->findRepository($repname); |
} |
else |
$rep = $reps[0]; |
// Retrieve other standard parameters |
# due to possible XSS exploit, we need to clean up path first |
$path = !empty($_REQUEST['path']) ? $_REQUEST['path'] : null; |
$vars['safepath'] = htmlentities($path); |
$rev = (int)@$_REQUEST["rev"]; |
$showchanged = (@$_REQUEST["sc"] == 1)?1:0; |
// Function to create the project selection HTML form |
function createProjectSelectionForm() |
{ |
global $config, $vars, $rep, $lang, $showchanged; |
$url = $config->getURL(-1, "", "form"); |
$vars["projects_form"] = "<form action=\"$url\" method=\"post\" id=\"projectform\">"; |
$reps = $config->getRepositories(); |
$vars["projects_select"] = "<select name=\"repname\" onchange=\"javascript:this.form.submit();\">"; |
foreach ($reps as $trep) |
{ |
if ($trep->hasReadAccess("/", true)) |
{ |
if ($rep->getDisplayName() == $trep->getDisplayName()) |
$sel = "selected"; |
else |
$sel = ""; |
$vars["projects_select"] .= "<option value=\"".$trep->getDisplayName()."\" $sel>".$trep->getDisplayName()."</option>"; |
} |
} |
$vars["projects_select"] .= "</select>"; |
$vars["projects_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\" />"; |
$vars["projects_endform"] = "<input type=\"hidden\" name=\"selectproj\" value=\"1\" /><input type=\"hidden\" name=\"op\" value=\"form\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>"; |
} |
// Create the form if we're not in MultiViews. Otherwise wsvn must create the form once the current project has |
// been found |
if (!$config->multiViews) |
{ |
createProjectSelectionForm(); |
} |
if ($rep) |
{ |
$vars["allowdownload"] = $rep->getAllowDownload(); |
$vars["repname"] = $rep->getDisplayName(); |
} |
// As of version 1.70 the output encoding is forced to be UTF-8, since this is the output |
// encoding returned by svn log --xml. This is good, since we are no longer reliant on PHP's |
// rudimentary conversions. |
$vars["charset"] = "UTF-8"; |
?> |
/WebSVN/include/svnlook.inc |
---|
1,770 → 1,769 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// svn-look.inc |
// |
// Svn bindings |
// |
// These binding currently use the svn command line to achieve their goal. Once a proper |
// SWIG binding has been produced for PHP, there'll be an option to use that instead. |
require_once("include/utils.inc"); |
// {{{ Classes for retaining log information --- |
$debugxml = false; |
Class SVNMod |
{ |
var $action = ''; |
var $copyfrom = ''; |
var $copyrev = ''; |
var $path = ''; |
} |
Class SVNLogEntry |
{ |
var $rev = 1; |
var $author = ''; |
var $date = ''; |
var $committime; |
var $age = ''; |
var $msg = ''; |
var $path = ''; |
var $mods; |
var $curMod; |
} |
Class SVNLog |
{ |
var $entries; // Array of entries |
var $curEntry; // Current entry |
var $path = ''; // Temporary variable used to trace path history |
// findEntry |
// |
// Return the entry for a given revision |
function findEntry($rev) |
{ |
foreach ($this->entries as $index => $entry) |
{ |
if ($entry->rev == $rev) |
return $index; |
} |
} |
} |
// }}} |
// {{{ XML parsing functions--- |
$curLog = 0; |
$curTag = ''; |
// {{{ startElement |
function startElement($parser, $name, $attrs) |
{ |
global $curLog, $curTag, $debugxml; |
switch ($name) |
{ |
case "LOGENTRY": |
if ($debugxml) print "Creating new log entry\n"; |
$curLog->curEntry = new SVNLogEntry; |
$curLog->curEntry->mods = array(); |
$curLog->curEntry->path = $curLog->path; |
if (sizeof($attrs)) |
{ |
while (list($k, $v) = each($attrs)) |
{ |
switch ($k) |
{ |
case "REVISION": |
if ($debugxml) print "Revision $v\n"; |
$curLog->curEntry->rev = $v; |
break; |
} |
} |
} |
break; |
case "PATH": |
if ($debugxml) print "Creating new path\n"; |
$curLog->curEntry->curMod = new SVNMod; |
if (sizeof($attrs)) |
{ |
while (list($k, $v) = each($attrs)) |
{ |
switch ($k) |
{ |
case "ACTION": |
if ($debugxml) print "Action $v\n"; |
$curLog->curEntry->curMod->action = $v; |
break; |
case "COPYFROM-PATH": |
if ($debugxml) print "Copy from: $v\n"; |
$curLog->curEntry->curMod->copyfrom = $v; |
break; |
case "COPYFROM-REV": |
$curLog->curEntry->curMod->copyrev = $v; |
break; |
} |
} |
} |
$curTag = $name; |
break; |
default: |
$curTag = $name; |
break; |
} |
} |
// }}} |
// {{{ endElement |
function endElement($parser, $name) |
{ |
global $curLog, $debugxml, $curTag; |
switch ($name) |
{ |
case "LOGENTRY": |
if ($debugxml) print "Ending new log entry\n"; |
$curLog->entries[] = $curLog->curEntry; |
break; |
case "PATH": |
if ($debugxml) print "Ending path\n"; |
$curLog->curEntry->mods[] = $curLog->curEntry->curMod; |
break; |
case "MSG": |
$curLog->curEntry->msg = trim($curLog->curEntry->msg); |
if ($debugxml) print "Completed msg = '".$curLog->curEntry->msg."'\n"; |
break; |
} |
$curTag = ""; |
} |
// }}} |
// {{{ characterData |
function characterData($parser, $data) |
{ |
global $curLog, $curTag, $lang, $debugxml; |
switch ($curTag) |
{ |
case "AUTHOR": |
if ($debugxml) print "Author: $data\n"; |
if (empty($data)) return; |
$curLog->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
break; |
case "DATE": |
if ($debugxml) print "Date: $data\n"; |
$data = trim($data); |
if (empty($data)) return; |
sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s); |
$mo = substr("00".$mo, -2); |
$d = substr("00".$d, -2); |
$h = substr("00".$h, -2); |
$m = substr("00".$m, -2); |
$s = substr("00".$s, -2); |
$curLog->curEntry->date = "$y-$mo-$d $h:$m:$s GMT"; |
$committime = strtotime($curLog->curEntry->date); |
$curLog->curEntry->committime = $committime; |
$curtime = time(); |
// Get the number of seconds since the commit |
$agesecs = $curtime - $committime; |
if ($agesecs < 0) $agesecs = 0; |
$curLog->curEntry->age = datetimeFormatDuration($agesecs, true, true); |
break; |
case "MSG": |
if ($debugxml) print "Msg: '$data'\n"; |
$curLog->curEntry->msg .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
break; |
case "PATH": |
if ($debugxml) print "Path name: '$data'\n"; |
$data = trim($data); |
if (empty($data)) return; |
$curLog->curEntry->curMod->path .= $data; |
// The XML returned when a file is renamed/branched in inconsistant. In the case |
// of a branch, the path information doesn't include the leafname. In the case of |
// a rename, it does. Ludicrous. |
if (!empty($curLog->path)) |
{ |
$pos = strrpos($curLog->path, "/"); |
$curpath = substr($curLog->path, 0, $pos); |
$leafname = substr($curLog->path, $pos + 1); |
} |
else |
{ |
$curpath = ""; |
$leafname = ""; |
} |
if ($curLog->curEntry->curMod->action == "A") |
{ |
if ($debugxml) print "Examining added path '".$curLog->curEntry->curMod->copyfrom."' - Current path = '$curpath', leafname = '$leafname'\n"; |
if ($data == $curLog->path) // For directories and renames |
{ |
if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."'\n"; |
$curLog->path = $curLog->curEntry->curMod->copyfrom; |
} |
else if ($data == $curpath || $data == $curpath."/") // Logs of files that have moved due to branching |
{ |
if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."/$leafname'\n"; |
$curLog->path = $curLog->curEntry->curMod->copyfrom."/$leafname"; |
} |
} |
break; |
} |
} |
// }}} |
// }}} |
// Function returns true if the give entry in a directory tree is at the top level |
function _topLevel($entry) |
{ |
// To be at top level, there must be one space before the entry |
return (strlen($entry) > 1 && $entry{0} == " " && $entry{1} != " "); |
} |
// Function to sort two given directory entries. Directories go at the top |
function _dirSort($e1, $e2) |
{ |
$isDir1 = $e1{strlen($e1) - 1} == "/"; |
$isDir2 = $e2{strlen($e2) - 1} == "/"; |
if ($isDir1 && !$isDir2) return -1; |
if ($isDir2 && !$isDir1) return 1; |
return strnatcasecmp($e1, $e2); |
} |
// Return the revision string to pass to a command |
function _revStr($rev) |
{ |
if ($rev > 0) |
return "-r $rev"; |
else |
return ""; |
} |
// {{{ encodePath |
// Function to encode a URL without encoding the /'s |
function encodePath($uri) |
{ |
global $config; |
$uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri); |
$parts = explode('/', $uri); |
for ($i = 0; $i < count($parts); $i++) |
- |
- if ( function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding")) |
- { |
- $parts[$i] = mb_convert_encoding($parts[$i], "UTF-8", mb_detect_encoding($parts[$i])); |
- } |
- |
- $parts[$i] = rawurlencode($parts[$i]); |
- } |
- |
- $uri = implode('/', $parts); |
- |
- // Quick hack. Subversion seems to have a bug surrounding the use of %3A instead of : |
- |
- $uri = str_replace("%3A" ,":", $uri); |
- |
- // Correct for Window share names |
- if ( $config->serverIsWindows==true ) |
- { |
- if ( substr($uri, 0,2)=="//" ) |
- $uri="\\".substr($uri, 2, strlen($uri)); |
- } |
- |
- return $uri; |
-} |
- |
-// }}} |
- |
-// The SVNRepository Class |
- |
-Class SVNRepository |
-{ |
- var $repConfig; |
- |
- function SVNRepository($repConfig) |
- { |
- $this->repConfig = $repConfig; |
- } |
- |
- // {{{ dirContents |
- |
- function dirContents($path, $rev = 0) |
- { |
- global $config, $locwebsvnreal; |
- |
- $revstr = _revStr($rev); |
- |
- $tree = array(); |
- |
- if ($rev == 0) |
- { |
- $headlog = $this->getLog("/", "", "", true, 1); |
- $rev = $headlog->entries[0]->rev; |
- } |
- |
- $path = encodepath($this->repConfig->path.$path); |
- $output = runCommand($config->svn." list $revstr ".$this->repConfig->svnParams().quote($path), true); |
- |
- foreach ($output as $entry) |
- { |
- if ($entry != "") |
- $tree[] = $entry; |
- } |
- |
- // Sort the entries into alphabetical order with the directories at the top of the list |
- usort($tree, "_dirSort"); |
- |
- return $tree; |
- } |
- |
- // }}} |
- |
- // {{{ highlightLine |
- // |
- // Distill line-spanning syntax highlighting so that each line can stand alone |
- // (when invoking on the first line, $attributes should be an empty array) |
- // Invoked to make sure all open syntax highlighting tags (<font>, <i>, <b>, etc.) |
- // are closed at the end of each line and re-opened on the next line |
- |
- function highlightLine($line, &$attributes) |
- { |
- $hline = ""; |
- |
- // Apply any highlighting in effect from the previous line |
- foreach($attributes as $attr) |
- { |
- $hline.=$attr['text']; |
- } |
- |
- // append the new line |
- $hline.=$line; |
- |
- // update attributes |
- for ($line = strstr($line, "<"); $line; $line = strstr(substr($line,1), "<")) |
- { |
- // if this closes a tag, remove most recent corresponding opener |
- if (substr($line,1,1) == "/") |
- { |
- $tagNamLen = strcspn($line, "> \t", 2); |
- $tagNam = substr($line,2,$tagNamLen); |
- foreach(array_reverse(array_keys($attributes)) as $k) |
- { |
- if ($attributes[$k]['tag'] == $tagNam) |
- { |
- unset($attributes[$k]); |
- break; |
- } |
- } |
- } |
- else |
- // if this opens a tag, add it to the list |
- { |
- $tagNamLen = strcspn($line, "> \t", 1); |
- $tagNam = substr($line,1,$tagNamLen); |
- $tagLen = strcspn($line, ">") + 1; |
- $attributes[] = array('tag' => $tagNam, 'text' => substr($line,0,$tagLen)); |
- } |
- } |
- |
- // close any still-open tags |
- foreach(array_reverse($attributes) as $attr) |
- { |
- $hline.="</".$attr['tag'].">"; |
- } |
- |
- return($hline); |
- } |
- |
- // }}} |
- |
- // {{{ getFileContents |
- // |
- // Dump the content of a file to the given filename |
- |
- function getFileContents($path, $filename, $rev = 0, $pipe = "", $perLineHighlighting = false) |
- { |
- global $config, $extEnscript; |
- |
- $revstr = _revStr($rev); |
- |
- // If there's no filename, we'll just deliver the contents as it is to the user |
- if ($filename == "") |
- { |
- $path = encodepath($this->repConfig->path.$path); |
- passthru(quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." $pipe", false)); |
- return; |
- } |
- |
- // Get the file contents info |
- |
- $ext = strrchr($path, "."); |
- $l = @$extEnscript[$ext]; |
- |
- if ($l == "php") |
- { |
- // Output the file to the filename |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false); |
- @exec($cmd); |
- |
- // Get the file as a string (memory hogging, but we have no other options) |
- $content = highlight_file($filename, true); |
- |
- // Destroy the previous version, and replace it with the highlighted version |
- $f = fopen($filename, "w"); |
- if ($f) |
- { |
- // The highlight file function doesn't deal with line endings very nicely at all. We'll have to do it |
- // by hand. |
- |
- // Remove the first line generated by highlight() |
- $pos = strpos($content, "\n"); |
- $content = substr($content, $pos+1); |
- |
- $content = explode("<br />", $content); |
- |
- if ($perLineHighlighting) |
- { |
- // If we need each line independently highlighted (e.g. for diff or blame) |
- // hen we'll need to filter the output of the highlighter |
- // to make sure tags like <font>, <i> or <b> don't span lines |
- |
- // $attributes is used to remember what highlighting attributes |
- // are in effect from one line to the next |
- $attributes = array(); // start with no attributes in effect |
- |
- foreach ($content as $line) |
- { |
- fputs($f, $this->highlightLine(rtrim($line),$attributes)."\n"); |
- } |
- } |
- else |
- { |
- foreach ($content as $line) |
- { |
- fputs($f, rtrim($line)."\n"); |
- } |
- } |
- |
- fclose($f); |
- } |
- } |
- else |
- { |
- if ($config->useEnscript) |
- { |
- // Get the files, feed it through enscript, then remove the enscript headers using sed |
- // |
- // Note that the sec command returns only the part of the file between <PRE> and </PRE>. |
- // It's complicated because it's designed not to return those lines themselves. |
- |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ". |
- $config->enscript." --language=html ". |
- ($l ? "--color --pretty-print=$l" : "")." -o - | ". |
- $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $filename", false); |
- @exec($cmd); |
- } |
- else |
- { |
- $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->repConfig->path.$path)); |
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false); |
- @exec($cmd); |
- } |
- } |
- } |
- |
- // }}} |
- |
- // {{{ listFileContents |
- // |
- // Print the contents of a file without filling up Apache's memory |
- |
- function listFileContents($path, $rev = 0) |
- { |
- global $config, $extEnscript; |
- |
- $revstr = _revStr($rev); |
- $pre = false; |
- |
- // Get the file contents info |
- |
- $ext = strrchr($path, "."); |
- $l = @$extEnscript[$ext]; |
- |
- // Deal with php highlighting internally |
- if ($l == "php") |
- { |
- $tmp = tempnam("temp", "wsvn"); |
- |
- // Output the file to a temporary file |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $tmp", false); |
- @exec($cmd); |
- highlight_file($tmp); |
- unlink($tmp); |
- } |
- else |
- { |
- if ($config->useEnscript) |
- { |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ". |
- $config->enscript." --language=html ". |
- ($l ? "--color --pretty-print=$l" : "")." -o - | ". |
- $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote." 2>&1", false); |
- |
- if (!($result = popen($cmd, "r"))) |
- return; |
- } |
- else |
- { |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." 2>&1", false); |
- |
- if (!($result = popen($cmd, "r"))) |
- return; |
- |
- $pre = true; |
- } |
- |
- if ($pre) |
- echo "<PRE>"; |
- |
- while (!feof($result)) |
- { |
- $line = fgets($result, 1024); |
- if ($pre) $line = replaceEntities($line, $this->repConfig); |
- |
- print hardspace($line); |
- } |
- |
- if ($pre) |
- echo "</PRE>"; |
- |
- pclose($result); |
- } |
- } |
- |
- // }}} |
- |
- // {{{ getBlameDetails |
- // |
- // Dump the blame content of a file to the given filename |
- |
- function getBlameDetails($path, $filename, $rev = 0) |
- { |
- global $config; |
- |
- $revstr = _revStr($rev); |
- |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." blame $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false); |
- |
- @exec($cmd); |
- } |
- |
- // }}} |
- |
- // {{{ getProperty |
- |
- function getProperty($path, $property, $rev = 0) |
- { |
- global $config; |
- |
- $revstr = _revStr($rev); |
- |
- $path = encodepath($this->repConfig->path.$path); |
- $ret = runCommand($config->svn." propget $property $revstr ".$this->repConfig->svnParams().quote($path), true); |
- |
- // Remove the surplus newline |
- if (count($ret)) |
- unset($ret[count($ret) - 1]); |
- |
- return implode("\n", $ret); |
- } |
- |
- // }}} |
- |
- // {{{ exportDirectory |
- // |
- // Exports the directory to the given location |
- |
- function exportDirectory($path, $filename, $rev = 0) |
- { |
- global $config; |
- |
- $revstr = _revStr($rev); |
- |
- $path = encodepath($this->repConfig->path.$path); |
- $cmd = quoteCommand($config->svn." export $revstr ".$this->repConfig->svnParams().quote($path)." ".quote($filename), false); |
- |
- @exec($cmd); |
- } |
- |
- // }}} |
- |
- // {{{ getLog |
- |
- function getLog($path, $brev = "", $erev = 1, $quiet = false, $limit = 2) |
- { |
- global $config, $curLog, $vars, $lang; |
- |
- $xml_parser = xml_parser_create("UTF-8"); |
- xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true); |
- xml_set_element_handler($xml_parser, "startElement", "endElement"); |
- xml_set_character_data_handler($xml_parser, "characterData"); |
- |
- // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove |
- // the trailing slash from the path for comparison purposes |
- |
- if ($path{strlen($path) - 1} == "/" && $path != "/") |
- $path = substr($path, 0, -1); |
- |
- $curLog = new SVNLog; |
- $curLog->entries = array(); |
- $curLog->path = $path; |
- |
- $revStr = ""; |
- |
- if ($brev && $erev) |
- $revStr = "-r$brev:$erev"; |
- else if ($brev) |
- $revStr = "-r$brev:1"; |
- |
- if (($config->subversionMajorVersion > 1 || $config->subversionMinorVersion >=2) && $limit != 0) |
- $revStr .= " --limit $limit"; |
- |
- // Get the log info |
- $path = encodepath($this->repConfig->path.$path); |
- $info = "--verbose"; |
- if ($quiet) |
- $info = "--quiet"; |
- |
- $cmd = quoteCommand($config->svn." log --xml $info $revStr ".$this->repConfig->svnParams().quote($path), false); |
- |
- if ($handle = popen($cmd, "r")) |
- { |
- $firstline = true; |
- while (!feof($handle)) |
- { |
- $line = fgets($handle); |
- if (!xml_parse($xml_parser, $line, feof($handle))) |
- { |
- if (xml_get_error_code($xml_parser) != 5) |
- { |
- die(sprintf("XML error: %s (%d) at line %d column %d byte %d<br>cmd: %s<nr>", |
- xml_error_string(xml_get_error_code($xml_parser)), |
- xml_get_error_code($xml_parser), |
- xml_get_current_line_number($xml_parser), |
- xml_get_current_column_number($xml_parser), |
- xml_get_current_byte_index($xml_parser), |
- $cmd)); |
- } |
- else |
- { |
- $vars["error"] = $lang["UNKNOWNREVISION"]; |
- return 0; |
- } |
- } |
- } |
- pclose($handle); |
- } |
- |
- xml_parser_free($xml_parser); |
- return $curLog; |
- } |
- |
- // }}} |
- |
-} |
- |
-// {{{ initSvnVersion |
- |
-function initSvnVersion(&$major, &$minor) |
-{ |
- global $config; |
- |
- $ret = runCommand($config->svn_noparams." --version", false); |
- |
- if (preg_match("~([0-9]?)\.([0-9]?)\.([0-9]?)~",$ret[0],$matches)) |
- { |
- $major = $matches[1]; |
- $minor = $matches[2]; |
- } |
- |
- $config->setSubversionMajorVersion($major); |
- $config->setSubversionMinorVersion($minor); |
-} |
- |
-// }}} |
- |
-?> |
+<?php |
+# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
+ |
+// WebSVN - Subversion repository viewing via the web using PHP |
+// Copyright © 2004-2006 Tim Armes, Matt Sicker |
+// |
+// This program is free software; you can redistribute it and/or modify |
+// it under the terms of the GNU General Public License as published by |
+// the Free Software Foundation; either version 2 of the License, or |
+// (at your option) any later version. |
+// |
+// This program is distributed in the hope that it will be useful, |
+// but WITHOUT ANY WARRANTY; without even the implied warranty of |
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+// GNU General Public License for more details. |
+// |
+// You should have received a copy of the GNU General Public License |
+// along with this program; if not, write to the Free Software |
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+// |
+// -- |
+// |
+// svn-look.inc |
+// |
+// Svn bindings |
+// |
+// These binding currently use the svn command line to achieve their goal. Once a proper |
+// SWIG binding has been produced for PHP, there'll be an option to use that instead. |
+ |
+require_once("include/utils.inc"); |
+ |
+// {{{ Classes for retaining log information --- |
+ |
+$debugxml = false; |
+ |
+Class SVNMod |
+{ |
+ var $action = ''; |
+ var $copyfrom = ''; |
+ var $copyrev = ''; |
+ var $path = ''; |
+} |
+ |
+Class SVNLogEntry |
+{ |
+ var $rev = 1; |
+ var $author = ''; |
+ var $date = ''; |
+ var $committime; |
+ var $age = ''; |
+ var $msg = ''; |
+ var $path = ''; |
+ |
+ var $mods; |
+ var $curMod; |
+} |
+ |
+Class SVNLog |
+{ |
+ var $entries; // Array of entries |
+ var $curEntry; // Current entry |
+ |
+ var $path = ''; // Temporary variable used to trace path history |
+ |
+ // findEntry |
+ // |
+ // Return the entry for a given revision |
+ |
+ function findEntry($rev) |
+ { |
+ foreach ($this->entries as $index => $entry) |
+ { |
+ if ($entry->rev == $rev) |
+ return $index; |
+ |
+ } |
+ } |
+} |
+ |
+// }}} |
+ |
+// {{{ XML parsing functions--- |
+ |
+$curLog = 0; |
+$curTag = ''; |
+ |
+// {{{ startElement |
+ |
+function startElement($parser, $name, $attrs) |
+{ |
+ global $curLog, $curTag, $debugxml; |
+ |
+ switch ($name) |
+ { |
+ case "LOGENTRY": |
+ if ($debugxml) print "Creating new log entry\n"; |
+ $curLog->curEntry = new SVNLogEntry; |
+ $curLog->curEntry->mods = array(); |
+ |
+ $curLog->curEntry->path = $curLog->path; |
+ |
+ if (sizeof($attrs)) |
+ { |
+ while (list($k, $v) = each($attrs)) |
+ { |
+ switch ($k) |
+ { |
+ case "REVISION": |
+ if ($debugxml) print "Revision $v\n"; |
+ $curLog->curEntry->rev = $v; |
+ break; |
+ } |
+ } |
+ } |
+ break; |
+ |
+ case "PATH": |
+ if ($debugxml) print "Creating new path\n"; |
+ $curLog->curEntry->curMod = new SVNMod; |
+ |
+ if (sizeof($attrs)) |
+ { |
+ while (list($k, $v) = each($attrs)) |
+ { |
+ switch ($k) |
+ { |
+ case "ACTION": |
+ if ($debugxml) print "Action $v\n"; |
+ $curLog->curEntry->curMod->action = $v; |
+ break; |
+ |
+ case "COPYFROM-PATH": |
+ if ($debugxml) print "Copy from: $v\n"; |
+ $curLog->curEntry->curMod->copyfrom = $v; |
+ break; |
+ |
+ case "COPYFROM-REV": |
+ $curLog->curEntry->curMod->copyrev = $v; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ $curTag = $name; |
+ break; |
+ |
+ default: |
+ $curTag = $name; |
+ break; |
+ } |
+} |
+ |
+// }}} |
+ |
+// {{{ endElement |
+ |
+function endElement($parser, $name) |
+{ |
+ global $curLog, $debugxml, $curTag; |
+ |
+ switch ($name) |
+ { |
+ case "LOGENTRY": |
+ if ($debugxml) print "Ending new log entry\n"; |
+ $curLog->entries[] = $curLog->curEntry; |
+ break; |
+ |
+ case "PATH": |
+ if ($debugxml) print "Ending path\n"; |
+ $curLog->curEntry->mods[] = $curLog->curEntry->curMod; |
+ break; |
+ |
+ case "MSG": |
+ $curLog->curEntry->msg = trim($curLog->curEntry->msg); |
+ if ($debugxml) print "Completed msg = '".$curLog->curEntry->msg."'\n"; |
+ break; |
+ } |
+ |
+ $curTag = ""; |
+} |
+ |
+// }}} |
+ |
+// {{{ characterData |
+ |
+function characterData($parser, $data) |
+{ |
+ global $curLog, $curTag, $lang, $debugxml; |
+ |
+ switch ($curTag) |
+ { |
+ case "AUTHOR": |
+ if ($debugxml) print "Author: $data\n"; |
+ if (empty($data)) return; |
+ $curLog->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
+ break; |
+ |
+ case "DATE": |
+ if ($debugxml) print "Date: $data\n"; |
+ $data = trim($data); |
+ if (empty($data)) return; |
+ |
+ sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s); |
+ |
+ $mo = substr("00".$mo, -2); |
+ $d = substr("00".$d, -2); |
+ $h = substr("00".$h, -2); |
+ $m = substr("00".$m, -2); |
+ $s = substr("00".$s, -2); |
+ |
+ $curLog->curEntry->date = "$y-$mo-$d $h:$m:$s GMT"; |
+ |
+ $committime = strtotime($curLog->curEntry->date); |
+ $curLog->curEntry->committime = $committime; |
+ $curtime = time(); |
+ |
+ // Get the number of seconds since the commit |
+ $agesecs = $curtime - $committime; |
+ if ($agesecs < 0) $agesecs = 0; |
+ |
+ $curLog->curEntry->age = datetimeFormatDuration($agesecs, true, true); |
+ |
+ break; |
+ |
+ case "MSG": |
+ if ($debugxml) print "Msg: '$data'\n"; |
+ $curLog->curEntry->msg .= htmlentities($data, ENT_COMPAT, "UTF-8"); |
+ break; |
+ |
+ case "PATH": |
+ if ($debugxml) print "Path name: '$data'\n"; |
+ $data = trim($data); |
+ if (empty($data)) return; |
+ |
+ $curLog->curEntry->curMod->path .= $data; |
+ |
+ // The XML returned when a file is renamed/branched in inconsistant. In the case |
+ // of a branch, the path information doesn't include the leafname. In the case of |
+ // a rename, it does. Ludicrous. |
+ |
+ if (!empty($curLog->path)) |
+ { |
+ $pos = strrpos($curLog->path, "/"); |
+ $curpath = substr($curLog->path, 0, $pos); |
+ $leafname = substr($curLog->path, $pos + 1); |
+ } |
+ else |
+ { |
+ $curpath = ""; |
+ $leafname = ""; |
+ } |
+ |
+ if ($curLog->curEntry->curMod->action == "A") |
+ { |
+ if ($debugxml) print "Examining added path '".$curLog->curEntry->curMod->copyfrom."' - Current path = '$curpath', leafname = '$leafname'\n"; |
+ if ($data == $curLog->path) // For directories and renames |
+ { |
+ if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."'\n"; |
+ $curLog->path = $curLog->curEntry->curMod->copyfrom; |
+ } |
+ else if ($data == $curpath || $data == $curpath."/") // Logs of files that have moved due to branching |
+ { |
+ if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."/$leafname'\n"; |
+ $curLog->path = $curLog->curEntry->curMod->copyfrom."/$leafname"; |
+ } |
+ } |
+ break; |
+ } |
+} |
+ |
+// }}} |
+ |
+// }}} |
+ |
+// Function returns true if the give entry in a directory tree is at the top level |
+ |
+function _topLevel($entry) |
+{ |
+ // To be at top level, there must be one space before the entry |
+ return (strlen($entry) > 1 && $entry{0} == " " && $entry{1} != " "); |
+} |
+ |
+// Function to sort two given directory entries. Directories go at the top |
+ |
+function _dirSort($e1, $e2) |
+{ |
+ $isDir1 = $e1{strlen($e1) - 1} == "/"; |
+ $isDir2 = $e2{strlen($e2) - 1} == "/"; |
+ |
+ if ($isDir1 && !$isDir2) return -1; |
+ if ($isDir2 && !$isDir1) return 1; |
+ |
+ return strnatcasecmp($e1, $e2); |
+} |
+ |
+// Return the revision string to pass to a command |
+ |
+function _revStr($rev) |
+{ |
+ if ($rev > 0) |
+ return "-r $rev"; |
+ else |
+ return ""; |
+} |
+ |
+// {{{ encodePath |
+ |
+// Function to encode a URL without encoding the /'s |
+ |
+function encodePath($uri) |
+{ |
+ global $config; |
+ |
+ $uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri); |
+ |
+ $parts = explode('/', $uri); |
+ for ($i = 0; $i < count($parts); $i++) |
+ { |
+ if ( function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding")) |
+ { |
+ $parts[$i] = mb_convert_encoding($parts[$i], "UTF-8", mb_detect_encoding($parts[$i])); |
+ } |
+ |
+ $parts[$i] = rawurlencode($parts[$i]); |
+ } |
+ |
+ $uri = implode('/', $parts); |
+ |
+ // Quick hack. Subversion seems to have a bug surrounding the use of %3A instead of : |
+ |
+ $uri = str_replace("%3A" ,":", $uri); |
+ |
+ // Correct for Window share names |
+ if ( $config->serverIsWindows==true ) |
+ { |
+ if ( substr($uri, 0,2)=="//" ) |
+ $uri="\\".substr($uri, 2, strlen($uri)); |
+ } |
+ |
+ return $uri; |
+} |
+ |
+// }}} |
+ |
+// The SVNRepository Class |
+ |
+Class SVNRepository |
+{ |
+ var $repConfig; |
+ |
+ function SVNRepository($repConfig) |
+ { |
+ $this->repConfig = $repConfig; |
+ } |
+ |
+ // {{{ dirContents |
+ |
+ function dirContents($path, $rev = 0) |
+ { |
+ global $config, $locwebsvnreal; |
+ |
+ $revstr = _revStr($rev); |
+ |
+ $tree = array(); |
+ |
+ if ($rev == 0) |
+ { |
+ $headlog = $this->getLog("/", "", "", true, 1); |
+ $rev = $headlog->entries[0]->rev; |
+ } |
+ |
+ $path = encodepath($this->repConfig->path.$path); |
+ $output = runCommand($config->svn." list $revstr ".$this->repConfig->svnParams().quote($path), true); |
+ |
+ foreach ($output as $entry) |
+ { |
+ if ($entry != "") |
+ $tree[] = $entry; |
+ } |
+ |
+ // Sort the entries into alphabetical order with the directories at the top of the list |
+ usort($tree, "_dirSort"); |
+ |
+ return $tree; |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ highlightLine |
+ // |
+ // Distill line-spanning syntax highlighting so that each line can stand alone |
+ // (when invoking on the first line, $attributes should be an empty array) |
+ // Invoked to make sure all open syntax highlighting tags (<font>, <i>, <b>, etc.) |
+ // are closed at the end of each line and re-opened on the next line |
+ |
+ function highlightLine($line, &$attributes) |
+ { |
+ $hline = ""; |
+ |
+ // Apply any highlighting in effect from the previous line |
+ foreach($attributes as $attr) |
+ { |
+ $hline.=$attr['text']; |
+ } |
+ |
+ // append the new line |
+ $hline.=$line; |
+ |
+ // update attributes |
+ for ($line = strstr($line, "<"); $line; $line = strstr(substr($line,1), "<")) |
+ { |
+ // if this closes a tag, remove most recent corresponding opener |
+ if (substr($line,1,1) == "/") |
+ { |
+ $tagNamLen = strcspn($line, "> \t", 2); |
+ $tagNam = substr($line,2,$tagNamLen); |
+ foreach(array_reverse(array_keys($attributes)) as $k) |
+ { |
+ if ($attributes[$k]['tag'] == $tagNam) |
+ { |
+ unset($attributes[$k]); |
+ break; |
+ } |
+ } |
+ } |
+ else |
+ // if this opens a tag, add it to the list |
+ { |
+ $tagNamLen = strcspn($line, "> \t", 1); |
+ $tagNam = substr($line,1,$tagNamLen); |
+ $tagLen = strcspn($line, ">") + 1; |
+ $attributes[] = array('tag' => $tagNam, 'text' => substr($line,0,$tagLen)); |
+ } |
+ } |
+ |
+ // close any still-open tags |
+ foreach(array_reverse($attributes) as $attr) |
+ { |
+ $hline.="</".$attr['tag'].">"; |
+ } |
+ |
+ return($hline); |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ getFileContents |
+ // |
+ // Dump the content of a file to the given filename |
+ |
+ function getFileContents($path, $filename, $rev = 0, $pipe = "", $perLineHighlighting = false) |
+ { |
+ global $config, $extEnscript; |
+ |
+ $revstr = _revStr($rev); |
+ |
+ // If there's no filename, we'll just deliver the contents as it is to the user |
+ if ($filename == "") |
+ { |
+ $path = encodepath($this->repConfig->path.$path); |
+ passthru(quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." $pipe", false)); |
+ return; |
+ } |
+ |
+ // Get the file contents info |
+ |
+ $ext = strrchr($path, "."); |
+ $l = @$extEnscript[$ext]; |
+ |
+ if ($l == "php") |
+ { |
+ // Output the file to the filename |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false); |
+ @exec($cmd); |
+ |
+ // Get the file as a string (memory hogging, but we have no other options) |
+ $content = highlight_file($filename, true); |
+ |
+ // Destroy the previous version, and replace it with the highlighted version |
+ $f = fopen($filename, "w"); |
+ if ($f) |
+ { |
+ // The highlight file function doesn't deal with line endings very nicely at all. We'll have to do it |
+ // by hand. |
+ |
+ // Remove the first line generated by highlight() |
+ $pos = strpos($content, "\n"); |
+ $content = substr($content, $pos+1); |
+ |
+ $content = explode("<br />", $content); |
+ |
+ if ($perLineHighlighting) |
+ { |
+ // If we need each line independently highlighted (e.g. for diff or blame) |
+ // hen we'll need to filter the output of the highlighter |
+ // to make sure tags like <font>, <i> or <b> don't span lines |
+ |
+ // $attributes is used to remember what highlighting attributes |
+ // are in effect from one line to the next |
+ $attributes = array(); // start with no attributes in effect |
+ |
+ foreach ($content as $line) |
+ { |
+ fputs($f, $this->highlightLine(rtrim($line),$attributes)."\n"); |
+ } |
+ } |
+ else |
+ { |
+ foreach ($content as $line) |
+ { |
+ fputs($f, rtrim($line)."\n"); |
+ } |
+ } |
+ |
+ fclose($f); |
+ } |
+ } |
+ else |
+ { |
+ if ($config->useEnscript) |
+ { |
+ // Get the files, feed it through enscript, then remove the enscript headers using sed |
+ // |
+ // Note that the sec command returns only the part of the file between <PRE> and </PRE>. |
+ // It's complicated because it's designed not to return those lines themselves. |
+ |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ". |
+ $config->enscript." --language=html ". |
+ ($l ? "--color --pretty-print=$l" : "")." -o - | ". |
+ $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $filename", false); |
+ @exec($cmd); |
+ } |
+ else |
+ { |
+ $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->repConfig->path.$path)); |
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false); |
+ @exec($cmd); |
+ } |
+ } |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ listFileContents |
+ // |
+ // Print the contents of a file without filling up Apache's memory |
+ |
+ function listFileContents($path, $rev = 0) |
+ { |
+ global $config, $extEnscript; |
+ |
+ $revstr = _revStr($rev); |
+ $pre = false; |
+ |
+ // Get the file contents info |
+ |
+ $ext = strrchr($path, "."); |
+ $l = @$extEnscript[$ext]; |
+ |
+ // Deal with php highlighting internally |
+ if ($l == "php") |
+ { |
+ $tmp = tempnam("temp", "wsvn"); |
+ |
+ // Output the file to a temporary file |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $tmp", false); |
+ @exec($cmd); |
+ highlight_file($tmp); |
+ unlink($tmp); |
+ } |
+ else |
+ { |
+ if ($config->useEnscript) |
+ { |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ". |
+ $config->enscript." --language=html ". |
+ ($l ? "--color --pretty-print=$l" : "")." -o - | ". |
+ $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote." 2>&1", false); |
+ |
+ if (!($result = popen($cmd, "r"))) |
+ return; |
+ } |
+ else |
+ { |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." 2>&1", false); |
+ |
+ if (!($result = popen($cmd, "r"))) |
+ return; |
+ |
+ $pre = true; |
+ } |
+ |
+ if ($pre) |
+ echo "<PRE>"; |
+ |
+ while (!feof($result)) |
+ { |
+ $line = fgets($result, 1024); |
+ if ($pre) $line = replaceEntities($line, $this->repConfig); |
+ |
+ print hardspace($line); |
+ } |
+ |
+ if ($pre) |
+ echo "</PRE>"; |
+ |
+ pclose($result); |
+ } |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ getBlameDetails |
+ // |
+ // Dump the blame content of a file to the given filename |
+ |
+ function getBlameDetails($path, $filename, $rev = 0) |
+ { |
+ global $config; |
+ |
+ $revstr = _revStr($rev); |
+ |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." blame $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false); |
+ |
+ @exec($cmd); |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ getProperty |
+ |
+ function getProperty($path, $property, $rev = 0) |
+ { |
+ global $config; |
+ |
+ $revstr = _revStr($rev); |
+ |
+ $path = encodepath($this->repConfig->path.$path); |
+ $ret = runCommand($config->svn." propget $property $revstr ".$this->repConfig->svnParams().quote($path), true); |
+ |
+ // Remove the surplus newline |
+ if (count($ret)) |
+ unset($ret[count($ret) - 1]); |
+ |
+ return implode("\n", $ret); |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ exportDirectory |
+ // |
+ // Exports the directory to the given location |
+ |
+ function exportDirectory($path, $filename, $rev = 0) |
+ { |
+ global $config; |
+ |
+ $revstr = _revStr($rev); |
+ |
+ $path = encodepath($this->repConfig->path.$path); |
+ $cmd = quoteCommand($config->svn." export $revstr ".$this->repConfig->svnParams().quote($path)." ".quote($filename), false); |
+ |
+ @exec($cmd); |
+ } |
+ |
+ // }}} |
+ |
+ // {{{ getLog |
+ |
+ function getLog($path, $brev = "", $erev = 1, $quiet = false, $limit = 2) |
+ { |
+ global $config, $curLog, $vars, $lang; |
+ |
+ $xml_parser = xml_parser_create("UTF-8"); |
+ xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true); |
+ xml_set_element_handler($xml_parser, "startElement", "endElement"); |
+ xml_set_character_data_handler($xml_parser, "characterData"); |
+ |
+ // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove |
+ // the trailing slash from the path for comparison purposes |
+ |
+ if ($path{strlen($path) - 1} == "/" && $path != "/") |
+ $path = substr($path, 0, -1); |
+ |
+ $curLog = new SVNLog; |
+ $curLog->entries = array(); |
+ $curLog->path = $path; |
+ |
+ $revStr = ""; |
+ |
+ if ($brev && $erev) |
+ $revStr = "-r$brev:$erev"; |
+ else if ($brev) |
+ $revStr = "-r$brev:1"; |
+ |
+ if (($config->subversionMajorVersion > 1 || $config->subversionMinorVersion >=2) && $limit != 0) |
+ $revStr .= " --limit $limit"; |
+ |
+ // Get the log info |
+ $path = encodepath($this->repConfig->path.$path); |
+ $info = "--verbose"; |
+ if ($quiet) |
+ $info = "--quiet"; |
+ |
+ $cmd = quoteCommand($config->svn." log --xml $info $revStr ".$this->repConfig->svnParams().quote($path), false); |
+ |
+ if ($handle = popen($cmd, "r")) |
+ { |
+ $firstline = true; |
+ while (!feof($handle)) |
+ { |
+ $line = fgets($handle); |
+ if (!xml_parse($xml_parser, $line, feof($handle))) |
+ { |
+ if (xml_get_error_code($xml_parser) != 5) |
+ { |
+ die(sprintf("XML error: %s (%d) at line %d column %d byte %d<br>cmd: %s<nr>", |
+ xml_error_string(xml_get_error_code($xml_parser)), |
+ xml_get_error_code($xml_parser), |
+ xml_get_current_line_number($xml_parser), |
+ xml_get_current_column_number($xml_parser), |
+ xml_get_current_byte_index($xml_parser), |
+ $cmd)); |
+ } |
+ else |
+ { |
+ $vars["error"] = $lang["UNKNOWNREVISION"]; |
+ return 0; |
+ } |
+ } |
+ } |
+ pclose($handle); |
+ } |
+ |
+ xml_parser_free($xml_parser); |
+ return $curLog; |
+ } |
+ |
+ // }}} |
+ |
+} |
+ |
+// {{{ initSvnVersion |
+ |
+function initSvnVersion(&$major, &$minor) |
+{ |
+ global $config; |
+ |
+ $ret = runCommand($config->svn_noparams." --version", false); |
+ |
+ if (preg_match("~([0-9]?)\.([0-9]?)\.([0-9]?)~",$ret[0],$matches)) |
+ { |
+ $major = $matches[1]; |
+ $minor = $matches[2]; |
+ } |
+ |
+ $config->setSubversionMajorVersion($major); |
+ $config->setSubversionMinorVersion($minor); |
+} |
+ |
+// }}} |
+ |
+?> |
/WebSVN/include/template.inc |
---|
1,306 → 1,306 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// templates.inc |
// |
// Templating system to allow advanced page customisation |
$ignore = false; |
// Stack of previous test results |
$ignorestack = array(); |
// Number of test levels currently ignored |
$ignorelevel = 0; |
// parseCommand |
// |
// Parse a special command |
function parseCommand($line, $vars, $handle) |
{ |
global $ignore, $ignorestack, $ignorelevel; |
// Check for test conditions |
if (strncmp(trim($line), "[websvn-test:", 13) == 0) |
{ |
if (!$ignore) |
{ |
$line = trim($line); |
$var = substr($line, 13, -1); |
if (empty($vars[$var])) |
{ |
array_push($ignorestack, $ignore); |
$ignore = true; |
} |
} |
else |
{ |
$ignorelevel++; |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-else]", 13) == 0) |
{ |
if ($ignorelevel == 0) |
{ |
$ignore = !$ignore; |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-endtest]", 16) == 0) |
{ |
if ($ignorelevel > 0) |
{ |
$ignorelevel--; |
} |
else |
{ |
$ignore = array_pop($ignorestack); |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-getlisting]", 19) == 0) |
{ |
global $path, $rev, $svnrep; |
if (!$ignore) |
$svnrep->listFileContents($path, $rev); |
return true; |
} |
if (strncmp(trim($line), "[websvn-defineicons]", 19) == 0) |
{ |
global $icons; |
if (!isset($icons)) |
$icons = array(); |
// Read all the lines until we reach the end of the definition, storing |
// each one... |
if (!$ignore) |
{ |
while (!feof($handle)) |
{ |
$line = trim(fgets($handle)); |
if (strncmp($line, "[websvn-enddefineicons]", 22) == 0) |
{ |
return true; |
} |
$eqsign = strpos($line, "="); |
$match = substr($line, 0, $eqsign); |
$def = substr($line, $eqsign + 1); |
$icons[$match] = $def; |
} |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-icon]", 13) == 0) |
{ |
global $icons, $vars; |
if (!$ignore) |
{ |
// The current filetype should be defined my $vars["filetype"] |
if (!empty($icons[$vars["filetype"]])) |
{ |
echo parseTags($icons[$vars["filetype"]], $vars); |
} |
else if (!empty($icons["*"])) |
{ |
echo parseTags($icons["*"], $vars); |
} |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-treenode]", 17) == 0) |
{ |
global $icons, $vars; |
if (!$ignore) |
{ |
if ((!empty($icons["i-node"])) && (!empty($icons["t-node"])) && (!empty($icons["l-node"]))) |
{ |
for ($n = 1; $n < $vars["level"]; $n++) |
{ |
if ($vars["last_i_node"][$n]) |
{ |
echo parseTags($icons["e-node"], $vars); |
} |
else |
{ |
echo parseTags($icons["i-node"], $vars); |
} |
} |
if ($vars["level"] != 0) |
{ |
if ($vars["node"] == 0) |
{ |
echo parseTags($icons["t-node"], $vars); |
} |
else |
{ |
echo parseTags($icons["l-node"], $vars); |
$vars["last_i_node"][$vars["level"]] = TRUE; |
} |
} |
} |
} |
return true; |
} |
return false; |
} |
// parseTemplate |
// |
// Parse the given template, replacing the variables with the values passed |
function parseTemplate($template, $vars, $listing) |
{ |
global $ignore, $vars; |
if (!is_file($template)) |
{ |
print "No template file found ($template)"; |
exit; |
} |
$handle = fopen($template, "r"); |
$inListing = false; |
$ignore = false; |
$listLines = array(); |
while (!feof($handle)) |
{ |
$line = fgets($handle); |
// Check for the end of the file list |
if ($inListing) |
{ |
if (strcmp(trim($line), "[websvn-endlisting]") == 0) |
{ |
$inListing = false; |
// For each item in the list |
foreach ($listing as $listvars) |
{ |
// Copy the value for this list item into the $vars array |
foreach ($listvars as $id => $value) |
{ |
$vars[$id] = $value; |
} |
// Output the list item |
foreach ($listLines as $line) |
{ |
if (!parseCommand($line, $vars, $handle)) |
{ |
if (!$ignore) |
print parseTags($line, $vars); |
} |
} |
} |
} |
else |
{ |
if ($ignore == false) |
$listLines[] = $line; |
} |
} |
else if (parseCommand($line, $vars, $handle)) |
{ |
continue; |
} |
else |
{ |
// Check for the start of the file list |
if (strncmp(trim($line), "[websvn-startlisting]", 21) == 0) |
{ |
$inListing = true; |
} |
else |
{ |
if ($ignore == false) |
print parseTags($line, $vars); |
} |
} |
} |
fclose($handle); |
} |
// parseTags |
// |
// Replace all occurences of [websvn:varname] with the give variable |
function parseTags($line, $vars) |
{ |
global $lang; |
// Replace the websvn variables |
while (ereg("\[websvn:([a-zA-Z0-9_]+)\]", $line, $matches)) |
{ |
// Make sure that the variable exists |
if (!isset($vars[$matches[1]])) |
{ |
$vars[$matches[1]] = "?${matches[1]}?"; |
} |
$line = str_replace($matches[0], $vars[$matches[1]], $line); |
} |
// Replace the language strings |
while (ereg("\[lang:([a-zA-Z0-9_]+)\]", $line, $matches)) |
{ |
// Make sure that the variable exists |
if (!isset($lang[$matches[1]])) |
{ |
$lang[$matches[1]] = "?${matches[1]}?"; |
} |
$line = str_replace($matches[0], $lang[$matches[1]], $line); |
} |
// Return the results |
return $line; |
} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// templates.inc |
// |
// Templating system to allow advanced page customisation |
$ignore = false; |
// Stack of previous test results |
$ignorestack = array(); |
// Number of test levels currently ignored |
$ignorelevel = 0; |
// parseCommand |
// |
// Parse a special command |
function parseCommand($line, $vars, $handle) |
{ |
global $ignore, $ignorestack, $ignorelevel; |
// Check for test conditions |
if (strncmp(trim($line), "[websvn-test:", 13) == 0) |
{ |
if (!$ignore) |
{ |
$line = trim($line); |
$var = substr($line, 13, -1); |
if (empty($vars[$var])) |
{ |
array_push($ignorestack, $ignore); |
$ignore = true; |
} |
} |
else |
{ |
$ignorelevel++; |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-else]", 13) == 0) |
{ |
if ($ignorelevel == 0) |
{ |
$ignore = !$ignore; |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-endtest]", 16) == 0) |
{ |
if ($ignorelevel > 0) |
{ |
$ignorelevel--; |
} |
else |
{ |
$ignore = array_pop($ignorestack); |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-getlisting]", 19) == 0) |
{ |
global $path, $rev, $svnrep; |
if (!$ignore) |
$svnrep->listFileContents($path, $rev); |
return true; |
} |
if (strncmp(trim($line), "[websvn-defineicons]", 19) == 0) |
{ |
global $icons; |
if (!isset($icons)) |
$icons = array(); |
// Read all the lines until we reach the end of the definition, storing |
// each one... |
if (!$ignore) |
{ |
while (!feof($handle)) |
{ |
$line = trim(fgets($handle)); |
if (strncmp($line, "[websvn-enddefineicons]", 22) == 0) |
{ |
return true; |
} |
$eqsign = strpos($line, "="); |
$match = substr($line, 0, $eqsign); |
$def = substr($line, $eqsign + 1); |
$icons[$match] = $def; |
} |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-icon]", 13) == 0) |
{ |
global $icons, $vars; |
if (!$ignore) |
{ |
// The current filetype should be defined my $vars["filetype"] |
if (!empty($icons[$vars["filetype"]])) |
{ |
echo parseTags($icons[$vars["filetype"]], $vars); |
} |
else if (!empty($icons["*"])) |
{ |
echo parseTags($icons["*"], $vars); |
} |
} |
return true; |
} |
if (strncmp(trim($line), "[websvn-treenode]", 17) == 0) |
{ |
global $icons, $vars; |
if (!$ignore) |
{ |
if ((!empty($icons["i-node"])) && (!empty($icons["t-node"])) && (!empty($icons["l-node"]))) |
{ |
for ($n = 1; $n < $vars["level"]; $n++) |
{ |
if ($vars["last_i_node"][$n]) |
{ |
echo parseTags($icons["e-node"], $vars); |
} |
else |
{ |
echo parseTags($icons["i-node"], $vars); |
} |
} |
if ($vars["level"] != 0) |
{ |
if ($vars["node"] == 0) |
{ |
echo parseTags($icons["t-node"], $vars); |
} |
else |
{ |
echo parseTags($icons["l-node"], $vars); |
$vars["last_i_node"][$vars["level"]] = TRUE; |
} |
} |
} |
} |
return true; |
} |
return false; |
} |
// parseTemplate |
// |
// Parse the given template, replacing the variables with the values passed |
function parseTemplate($template, $vars, $listing) |
{ |
global $ignore, $vars; |
if (!is_file($template)) |
{ |
print "No template file found ($template)"; |
exit; |
} |
$handle = fopen($template, "r"); |
$inListing = false; |
$ignore = false; |
$listLines = array(); |
while (!feof($handle)) |
{ |
$line = fgets($handle); |
// Check for the end of the file list |
if ($inListing) |
{ |
if (strcmp(trim($line), "[websvn-endlisting]") == 0) |
{ |
$inListing = false; |
// For each item in the list |
foreach ($listing as $listvars) |
{ |
// Copy the value for this list item into the $vars array |
foreach ($listvars as $id => $value) |
{ |
$vars[$id] = $value; |
} |
// Output the list item |
foreach ($listLines as $line) |
{ |
if (!parseCommand($line, $vars, $handle)) |
{ |
if (!$ignore) |
print parseTags($line, $vars); |
} |
} |
} |
} |
else |
{ |
if ($ignore == false) |
$listLines[] = $line; |
} |
} |
else if (parseCommand($line, $vars, $handle)) |
{ |
continue; |
} |
else |
{ |
// Check for the start of the file list |
if (strncmp(trim($line), "[websvn-startlisting]", 21) == 0) |
{ |
$inListing = true; |
} |
else |
{ |
if ($ignore == false) |
print parseTags($line, $vars); |
} |
} |
} |
fclose($handle); |
} |
// parseTags |
// |
// Replace all occurences of [websvn:varname] with the give variable |
function parseTags($line, $vars) |
{ |
global $lang; |
// Replace the websvn variables |
while (ereg("\[websvn:([a-zA-Z0-9_]+)\]", $line, $matches)) |
{ |
// Make sure that the variable exists |
if (!isset($vars[$matches[1]])) |
{ |
$vars[$matches[1]] = "?${matches[1]}?"; |
} |
$line = str_replace($matches[0], $vars[$matches[1]], $line); |
} |
// Replace the language strings |
while (ereg("\[lang:([a-zA-Z0-9_]+)\]", $line, $matches)) |
{ |
// Make sure that the variable exists |
if (!isset($lang[$matches[1]])) |
{ |
$lang[$matches[1]] = "?${matches[1]}?"; |
} |
$line = str_replace($matches[0], $lang[$matches[1]], $line); |
} |
// Return the results |
return $line; |
} |
?> |
/WebSVN/include/utils.inc |
---|
1,259 → 1,340 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// utils.inc |
// |
// General utility commands |
#require_once 'php5compat.inc'; |
// {{{ createDirLinks |
// |
// Create a list of links to the current path that'll be available from the template |
function createDirLinks($rep, $path, $rev, $showchanged) |
{ |
global $vars, $config; |
$subs = explode('/', htmlentities($path)); |
$sofar = ""; |
$count = count($subs); |
$vars["curdirlinks"] = ""; |
// The number of links depends on the last item. It's empty if |
// we're looing at a directory, and full if it's a file |
if (empty($subs[$count - 1])) |
{ |
$limit = $count - 2; |
$dir = true; |
} |
else |
{ |
$limit = $count - 1; |
$dir = false; |
} |
for ($n = 0; $n < $limit; $n++) |
{ |
$sofar .= html_entity_decode($subs[$n])."/"; |
$sofarurl = $config->getURL($rep, $sofar, "dir"); |
$vars["curdirlinks"] .= "[<a href=\"${sofarurl}rev=$rev&sc=$showchanged\">".$subs[$n]."/]</a> "; |
} |
if ($dir) |
{ |
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>/]"; |
} |
else |
{ |
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>]"; |
} |
} |
// }}} |
// {{{ create_anchors |
// |
// Create links out of http:// and mailto: tags |
# TODO: the target="_blank" nonsense should be optional (or specified by the template) |
function create_anchors($text) |
{ |
$ret = $text; |
// Match correctly formed URLs that aren't already links |
$ret = preg_replace("#\b(?<!href=\")([a-z]+?)://(\S*)([\w/]+)#i", |
"<a href=\"\\1://\\2\\3\" target=\"_blank\">\\1://\\2\\3</a>", |
$ret); |
// Now match anything beginning with www, as long as it's not //www since they were matched above |
$ret = preg_replace("#\b(?<!//)www\.(\S*)([\w/]+)#i", |
"<a href=\"http://www.\\1\\2\" target=\"_blank\">www.\\1\\2</a>", |
$ret); |
// Match email addresses |
$ret = preg_replace("#\b([\w\-_.]+)@([\w\-.]+)\b#i", |
"<a href=\"mailto:\\1@\\2\">\\1@\\2</a>", |
$ret); |
return ($ret); |
} |
// }}} |
// {{{ getFullURL |
function getFullURL($loc) |
{ |
$protocol = 'http'; |
if (isset($_SERVER["HTTPS"]) && (strtolower($_SERVER["HTTPS"]) != "off")) |
{ |
$protocol = "https"; |
} |
$port = ":".$_SERVER["SERVER_PORT"]; |
if ((":80" == $port && "http" == $protocol) || |
(":443" == $port && "https" == $protocol)) |
{ |
$port = ""; |
} |
if (isset($_SERVER["HTTP_HOST"])) |
{ |
$host = $_SERVER["HTTP_HOST"]; |
} |
else if (isset($_SERVER["SERVER_NAME"])) |
{ |
$host = $_SERVER["SERVER_NAME"].$port; |
} |
else if (isset($_SERVER["SERVER_ADDR"])) |
{ |
$host = $_SERVER["SERVER_ADDR"].$port; |
} |
else |
{ |
print "Unable to redirect"; |
exit; |
} |
$url = $protocol . "://" . $host . $loc; |
return $url; |
} |
// }}} |
// {{{ hardspace |
// |
// Replace the spaces at the front of a line with hard spaces |
# XXX: this is an unnecessary function; you can prevent whitespace from being |
# trimmed via CSS (use the "white-space: pre;" properties). ~J |
# in the meantime, here's an improved function (does nothing) |
function hardspace($s) |
{ |
return '<code>'.$s.'</code>'; |
} |
// }}} |
// {{{ datetimeFormatDuration |
// |
// Formats a duration of seconds for display. |
// |
// $seconds the number of seconds until something |
// $nbsp true if spaces should be replaced by nbsp |
// $skipSeconds true if seconds should be omitted |
// |
// return the formatted duration (e.g. @c "8h 6m 1s") |
function datetimeFormatDuration($seconds, $nbsp = false, $skipSeconds = false) |
{ |
global $lang; |
if ($seconds < 0) |
{ |
$seconds = -$seconds; |
$neg = true; |
} |
else |
$neg = false; |
$qty = array(); |
$qty[] = (int)($seconds / (60 * 60 * 24)); |
$seconds %= 60 * 60 * 24; |
$qty[] = (int)($seconds / (60 * 60)); |
$seconds %= 60 * 60; |
$qty[] = (int)($seconds / 60); |
$qty[] = (int)($seconds % 60); |
$text = ""; |
$names = $lang["DAYLETTER"].$lang["HOURLETTER"].$lang["MINUTELETTER"]; |
if (!$skipSeconds) $names .= $lang["SECONDLETTER"]; |
$any = false; |
for ($i = 0; $i < strlen($names); $i++) |
// If a "higher valued" time slot had a value or this time slot |
// has a value or this is the very last entry (i.e. all values |
// are 0 and we still want to print seconds) |
if ($any || $qty[$i] || $i == sizeof($qty) - 1) |
{ |
$any = true; |
if ($i) |
$text .= sprintf("%2d", $qty[$i]); |
else |
$text .= $qty[$i]; |
$text .= "{$names{$i}} "; |
} |
$text = trim($text); |
if ($neg) |
$text = "-$text"; |
return $nbsp ? str_replace(" ", " ", $text) : $text; |
} |
// }}} |
// {{{ getParameterisedSelfUrl |
// |
// Get the relative URL (PHP_SELF) with GET and POST data |
function getParameterisedSelfUrl($params = true) |
{ |
$url = null; |
if ($config->multiViews) |
{ |
// Get rid of the file's name |
$url = preg_replace('/\.php/', '', $_SERVER['PHP_SELF'], 1); |
} |
else |
{ |
$url = basename($_SERVER['PHP_SELF']); |
// Sometimes the .php isn't on the end. Damn strange... |
if (strchr($url, '.') === false) |
$url .= '.php'; |
} |
if ($params) |
{ |
$arr = $_GET + $_POST; |
# XXX: the point of HTTP POST is that URIs have a set size limit, so POST |
# data is typically too large to bother with; why include it? |
$url .= '?'.htmlentities(http_build_query($arr)); |
} |
return $url; |
} |
// }}} |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// utils.inc |
// |
// General utility commands |
// {{{ createDirLinks |
// |
// Create a list of links to the current path that'll be available from the template |
function createDirLinks($rep, $path, $rev, $showchanged) |
{ |
global $vars, $config; |
$subs = explode('/', htmlentities($path)); |
$sofar = ""; |
$count = count($subs); |
$vars["curdirlinks"] = ""; |
// The number of links depends on the last item. It's empty if |
// we're looing at a directory, and full if it's a file |
if (empty($subs[$count - 1])) |
{ |
$limit = $count - 2; |
$dir = true; |
} |
else |
{ |
$limit = $count - 1; |
$dir = false; |
} |
for ($n = 0; $n < $limit; $n++) |
{ |
$sofar .= html_entity_decode($subs[$n])."/"; |
$sofarurl = $config->getURL($rep, $sofar, "dir"); |
$vars["curdirlinks"] .= "[<a href=\"${sofarurl}rev=$rev&sc=$showchanged\">".$subs[$n]."/]</a> "; |
} |
if ($dir) |
{ |
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>/]"; |
} |
else |
{ |
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>]"; |
} |
} |
// }}} |
// {{{ create_anchors |
// |
// Create links out of http:// and mailto: tags |
# TODO: the target="_blank" nonsense should be optional (or specified by the template) |
function create_anchors($text) |
{ |
$ret = $text; |
// Match correctly formed URLs that aren't already links |
$ret = preg_replace("#\b(?<!href=\")([a-z]+?)://(\S*)([\w/]+)#i", |
"<a href=\"\\1://\\2\\3\" target=\"_blank\">\\1://\\2\\3</a>", |
$ret); |
// Now match anything beginning with www, as long as it's not //www since they were matched above |
$ret = preg_replace("#\b(?<!//)www\.(\S*)([\w/]+)#i", |
"<a href=\"http://www.\\1\\2\" target=\"_blank\">www.\\1\\2</a>", |
$ret); |
// Match email addresses |
$ret = preg_replace("#\b([\w\-_.]+)@([\w\-.]+)\b#i", |
"<a href=\"mailto:\\1@\\2\">\\1@\\2</a>", |
$ret); |
return ($ret); |
} |
// }}} |
// {{{ getFullURL |
function getFullURL($loc) |
{ |
$protocol = 'http'; |
if (isset($_SERVER["HTTPS"]) && (strtolower($_SERVER["HTTPS"]) != "off")) |
{ |
$protocol = "https"; |
} |
$port = ":".$_SERVER["SERVER_PORT"]; |
if ((":80" == $port && "http" == $protocol) || |
(":443" == $port && "https" == $protocol)) |
{ |
$port = ""; |
} |
if (isset($_SERVER["HTTP_HOST"])) |
{ |
$host = $_SERVER["HTTP_HOST"]; |
} |
else if (isset($_SERVER["SERVER_NAME"])) |
{ |
$host = $_SERVER["SERVER_NAME"].$port; |
} |
else if (isset($_SERVER["SERVER_ADDR"])) |
{ |
$host = $_SERVER["SERVER_ADDR"].$port; |
} |
else |
{ |
print "Unable to redirect"; |
exit; |
} |
$url = $protocol . "://" . $host . $loc; |
return $url; |
} |
// }}} |
// {{{ hardspace |
// |
// Replace the spaces at the front of a line with hard spaces |
# XXX: this is an unnecessary function; you can prevent whitespace from being |
# trimmed via CSS (use the "white-space: pre;" properties). ~J |
# in the meantime, here's an improved function (does nothing) |
function hardspace($s) |
{ |
return '<code>' . expandTabs($s) . '</code>'; |
} |
// }}} |
// {{{ expandTabs |
/** |
* Expands the tabs in a line that may or may not include HTML. |
* |
* Enscript generates code with HTML, so we need to take that into account. |
* |
* @param string $s Line of possibly HTML-encoded text to expand |
* @param int $tabwidth Tab width, -1 to use repository's default, 0 to collapse |
* all tabs. |
* @return string The expanded line. |
* @since 2.1 |
*/ |
function expandTabs($s, $tabwidth = -1) |
{ |
global $rep; |
if ($tabwidth == -1) |
$tabwidth = $rep->getExpandTabsBy(); |
$pos = 0; |
// Parse the string into chunks that are either 1 of: HTML tag, tab char, run of any other stuff |
$chunks = preg_split("/((?:<.+?>)|(?:&.+?;)|(?:\t))/", $s, -1, PREG_SPLIT_DELIM_CAPTURE); |
// Count the sizes of the chunks and replace tabs as we go |
for ($i = 0; $i < count($chunks); $i++) |
{ |
# make sure we're not dealing with an empty string |
if (empty($chunks[$i])) continue; |
switch ($chunks[$i]{0}) |
{ |
case '<': // HTML tag : ignore its width by doing nothing |
break; |
case '&': // HTML entity: count its width as 1 char |
$pos += 1; |
break; |
case "\t": // Tab char: replace it with a run of spaces between length tabwidth and 1 |
$tabsize = $tabwidth - ($pos % $tabwidth); |
$chunks[$i] = str_repeat(' ', $tabsize); |
$pos += $tabsize; |
break; |
default: // Anything else: just keep track of its width |
$pos += strlen($chunks[$i]); |
break; |
} |
} |
// Put the chunks back together and we've got the original line, detabbed. |
return join('', $chunks); |
} |
// }}} |
// {{{ datetimeFormatDuration |
// |
// Formats a duration of seconds for display. |
// |
// $seconds the number of seconds until something |
// $nbsp true if spaces should be replaced by nbsp |
// $skipSeconds true if seconds should be omitted |
// |
// return the formatted duration (e.g. @c "8h 6m 1s") |
function datetimeFormatDuration($seconds, $nbsp = false, $skipSeconds = false) |
{ |
global $lang; |
if ($seconds < 0) |
{ |
$seconds = -$seconds; |
$neg = true; |
} |
else |
$neg = false; |
$qty = array(); |
$qty[] = (int)($seconds / (60 * 60 * 24)); |
$seconds %= 60 * 60 * 24; |
$qty[] = (int)($seconds / (60 * 60)); |
$seconds %= 60 * 60; |
$qty[] = (int)($seconds / 60); |
$qty[] = (int)($seconds % 60); |
$text = ""; |
$names = $lang["DAYLETTER"].$lang["HOURLETTER"].$lang["MINUTELETTER"]; |
if (!$skipSeconds) $names .= $lang["SECONDLETTER"]; |
$any = false; |
for ($i = 0; $i < strlen($names); $i++) |
// If a "higher valued" time slot had a value or this time slot |
// has a value or this is the very last entry (i.e. all values |
// are 0 and we still want to print seconds) |
if ($any || $qty[$i] || $i == sizeof($qty) - 1) |
{ |
$any = true; |
if ($i) |
$text .= sprintf("%2d", $qty[$i]); |
else |
$text .= $qty[$i]; |
$text .= "{$names{$i}} "; |
} |
$text = trim($text); |
if ($neg) |
$text = "-$text"; |
return $nbsp ? str_replace(" ", " ", $text) : $text; |
} |
// }}} |
// {{{ getParameterisedSelfUrl |
// |
// Get the relative URL (PHP_SELF) with GET and POST data |
function getParameterisedSelfUrl($params = true) |
{ |
global $config; |
$url = null; |
if ($config->multiViews) |
{ |
// Get rid of the file's name |
$url = preg_replace('/\.php/', '', $_SERVER['PHP_SELF'], 1); |
} |
else |
{ |
$url = basename($_SERVER['PHP_SELF']); |
// Sometimes the .php isn't on the end. Damn strange... |
if (strchr($url, '.') === false) |
$url .= '.php'; |
} |
if ($params) |
{ |
$arr = $_GET + $_POST; |
# XXX: the point of HTTP POST is that URIs have a set size limit, so POST |
# data is typically too large to bother with; why include it? |
$url .= '?'.htmlentities(http_build_query($arr)); |
} |
return $url; |
} |
// }}} |
// {{{ getUserLanguage |
function getUserLanguage() { |
$languages = !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : 'en'; |
if (strpos($languages, ',') === false) return $languages; # only one specified |
# split off the languages into an array of languages and q-values |
$qvals = array(); |
$langs = array(); |
preg_match_all('#(\S+?)\s*(?:;q=([01](?:\.\d{1,3})?))?\s*,\s*#', $languages, $qvals, PREG_SET_ORDER); |
foreach ($qvals as $q) { |
$langs[] = array ( |
$q[1], |
floatval(!empty($q[2]) ? $q[2] : 1.0) |
); |
} |
# XXX: now, we should loop through our available languages and choose an |
# appropriate one for the user |
# note that we shouldn't match the region unless we have a specific region |
# to use (e.g. zh-CN vs. zh-TW) |
# FIXME: see above; otherwise, this function doesn't really do anything |
} |
// }}} |
?> |
/WebSVN/include/version.inc |
---|
1,28 → 1,28 |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// version.inc |
// |
// Version information |
$version = '2.1 alpha 1'; |
?> |
<?php |
# vim:et:ts=3:sts=3:sw=3:fdm=marker: |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright © 2004-2006 Tim Armes, Matt Sicker |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// version.inc |
// |
// Version information |
$version = '2.1 alpha 1'; |
?> |