Merge pull request #11 from bessl/patch-2
Merge pull request #11 from bessl/patch-2

Update gitphp.po

<?php <?php
/** /**
* GitPHP Config defaults * GitPHP Config defaults
* *
* Lists all the config options and their default values * Lists all the config options and their default values
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Config * @subpackage Config
*/ */
   
   
/** /**
* This file is not usable as an actual config file. * This file is not usable as an actual config file.
* To use a config value you should copy the value * To use a config value you should copy the value
* into gitphp.conf.php * into gitphp.conf.php
*/ */
throw new Exception('The defaults file should not be used as your config.'); throw new Exception('The defaults file should not be used as your config.');
   
   
/********************************************************* /*********************************************************
* Projects * Projects
*/ */
   
/* /*
* projectroot * projectroot
* Full directory on server where projects are located * Full directory on server where projects are located
*/ */
//$gitphp_conf['projectroot'] = '/pub/gitprojects/'; //$gitphp_conf['projectroot'] = '/pub/gitprojects/';
   
/* /*
* exportedonly * exportedonly
* When listing all projects in the project root, * When listing all projects in the project root,
* (not specifying any projects manually or using a project list file) * (not specifying any projects manually or using a project list file)
* set this to true to only allow repositories with the * set this to true to only allow repositories with the
* special file git-daemon-export-ok (see the git-daemon man page) * special file git-daemon-export-ok (see the git-daemon man page)
*/ */
$gitphp_conf['exportedonly'] = false; $gitphp_conf['exportedonly'] = false;
   
   
   
/********************************************************* /*********************************************************
* Appearance * Appearance
*/ */
   
/* /*
* locale * locale
* This is the default locale/language used in the interface. * This is the default locale/language used in the interface.
* The locale must exist in the locale/ directory * The locale must exist in the locale/ directory
*/ */
$gitphp_conf['locale'] = 'en_US'; $gitphp_conf['locale'] = 'en_US';
   
/* /*
* title * title
* The string that will be used as the page title * The string that will be used as the page title
* The variable '$gitphp_appstring' will expand to * The variable '$gitphp_appstring' will expand to
* the name (gitphp) and version * the name (gitphp) and version
* The variable '$gitphp_version' will expand to the * The variable '$gitphp_version' will expand to the
* version number only * version number only
*/ */
$gitphp_conf['title'] = "$gitphp_appstring"; $gitphp_conf['title'] = "$gitphp_appstring";
   
/* /*
* homelink * homelink
* This is the text of the link in the upper left corner * This is the text of the link in the upper left corner
* that takes you back to the project list. * that takes you back to the project list.
*/ */
$gitphp_conf['homelink'] = 'projects'; $gitphp_conf['homelink'] = 'projects';
   
/* /*
* cloneurl * cloneurl
* Sets the base clone url to display for a project. * Sets the base clone url to display for a project.
* This is the publicly-accessible url of the projectroot * This is the publicly-accessible url of the projectroot
* that gets prepended to the project path to create the clone * that gets prepended to the project path to create the clone
* url. It can be any format, for example: * url. It can be any format, for example:
* *
* http://server.com/ * http://server.com/
* ssh://server.com/git/ * ssh://server.com/git/
* git://server.com/gitprojects/ * git://server.com/gitprojects/
* *
* If left blank/commented, no clone url will display. * If left blank/commented, no clone url will display.
*/ */
$gitphp_conf['cloneurl'] = 'http://localhost/git/'; $gitphp_conf['cloneurl'] = 'http://localhost/git/';
   
/* /*
* pushurl * pushurl
* Sets the base push url to display for a project. * Sets the base push url to display for a project.
* Works the same as cloneurl. * Works the same as cloneurl.
*/ */
$gitphp_conf['pushurl'] = 'ssh://localhost/git/'; $gitphp_conf['pushurl'] = 'ssh://localhost/git/';
   
/* /*
* bugpattern * bugpattern
* Sets the regular expression to use to find bug number * Sets the regular expression to use to find bug number
* references in log messages. The pattern should have a * references in log messages. The pattern should have a
* group that extracts just the bug ID to pass to the * group that extracts just the bug ID to pass to the
* bug tracker. * bug tracker.
* For example, '/#([0-9]+)/' will recognize any number * For example, '/#([0-9]+)/' will recognize any number
* with a '#' in front of it, and groups the numeric part * with a '#' in front of it, and groups the numeric part
* only. Another common example is '/bug:([0-9]+)/' to * only. Another common example is '/bug:([0-9]+)/' to
* extract bug numbers with 'bug:' in front of them. * extract bug numbers with 'bug:' in front of them.
*/ */
//$gitphp_conf['bugpattern'] = '/#([0-9]+)/'; //$gitphp_conf['bugpattern'] = '/#([0-9]+)/';
   
/* /*
* bugurl * bugurl
* Sets the URL for the bug tracker. This URL must have * Sets the URL for the bug tracker. This URL must have
* a backreference to the group in the bug pattern that * a backreference to the group in the bug pattern that
* contains the ID. For example, ${1} uses the first * contains the ID. For example, ${1} uses the first
* group. * group.
*/ */
//$gitphp_conf['bugurl'] = 'http://localhost/mantis/view.php?id=${1}'; //$gitphp_conf['bugurl'] = 'http://localhost/mantis/view.php?id=${1}';
   
/* /*
* self * self
* This is the path to the script that will be inserted * This is the path to the script that will be inserted
* in urls. If you leave this blank/commented the script * in urls. If you leave this blank/commented the script
* will try to guess the correct URL, but you can override * will try to guess the correct URL, but you can override
* it here if it's not being guessed correctly. * it here if it's not being guessed correctly.
*/ */
$gitphp_conf['self'] = 'http://localhost/gitphp/'; $gitphp_conf['self'] = 'http://localhost/gitphp/';
   
/* /*
* stylesheet * stylesheet
* Path to look and feel (skin) stylesheet * Path to look and feel (skin) stylesheet
*/ */
$gitphp_conf['stylesheet'] = 'gitphpskin.css'; $gitphp_conf['stylesheet'] = 'gitphpskin.css';
   
/* /*
* javascript * javascript
* Toggles on javascript features * Toggles on javascript features
*/ */
$gitphp_conf['javascript'] = true; $gitphp_conf['javascript'] = true;
   
/* /*
* googlejs * googlejs
* Toggles whether to use the Google Libraries API to * Toggles whether to use the Google Libraries API to
* load javascript libraries, which takes advantage of the * load javascript libraries, which takes advantage of the
* speed and caching of Google's servers and content * speed and caching of Google's servers and content
* delivery network. * delivery network.
* http://developers.google.com/speed/libraries/ * http://developers.google.com/speed/libraries/
* The libraries are served from Google's servers, which * The libraries are served from Google's servers, which
* means your users must have an internet connection, * means your users must have an internet connection,
* so this may not be appropriate for closed intranets. * so this may not be appropriate for closed intranets.
* By enabling this you agree to Google's terms for their * By enabling this you agree to Google's terms for their
* library API. * library API.
*/ */
$gitphp_conf['googlejs'] = false; $gitphp_conf['googlejs'] = false;
   
   
   
/********************************************************* /*********************************************************
* Features * Features
*/ */
   
/* /*
* compat * compat
* Set this to true to turn on compatibility mode. This will cause * Set this to true to turn on compatibility mode. This will cause
* GitPHP to rely more on the git executable for loading data, * GitPHP to rely more on the git executable for loading data,
* which will bypass some of the limitations of PHP at the expense * which will bypass some of the limitations of PHP at the expense
* of performance. * of performance.
* Turn this on if you are experiencing issues viewing data for * Turn this on if you are experiencing issues viewing data for
* your projects. * your projects.
*/ */
$gitphp_conf['compat'] = false; $gitphp_conf['compat'] = false;
   
/** /**
* largeskip * largeskip
* When GitPHP is reading through the history for pages of the shortlog/log * When GitPHP is reading through the history for pages of the shortlog/log
* beyond the first, it needs to read from the tip but skip a number of commits * beyond the first, it needs to read from the tip but skip a number of commits
* for the previous pages. The more commits it needs to skip, the longer it takes. * for the previous pages. The more commits it needs to skip, the longer it takes.
* Calling the git executable is faster when skipping a large number of commits, * Calling the git executable is faster when skipping a large number of commits,
* ie reading a log page significantly beyond the first. This determines * ie reading a log page significantly beyond the first. This determines
* the threshold at which GitPHP will fall back to using the git exe for the log. * the threshold at which GitPHP will fall back to using the git exe for the log.
* Currently each log page shows 100 commits, so this would be calculated at * Currently each log page shows 100 commits, so this would be calculated at
* page number * 100. So for example at the default of 200, pages 0-2 would be * page number * 100. So for example at the default of 200, pages 0-2 would be
* loaded natively and pages 3+ would fall back on the git exe. * loaded natively and pages 3+ would fall back on the git exe.
*/ */
$gitphp_conf['largeskip'] = 200; $gitphp_conf['largeskip'] = 200;
   
/* /*
* uniqueabbrev * uniqueabbrev
* If this is turned on, when GitPHP abbreviates hashes, it will ensure * If this is turned on, when GitPHP abbreviates hashes, it will ensure
* that the abbreviated hash is unique for the project, extending the * that the abbreviated hash is unique for the project, extending the
* abbreviated hash if necessary until it becomes unique. * abbreviated hash if necessary until it becomes unique.
* Searching through every single pack in the repository for collisions is a * Searching through every single pack in the repository for collisions is a
* performance intensive process that can slow down page loads. If you turn * performance intensive process that can slow down page loads. If you turn
* this on, it's highly recommended to merge all of your existing packs into * this on, it's highly recommended to merge all of your existing packs into
* a single pack using git gc --aggressive (or git repack -a -d). * a single pack using git gc --aggressive (or git repack -a -d).
*/ */
$gitphp_conf['uniqueabbrev'] = false; $gitphp_conf['uniqueabbrev'] = false;
   
/* /*
* compressformat * compressformat
* Indicates what kind of compression will be done on the * Indicates what kind of compression will be done on the
* snapshot archive. Recognized settings are: * snapshot archive. Recognized settings are:
* *
* GITPHP_COMPRESS_BZ2 - create a tar.bz2 file (php must have bz2 support) * GITPHP_COMPRESS_BZ2 - create a tar.bz2 file (php must have bz2 support)
* GITPHP_COMPRESS_GZ - create a tar.gz file (php must have gzip support) * GITPHP_COMPRESS_GZ - create a tar.gz file (php must have gzip support)
* GITPHP_COMPRESS_ZIP - create a zip file * GITPHP_COMPRESS_ZIP - create a zip file
* *
* Any other setting, or no setting, will create uncompressed tar archives * Any other setting, or no setting, will create uncompressed tar archives
* If you choose a compression format and your php does not support it, * If you choose a compression format and your php does not support it,
* gitphp will fall back to uncompressed tar archives * gitphp will fall back to uncompressed tar archives
* *
* Note that users with javascript get to choose their snapshot format when * Note that users with javascript get to choose their snapshot format when
* they request it, so this only applies to users without javascript or if * they request it, so this only applies to users without javascript or if
* you turn the javascript setting off * you turn the javascript setting off
*/ */
$gitphp_conf['compressformat'] = GITPHP_COMPRESS_ZIP; $gitphp_conf['compressformat'] = GITPHP_COMPRESS_ZIP;
   
/* /*
* compresslevel * compresslevel
* Sets the compression level for snapshots. Ranges from 1-9, with * Sets the compression level for snapshots. Ranges from 1-9, with
* 9 being the most compression but requiring the most processing * 9 being the most compression but requiring the most processing
* (bzip defaults to 4, gzip defaults to -1) * (bzip defaults to 4, gzip defaults to -1)
*/ */
$gitphp_conf['compresslevel'] = 9; $gitphp_conf['compresslevel'] = 9;
   
/* /*
* geshi * geshi
* Run blob output through geshi syntax highlighting * Run blob output through geshi syntax highlighting
* and line numbering * and line numbering
*/ */
$gitphp_conf['geshi'] = true; $gitphp_conf['geshi'] = true;
   
/* /*
* search * search
* Set this to false to disable searching * Set this to false to disable searching
*/ */
$gitphp_conf['search'] = true; $gitphp_conf['search'] = true;
   
/* /*
* filesearch * filesearch
* Set this to false to disable searching within files * Set this to false to disable searching within files
* (it can be resource intensive) * (it can be resource intensive)
*/ */
$gitphp_conf['filesearch'] = true; $gitphp_conf['filesearch'] = true;
   
/* /*
* filemimetype * filemimetype
* Attempt to read the file's mimetype when displaying * Attempt to read the file's mimetype when displaying
* (for example, displaying an image as an actual image * (for example, displaying an image as an actual image
* in a browser) * in a browser)
* This requires either PHP >= 5.3.0, PECL fileinfo, or * This requires either PHP >= 5.3.0, PECL fileinfo, or
* Linux * Linux
*/ */
$gitphp_conf['filemimetype'] = true; $gitphp_conf['filemimetype'] = true;
   
/* /*
* abbreviateurl * abbreviateurl
* Generates urls using abbreviated hashes instead of * Generates urls using abbreviated hashes instead of
* full hashes. * full hashes.
* Note that urls with abbreviated hashes are not safe * Note that urls with abbreviated hashes are not safe
* to be saved long term (eg bookmarks), as future objects * to be saved long term (eg bookmarks), as future objects
* may be added to the repository that cause an abbreviated * may be added to the repository that cause an abbreviated
* hash to no longer be unique. * hash to no longer be unique.
* This option only takes effect with the 'compat' option * This option only takes effect with the 'compat' option
* turned off. * turned off.
* Additionally, this option will automatically enable * Additionally, this option will automatically enable
* 'uniqueabbrev', as an abbreviated hash must be unique * 'uniqueabbrev', as an abbreviated hash must be unique
* in order to resolve it to a full hash. * in order to resolve it to a full hash.
*/ */
$gitphp_conf['abbreviateurl'] = false; $gitphp_conf['abbreviateurl'] = false;
   
/* /*
* cleanurl * cleanurl
* Uses clean, rest-style urls throughout gitphp. * Uses clean, rest-style urls throughout gitphp.
* This requires additional setup in your web server * This requires additional setup in your web server
* to rewrite urls (mod_rewrite on Apache, HttpRewriteModule * to rewrite urls (mod_rewrite on Apache, HttpRewriteModule
* on Nginx, etc). URLs must be rewritten to point * on Nginx, etc). URLs must be rewritten to point
* to index.php?q={query}. * to index.php?q={query}.
* For more instructions on how to set this up, see * For more instructions on how to set this up, see
* http://www.gitphp.org/projects/gitphp/wiki/Clean_URLs * http://www.gitphp.org/projects/gitphp/wiki/Clean_URLs
*/ */
$gitphp_conf['cleanurl'] = false; $gitphp_conf['cleanurl'] = false;
   
  /*
  * feedfilter
  * Sets a regular expression to use to filter commits out
  * of the project atom/rss feed. Commits that have a
  * commit message matching this pattern will be excluded.
  * For example, '/GIT_SILENT/' will exclude any commit
  * with the string GIT_SILENT in the commit message.
  */
  //$gitphp_conf['feedfilter'] = '/GIT_SILENT/';
   
  /*
  * showrestrictedprojects
  * By default, when user-based restrictions are enabled,
  * projects that are not available to the logged in user
  * will be hidden in the project list. Setting this option
  * will instead display these projects as disabled in the
  * project list.
  */
  $gitphp_conf['showrestrictedprojects'] = false;
   
  /*
   
   
/********************************************************* /*********************************************************
* Executable/filesystem options * Executable/filesystem options
* Important to check if you're running windows * Important to check if you're running windows
*/ */
   
/* /*
* gitbin * gitbin
* Path to git binary * Path to git binary
* For example, /usr/bin/git on Linux * For example, /usr/bin/git on Linux
* or C:\\Program Files\\Git\\bin\\git.exe on Windows * or C:\\Program Files\\Git\\bin\\git.exe on Windows
* with msysgit. You can also omit the full path and just * with msysgit. You can also omit the full path and just
* use the executable name to search the user's $PATH. * use the executable name to search the user's $PATH.
* Note: Versions of PHP below 5.2 have buggy handling of spaces * Note: Versions of PHP below 5.2 have buggy handling of spaces
* in paths. Use the 8.3 version of the filename if you're * in paths. Use the 8.3 version of the filename if you're
* having trouble, e.g. C:\\Progra~1\\Git\\bin\\git.exe * having trouble, e.g. C:\\Progra~1\\Git\\bin\\git.exe
*/ */
// Linux: // Linux:
$gitphp_conf['gitbin'] = 'git'; $gitphp_conf['gitbin'] = 'git';
// Windows (msysgit): // Windows (msysgit):
$gitphp_conf['gitbin'] = 'C:\\Progra~1\\Git\\bin\\git.exe'; $gitphp_conf['gitbin'] = 'C:\\Progra~1\\Git\\bin\\git.exe';
   
/* /*
* magicdb * magicdb
* Path to the libmagic db used to read mimetype * Path to the libmagic db used to read mimetype
* Only applies if filemimetype = true * Only applies if filemimetype = true
* You can leave this as null and let the system * You can leave this as null and let the system
* try to find the database for you, but that method * try to find the database for you, but that method
* is known to have issues * is known to have issues
* If the path is correct but it's still not working, * If the path is correct but it's still not working,
* try removing the file extension if you have it on, * try removing the file extension if you have it on,
* or vice versa * or vice versa
*/ */
// Linux: // Linux:
$gitphp_conf['magicdb'] = '/usr/share/misc/magic'; $gitphp_conf['magicdb'] = '/usr/share/misc/magic';
// Windows: // Windows:
$gitphp_conf['magicdb'] = 'C:\\wamp\\php\\extras\\magic'; $gitphp_conf['magicdb'] = 'C:\\wamp\\php\\extras\\magic';
   
   
   
   
   
/******************************************************* /*******************************************************
* Cache options * Cache options
*/ */
   
/* /*
* cache * cache
* Turns on template caching. If in doubt, leave it off * Turns on template caching. If in doubt, leave it off
* You will need to create a directory 'cache' and make it * You will need to create a directory 'cache' and make it
* writable by the server * writable by the server
*/ */
$gitphp_conf['cache'] = false; $gitphp_conf['cache'] = false;
   
/* /*
* objectcache * objectcache
* Turns on object caching. This caches immutable pieces of * Turns on object caching. This caches immutable pieces of
* data from the git repository. You will need to create a * data from the git repository. You will need to create a
* directory 'cache' and make it writable by the server. * directory 'cache' and make it writable by the server.
* This can be used in place of the template cache, or * This can be used in place of the template cache, or
* in addition to it for the maximum benefit. * in addition to it for the maximum benefit.
*/ */
$gitphp_conf['objectcache'] = false; $gitphp_conf['objectcache'] = false;
   
/* /*
* cacheexpire * cacheexpire
* Attempts to automatically expire cache when a new commit renders * Attempts to automatically expire cache when a new commit renders
* it out of date. * it out of date.
* This is a good option for most users because it ensures the cache * This is a good option for most users because it ensures the cache
* is always up to date and users are seeing correct information, * is always up to date and users are seeing correct information,
* although it is a slight performance hit. * although it is a slight performance hit.
* However, if your commits are coming in so quickly that the cache * However, if your commits are coming in so quickly that the cache
* is constantly being expired, turn this off. * is constantly being expired, turn this off.
*/ */
$gitphp_conf['cacheexpire'] = true; $gitphp_conf['cacheexpire'] = true;
   
/* /*
* cachelifetime * cachelifetime
* Sets how long a page will be cached, in seconds * Sets how long a page will be cached, in seconds
* If you are automatically expiring the cache * If you are automatically expiring the cache
* (see the 'cacheexpire' option above), then this can be set * (see the 'cacheexpire' option above), then this can be set
* relatively high - 3600 seconds (1 hour) or even longer. * relatively high - 3600 seconds (1 hour) or even longer.
* -1 means no timeout. * -1 means no timeout.
* If you have turned cacheexpire off because of too many * If you have turned cacheexpire off because of too many
* cache expirations, set this low (5-10 seconds). * cache expirations, set this low (5-10 seconds).
*/ */
$gitphp_conf['cachelifetime'] = 3600; $gitphp_conf['cachelifetime'] = 3600;
   
/* /*
* objectcachelifetime * objectcachelifetime
* Sets how long git objects will be cached, in seconds * Sets how long git objects will be cached, in seconds
* The object cache only stores immutable objects from * The object cache only stores immutable objects from
* the git repository, so there's no harm in setting * the git repository, so there's no harm in setting
* this to a high number. Set to -1 to never expire. * this to a high number. Set to -1 to never expire.
*/ */
$gitphp_conf['objectcachelifetime'] = 86400; $gitphp_conf['objectcachelifetime'] = 86400;
   
/* /*
* objectcachecompress * objectcachecompress
* Sets the size threshold at which objects will be compressed * Sets the size threshold at which objects will be compressed
* when being stored into the object cache. Compression saves * when being stored into the object cache. Compression saves
* cache space but adds a very slight decompression overhead. * cache space but adds a very slight decompression overhead.
* Set to 0 to disable compression. * Set to 0 to disable compression.
*/ */
$gitphp_conf['objectcachecompress'] = 500; $gitphp_conf['objectcachecompress'] = 500;
   
/* /*
* memcache * memcache
* Enables memcache support for caching data, instead of * Enables memcache support for caching data, instead of
* Smarty's standard on-disk cache. * Smarty's standard on-disk cache.
* Only applies if cache = true or objectcache = true (or both) * Only applies if cache = true or objectcache = true (or both)
* Requires either the Memcached or Memcache PHP extensions. * Requires either the Memcached or Memcache PHP extensions.
* This is an array of servers. Each server is specified as an * This is an array of servers. Each server is specified as an
* array. * array.
* Index 0 (required): The server hostname/IP * Index 0 (required): The server hostname/IP
* Index 1 (optional): The port, default is 11211 * Index 1 (optional): The port, default is 11211
* Index 2 (optional): The weight, default is 1 * Index 2 (optional): The weight, default is 1
*/ */
//$gitphp_conf['memcache'] = array( //$gitphp_conf['memcache'] = array(
// array('127.0.0.1', 11211, 2), // array('127.0.0.1', 11211, 2),
// array('memcacheserver1', 11211), // array('memcacheserver1', 11211),
// array('memcacheserver2') // array('memcacheserver2')
//); //);
   
/* /*
* objectmemory * objectmemory
* If set, this will limit the number of git objects GitPHP * If set, this will limit the number of git objects GitPHP
* keeps in PHP's memory during execution, to this specific number. * keeps in PHP's memory during execution, to this specific number.
* This can be set if you have a low memory limit on your * This can be set if you have a low memory limit on your
* webserver. * webserver.
* Please note that setting this too low will severely degrade * Please note that setting this too low will severely degrade
* performance, as GitPHP will have to repeatedly load the same * performance, as GitPHP will have to repeatedly load the same
* objects off of the disk since the limit prevents them from * objects off of the disk since the limit prevents them from
* being kept in memory. It's strongly recommended that you * being kept in memory. It's strongly recommended that you
* turn debug mode on and view the MemoryCache size on various * turn debug mode on and view the MemoryCache size on various
* pages (in the debug output) to get a feel for the size of * pages (in the debug output) to get a feel for the size of
* your projects before setting this. * your projects before setting this.
* 0 means no limit. * 0 means no limit.
*/ */
$gitphp_conf['objectmemory'] = 0; $gitphp_conf['objectmemory'] = 0;
   
   
   
/******************************************************* /*******************************************************
* Debugging options * Debugging options
*/ */
   
/* /*
* debug * debug
* Turns on extra warning messages * Turns on extra warning messages
* Not recommended for production systems, as it will give * Not recommended for production systems, as it will give
* way more info about what's happening than you care about, and * way more info about what's happening than you care about, and
* will screw up non-html output (rss, opml, snapshots, etc) * will screw up non-html output (rss, opml, snapshots, etc)
*/ */
$gitphp_conf['debug'] = false; $gitphp_conf['debug'] = false;
   
/* /*
* benchmark * benchmark
* Turns on extra timestamp and memory benchmarking info * Turns on extra timestamp and memory benchmarking info
* when debug mode is turned on. Generates lots of output. * when debug mode is turned on. Generates lots of output.
*/ */
$gitphp_conf['benchmark'] = false; $gitphp_conf['benchmark'] = false;
   
   
<?php <?php
/** /**
* GitPHP Project config file * GitPHP Project config file
* *
* Copy this example file to config/projects.conf.php * Copy this example file to config/projects.conf.php
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Config * @subpackage Config
*/ */
   
   
/* /*
* git_projects * git_projects
* List of projects * List of projects
* *
* There are several ways to list projects: * There are several ways to list projects:
* *
* 1. Array of projects * 1. Array of projects
*/ */
//$git_projects = array( //$git_projects = array(
// 'gentoo.git' // 'gentoo.git'
// 'core/fbx.git', // 'core/fbx.git',
// 'php/gitphp.git', // 'php/gitphp.git',
// 'php/mdb.git', // 'php/mdb.git',
// 'php/xxcache.git', // 'php/xxcache.git',
// 'websites/bth.git', // 'websites/bth.git',
//); //);
   
   
/* /*
* 2. Path to file with list of projects * 2. Path to file with list of projects
* Points to a flat file with a list of projects, * Points to a flat file with a list of projects,
* one on each line. Can also read Gitosis project lists. * one on each line. Can also read Gitosis project lists.
*/ */
//$git_projects = '/git/projectlist.txt'; //$git_projects = '/git/projectlist.txt';
   
/* /*
* 3. Path to scm-manager repositories.xml file * 3. Path to scm-manager repositories.xml file
* Points to the repository config file used by the scm-manager * Points to the repository config file used by the scm-manager
* program. In order for this to work, the projectroot for GitPHP * program. In order for this to work, the projectroot for GitPHP
* must be the same as the git repository directory configured * must be the same as the git repository directory configured
* in scm-manager * in scm-manager
*/ */
//$git_projects = '~/.scm/config/repositories.xml'; //$git_projects = '~/.scm/config/repositories.xml';
   
/* /*
* 4. Leave commented to read all projects in the project root * 4. Leave commented to read all projects in the project root
*/ */
   
   
/* /*
* git_projects_settings * git_projects_settings
* *
* This is used to specify override settings for individual projects. * This is used to specify override settings for individual projects.
* This is an array, where each key is the project, and the value is an * This is an array, where each key is the project, and the value is an
* array of settings. This can be used with any of the project list * array of settings. This can be used with any of the project list
* methods above. * methods above.
* *
* These config values can also be specified in the project's git config file, * These config values can also be specified in the project's git config file,
* under the section [gitphp]. A setting set in this file will override * under the section [gitphp]. A setting set in this file will override
* the setting set in the project's config file. * the setting set in the project's config file.
* *
* The following settings can be used: * The following settings can be used:
* *
* 'category': the category for the project. * 'category': the category for the project.
* *
* 'owner': the owner of the project. This overrides the actual owner * 'owner': the owner of the project. This overrides the actual owner
* *
* 'description': the description of the project. This overrides the * 'description': the description of the project. This overrides the
* description in the project's description file * description in the project's description file
* *
* 'cloneurl': the full clone url of the project. This overrides the * 'cloneurl': the full clone url of the project. This overrides the
* clone URL setting in the config for this project. * clone URL setting in the config for this project.
* This can also be an empty string to override the global * This can also be an empty string to override the global
* clone url to say that only this project has no clone url. * clone url to say that only this project has no clone url.
* *
* 'pushurl': the full push url of the project. This overrides the * 'pushurl': the full push url of the project. This overrides the
* push URL setting in the config for this project. * push URL setting in the config for this project.
* This can also be an empty string to override the global * This can also be an empty string to override the global
* push url to say that only this project has no push url. * push url to say that only this project has no push url.
* *
* 'bugpattern': the bug number pattern of the project. This overrides * 'bugpattern': the bug number pattern of the project. This overrides
* the bug pattern setting in the config for this project. * the bug pattern setting in the config for this project.
* This can also be an empty string to override the global * This can also be an empty string to override the global
* bug pattern to say that only this project has no bug * bug pattern to say that only this project has no bug
* pattern. * pattern.
* *
* 'bugurl': the bug url for this project. This overrides the bug url * 'bugurl': the bug url for this project. This overrides the bug url
* setting in the config for this project. This can also be * setting in the config for this project. This can also be
* an empty string to override the global bug url to say that * an empty string to override the global bug url to say that
* only this project has no bug url. * only this project has no bug url.
* *
* 'compat': whether this project runs in compatibility mode. (true/false) * 'compat': whether this project runs in compatibility mode. (true/false)
* This overrides the compat setting in the config for this project. * This overrides the compat setting in the config for this project.
* Compatibility mode relies more on the git executable for loading * Compatibility mode relies more on the git executable for loading
* data, at the expense of performance. Use if you are having * data, at the expense of performance. Use if you are having
* trouble loading data for this project. * trouble loading data for this project.
* *
* 'website': the website url for the project. * 'website': the website url for the project.
  *
  * 'allowedusers': an array of usernames allowed to access this project.
  * Anonymous users and logged in users not in this list
  * will not be able to see or access this project.
  * By default, all users will have access to a project if
  * this option is not set.
*/ */
//$git_projects_settings['php/gitphp.git'] = array( //$git_projects_settings['php/gitphp.git'] = array(
// 'category' => 'PHP', // 'category' => 'PHP',
// 'description' => 'GitPHP, a web-based git repository browser in PHP', // 'description' => 'GitPHP, a web-based git repository browser in PHP',
// 'owner' => 'Christopher Han', // 'owner' => 'Christopher Han',
// 'cloneurl' => 'http://git.gitphp.org/gitphp.git', // 'cloneurl' => 'http://git.gitphp.org/gitphp.git',
// 'pushurl' => '', // 'pushurl' => '',
// 'bugpattern' => '/#([0-9]+)/', // 'bugpattern' => '/#([0-9]+)/',
// 'bugurl' => 'http://www.gitphp.org/projects/gitphp/issues/${1}', // 'bugurl' => 'http://www.gitphp.org/projects/gitphp/issues/${1}',
// 'compat' => false, // 'compat' => false,
// 'website' => 'http://www.gitphp.org/' // 'website' => 'http://www.gitphp.org/',
  // 'allowedusers' => array(
  // 'user1',
  // 'user2'
  // )
//); //);
//$git_projects_settings['gentoo.git'] = array( //$git_projects_settings['gentoo.git'] = array(
// 'description' => 'Gentoo portage overlay', // 'description' => 'Gentoo portage overlay',
// 'compat' => true // 'compat' => true
//); //);
//$git_projects_settings['core/fbx.git'] = array( //$git_projects_settings['core/fbx.git'] = array(
// 'description' => 'FBX music player', // 'description' => 'FBX music player',
// 'category' => 'Core' // 'category' => 'Core'
//); //);
//$git_projects_settings['php/mdb.git'] = array( //$git_projects_settings['php/mdb.git'] = array(
// 'category' => 'PHP', // 'category' => 'PHP',
// 'description' => 'MDB: Media Database', // 'description' => 'MDB: Media Database',
// 'cloneurl' => '', // 'cloneurl' => '',
// 'pushurl' => '' // 'pushurl' => ''
//); //);
   
/* /*
* Or, in your project's config file (for example gitphp.git/config): * Or, in your project's config file (for example gitphp.git/config):
* *
* [gitphp] * [gitphp]
* category = PHP * category = PHP
* description = GitPHP, a web-based git repository browser in PHP * description = GitPHP, a web-based git repository browser in PHP
* owner = Chris Han * owner = Chris Han
* cloneurl = http://git.gitphp.org/gitphp.git * cloneurl = http://git.gitphp.org/gitphp.git
* pushurl = * pushurl =
* bugpattern = "/#([0-9]+)/" * bugpattern = "/#([0-9]+)/"
* bugurl = http://www.gitphp.org/projects/gitphp/issues/${1} * bugurl = http://www.gitphp.org/projects/gitphp/issues/${1}
* compat = false * compat = false
* website = http://www.gitphp.org/ * website = http://www.gitphp.org/
  * allowedusers = user1
  * allowedusers = user2
*/ */
   
/* /*
* gitphp.css * gitphp.css
* *
* GitPHP layout functional stylesheet * GitPHP layout functional stylesheet
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2006-2011 Christopher Han * @copyright Copyright (c) 2006-2011 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
   
   
/* /*
* Base styles * Base styles
*/ */
.monospace { .monospace {
font-family: monospace; font-family: monospace;
} }
   
table { table {
border-spacing: 0px; border-spacing: 0px;
} }
   
   
/* /*
* Title bar * Title bar
* (main header with commit message) * (main header with commit message)
*/ */
div.title a.title { div.title a.title {
display: block; display: block;
} }
   
/* /*
* Project list * Project list
*/ */
.projectList .projectRow .projectOwner { .projectList .projectRow .projectOwner {
white-space: nowrap; white-space: nowrap;
} }
   
.projectList .projectRow .projectAge { .projectList .projectRow .projectAge {
white-space: nowrap; white-space: nowrap;
} }
   
.projectList .projectRow .link { .projectList .projectRow .link {
white-space: nowrap; white-space: nowrap;
} }
   
   
/* /*
* Full log view * Full log view
*/ */
div.log_body { div.log_body {
padding: 8px 8px 8px 150px; padding: 8px 8px 8px 150px;
} }
span.age { span.age {
position: relative; position: relative;
float: left; float: left;
width: 142px; width: 142px;
} }
div.log_link { div.log_link {
margin: -5px 0px; margin: -5px 0px;
padding: 0px 8px; padding: 0px 8px;
position: relative; position: relative;
float: left; float: left;
width: 136px; width: 136px;
} }
   
   
/* /*
* Diff display * Diff display
*/ */
.pre { .pre {
white-space: pre; white-space: pre;
} }
   
   
/* /*
* Search box * Search box
*/ */
div.search { div.search {
margin: 4px 8px; margin: 4px 8px;
position: absolute; position: absolute;
top: 56px; top: 56px;
right: 12px right: 12px
} }
   
   
/* /*
* Badges * Badges
*/ */
a.rss_logo { a.rss_logo {
float: right; float: right;
} }
   
   
/* /*
* Language selector * Language selector
*/ */
div.lang_select { div.lang_select {
width: 300px; width: 300px;
float: right; float: right;
text-align: right; text-align: right;
padding-right: 10px; padding-right: 10px;
} }
   
   
/* /*
* Blob/blame display * Blob/blame display
*/ */
table.code td.num { table.code td.num {
white-space: pre; white-space: pre;
} }
   
table.code td.codeline { table.code td.codeline {
white-space: pre; white-space: pre;
} }
   
table.code td.author, table.code td.date { table.code td.author, table.code td.date {
white-space: nowrap; white-space: nowrap;
} }
   
   
/* /*
* Blob view * Blob view
*/ */
td#blameData { td#blameData {
white-space: nowrap; white-space: nowrap;
} }
   
/* /*
* File search view * File search view
*/ */
span.matchline { span.matchline {
white-space: pre; white-space: pre;
} }
   
/* /*
* side-by-side-diff diff * side-by-side-diff diff
*/ */
table.diffTable { table.diffTable {
width: 100%; width: 100%;
white-space: pre-wrap; white-space: pre-wrap;
} }
   
table.diffTable td { table.diffTable td {
width: 50%; width: 50%;
} }
   
/* /*
* side-by-side commitdiff * side-by-side commitdiff
*/ */
div.commitDiffSBS div.SBSTOC .showAll div.commitDiffSBS div.SBSTOC .showAll
{ {
display: none; display: none;
} }
   
/* /*
* login link * login link
*/ */
div.login { div.login {
float: right; float: right;
} }
   
/* /*
* login form * login form
*/ */
div.loginError { div.loginError {
padding: 10px 5px 5px 96px; padding: 10px 5px 5px 96px;
} }
div.loginForm { div.loginForm {
padding: 5px 5px; padding: 5px 5px;
} }
div.loginForm label { div.loginForm div.field {
  padding: 2px 0px;
  }
  div.loginForm div.submit {
  padding: 2px 0px;
  }
  div.loginForm div.field label {
float: left; float: left;
display: block; display: block;
width: 90px; width: 90px;
line-height: 23px; line-height: 22px;
} }
div.loginForm input[type='submit'] { div.loginForm div.submit input[type='submit'] {
margin-left: 92px; margin-left: 92px;
} }
   
   
/* /*
* Geshi styles * Geshi styles
* These are generated by Geshi, don't change them * These are generated by Geshi, don't change them
*/ */
#blobData .de1, #blobData .de2 { #blobData .de1, #blobData .de2 {
font: normal normal 1em/1.2em monospace; font: normal normal 1em/1.2em monospace;
margin: 0; margin: 0;
padding: 0; padding: 0;
background: none; background: none;
vertical-align: top; vertical-align: top;
} }
   
#blobData { #blobData {
font-family: monospace; font-family: monospace;
} }
   
#blobData li, #blobData .li1 { #blobData li, #blobData .li1 {
font-weight: normal; font-weight: normal;
vertical-align: top; vertical-align: top;
} }
   
  /*
  * Debug styles
  */
  .debug {
  border: 0;
  border-spacing: 0;
  width: 100%;
  }
   
  .debug_toggle {
  display: inline-block;
  margin: 3px;
  cursor: pointer;
  }
   
  .debug_key {
  max-width: 100px;
  word-wrap: break-word;
  }
   
  .debug_value {
  max-width: 900px;
  word-wrap: break-word;
  }
   
  .debug_bt {
  white-space: pre;
  display: none;
  }
   
   
/* /*
* gitphpskin.css * gitphpskin.css
* *
* GitPHP look and feel stylesheet * GitPHP look and feel stylesheet
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2006-2011 Christopher Han * @copyright Copyright (c) 2006-2011 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
   
   
/* /*
* Base styles * Base styles
*/ */
body { body {
font-family: sans-serif; font-family: sans-serif;
font-size: 12px; font-size: 12px;
border: solid #d9d8d1; border: solid #d9d8d1;
border-width: 1px; border-width: 1px;
margin: 10px; margin: 10px;
background-color: #ffffff; background-color: #ffffff;
color: #000000; color: #000000;
} }
   
a { a {
color: #0000cc; color: #0000cc;
} }
   
a:hover, a:visited, a:active { a:hover, a:visited, a:active {
color: #880000; color: #880000;
} }
   
.empty { .empty {
/* various empty / no data messages */ /* various empty / no data messages */
color: gray; color: gray;
} }
   
   
/* /*
* Page header * Page header
* (topmost bar with project link, language bar, etc) * (topmost bar with project link, language bar, etc)
*/ */
div.page_header { div.page_header {
height: 25px; height: 25px;
padding: 8px; padding: 8px;
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
background-color: #d9d8d1; background-color: #d9d8d1;
} }
   
div.page_header a:visited, a.header { div.page_header a:visited, a.header {
color: #0000cc; color: #0000cc;
} }
   
div.page_header a:hover { div.page_header a:hover {
color: #880000; color: #880000;
} }
   
div.login { div.login {
padding-top: 2px; padding-top: 2px;
font-size: 15px; font-size: 15px;
font-weight: normal; font-weight: normal;
} }
   
   
   
/* /*
* Navigation header links * Navigation header links
*/ */
div.page_nav { div.page_nav {
padding: 8px; padding: 8px;
} }
   
div.page_nav a:visited { div.page_nav a:visited {
color: #0000cc; color: #0000cc;
} }
   
   
/* /*
* Path header * Path header
* (tree/blob path navigation links) * (tree/blob path navigation links)
*/ */
div.page_path { div.page_path {
padding: 8px; padding: 8px;
border: solid #d9d8d1; border: solid #d9d8d1;
border-width: 0px 0px 1px; border-width: 0px 0px 1px;
} }
   
   
/* /*
* Page footer * Page footer
* (footer bar with project description and atom/rss links) * (footer bar with project description and atom/rss links)
*/ */
div.page_footer { div.page_footer {
height: 17px; height: 17px;
padding: 4px 8px; padding: 4px 8px;
background-color: #d9d8d1; background-color: #d9d8d1;
} }
   
   
/* /*
* Attribution footer * Attribution footer
* (bottommost footer) * (bottommost footer)
*/ */
div.attr_footer { div.attr_footer {
text-align: center; text-align: center;
padding: 4px 8px; padding: 4px 8px;
color: #888888; color: #888888;
font-style: italic; font-style: italic;
} }
   
div.attr_footer a { div.attr_footer a {
color: #888888; color: #888888;
font-style: italic; font-style: italic;
text-decoration: none; text-decoration: none;
} }
   
div.attr_footer a:hover { div.attr_footer a:hover {
text-decoration: underline; text-decoration: underline;
} }
   
   
div.page_footer_text { div.page_footer_text {
float: left; float: left;
color: #555555; color: #555555;
font-style: italic; font-style: italic;
} }
   
div.page_footer_text a { div.page_footer_text a {
color: #555555; color: #555555;
font-style: italic; font-style: italic;
text-decoration: none; text-decoration: none;
} }
   
div.page_footer_text a:hover { div.page_footer_text a:hover {
text-decoration: underline; text-decoration: underline;
} }
   
   
/* /*
* Page body * Page body
*/ */
div.page_body { div.page_body {
padding: 8px; padding: 8px;
} }
   
   
/* /*
* Table displays * Table displays
*/ */
table { table {
padding: 8px 4px; padding: 8px 4px;
} }
   
th { th {
padding: 2px 5px; padding: 2px 5px;
font-size: 12px; font-size: 12px;
text-align: left; text-align: left;
} }
   
tr.light:hover { tr.light:hover {
/* odd rows */ /* odd rows */
background-color: #edece6; background-color: #edece6;
} }
   
tr.dark { tr.dark {
/* even rows */ /* even rows */
background-color: #f6f6f0; background-color: #f6f6f0;
} }
   
tr.dark:hover { tr.dark:hover {
background-color: #edece6; background-color: #edece6;
} }
   
td { td {
padding: 2px 5px; padding: 2px 5px;
font-size: 12px; font-size: 12px;
vertical-align: top; vertical-align: top;
} }
   
td.link { td.link {
/* navigation links on the right side of each row */ /* navigation links on the right side of each row */
padding: 2px 5px; padding: 2px 5px;
font-family: sans-serif; font-family: sans-serif;
font-size: 10px; font-size: 10px;
} }
   
   
/* /*
* Messages * Messages
*/ */
div.message { div.message {
/* used to display information/error message to user */ /* used to display information/error message to user */
padding: 12px; padding: 12px;
} }
   
div.error { div.error {
/* highlights error messages */ /* highlights error messages */
color: #ff0000; color: #ff0000;
} }
   
   
/* /*
* Badges * Badges
*/ */
a.rss_logo { a.rss_logo {
/* the rss/atom/opml/txt buttons */ /* the rss/atom/opml/txt buttons */
padding: 3px 0px; padding: 3px 0px;
width: 35px; width: 35px;
line-height: 10px; line-height: 10px;
border: 1px solid; border: 1px solid;
border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
color: #ffffff; color: #ffffff;
background-color: #ff6600; background-color: #ff6600;
font-weight: bold; font-weight: bold;
font-family: sans-serif; font-family: sans-serif;
font-size: 10px; font-size: 10px;
text-align: center; text-align: center;
text-decoration: none; text-decoration: none;
} }
   
a.rss_logo:hover { a.rss_logo:hover {
background-color: #ee5500; background-color: #ee5500;
} }
   
span.refs a { span.refs a {
/* for both tag and head badges */ /* for both tag and head badges */
color: #000000; color: #000000;
text-decoration: none; text-decoration: none;
} }
   
span.refs a:hover { span.refs a:hover {
color: #880000; color: #880000;
text-decoration: underline; text-decoration: underline;
} }
   
span.tag { span.tag {
/* tag badge */ /* tag badge */
padding: 0px 4px; padding: 0px 4px;
font-size: 10px; font-size: 10px;
font-weight: normal; font-weight: normal;
background-color: #ffffaa; background-color: #ffffaa;
border: 1px solid; border: 1px solid;
border-color: #ffffcc #ffee00 #ffee00 #ffffcc; border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
} }
   
span.head { span.head {
/* head badge */ /* head badge */
padding: 0px 4px; padding: 0px 4px;
font-size: 10px; font-size: 10px;
font-weight: normal; font-weight: normal;
background-color: #aaffaa; background-color: #aaffaa;
border: 1px solid; border: 1px solid;
border-color: #ccffcc #00cc33 #00cc33 #ccffcc; border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
} }
   
   
/* /*
* Title bar * Title bar
* (main header with commit message) * (main header with commit message)
*/ */
div.title { div.title {
padding: 6px 8px; padding: 6px 8px;
background-color: #edece6; background-color: #edece6;
} }
   
div.title a.title { div.title a.title {
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
color: #000000; color: #000000;
} }
   
div.title:hover { div.title:hover {
background-color: #d9d8d1; background-color: #d9d8d1;
} }
   
div.title_text { div.title_text {
padding: 6px 0px; padding: 6px 0px;
border: solid #d9d8d1; border: solid #d9d8d1;
border-width: 0px 0px 1px; border-width: 0px 0px 1px;
} }
   
   
/* /*
* Search box * Search box
*/ */
div.search { div.search {
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
} }
   
   
/* /*
* Language selector * Language selector
*/ */
div.lang_select { div.lang_select {
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
} }
   
   
/* /*
* Full log view * Full log view
*/ */
span.age { span.age {
/* Age display by each log commit */ /* Age display by each log commit */
font-style: italic; font-style: italic;
} }
   
div.log_link { div.log_link {
/* Links by each log commit */ /* Links by each log commit */
font-size: 10px; font-size: 10px;
font-family: sans-serif; font-family: sans-serif;
font-style: normal; font-style: normal;
} }
   
   
/* /*
* Commit view * Commit view
*/ */
div.list_head { div.list_head {
/* Header above commit's changed files (shows # of changed files) */ /* Header above commit's changed files (shows # of changed files) */
padding: 6px 8px 4px; padding: 6px 8px 4px;
border: solid #d9d8d1; border: solid #d9d8d1;
border-width: 1px 0px 0px; border-width: 1px 0px 0px;
font-style: italic; font-style: italic;
} }
   
a.list { a.list {
/* Filename in list of changed files */ /* Filename in list of changed files */
text-decoration: none; text-decoration: none;
color: #000000; color: #000000;
} }
   
a.list:hover { a.list:hover {
text-decoration: underline; text-decoration: underline;
color: #880000; color: #880000;
} }
   
span.commit_title { span.commit_title {
font-weight: bold; font-weight: bold;
} }
   
span.merge_title { span.merge_title {
color: #777777; color: #777777;
} }
   
span.newfile { span.newfile {
color: #008000; color: #008000;
} }
   
span.deletedfile { span.deletedfile {
color: #c00000; color: #c00000;
} }
   
span.changedfile { span.changedfile {
color: #777777; color: #777777;
} }
   
span.movedfile { span.movedfile {
color: #777777; color: #777777;
} }
   
span.latenight { span.latenight {
/* highlights the time if it's after hours */ /* highlights the time if it's after hours */
color: #cc0000; color: #cc0000;
} }
   
span.signedOffBy { span.signedOffBy {
color: gray; color: gray;
} }
   
   
/* /*
* Diff display * Diff display
*/ */
div.pre { div.pre {
/* the entire diff output block */ /* the entire diff output block */
font-family: monospace; font-family: monospace;
font-size: 12px; font-size: 12px;
} }
   
div.diff_info { div.diff_info {
/* the from -> to file header */ /* the from -> to file header */
font-family: monospace; font-family: monospace;
color: #000099; color: #000099;
background-color: #edece6; background-color: #edece6;
font-style: italic; font-style: italic;
} }
   
.diffplus { .diffplus {
color: #008800; color: #008800;
} }
   
.diffminus { .diffminus {
color: #cc0000; color: #cc0000;
} }
   
.diffat { .diffat {
color: #990099; color: #990099;
} }
   
   
/* /*
* side-by-side-diff diff * side-by-side-diff diff
*/ */
table.diffTable { table.diffTable {
font-family: monospace; font-family: monospace;
} }
   
table.diffTable tr.diff-added { table.diffTable tr.diff-added {
background-color: #C1FFC1; background-color: #C1FFC1;
} }
   
table.diffTable tr.diff-modified { table.diffTable tr.diff-modified {
background-color: #DDEEFF; background-color: #DDEEFF;
} }
   
table.diffTable tr.diff-deleted { table.diffTable tr.diff-deleted {
background-color: #FFDDDD; background-color: #FFDDDD;
} }
   
table.diffTable td.diff-left { table.diffTable td.diff-left {
border-right: 1px solid #d9d8d1; border-right: 1px solid #d9d8d1;
} }
   
/* /*
* side-by-side commitdiff * side-by-side commitdiff
*/ */
div.commitDiffSBS div.commitDiffSBS
{ {
width: 100%; width: 100%;
border-top: 2px solid #edece6; border-top: 2px solid #edece6;
} }
   
div.commitDiffSBS div.SBSTOC div.commitDiffSBS div.SBSTOC
{ {
float: left; float: left;
width: 19%; width: 19%;
word-wrap: break-word; word-wrap: break-word;
background-color: #ffffff; background-color: #ffffff;
border-bottom: 1px solid #edece6; border-bottom: 1px solid #edece6;
} }
   
div.commitDiffSBS div.SBSTOC a div.commitDiffSBS div.SBSTOC a
{ {
text-decoration: none; text-decoration: none;
} }
   
div.commitDiffSBS div.SBSTOC ul div.commitDiffSBS div.SBSTOC ul
{ {
margin-left: 8px; margin-left: 8px;
padding-left: 8px; padding-left: 8px;
} }
   
div.commitDiffSBS div.SBSTOC .listcount div.commitDiffSBS div.SBSTOC .listcount
{ {
list-style-type: none; list-style-type: none;
} }
   
div.commitDiffSBS div.SBSTOC .activeItem div.commitDiffSBS div.SBSTOC .activeItem
{ {
background-color: #edece6; background-color: #edece6;
} }
   
div.commitDiffSBS .SBSContent div.commitDiffSBS .SBSContent
{ {
float: right; float: right;
width: 80%; width: 80%;
border-left: 1px solid #edece6; border-left: 1px solid #edece6;
} }
   
div.commitDiffSBS .SBSFooter div.commitDiffSBS .SBSFooter
{ {
clear: both; clear: both;
} }
   
   
/* /*
* Blob/blame display * Blob/blame display
*/ */
a.linenr { a.linenr {
/* Line numbers (non-geshi only) */ /* Line numbers (non-geshi only) */
color: #999999; color: #999999;
text-decoration: none; text-decoration: none;
} }
   
table.code td { table.code td {
/* code table (non-geshi only) */ /* code table (non-geshi only) */
padding: 0px 0px; padding: 0px 0px;
} }
   
table.code td.num { table.code td.num {
text-align: right; text-align: right;
font-family: monospace; font-family: monospace;
font-size: 12px; font-size: 12px;
} }
   
table.code td.codeline { table.code td.codeline {
padding-left: 5px; padding-left: 5px;
font-family: monospace; font-family: monospace;
font-size: 12px; font-size: 12px;
} }
   
table.code tr.light:hover { table.code tr.light:hover {
background-color: #ffffff; background-color: #ffffff;
} }
   
table.code tr.dark:hover { table.code tr.dark:hover {
background-color: #f6f6f0; background-color: #f6f6f0;
} }
   
td#blameData { td#blameData {
/* the blame info column */ /* the blame info column */
text-align: left; text-align: left;
} }
   
td#blameData div.light:hover { td#blameData div.light:hover {
background-color: #edece6; background-color: #edece6;
} }
   
td#blameData div.dark { td#blameData div.dark {
background-color: #f6f6f0; background-color: #f6f6f0;
} }
   
td#blameData div.dark:hover { td#blameData div.dark:hover {
background-color: #edece6; background-color: #edece6;
} }
   
   
/* /*
* Project list page * Project list page
*/ */
div.index_header { div.index_header {
/* the customizable info header above the list of projects */ /* the customizable info header above the list of projects */
border: solid #d9d8d1; border: solid #d9d8d1;
border-width: 0px 0px 1px; border-width: 0px 0px 1px;
padding: 12px 8px; padding: 12px 8px;
} }
   
span.agehighlight { span.agehighlight {
/* highlights recently changed project ages */ /* highlights recently changed project ages */
color: #009900; color: #009900;
} }
   
div.projectSearch { div.projectSearch {
/* the project search box */ /* the project search box */
padding: 8px; padding: 8px;
border: solid #d9d8d1; border: solid #d9d8d1;
border-width: 0px 0px 1px; border-width: 0px 0px 1px;
} }
   
.projectName .indent { .projectName .indent {
/* indents projects underneath a category */ /* indents projects underneath a category */
margin-left: 8px; margin-left: 8px;
} }
   
  .projectRow.disabled {
  color: #888;
  }
   
   
/* /*
* Tree view * Tree view
*/ */
table.treeTable td.filesize { table.treeTable td.filesize {
/* the file size column */ /* the file size column */
text-align: right; text-align: right;
} }
   
table.treeTable td.expander { table.treeTable td.expander {
/* the javascript tree expander cell */ /* the javascript tree expander cell */
padding-right: 0px; padding-right: 0px;
} }
   
   
/* /*
* Tag view * Tag view
*/ */
table.tagTable td.link { table.tagTable td.link {
/* links at the right end of each tag */ /* links at the right end of each tag */
text-align: right; text-align: right;
} }
   
span.pgpSig { span.pgpSig {
color: gray; color: gray;
} }
   
   
/* /*
* Search view * Search view
*/ */
span.searchmatch { span.searchmatch {
/* highlights string matches */ /* highlights string matches */
color: #e00000; color: #e00000;
} }
   
   
/* /*
* Tooltips * Tooltips
*/ */
.ui-tooltip-gitphp { .ui-tooltip-gitphp {
font-size: inherit !important; font-size: inherit !important;
line-height: inherit !important; line-height: inherit !important;
border-width: 2px !important; border-width: 2px !important;
max-width: 500px !important; max-width: 500px !important;
} }
   
  /*
  * Debug styles
  */
  .debug_toggle {
  color: #88a; border-bottom: 1px dashed blue;
  }
   
  .debug_key {
  background: #ccf; border-bottom: 1px solid #888;
  }
   
  .debug_value {
  background: #ccc; border-bottom: 1px solid #888;
  }
   
  .debug_value .debug_addl {
  font-style: italic;
  }
   
  .debug_time {
  background: #cff; border-bottom: 1px solid #888;
  }
   
<?php <?php
/** /**
* Class to handle autoloading other classes * Class to handle autoloading other classes
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
class GitPHP_AutoLoader class GitPHP_AutoLoader
{ {
/** /**
* Autoload a class * Autoload a class
* *
* @param string $classname class name * @param string $classname class name
*/ */
public static function AutoLoad($classname) public static function AutoLoad($classname)
{ {
$filename = GitPHP_AutoLoader::ClassFilename($classname); $filename = GitPHP_AutoLoader::ClassFilename($classname);
   
if (empty($filename)) if (empty($filename))
return; return;
   
$path = dirname(__FILE__) . '/' . $filename; $path = dirname(__FILE__) . '/' . $filename;
   
if (is_readable($path)) if (is_readable($path))
require($path); require($path);
} }
   
/** /**
* Get the path to a class * Get the path to a class
* *
* @param string $classname class name * @param string $classname class name
* @return string path * @return string path
*/ */
public static function ClassFilename($classname) public static function ClassFilename($classname)
{ {
if (empty($classname)) if (empty($classname))
return null; return null;
   
if (strncmp($classname, 'GitPHP_', 7) !== 0) if (strncmp($classname, 'GitPHP_', 7) !== 0)
return null; return null;
   
$classname = substr($classname, 7); $classname = substr($classname, 7);
   
$path = ''; $path = '';
if (strncmp($classname, 'Controller', 10) === 0) { if (strncmp($classname, 'Controller', 10) === 0) {
$path = 'controller/'; $path = 'controller/';
} else if (strncmp($classname, 'ProjectList', 11) === 0) { } else if (strncmp($classname, 'ProjectList', 11) === 0) {
$path = 'git/projectlist/'; $path = 'git/projectlist/';
} else if (strncmp($classname, 'FileMimeType', 12) === 0) { } else if (strncmp($classname, 'FileMimeType', 12) === 0) {
$path = 'git/filemimetype/'; $path = 'git/filemimetype/';
} else if (strncmp($classname, 'RefList', 7) === 0) { } else if (strncmp($classname, 'RefList', 7) === 0) {
$path = 'git/reflist/'; $path = 'git/reflist/';
} else if (strncmp($classname, 'TagList', 7) === 0) { } else if (strncmp($classname, 'TagList', 7) === 0) {
$path = 'git/taglist/'; $path = 'git/taglist/';
} else if (strncmp($classname, 'HeadList', 8) === 0) { } else if (strncmp($classname, 'HeadList', 8) === 0) {
$path = 'git/headlist/'; $path = 'git/headlist/';
} else if (strncmp($classname, 'RevList', 7) === 0) { } else if (strncmp($classname, 'RevList', 7) === 0) {
$path = 'git/revlist/'; $path = 'git/revlist/';
} else if (($classname == 'Project') || (strncmp($classname, 'ProjectLoad', 11) === 0)) { } else if (($classname == 'Project') || (strncmp($classname, 'ProjectLoad', 11) === 0)) {
$path = 'git/project/'; $path = 'git/project/';
} else if (($classname == 'Blob') || (strncmp($classname, 'BlobLoad', 8) === 0)) { } else if (($classname == 'Blob') || (strncmp($classname, 'BlobLoad', 8) === 0)) {
$path = 'git/blob/'; $path = 'git/blob/';
} else if (($classname == 'Commit') || (strncmp($classname, 'CommitLoad', 10) === 0)) { } else if (($classname == 'Commit') || (strncmp($classname, 'CommitLoad', 10) === 0)) {
$path = 'git/commit/'; $path = 'git/commit/';
} else if (($classname == 'Tag') || (strncmp($classname, 'TagLoad', 7) === 0)) { } else if (($classname == 'Tag') || (strncmp($classname, 'TagLoad', 7) === 0)) {
$path = 'git/tag/'; $path = 'git/tag/';
} else if (($classname == 'Tree') || (strncmp($classname, 'TreeLoad', 8) === 0)) { } else if (($classname == 'Tree') || (strncmp($classname, 'TreeLoad', 8) === 0)) {
$path = 'git/tree/'; $path = 'git/tree/';
} else if (($classname == 'Log') || (strncmp($classname, 'LogLoad', 7) === 0)) { } else if (($classname == 'Log') || (strncmp($classname, 'LogLoad', 7) === 0)) {
$path = 'git/log/'; $path = 'git/log/';
} else if (strncmp($classname, 'Archive', 7) === 0) { } else if (strncmp($classname, 'Archive', 7) === 0) {
$path = 'git/archive/'; $path = 'git/archive/';
} else if (strncmp($classname, 'Pack', 4) === 0) { } else if (strncmp($classname, 'Pack', 4) === 0) {
$path = 'git/pack/'; $path = 'git/pack/';
} else if ((strlen($classname) > 9) && (substr_compare($classname, 'Exception', -9, 9) === 0)) { } else if ((strlen($classname) > 9) && (substr_compare($classname, 'Exception', -9, 9) === 0)) {
$path = 'exception/'; $path = 'exception/';
} else if (strpos($classname, 'Cache') !== false) { } else if (strpos($classname, 'Cache') !== false) {
$path = 'cache/'; $path = 'cache/';
} else if (strncmp($classname, 'Route', 5) === 0) { } else if (strncmp($classname, 'Route', 5) === 0) {
$path = 'router/'; $path = 'router/';
} else if (strncmp($classname, 'User', 4) === 0) { } else if (strncmp($classname, 'User', 4) === 0) {
$path = 'auth/'; $path = 'auth/';
} else if (in_array($classname, array( } else if (in_array($classname, array(
'Config', 'Config',
'DebugLog', 'DebugLog',
  'DebugAutoLog',
'Resource', 'Resource',
'Util' 'Util'
))) { ))) {
$path = ''; $path = '';
} else { } else {
$path = 'git/'; $path = 'git/';
} }
   
if ((strlen($classname) > 10) && (substr_compare($classname, '_Interface', -10, 10) === 0)) { if ((strlen($classname) > 10) && (substr_compare($classname, '_Interface', -10, 10) === 0)) {
$classname = substr($classname, 0, -10); $classname = substr($classname, 0, -10);
$path .= $classname . '.interface.php'; $path .= $classname . '.interface.php';
} else { } else {
$path .= $classname . '.class.php'; $path .= $classname . '.class.php';
} }
   
return $path; return $path;
} }
   
} }
   
  <?php
  /**
  * Debug auto logging class (destructor-based)
  *
  * @author Yuriy Nasretdinov <nasretdinov@gmail.com>
  * @copyright Copyright (c) 2013 Christopher Han
  * @package GitPHP
  */
  class GitPHP_DebugAutoLog
  {
  private $name;
 
  public function __construct($name = null)
  {
  if (is_null($name)) {
  if (PHP_VERSION_ID >= 50306)
  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  else
  $trace = debug_backtrace();
  if (!isset($trace[1]['class']) || !isset($trace[1]['function'])) {
  throw new InvalidArgumentException("You need to specify name when not in method context");
  }
  $name = $trace[1]['class'] . '::' . $trace[1]['function'];
  }
  $this->name = $name;
  GitPHP_DebugLog::GetInstance()->TimerStart();
  }
 
  public function __destruct()
  {
  GitPHP_DebugLog::GetInstance()->TimerStop($this->name);
  }
  }
 
<?php <?php
/** /**
* Debug logging class * Debug logging class
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
class GitPHP_DebugLog implements GitPHP_Observer_Interface class GitPHP_DebugLog implements GitPHP_Observer_Interface
{ {
/** /**
* Stores whether logging is enabled * Stores whether logging is enabled
* *
* @var boolean * @var boolean
*/ */
protected $enabled = false; protected $enabled = false;
   
/** /**
* Stores whether benchmarking is enabled * Stores whether benchmarking is enabled
* *
* @var boolean * @var boolean
*/ */
protected $benchmark = false; protected $benchmark = false;
   
/** /**
* Stores the starting instant * Stores the starting instant
* *
* @var float * @var float
*/ */
protected $startTime; protected $startTime;
   
/** /**
* Stores the starting memory * Stores the starting memory
* *
* @var int * @var int
*/ */
protected $startMem; protected $startMem;
   
/** /**
* Stores the log entries * Stores the log entries
* *
* @var string[] * @var string[]
*/ */
protected $entries = array(); protected $entries = array();
   
/** /**
  * Stores the timers
  *
  * @var float[]
  */
  protected $timers = array();
   
  /**
  * @return GitPHP_DebugLog
  */
  public static function GetInstance()
  {
  static $instance;
  if (!$instance) $instance = new self();
  return $instance;
  }
   
  /**
  * You must use GetInstance()
  */
  private function __construct()
  {
  }
   
  /**
* Constructor * Constructor
* *
* @param boolean $enabled whether log should be enabled * @param boolean $enabled whether log should be enabled
* @param boolean $benchmark whether benchmarking should be enabled * @param boolean $benchmark whether benchmarking should be enabled
*/ */
public function __construct($enabled = false, $benchmark = false) public function init($enabled = false, $benchmark = false)
{ {
$this->startTime = microtime(true); $this->startTime = microtime(true);
$this->startMem = memory_get_usage(); $this->startMem = memory_get_usage();
   
$this->enabled = $enabled; $this->enabled = $enabled;
$this->benchmark = $benchmark; $this->benchmark = $benchmark;
} }
   
/** /**
* Sets start time * Sets start time
* *
* @param float $start starting microtime * @param float $start starting microtime
*/ */
public function SetStartTime($start) public function SetStartTime($start)
{ {
$this->startTime = $start; $this->startTime = $start;
} }
   
/** /**
* Sets start memory * Sets start memory
* *
* @param integer $start starting memory * @param integer $start starting memory
*/ */
public function SetStartMemory($start) public function SetStartMemory($start)
{ {
$this->startMem = $start; $this->startMem = $start;
} }
   
/** /**
  * Shortcut to start timer
  */
  public function TimerStart()
  {
  if (!$this->benchmark) return;
  $this->Log('', '', 'start');
  }
   
  /**
  * Shortcut to stop timer
  *
  * @param $msg
  * @param $msg_data
  */
  public function TimerStop($msg, $msg_data = '')
  {
  if (!$this->benchmark) return;
  $this->Log($msg, $msg_data, 'stop');
  }
   
  /**
* Log an entry * Log an entry
* *
* @param string $message message to log * @param string $msg message to log
*/ */
public function Log($message) public function Log($msg, $msg_data = '', $type = 'ts')
{ {
if (!$this->enabled) if (!$this->enabled)
return; return;
   
$entry = array(); $entry = array();
   
if ($this->benchmark) { if ($type == 'start') {
$entry['time'] = microtime(true); array_push($this->timers, microtime(true));
$entry['mem'] = memory_get_usage(); return;
} } else if ($type == 'stop') {
  $timer = array_pop($this->timers);
$entry['msg'] = $message; $entry['time'] = $duration = microtime(true) - $timer;
$this->entries[] = $entry; foreach ($this->timers as &$item) $item += $duration;
} } else {
   
/**  
* Gets whether logging is enabled  
*  
* @return boolean true if logging is enabled  
*/  
public function GetEnabled()  
{  
return $this->enabled;  
}  
   
/**  
* Sets whether logging is enabled  
*  
* @param boolean $enable true if logging is enabled  
*/  
public function SetEnabled($enable)  
{  
$this->enabled = $enable;  
}  
   
/**  
* Gets whether benchmarking is enabled  
*  
* @return boolean true if benchmarking is enabled  
*/  
public function GetBenchmark()  
{  
return $this->benchmark;  
}  
   
/**  
* Sets whether benchmarking is enabled  
*  
* @param boolean $bench true if benchmarking is enabled  
*/  
public function SetBenchmark($bench)  
{  
$this->benchmark = $bench;  
}  
   
/**  
* Gets log entries  
*  
* @return string[] log entries  
*/  
public function GetEntries()  
{  
$data = array();  
   
if ($this->enabled) {  
   
if ($this->benchmark) { if ($this->benchmark) {
$endTime = microtime(true); $entry['time'] = (microtime(true) - $this->startTime);
$endMem = memory_get_usage(); $entry['reltime'] = true;
  $entry['mem'] = memory_get_usage();
$lastTime = $this->startTime;  
$lastMem = $this->startMem;  
   
$data[] = 'DEBUG: [' . $this->startTime . '] [' . $this->startMem . ' bytes] Start';  
   
}  
   
foreach ($this->entries as $entry) {  
if ($this->benchmark) {  
$data[] = 'DEBUG: [' . $entry['time'] . '] [' . ($entry['time'] - $this->startTime) . ' sec since start] [' . ($entry['time'] - $lastTime) . ' sec since last] [' . $entry['mem'] . ' bytes] [' . ($entry['mem'] - $this->startMem) . ' bytes since start] [' . ($entry['mem'] - $lastMem) . ' bytes since last] ' . $entry['msg'];  
$lastTime = $entry['time'];  
$lastMem = $entry['mem'];  
} else {  
$data[] = 'DEBUG: ' . $entry['msg'];  
}  
}  
   
if ($this->benchmark) {  
$data[] = 'DEBUG: [' . $endTime . '] [' . ($endTime - $this->startTime) . ' sec since start] [' . ($endTime - $lastTime) . ' sec since last] [' . $endMem . ' bytes] [' . ($endMem - $this->startMem) . ' bytes since start] [' . ($endMem - $lastMem) . ' bytes since last] End';  
} }
} }
   
return $data; $entry['name'] = $msg;
  $entry['value'] = $msg_data;
  $bt = explode("\n", new Exception());
  array_shift($bt);
  array_shift($bt);
  $entry['bt'] = implode("\n", $bt);
  $this->entries[] = $entry;
  }
   
  /**
  * Gets whether logging is enabled
  *
  * @return boolean true if logging is enabled
  */
  public function GetEnabled()
  {
  return $this->enabled;
  }
   
  /**
  * Sets whether logging is enabled
  *
  * @param boolean $enable true if logging is enabled
  */
  public function SetEnabled($enable)
  {
  $this->enabled = $enable;
  }
   
  /**
  * Gets whether benchmarking is enabled
  *
  * @return boolean true if benchmarking is enabled
  */
  public function GetBenchmark()
  {
  return $this->benchmark;
  }
   
  /**
  * Sets whether benchmarking is enabled
  *
  * @param boolean $bench true if benchmarking is enabled
  */
  public function SetBenchmark($bench)
  {
  $this->benchmark = $bench;
} }
   
/** /**
* Clears the log * Clears the log
*/ */
public function Clear() public function Clear()
{ {
$this->entries = array(); $this->entries = array();
  }
   
  /**
  * Gets the log entries
  *
  * @return array entry data
  */
  public function GetEntries()
  {
  return $this->entries;
} }
   
/** /**
* Notify that observable object changed * Notify that observable object changed
* *
* @param GitPHP_Observable_Interface $object object * @param GitPHP_Observable_Interface $object object
* @param int $changeType type of change * @param int $changeType type of change
* @param array $args argument array * @param array $args argument array
*/ */
public function ObjectChanged($object, $changeType, $args = array()) public function ObjectChanged($object, $changeType, $args = array())
{ {
if ($changeType !== GitPHP_Observer_Interface::LoggableChange) if ($changeType !== GitPHP_Observer_Interface::LoggableChange)
return; return;
   
if (!$this->enabled) if (!$this->enabled)
return; return;
   
if (!isset($args[0]) || empty($args[0])) if (!isset($args[0]) || empty($args[0]))
return; return;
   
$msg = $args[0]; $msg = $args[0];
  $msg_data = !empty($args[1]) ? $args[1] : '';
$this->Log($msg); $type = !empty($args[2]) ? $args[2] : 'ts';
   
  $this->Log($msg, $msg_data, $type);
} }
   
} }
   
<?php <?php
/** /**
* Utility function class * Utility function class
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
class GitPHP_Util class GitPHP_Util
{ {
   
/** /**
* Adds a trailing slash to a directory path if necessary * Adds a trailing slash to a directory path if necessary
* *
* @param string $path path to add slash to * @param string $path path to add slash to
* @param boolean $filesystem true if this is a filesystem path (to also check for backslash for windows paths) * @param boolean $filesystem true if this is a filesystem path (to also check for backslash for windows paths)
* @return string path with a trailing slash * @return string path with a trailing slash
*/ */
public static function AddSlash($path, $filesystem = true) public static function AddSlash($path, $filesystem = true)
{ {
if (empty($path)) if (empty($path))
return $path; return $path;
   
$end = substr($path, -1); $end = substr($path, -1);
   
if (!(( ($end == '/') || ($end == ':')) || ($filesystem && GitPHP_Util::IsWindows() && ($end == '\\')))) { if (!(( ($end == '/') || ($end == ':')) || ($filesystem && GitPHP_Util::IsWindows() && ($end == '\\')))) {
if (GitPHP_Util::IsWindows() && $filesystem) { if (GitPHP_Util::IsWindows() && $filesystem) {
$path .= '\\'; $path .= '\\';
} else { } else {
$path .= '/'; $path .= '/';
} }
} }
   
return $path; return $path;
} }
   
/** /**
* Tests if this is running on windows * Tests if this is running on windows
* *
* @return bool true if on windows * @return bool true if on windows
*/ */
public static function IsWindows() public static function IsWindows()
{ {
return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
} }
   
  public static function NullFile()
  {
  return self::IsWindows() ? 'NUL' : '/dev/null';
  }
   
/** /**
* Tests if this is a 64 bit machine * Tests if this is a 64 bit machine
* *
* @return bool true if on 64 bit * @return bool true if on 64 bit
*/ */
public static function Is64Bit() public static function Is64Bit()
{ {
return (strpos(php_uname('m'), '64') !== false); return (strpos(php_uname('m'), '64') !== false);
} }
   
/** /**
* Turn a string into a filename-friendly slug * Turn a string into a filename-friendly slug
* *
* @param string $str string to slugify * @param string $str string to slugify
* @return string slug * @return string slug
*/ */
public static function MakeSlug($str) public static function MakeSlug($str)
{ {
$from = array( $from = array(
'/' '/&'
); );
$to = array( $to = array(
'-' '--'
); );
return str_replace($from, $to, $str); return str_replace($from, $to, $str);
} }
   
/** /**
* Get the filename of a given path * Get the filename of a given path
* *
* Based on Drupal's basename * Based on Drupal's basename
* *
* @param string $path path * @param string $path path
* @param string $suffix optionally trim this suffix * @param string $suffix optionally trim this suffix
* @return string filename * @return string filename
*/ */
public static function BaseName($path, $suffix = null) public static function BaseName($path, $suffix = null)
{ {
$sep = '/'; $sep = '/';
if (GitPHP_Util::IsWindows()) { if (GitPHP_Util::IsWindows()) {
$sep .= '\\'; $sep .= '\\';
} }
   
$path = rtrim($path, $sep); $path = rtrim($path, $sep);
   
if (!preg_match('@[^' . preg_quote($sep) . ']+$@', $path, $matches)) { if (!preg_match('@[^' . preg_quote($sep) . ']+$@', $path, $matches)) {
return ''; return '';
} }
   
$filename = $matches[0]; $filename = $matches[0];
   
if ($suffix) { if ($suffix) {
$filename = preg_replace('@' . preg_quote($suffix, '@') . '$@', '', $filename); $filename = preg_replace('@' . preg_quote($suffix, '@') . '$@', '', $filename);
} }
return $filename; return $filename;
} }
   
/** /**
* Provides a geshi language for a given filename * Provides a geshi language for a given filename
* *
* @param string $filename file name * @param string $filename file name
* @return string language * @return string language
*/ */
public static function GeshiFilenameToLanguage($filename) public static function GeshiFilenameToLanguage($filename)
{ {
if (strncasecmp($filename, 'Makefile', 8) === 0) { if (strncasecmp($filename, 'Makefile', 8) === 0) {
return 'make'; return 'make';
} }
   
return null; return null;
} }
   
/** /**
* Recurses into a directory and lists files inside * Recurses into a directory and lists files inside
* *
* @param string $dir directory * @param string $dir directory
* @return string[] array of filenames * @return string[] array of filenames
*/ */
public static function ListDir($dir) public static function ListDir($dir)
{ {
$files = array(); $files = array();
if ($dh = opendir($dir)) { if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
if (($file == '.') || ($file == '..')) { if (($file == '.') || ($file == '..')) {
continue; continue;
} }
$fullFile = $dir . '/' . $file; $fullFile = $dir . '/' . $file;
if (is_dir($fullFile)) { if (is_dir($fullFile)) {
$subFiles = GitPHP_Util::ListDir($fullFile); $subFiles = GitPHP_Util::ListDir($fullFile);
if (count($subFiles) > 0) { if (count($subFiles) > 0) {
$files = array_merge($files, $subFiles); $files = array_merge($files, $subFiles);
} }
} else { } else {
$files[] = $fullFile; $files[] = $fullFile;
} }
} }
} }
return $files; return $files;
} }
   
/** /**
* Get the base install url (without index) * Get the base install url (without index)
* *
* @param boolean $full true to return full url (include protocol and hostname) * @param boolean $full true to return full url (include protocol and hostname)
* @return string base url * @return string base url
*/ */
public static function BaseUrl($full = false) public static function BaseUrl($full = false)
{ {
$baseurl = $_SERVER['SCRIPT_NAME']; $baseurl = $_SERVER['SCRIPT_NAME'];
if (substr_compare($baseurl, 'index.php', -9) === 0) if (substr_compare($baseurl, 'index.php', -9) === 0)
$baseurl = dirname($baseurl); $baseurl = dirname($baseurl);
if ($full) { if ($full) {
$baseurl = $_SERVER['HTTP_HOST'] . $baseurl; $baseurl = $_SERVER['HTTP_HOST'] . $baseurl;
if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on'))
$baseurl = 'https://' . $baseurl; $baseurl = 'https://' . $baseurl;
else else
$baseurl = 'http://' . $baseurl; $baseurl = 'http://' . $baseurl;
} }
  if (GitPHP_Util::IsWindows())
  $baseurl = rtrim($baseurl, "\\");
return rtrim($baseurl, "/"); return rtrim($baseurl, "/");
  }
   
  /**
  * Tests whether a function is allowed to be called
  *
  * @param string $function functio name
  * @return true if allowed
  */
  public static function FunctionAllowed($function)
  {
  if (empty($function))
  return false;
   
  $disabled = @ini_get('disable_functions');
  if (!$disabled) {
  // no disabled functions
  // or ini_get is disabled so we can't reliably figure this out
  return true;
  }
   
  $disabledlist = explode(', ', $disabled);
  return !in_array($function, $disabledlist);
} }
   
} }
   
<?php <?php
/** /**
* Base class that all controllers extend * Base class that all controllers extend
* *
* @author Christopher Han <xiphux@gmail.com * @author Christopher Han <xiphux@gmail.com
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
abstract class GitPHP_ControllerBase abstract class GitPHP_ControllerBase
{ {
   
/** /**
* Config handler instance * Config handler instance
* *
* @var GitPHP_Config * @var GitPHP_Config
*/ */
protected $config; protected $config;
   
/** /**
* User list instance * User list instance
* *
* @var GitPHP_UserList * @var GitPHP_UserList
*/ */
protected $userList; protected $userList;
   
/** /**
* Resource handler instance * Resource handler instance
* *
* @var GitPHP_Resource * @var GitPHP_Resource
*/ */
protected $resource; protected $resource;
   
/** /**
* Smarty instance * Smarty instance
* *
* @var Smarty * @var Smarty
*/ */
protected $tpl; protected $tpl;
   
/** /**
* Project list * Project list
* *
* @var GitPHP_ProjectListBase * @var GitPHP_ProjectListBase
*/ */
protected $projectList; protected $projectList;
   
/** /**
* Current project * Current project
* *
* @var GitPHP_Project * @var GitPHP_Project
*/ */
protected $project; protected $project;
   
/** /**
* Flag if this is a multi project controller * Flag if this is a multi project controller
* *
* @var boolean * @var boolean
*/ */
protected $multiProject; protected $multiProject;
   
/** /**
* Parameters * Parameters
* *
* @var array * @var array
*/ */
protected $params = array(); protected $params = array();
   
/** /**
* HTTP Headers * HTTP Headers
* *
* @var string[] * @var string[]
*/ */
protected $headers = array(); protected $headers = array();
   
/** /**
* Flag to preserve whitespace in output (for non-html output) * Flag to preserve whitespace in output (for non-html output)
* *
* @var boolean * @var boolean
*/ */
protected $preserveWhitespace = false; protected $preserveWhitespace = false;
   
/** /**
* Logger instance * Logger instance
* *
* @var GitPHP_DebugLog * @var GitPHP_DebugLog
*/ */
protected $log; protected $log;
   
/** /**
* Git executable instance * Git executable instance
* *
* @var GitPHP_GitExe * @var GitPHP_GitExe
*/ */
protected $exe; protected $exe;
   
/** /**
* Url router instance * Url router instance
* *
* @var GitPHP_Router * @var GitPHP_Router
*/ */
protected $router; protected $router;
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
$this->InitializeConfig(); $this->InitializeConfig();
   
$this->InitializeResource(); $this->InitializeResource();
   
$this->InitializeUserList(); $this->InitializeUserList();
   
$this->EnableLogging(); $this->EnableLogging();
   
$this->InitializeGitExe(); $this->InitializeGitExe();
   
$this->InitializeProjectList(); $this->InitializeProjectList();
   
$this->InitializeSmarty(); $this->InitializeSmarty();
   
if ($this->multiProject) { if ($this->multiProject) {
$this->projectList->LoadProjects(); $this->projectList->LoadProjects();
} }
   
if (!empty($this->params['project'])) { if (!empty($this->params['project'])) {
$project = $this->projectList->GetProject($this->params['project']); $project = $this->projectList->GetProject($this->params['project']);
if (!$project) { if (!$project) {
throw new GitPHP_InvalidProjectParameterException($this->params['project']); throw new GitPHP_InvalidProjectParameterException($this->params['project']);
} }
  if ($this->userList && ($this->userList->GetCount() > 0)) {
  if (!$project->UserCanAccess((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null))) {
  throw new GitPHP_UnauthorizedProjectException($this->params['project']);
  }
  }
$this->project = $project->GetProject(); $this->project = $project->GetProject();
} }
   
if (!($this->project || $this->multiProject)) { if (!($this->project || $this->multiProject)) {
throw new GitPHP_MissingProjectParameterException(); throw new GitPHP_MissingProjectParameterException();
} }
} }
   
/** /**
* Initialize config * Initialize config
*/ */
protected function InitializeConfig() protected function InitializeConfig()
{ {
$this->config = new GitPHP_Config(); $this->config = new GitPHP_Config();
$this->config->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php'); $this->config->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php');
} }
   
/** /**
* Initialize resource manager * Initialize resource manager
*/ */
protected function InitializeResource() protected function InitializeResource()
{ {
$locale = null; $locale = null;
   
$baseurl = GitPHP_Util::BaseUrl(); $baseurl = GitPHP_Util::BaseUrl();
  if (empty($baseurl) && $this->config->GetValue('cleanurl'))
  $baseurl = '/';
   
if (!empty($this->params['lang'])) { if (!empty($this->params['lang'])) {
/* /*
* User picked something * User picked something
*/ */
setcookie(GitPHP_Resource::LocaleCookie, $this->params['lang'], time()+GitPHP_Resource::LocaleCookieLifetime, $baseurl); setcookie(GitPHP_Resource::LocaleCookie, $this->params['lang'], time()+GitPHP_Resource::LocaleCookieLifetime, $baseurl);
$locale = $this->params['lang']; $locale = $this->params['lang'];
} else if (!empty($_COOKIE[GitPHP_Resource::LocaleCookie])) { } else if (!empty($_COOKIE[GitPHP_Resource::LocaleCookie])) {
/** /**
* Returning user with a preference * Returning user with a preference
*/ */
$locale = $_COOKIE[GITPHP_Resource::LocaleCookie]; $locale = $_COOKIE[GITPHP_Resource::LocaleCookie];
} else { } else {
/* /*
* User's first time here, try by HTTP_ACCEPT_LANGUAGE * User's first time here, try by HTTP_ACCEPT_LANGUAGE
*/ */
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$httpAcceptLang = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);  
$locale = GitPHP_Resource::FindPreferredLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']); $locale = GitPHP_Resource::FindPreferredLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
if (!empty($locale)) { if (!empty($locale)) {
setcookie(GitPHP_Resource::LocaleCookie, $locale, time()+GitPHP_Resource::LocaleCookieLifetime, $baseurl); setcookie(GitPHP_Resource::LocaleCookie, $locale, time()+GitPHP_Resource::LocaleCookieLifetime, $baseurl);
} }
} }
} }
   
if (empty($locale) && $this->config) { if (empty($locale) && $this->config) {
/* /*
* No preference, fall back on setting * No preference, fall back on setting
*/ */
$locale = $this->config->GetValue('locale'); $locale = $this->config->GetValue('locale');
} }
   
if (!empty($locale) && ($locale != 'en_US')) { if (!empty($locale) && ($locale != 'en_US')) {
try { try {
$this->resource = new GitPHP_Resource($locale); $this->resource = new GitPHP_Resource($locale);
} catch (Exception $e) { } catch (Exception $e) {
} }
} }
} }
   
/** /**
* Initialize user list * Initialize user list
*/ */
public function InitializeUserList() public function InitializeUserList()
{ {
$this->userList = new GitPHP_UserList(); $this->userList = new GitPHP_UserList();
$this->userList->LoadUsers(GITPHP_CONFIGDIR . 'users.conf.php'); $this->userList->LoadUsers(GITPHP_CONFIGDIR . 'users.conf.php');
if ($this->userList->GetCount() > 0) { if ($this->userList->GetCount() > 0) {
if (!isset($_SESSION)) if (!isset($_SESSION))
session_start(); session_start();
} }
} }
   
/** /**
* Initialize executable * Initialize executable
* *
* @param boolean $validate whether the exe should be validated * @param boolean $validate whether the exe should be validated
*/ */
protected function InitializeGitExe($validate = true) protected function InitializeGitExe($validate = true)
{ {
$this->exe = new GitPHP_GitExe($this->config->GetValue('gitbin')); $this->exe = new GitPHP_GitExe($this->config->GetValue('gitbin'));
if ($this->log) if ($this->log)
$this->exe->AddObserver($this->log); $this->exe->AddObserver($this->log);
if ($validate && !$this->exe->Valid()) { if ($validate && !$this->exe->Valid()) {
throw new GitPHP_InvalidGitExecutableException($this->exe->GetBinary()); throw new GitPHP_InvalidGitExecutableException($this->exe->GetBinary());
} }
} }
   
/** /**
* Initialize project list * Initialize project list
*/ */
protected function InitializeProjectList() protected function InitializeProjectList()
{ {
if (file_exists(GITPHP_CONFIGDIR . 'projects.conf.php')) { if (file_exists(GITPHP_CONFIGDIR . 'projects.conf.php')) {
$this->projectList = GitPHP_ProjectList::Instantiate($this->config, GITPHP_CONFIGDIR . 'projects.conf.php', false); $this->projectList = GitPHP_ProjectList::Instantiate($this->config, GITPHP_CONFIGDIR . 'projects.conf.php', false);
} else { } else {
$this->projectList = GitPHP_ProjectList::Instantiate($this->config, GITPHP_CONFIGDIR . 'gitphp.conf.php', true); $this->projectList = GitPHP_ProjectList::Instantiate($this->config, GITPHP_CONFIGDIR . 'gitphp.conf.php', true);
} }
   
$this->projectList->SetMemoryCache(new GitPHP_MemoryCache($this->config->GetValue('objectmemory'))); $this->projectList->SetMemoryCache(new GitPHP_MemoryCache($this->config->GetValue('objectmemory')));
if ($this->config->GetValue('objectcache')) { if ($this->config->GetValue('objectcache')) {
$strategy = null; $strategy = null;
$servers = $this->config->GetValue('memcache'); $servers = $this->config->GetValue('memcache');
if ($servers) { if ($servers) {
if (class_exists('Memcached')) { if (class_exists('Memcached')) {
$strategy = new GitPHP_Cache_Memcached($servers); $strategy = new GitPHP_Cache_Memcached($servers);
} else if (class_exists('Memcache')) { } else if (class_exists('Memcache')) {
$strategy = new GitPHP_Cache_Memcache($servers); $strategy = new GitPHP_Cache_Memcache($servers);
} else { } else {
throw new GitPHP_MissingMemcacheException(); throw new GitPHP_MissingMemcacheException();
} }
} else { } else {
$strategy = new GitPHP_Cache_File(GITPHP_CACHEDIR . 'objects', $this->config->GetValue('objectcachecompress')); $strategy = new GitPHP_Cache_File(GITPHP_CACHEDIR . 'objects', $this->config->GetValue('objectcachecompress'));
} }
$cache = new GitPHP_Cache($strategy); $cache = new GitPHP_Cache($strategy);
$cache->SetLifetime($this->config->GetValue('objectcachelifetime')); $cache->SetLifetime($this->config->GetValue('objectcachelifetime'));
$this->projectList->SetCache($cache); $this->projectList->SetCache($cache);
} }
   
$this->projectList->SetExe($this->exe); $this->projectList->SetExe($this->exe);
   
if ($this->log) if ($this->log)
$this->projectList->AddObserver($this->log); $this->projectList->AddObserver($this->log);
   
} }
   
/** /**
* Initialize smarty * Initialize smarty
*/ */
protected function InitializeSmarty() protected function InitializeSmarty()
{ {
require_once(GITPHP_SMARTYDIR . 'Smarty.class.php'); require_once(GITPHP_SMARTYDIR . 'Smarty.class.php');
$this->tpl = new Smarty; $this->tpl = new Smarty;
$this->tpl->error_reporting = E_ALL & ~E_NOTICE; $this->tpl->error_reporting = E_ALL & ~E_NOTICE;
$this->tpl->merge_compiled_includes = true; $this->tpl->merge_compiled_includes = true;
$this->tpl->addPluginsDir(GITPHP_INCLUDEDIR . 'smartyplugins'); $this->tpl->addPluginsDir(GITPHP_INCLUDEDIR . 'smartyplugins');
   
if ($this->config->GetValue('cache')) { if ($this->config->GetValue('cache')) {
$cacheDir = GITPHP_CACHEDIR . 'templates'; $cacheDir = GITPHP_CACHEDIR . 'templates';
   
if (file_exists($cacheDir)) { if (file_exists($cacheDir)) {
if (!is_dir($cacheDir)) { if (!is_dir($cacheDir)) {
throw new Exception($cacheDir . ' exists but is not a directory'); throw new Exception($cacheDir . ' exists but is not a directory');
} else if (!is_writable($cacheDir)) { } else if (!is_writable($cacheDir)) {
throw new Exception($cacheDir . ' is not writable'); throw new Exception($cacheDir . ' is not writable');
} }
} else { } else {
if (!mkdir($cacheDir, 0777)) if (!mkdir($cacheDir, 0777))
throw new Exception($cacheDir . ' could not be created'); throw new Exception($cacheDir . ' could not be created');
chmod($cacheDir, 0777); chmod($cacheDir, 0777);
} }
$this->tpl->setCacheDir($cacheDir); $this->tpl->setCacheDir($cacheDir);
   
$this->tpl->caching = Smarty::CACHING_LIFETIME_SAVED; $this->tpl->caching = Smarty::CACHING_LIFETIME_SAVED;
if ($this->config->HasKey('cachelifetime')) { if ($this->config->HasKey('cachelifetime')) {
$this->tpl->cache_lifetime = $this->config->GetValue('cachelifetime'); $this->tpl->cache_lifetime = $this->config->GetValue('cachelifetime');
} }
   
$servers = $this->config->GetValue('memcache'); $servers = $this->config->GetValue('memcache');
if (isset($servers) && is_array($servers) && (count($servers) > 0)) { if (isset($servers) && is_array($servers) && (count($servers) > 0)) {
$this->tpl->registerCacheResource('memcache', new GitPHP_CacheResource_Memcache($servers)); $this->tpl->registerCacheResource('memcache', new GitPHP_CacheResource_Memcache($servers));
$this->tpl->caching_type = 'memcache'; $this->tpl->caching_type = 'memcache';
} }
   
} }
   
} }
   
/** /**
* Set router instance * Set router instance
* *
* @param GitPHP_Router $router router * @param GitPHP_Router $router router
*/ */
public function SetRouter($router) public function SetRouter($router)
{ {
$this->router = $router; $this->router = $router;
} }
   
/** /**
* Get config instance * Get config instance
* *
* @return GitPHP_Config * @return GitPHP_Config
*/ */
public function GetConfig() public function GetConfig()
{ {
return $this->config; return $this->config;
} }
   
/** /**
* Get log instance * Get log instance
* *
* @return GitPHP_DebugLog * @return GitPHP_DebugLog
*/ */
public function GetLog() public function GetLog()
{ {
return $this->log; return $this->log;
} }
   
/** /**
* Enable logging * Enable logging
*/ */
public function EnableLogging() public function EnableLogging()
{ {
if ($this->log) if ($this->log)
return; return;
   
$debug = $this->config->GetValue('debug'); $debug = $this->config->GetValue('debug');
if ($debug) { if ($debug) {
$this->log = new GitPHP_DebugLog($debug, $this->config->GetValue('benchmark')); $this->log = GitPHP_DebugLog::GetInstance();
  $this->log->init($debug, $this->config->GetValue('benchmark'));
$this->log->SetStartTime(GITPHP_START_TIME); $this->log->SetStartTime(GITPHP_START_TIME);
$this->log->SetStartMemory(GITPHP_START_MEM); $this->log->SetStartMemory(GITPHP_START_MEM);
if ($this->exe) if ($this->exe)
$this->exe->AddObserver($this->log); $this->exe->AddObserver($this->log);
if ($this->projectList) if ($this->projectList)
$this->projectList->AddObserver($this->log); $this->projectList->AddObserver($this->log);
} }
} }
   
/** /**
* Disable logging * Disable logging
*/ */
protected function DisableLogging() protected function DisableLogging()
{ {
if (!$this->log) if (!$this->log)
return; return;
   
if ($this->projectList) if ($this->projectList)
$this->projectList->RemoveObserver($this->log); $this->projectList->RemoveObserver($this->log);
if ($this->exe) if ($this->exe)
$this->exe->RemoveObserver($this->log); $this->exe->RemoveObserver($this->log);
   
$this->log->SetEnabled(false); $this->log->SetEnabled(false);
   
$this->log = null; $this->log = null;
} }
   
/** /**
* Gets the project for this controller * Gets the project for this controller
* *
* @return GitPHP_Project|null project * @return GitPHP_Project|null project
*/ */
public function GetProject() public function GetProject()
{ {
if ($this->project) if ($this->project)
return $this->projectList->GetProject($this->project); return $this->projectList->GetProject($this->project);
return null; return null;
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected abstract function GetTemplate(); protected abstract function GetTemplate();
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected abstract function GetCacheKey(); protected abstract function GetCacheKey();
   
/** /**
* Get the prefix for all cache keys * Get the prefix for all cache keys
* *
* @param boolean $projectKeys include project-specific key pieces * @param boolean $projectKeys include project-specific key pieces
* @return string cache key prefix * @return string cache key prefix
*/ */
private function GetCacheKeyPrefix($projectKeys = true) private function GetCacheKeyPrefix($projectKeys = true)
{ {
if ($this->resource) if ($this->resource)
$cacheKeyPrefix = $this->resource->GetLocale(); $cacheKeyPrefix = $this->resource->GetLocale();
else else
$cacheKeyPrefix = 'en_US'; $cacheKeyPrefix = 'en_US';
   
if ($this->projectList) { if ($this->projectList) {
$cacheKeyPrefix .= '|' . sha1(serialize($this->projectList->GetProjectListConfig())) . '|' . sha1(serialize($this->projectList->GetProjectSettings())); $cacheKeyPrefix .= '|' . sha1(serialize($this->projectList->GetProjectListConfig())) . '|' . sha1(serialize($this->projectList->GetProjectSettings()));
} }
if ($this->project && $projectKeys) { if ($this->project && $projectKeys) {
$cacheKeyPrefix .= '|' . sha1($this->project); $cacheKeyPrefix .= '|' . sha1($this->project);
} }
   
return $cacheKeyPrefix; return $cacheKeyPrefix;
} }
   
/** /**
* Get the full cache key * Get the full cache key
* *
* @return string full cache key * @return string full cache key
*/ */
protected function GetFullCacheKey() protected function GetFullCacheKey()
{ {
$cacheKey = $this->GetCacheKeyPrefix(); $cacheKey = $this->GetCacheKeyPrefix();
   
$subCacheKey = $this->GetCacheKey(); $subCacheKey = $this->GetCacheKey();
   
if (!empty($subCacheKey)) if (!empty($subCacheKey))
$cacheKey .= '|' . $subCacheKey; $cacheKey .= '|' . $subCacheKey;
   
if (strlen($cacheKey) > 100) { if (strlen($cacheKey) > 100) {
$cacheKey = sha1($cacheKey); $cacheKey = sha1($cacheKey);
} }
   
return $cacheKey; return $cacheKey;
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public abstract function GetName($local = false); public abstract function GetName($local = false);
   
/** /**
* Set a parameter * Set a parameter
* *
* @param string $key key to set * @param string $key key to set
* @param mixed $value value to set * @param mixed $value value to set
*/ */
public function SetParam($key, $value) public function SetParam($key, $value)
{ {
if (empty($key)) if (empty($key))
return; return;
   
if (empty($value)) if (empty($value))
unset($this->params[$key]); unset($this->params[$key]);
   
if (is_string($value)) if (is_string($value))
$value = str_replace(chr(0), '', $value); $value = str_replace(chr(0), '', $value);
   
$this->params[$key] = $value; $this->params[$key] = $value;
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
$this->headers[] = 'Content-Type: text/html; charset=UTF-8'; $this->headers[] = 'Content-Type: text/html; charset=UTF-8';
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected abstract function LoadData(); protected abstract function LoadData();
   
/** /**
* Loads common data used by all templates * Loads common data used by all templates
*/ */
private function LoadCommonData() private function LoadCommonData()
{ {
global $gitphp_version, $gitphp_appstring; global $gitphp_version, $gitphp_appstring;
   
$this->tpl->assign('version', $gitphp_version); $this->tpl->assign('version', $gitphp_version);
   
$stylesheet = $this->config->GetValue('stylesheet'); $stylesheet = $this->config->GetValue('stylesheet');
if ($stylesheet == 'gitphp.css') { if ($stylesheet == 'gitphp.css') {
// backwards compatibility // backwards compatibility
$stylesheet = 'gitphpskin.css'; $stylesheet = 'gitphpskin.css';
} }
$this->tpl->assign('stylesheet', preg_replace('/\.css$/', '', $stylesheet)); $this->tpl->assign('stylesheet', preg_replace('/\.css$/', '', $stylesheet));
   
$this->tpl->assign('javascript', $this->config->GetValue('javascript')); $this->tpl->assign('javascript', $this->config->GetValue('javascript'));
$this->tpl->assign('googlejs', $this->config->GetValue('googlejs')); $this->tpl->assign('googlejs', $this->config->GetValue('googlejs'));
if ($this->config->HasKey('title')) { if ($this->config->HasKey('title')) {
$this->tpl->assign('pagetitle', $this->config->GetValue('title')); $this->tpl->assign('pagetitle', $this->config->GetValue('title'));
} else { } else {
$this->tpl->assign('pagetitle', $gitphp_appstring); $this->tpl->assign('pagetitle', $gitphp_appstring);
} }
if ($this->config->HasKey('homelink')) { if ($this->config->HasKey('homelink')) {
$this->tpl->assign('homelink', $this->config->GetValue('homelink')); $this->tpl->assign('homelink', $this->config->GetValue('homelink'));
} else { } else {
if ($this->resource) if ($this->resource)
$this->tpl->assign('homelink', $this->resource->translate('projects')); $this->tpl->assign('homelink', $this->resource->translate('projects'));
else else
$this->tpl->assign('homelink', 'projects'); $this->tpl->assign('homelink', 'projects');
} }
$this->tpl->assign('action', $this->GetName()); $this->tpl->assign('action', $this->GetName());
$this->tpl->assign('actionlocal', $this->GetName(true)); $this->tpl->assign('actionlocal', $this->GetName(true));
if ($this->project) if ($this->project)
$this->tpl->assign('project', $this->GetProject()); $this->tpl->assign('project', $this->GetProject());
if ($this->config->GetValue('search')) if ($this->config->GetValue('search'))
$this->tpl->assign('enablesearch', true); $this->tpl->assign('enablesearch', true);
if ($this->config->GetValue('filesearch')) if ($this->config->GetValue('filesearch'))
$this->tpl->assign('filesearch', true); $this->tpl->assign('filesearch', true);
if (isset($this->params['search'])) if (isset($this->params['search']))
$this->tpl->assign('search', $this->params['search']); $this->tpl->assign('search', $this->params['search']);
if (isset($this->params['searchtype'])) if (isset($this->params['searchtype']))
$this->tpl->assign('searchtype', $this->params['searchtype']); $this->tpl->assign('searchtype', $this->params['searchtype']);
if ($this->resource) { if ($this->resource) {
$this->tpl->assign('currentlocale', $this->resource->GetLocale()); $this->tpl->assign('currentlocale', $this->resource->GetLocale());
$this->tpl->assign('currentprimarylocale', $this->resource->GetPrimaryLocale()); $this->tpl->assign('currentprimarylocale', $this->resource->GetPrimaryLocale());
$this->tpl->assign('resource', $this->resource); $this->tpl->assign('resource', $this->resource);
} else { } else {
$this->tpl->assign('currentlocale', 'en_US'); $this->tpl->assign('currentlocale', 'en_US');
$this->tpl->assign('currentprimarylocale', 'en'); $this->tpl->assign('currentprimarylocale', 'en');
} }
$this->tpl->assign('supportedlocales', GitPHP_Resource::SupportedLocales(true)); $this->tpl->assign('supportedlocales', GitPHP_Resource::SupportedLocales(true));
if ($this->config->GetValue('graphs')) if ($this->config->GetValue('graphs'))
$this->tpl->assign('enablegraphs', true); $this->tpl->assign('enablegraphs', true);
   
$this->tpl->assign('baseurl', GitPHP_Util::BaseUrl()); $this->tpl->assign('baseurl', GitPHP_Util::BaseUrl());
   
$requesturl = $_SERVER['REQUEST_URI']; $requesturl = $_SERVER['REQUEST_URI'];
$querypos = strpos($requesturl, '?'); $querypos = strpos($requesturl, '?');
if ($querypos !== false) if ($querypos !== false)
$requesturl = substr($requesturl, 0, $querypos); $requesturl = substr($requesturl, 0, $querypos);
$this->tpl->assign('requesturl', $requesturl); $this->tpl->assign('requesturl', $requesturl);
   
if ($this->router) { if ($this->router) {
$this->router->SetCleanUrl($this->config->GetValue('cleanurl') ? true : false); $this->router->SetCleanUrl($this->config->GetValue('cleanurl') ? true : false);
$this->router->SetAbbreviate($this->config->GetValue('abbreviateurl') ? true : false); $this->router->SetAbbreviate($this->config->GetValue('abbreviateurl') ? true : false);
if ($this->config->HasKey('self')) { if ($this->config->GetValue('self')) {
$this->router->SetBaseUrl($this->config->GetValue('self')); $this->router->SetBaseUrl($this->config->GetValue('self'));
} }
$this->tpl->assign('router', $this->router); $this->tpl->assign('router', $this->router);
} }
   
$getvars = explode('&', $_SERVER['QUERY_STRING']); $getvars = array();
  if (isset($_SERVER['QUERY_STRING'])) {
  $getvars = explode('&', $_SERVER['QUERY_STRING']);
  }
   
$getvarsmapped = array(); $getvarsmapped = array();
foreach ($getvars as $varstr) { foreach ($getvars as $varstr) {
$eqpos = strpos($varstr, '='); $eqpos = strpos($varstr, '=');
if ($eqpos > 0) { if ($eqpos > 0) {
$var = substr($varstr, 0, $eqpos); $var = substr($varstr, 0, $eqpos);
$val = substr($varstr, $eqpos + 1); $val = substr($varstr, $eqpos + 1);
if (!(empty($var) || empty($val) || ($var == 'q'))) { if (!(empty($var) || empty($val) || ($var == 'q'))) {
$getvarsmapped[$var] = urldecode($val); $getvarsmapped[$var] = urldecode($val);
} }
} }
} }
$this->tpl->assign('requestvars', $getvarsmapped); $this->tpl->assign('requestvars', $getvarsmapped);
   
$this->tpl->assign('snapshotformats', GitPHP_Archive::SupportedFormats()); $this->tpl->assign('snapshotformats', GitPHP_Archive::SupportedFormats());
   
if ($this->userList && ($this->userList->GetCount() > 0)) { if ($this->userList && ($this->userList->GetCount() > 0)) {
$this->tpl->assign('loginenabled', true); $this->tpl->assign('loginenabled', true);
if (!empty($_SESSION['gitphpuser'])) { if (!empty($_SESSION['gitphpuser'])) {
$user = $this->userList->GetUser($_SESSION['gitphpuser']); $user = $this->userList->GetUser($_SESSION['gitphpuser']);
if ($user) { if ($user) {
$this->tpl->assign('loggedinuser', $user->GetUsername()); $this->tpl->assign('loggedinuser', $user->GetUsername());
} }
} }
} }
   
  if ($this->log && $this->log->GetEnabled()) {
  $this->tpl->assign('debug', true);
  }
} }
   
/** /**
* Renders any special headers * Renders any special headers
*/ */
public function RenderHeaders() public function RenderHeaders()
{ {
$this->LoadHeaders(); $this->LoadHeaders();
   
if (count($this->headers) > 0) { if (count($this->headers) > 0) {
$hascontenttype = false; $hascontenttype = false;
foreach ($this->headers as $hdr) { foreach ($this->headers as $hdr) {
if (empty($hdr)) if (empty($hdr))
continue; continue;
   
if (strncmp($hdr, 'Content-Type:', 13) === 0) { if (strncmp($hdr, 'Content-Type:', 13) === 0) {
if ($hascontenttype) if ($hascontenttype)
throw new Exception('Duplicate Content-Type header'); throw new Exception('Duplicate Content-Type header');
$hascontenttype = true; $hascontenttype = true;
} }
header($hdr); header($hdr);
} }
} }
} }
   
/** /**
* Renders the output * Renders the output
*/ */
public function Render() public function Render()
{ {
if (($this->config->GetValue('cache') == true) && ($this->config->GetValue('cacheexpire') === true)) if (($this->config->GetValue('cache') == true) && ($this->config->GetValue('cacheexpire') === true))
$this->CacheExpire(); $this->CacheExpire();
   
  $log = GitPHP_DebugLog::GetInstance();
   
if (!$this->tpl->isCached($this->GetTemplate(), $this->GetFullCacheKey())) { if (!$this->tpl->isCached($this->GetTemplate(), $this->GetFullCacheKey())) {
$this->tpl->clearAllAssign(); $this->tpl->clearAllAssign();
if ($this->log && $this->log->GetBenchmark())  
$this->log->Log("Data load begin"); $log->TimerStart();
$this->LoadCommonData(); $this->LoadCommonData();
  $log->TimerStop('Common data');
   
  $log->TimerStart();
$this->LoadData(); $this->LoadData();
if ($this->log && $this->log->GetBenchmark()) $log->TimerStop('Data');
$this->log->Log("Data load end");  
} }
   
if (!$this->preserveWhitespace) { if (!$this->preserveWhitespace) {
//$this->tpl->loadFilter('output', 'trimwhitespace'); //$this->tpl->loadFilter('output', 'trimwhitespace');
} }
   
if ($this->log && $this->log->GetBenchmark()) $log->TimerStart();
$this->log->Log("Smarty render begin");  
$this->tpl->display($this->GetTemplate(), $this->GetFullCacheKey()); $this->tpl->display($this->GetTemplate(), $this->GetFullCacheKey());
if ($this->log && $this->log->GetBenchmark()) $log->TimerStop('Render');
$this->log->Log("Smarty render end");  
   
$this->tpl->clearAllAssign(); $this->tpl->clearAllAssign();
   
if ($this->log && $this->projectList) if ($this->projectList)
$this->log->Log('MemoryCache count: ' . $this->projectList->GetMemoryCache()->GetCount()); $log->Log('MemoryCache', 'Count: ' . $this->projectList->GetMemoryCache()->GetCount());
   
  if ($log->GetEnabled()) {
  $this->tpl->assign('debuglog', $log);
  $this->tpl->display('debug.tpl');
  }
} }
   
/** /**
* Expires the cache * Expires the cache
* *
* @param boolean $expireAll expire the whole cache * @param boolean $expireAll expire the whole cache
*/ */
public function CacheExpire($expireAll = false) public function CacheExpire($expireAll = false)
{ {
if ($expireAll) { if ($expireAll) {
$this->tpl->clearAllCache(); $this->tpl->clearAllCache();
return; return;
} }
   
if (!$this->project) if (!$this->project)
return; return;
   
$epoch = $this->GetProject()->GetEpoch(); $epoch = $this->GetProject()->GetEpoch();
if (empty($epoch)) if (empty($epoch))
return; return;
   
$age = $this->GetProject()->GetAge(); $age = $this->GetProject()->GetAge();
   
$this->tpl->clearCache(null, $this->GetCacheKeyPrefix(), null, $age); $this->tpl->clearCache(null, $this->GetCacheKeyPrefix(), null, $age);
$this->tpl->clearCache('projectlist.tpl', $this->GetCacheKeyPrefix(false), null, $age); $this->tpl->clearCache('projectlist.tpl', $this->GetCacheKeyPrefix(false), null, $age);
} }
   
} }
   
<?php <?php
/** /**
* Controller for displaying a blob * Controller for displaying a blob
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Blob extends GitPHP_ControllerBase class GitPHP_Controller_Blob extends GitPHP_ControllerBase
{ {
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
parent::Initialize(); parent::Initialize();
   
if (empty($this->params['hashbase'])) if (empty($this->params['hashbase']))
$this->params['hashbase'] = 'HEAD'; $this->params['hashbase'] = 'HEAD';
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
if ($this->Plain()) if ($this->Plain())
return 'blobplain.tpl'; return 'blobplain.tpl';
return 'blob.tpl'; return 'blob.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return (isset($this->params['hashbase']) ? $this->params['hashbase'] : '') . '|' . (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : ''); return (isset($this->params['hashbase']) ? $this->params['hashbase'] : '') . '|' . (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('blob'); return $this->resource->translate('blob');
} }
return 'blob'; return 'blob';
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
if ($this->Plain()) { if ($this->Plain()) {
   
$this->DisableLogging(); $this->DisableLogging();
$this->preserveWhitespace = true; $this->preserveWhitespace = true;
   
// XXX: Nasty hack to cache headers // XXX: Nasty hack to cache headers
if (!$this->tpl->isCached('blobheaders.tpl', $this->GetFullCacheKey())) { if (!$this->tpl->isCached('blobheaders.tpl', $this->GetFullCacheKey())) {
if (isset($this->params['file'])) if (isset($this->params['file']))
$saveas = GitPHP_Util::BaseName($this->params['file']); $saveas = GitPHP_Util::BaseName($this->params['file']);
else else
$saveas = $this->params['hash'] . ".txt"; $saveas = $this->params['hash'] . ".txt";
   
$headers = array(); $headers = array();
   
$mime = null; $mime = null;
if ($this->config->GetValue('filemimetype')) { if ($this->config->GetValue('filemimetype')) {
if ((!isset($this->params['hash'])) && (isset($this->params['file']))) { if ((!isset($this->params['hash'])) && (isset($this->params['file']))) {
$commit = $this->GetProject()->GetCommit($this->params['hashbase']); $commit = $this->GetProject()->GetCommit($this->params['hashbase']);
$this->params['hash'] = $commit->GetTree()->PathToHash($this->params['file']); $this->params['hash'] = $commit->GetTree()->PathToHash($this->params['file']);
if (empty($this->params['hash'])) if (empty($this->params['hash']))
throw new GitPHP_FileNotFoundException($this->params['file']); throw new GitPHP_FileNotFoundException($this->params['file']);
} }
   
$blob = $this->GetProject()->GetObjectManager()->GetBlob($this->params['hash']); $blob = $this->GetProject()->GetObjectManager()->GetBlob($this->params['hash']);
if (!empty($this->params['file'])) if (!empty($this->params['file']))
$blob->SetPath($this->params['file']); $blob->SetPath($this->params['file']);
   
$mimeReader = new GitPHP_FileMimeTypeReader($blob, $this->GetMimeStrategy()); $mimeReader = new GitPHP_FileMimeTypeReader($blob, $this->GetMimeStrategy());
$mime = $mimeReader->GetMimeType(); $mime = trim($mimeReader->GetMimeType());
} }
   
if ($mime) if ($mime)
$headers[] = "Content-type: " . $mime; $headers[] = "Content-type: $mime; charset=UTF-8";
else else
$headers[] = "Content-type: text/plain; charset=UTF-8"; $headers[] = "Content-type: text/plain; charset=UTF-8";
   
$headers[] = "Content-disposition: inline; filename=\"" . $saveas . "\""; $headers[] = "Content-disposition: inline; filename=\"" . $saveas . "\"";
   
$this->tpl->assign("blobheaders", serialize($headers)); $this->tpl->assign("blobheaders", serialize($headers));
} }
$out = $this->tpl->fetch('blobheaders.tpl', $this->GetFullCacheKey()); $out = $this->tpl->fetch('blobheaders.tpl', $this->GetFullCacheKey());
   
$this->headers = unserialize(trim($out)); $this->headers = unserialize(trim($out));
} else { } else {
parent::LoadHeaders(); parent::LoadHeaders();
} }
   
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
$commit = $this->GetProject()->GetCommit($this->params['hashbase']); $commit = $this->GetProject()->GetCommit($this->params['hashbase']);
$this->tpl->assign('commit', $commit); $this->tpl->assign('commit', $commit);
   
$tree = $commit->GetTree(); $tree = $commit->GetTree();
$this->tpl->assign('tree', $commit->GetTree()); $this->tpl->assign('tree', $commit->GetTree());
   
if ((!isset($this->params['hash'])) && (isset($this->params['file']))) { if ((!isset($this->params['hash'])) && (isset($this->params['file']))) {
$this->params['hash'] = $tree->PathToHash($this->params['file']); $this->params['hash'] = $tree->PathToHash($this->params['file']);
if (empty($this->params['hash'])) if (empty($this->params['hash']))
throw new GitPHP_FileNotFoundException($this->params['file']); throw new GitPHP_FileNotFoundException($this->params['file']);
} }
   
$blob = $this->GetProject()->GetObjectManager()->GetBlob($this->params['hash']); $blob = $this->GetProject()->GetObjectManager()->GetBlob($this->params['hash']);
if (!empty($this->params['file'])) if (!empty($this->params['file']))
$blob->SetPath($this->params['file']); $blob->SetPath($this->params['file']);
$blob->SetCommit($commit); $blob->SetCommit($commit);
$this->tpl->assign('blob', $blob); $this->tpl->assign('blob', $blob);
   
if ($this->Plain()) { if ($this->Plain()) {
return; return;
} }
   
$head = $this->GetProject()->GetHeadCommit(); $head = $this->GetProject()->GetHeadCommit();
$this->tpl->assign('head', $head); $this->tpl->assign('head', $head);
   
if ($this->config->GetValue('filemimetype')) { if ($this->config->GetValue('filemimetype')) {
$mimeReader = new GitPHP_FileMimeTypeReader($blob, $this->GetMimeStrategy()); $mimeReader = new GitPHP_FileMimeTypeReader($blob, $this->GetMimeStrategy());
$mimetype = $mimeReader->GetMimeType(true); $mimetype = $mimeReader->GetMimeType(true);
if ($mimetype == 'image') { if ($mimetype == 'image') {
$this->tpl->assign('datatag', true); $this->tpl->assign('datatag', true);
$this->tpl->assign('mime', $mimeReader->GetMimeType()); $this->tpl->assign('mime', $mimeReader->GetMimeType());
$this->tpl->assign('data', base64_encode($blob->GetData())); $this->tpl->assign('data', base64_encode($blob->GetData()));
return; return;
} }
} }
   
if ($this->config->GetValue('geshi')) { if ($this->config->GetValue('geshi')) {
include_once(GITPHP_GESHIDIR . "geshi.php"); include_once(GITPHP_GESHIDIR . "geshi.php");
if (class_exists('GeSHi')) { if (class_exists('GeSHi')) {
$geshi = new GeSHi("",'php'); $geshi = new GeSHi("",'php');
if ($geshi) { if ($geshi) {
$lang = GitPHP_Util::GeshiFilenameToLanguage($blob->GetName()); $lang = GitPHP_Util::GeshiFilenameToLanguage($blob->GetName());
if (empty($lang)) { if (empty($lang)) {
$lang = $geshi->get_language_name_from_extension(substr(strrchr($blob->GetName(),'.'),1)); $lang = $geshi->get_language_name_from_extension(substr(strrchr($blob->GetName(),'.'),1));
} }
if (!empty($lang)) { if (!empty($lang)) {
$geshi->enable_classes(); $geshi->enable_classes();
$geshi->enable_strict_mode(GESHI_MAYBE); $geshi->enable_strict_mode(GESHI_MAYBE);
$geshi->set_source($blob->GetData()); $geshi->set_source($blob->GetData());
$geshi->set_language($lang); $geshi->set_language($lang);
$geshi->set_header_type(GESHI_HEADER_PRE_TABLE); $geshi->set_header_type(GESHI_HEADER_PRE_TABLE);
$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
$geshi->set_overall_id('blobData'); $geshi->set_overall_id('blobData');
$this->tpl->assign('geshiout', $geshi->parse_code()); $this->tpl->assign('geshiout', $geshi->parse_code());
$this->tpl->assign('geshicss', $geshi->get_stylesheet()); $this->tpl->assign('geshicss', $geshi->get_stylesheet());
$this->tpl->assign('geshi', true); $this->tpl->assign('geshi', true);
return; return;
} }
} }
} }
} }
   
$this->tpl->assign('bloblines', $blob->GetData(true)); $this->tpl->assign('bloblines', $blob->GetData(true));
} }
   
/** /**
* Get valid mime strategy * Get valid mime strategy
*/ */
private function GetMimeStrategy() private function GetMimeStrategy()
{ {
$strategy = new GitPHP_FileMimeType_Fileinfo($this->config->GetValue('magicdb')); $strategy = new GitPHP_FileMimeType_Fileinfo($this->config->GetValue('magicdb'));
if ($strategy->Valid()) if ($strategy->Valid())
return $strategy; return $strategy;
   
$strategy = new GitPHP_FileMimeType_FileExe(); $strategy = new GitPHP_FileMimeType_FileExe();
if ($strategy->Valid()) if ($strategy->Valid())
return $strategy; return $strategy;
   
$strategy = new GitPHP_FileMimeType_Extension(); $strategy = new GitPHP_FileMimeType_Extension();
if ($strategy->Valid()) if ($strategy->Valid())
return $strategy; return $strategy;
} }
   
/** /**
* Tests whether we are outputting a plaintext blob * Tests whether we are outputting a plaintext blob
* *
* @return boolean true if plaintext blob * @return boolean true if plaintext blob
*/ */
public function Plain() public function Plain()
{ {
if (isset($this->params['output']) && ($this->params['output'] == 'plain')) if (isset($this->params['output']) && ($this->params['output'] == 'plain'))
return true; return true;
   
return false; return false;
} }
   
} }
   
<?php <?php
/** /**
* Controller for displaying a project's feed * Controller for displaying a project's feed
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @author Christian Weiske <cweiske@cweiske.de> * @author Christian Weiske <cweiske@cweiske.de>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Feed extends GitPHP_ControllerBase class GitPHP_Controller_Feed extends GitPHP_ControllerBase
{ {
/** /**
* Number of items to put in feed * Number of items to put in feed
* *
* @var int * @var int
*/ */
const FeedItemCount = 150; const FeedItemCount = 100;
   
/** /**
* Rss feed format * Rss feed format
* *
* @var string * @var string
*/ */
const RssFormat = 'rss'; const RssFormat = 'rss';
   
/** /**
* Atom feed format * Atom feed format
* *
* @var string * @var string
*/ */
const AtomFormat = 'atom'; const AtomFormat = 'atom';
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
parent::Initialize(); parent::Initialize();
$this->preserveWhitespace = true; $this->preserveWhitespace = true;
$this->DisableLogging(); $this->DisableLogging();
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
if ($this->params['format'] == GitPHP_Controller_Feed::RssFormat) if ($this->params['format'] == GitPHP_Controller_Feed::RssFormat)
return 'rss.tpl'; return 'rss.tpl';
else if ($this->params['format'] == GitPHP_Controller_Feed::AtomFormat) else if ($this->params['format'] == GitPHP_Controller_Feed::AtomFormat)
return 'atom.tpl'; return 'atom.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return ''; return '';
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if ($this->params['format'] == GitPHP_Controller_Feed::RssFormat) { if ($this->params['format'] == GitPHP_Controller_Feed::RssFormat) {
if ($local && $this->resource) if ($local && $this->resource)
return $this->resource->translate('rss'); return $this->resource->translate('rss');
else else
return 'rss'; return 'rss';
} else if ($this->params['format'] == GitPHP_Controller_Feed::AtomFormat) { } else if ($this->params['format'] == GitPHP_Controller_Feed::AtomFormat) {
if ($local && $this->resource) if ($local && $this->resource)
return $this->resource->translate('atom'); return $this->resource->translate('atom');
else else
return 'atom'; return 'atom';
} }
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
if ((!isset($this->params['format'])) || empty($this->params['format'])) { if ((!isset($this->params['format'])) || empty($this->params['format'])) {
throw new Exception('A feed format is required'); throw new Exception('A feed format is required');
} }
   
if ($this->params['format'] == GitPHP_Controller_Feed::RssFormat) { if ($this->params['format'] == GitPHP_Controller_Feed::RssFormat) {
$this->headers[] = "Content-type: text/xml; charset=UTF-8"; $this->headers[] = "Content-type: application/rss+xml; charset=UTF-8";
} else if ($this->params['format'] == GitPHP_Controller_Feed::AtomFormat) { } else if ($this->params['format'] == GitPHP_Controller_Feed::AtomFormat) {
$this->headers[] = "Content-type: application/atom+xml; charset=UTF-8"; $this->headers[] = "Content-type: application/atom+xml; charset=UTF-8";
} }
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
$compat = $this->GetProject()->GetCompat(); //$compat = $this->GetProject()->GetCompat();
$strategy = null; $strategy = null;
//if ($compat) { //if ($compat) {
$strategy = new GitPHP_LogLoad_Git($this->exe); $strategy = new GitPHP_LogLoad_Git($this->exe);
//} else { //} else {
// $strategy = new GitPHP_LogLoad_Raw(); // $strategy = new GitPHP_LogLoad_Raw();
//} //}
$log = new GitPHP_Log($this->GetProject(), $this->GetProject()->GetHeadCommit(), $strategy, GitPHP_Controller_Feed::FeedItemCount); $log = new GitPHP_Log($this->GetProject(), $this->GetProject()->GetHeadCommit(), $strategy, GitPHP_Controller_Feed::FeedItemCount);
  if ($this->config->HasKey('feedfilter')) {
  $log->FilterCommits($this->config->GetValue('feedfilter'));
  }
$log->FilterOldCommits(48*60*60, 20); $log->FilterOldCommits(48*60*60, 20);
   
$this->tpl->assign('log', $log); $this->tpl->assign('log', $log);
   
$this->tpl->assign('gitexe', $this->exe); $this->tpl->assign('gitexe', $this->exe);
} }
   
} }
   
<?php <?php
/** /**
* Controller for displaying a log * Controller for displaying a log
* *
* @author Christopher Han * @author Christopher Han
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Log extends GitPHP_ControllerBase class GitPHP_Controller_Log extends GitPHP_ControllerBase
{ {
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
parent::Initialize(); parent::Initialize();
   
if (empty($this->params['hash'])) if (empty($this->params['hash']))
$this->params['hash'] = 'HEAD'; $this->params['hash'] = 'HEAD';
   
if (empty($this->params['page'])) if (empty($this->params['page']))
$this->params['page'] = 0; $this->params['page'] = 0;
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
if (isset($this->params['short']) && ($this->params['short'] === true)) { if (isset($this->params['short']) && ($this->params['short'] === true)) {
return 'shortlog.tpl'; return 'shortlog.tpl';
} }
return 'log.tpl'; return 'log.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return $this->params['hash'] . '|' . $this->params['page'] . '|' . (isset($this->params['mark']) ? $this->params['mark'] : ''); return $this->params['hash'] . '|' . $this->params['page'] . '|' . (isset($this->params['mark']) ? $this->params['mark'] : '');
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if (isset($this->params['short']) && ($this->params['short'] === true)) { if (isset($this->params['short']) && ($this->params['short'] === true)) {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('shortlog'); return $this->resource->translate('shortlog');
} }
return 'shortlog'; return 'shortlog';
} }
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('log'); return $this->resource->translate('log');
} }
return 'log'; return 'log';
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
$commit = $this->GetProject()->GetCommit($this->params['hash']); $commit = $this->GetProject()->GetCommit($this->params['hash']);
$this->tpl->assign('commit', $commit); $this->tpl->assign('commit', $commit);
$this->tpl->assign('head', $this->GetProject()->GetHeadCommit()); $this->tpl->assign('head', $this->GetProject()->GetHeadCommit());
$this->tpl->assign('page',$this->params['page']); $this->tpl->assign('page',$this->params['page']);
   
$compat = $this->GetProject()->GetCompat(); //$compat = $this->GetProject()->GetCompat();
$skip = $this->params['page'] * 100; $skip = $this->params['page'] * 100;
$strategy = null; $strategy = null;
//if ($compat || ($skip > $this->config->GetValue('largeskip'))) { //if ($compat || ($skip > $this->config->GetValue('largeskip'))) {
$strategy = new GitPHP_LogLoad_Git($this->exe); $strategy = new GitPHP_LogLoad_Git($this->exe);
//} else { //} else {
// $strategy = new GitPHP_LogLoad_Raw(); // $strategy = new GitPHP_LogLoad_Raw();
//} //}
$revlist = new GitPHP_Log($this->GetProject(), $commit, $strategy, 101, $skip); $revlist = new GitPHP_Log($this->GetProject(), $commit, $strategy, 101, $skip);
   
if ($revlist->GetCount() > 100) { if ($revlist->GetCount() > 100) {
$this->tpl->assign('hasmorerevs', true); $this->tpl->assign('hasmorerevs', true);
$revlist->SetLimit(100); $revlist->SetLimit(100);
} }
$this->tpl->assign('revlist', $revlist); $this->tpl->assign('revlist', $revlist);
   
if (isset($this->params['mark'])) { if (isset($this->params['mark'])) {
$this->tpl->assign('mark', $this->GetProject()->GetCommit($this->params['mark'])); $this->tpl->assign('mark', $this->GetProject()->GetCommit($this->params['mark']));
} }
} }
   
} }
   
<?php <?php
/** /**
* Controller for login * Controller for login
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Login extends GitPHP_ControllerBase class GitPHP_Controller_Login extends GitPHP_ControllerBase
{ {
/** /**
* Flag whether login was successful * Flag whether login was successful
* *
* @var boolean|null * @var boolean|null
*/ */
protected $loginSuccess = null; protected $loginSuccess = null;
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
$this->InitializeConfig(); $this->InitializeConfig();
   
$this->InitializeResource(); $this->InitializeResource();
   
$this->InitializeUserList(); $this->InitializeUserList();
   
$this->EnableLogging(); $this->EnableLogging();
   
$this->InitializeSmarty(); $this->InitializeSmarty();
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
return 'login.tpl'; return 'login.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
$key = (isset($this->params['username']) ? $this->params['username'] : '') . '|' . (isset($this->params['password']) ? $this->params['password'] : ''); $key = (isset($this->params['username']) ? $this->params['username'] : '') . '|' . (isset($this->params['password']) ? $this->params['password'] : '');
if (!empty($key)) if (!empty($key))
$key = sha1($key); $key = sha1($key);
return $key; return $key;
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('login'); return $this->resource->translate('login');
} }
return 'login'; return 'login';
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
if (isset($this->params['output']) && ($this->params['output'] == 'js')) { if (isset($this->params['output']) && ($this->params['output'] == 'js')) {
$this->headers[] = 'Content-Type: application/json'; $this->headers[] = 'Content-Type: application/json';
$this->DisableLogging(); $this->DisableLogging();
} }
   
if (!empty($_SESSION['gitphpuser'])) { if (!empty($_SESSION['gitphpuser'])) {
$user = $this->userList->GetUser($_SESSION['gitphpuser']); $user = $this->userList->GetUser($_SESSION['gitphpuser']);
if ($user) { if ($user) {
if (!(isset($this->params['output']) && ($this->params['output'] == 'js'))) { if (!(isset($this->params['output']) && ($this->params['output'] == 'js'))) {
if (!empty($this->params['redirect'])) if (!empty($this->params['redirect']))
$this->headers[] = 'Location: ' . $this->params['redirect']; $this->headers[] = 'Location: ' . $this->params['redirect'];
else else
$this->headers[] = 'Location: ' . $this->router->GetUrl(array(), true); $this->headers[] = 'Location: ' . $this->router->GetUrl(array(), true);
  $this->DisableLogging();
} }
$this->loginSuccess = true; $this->loginSuccess = true;
} else { } else {
unset($_SESSION['gitphpuser']); unset($_SESSION['gitphpuser']);
} }
} }
   
if (!(empty($this->params['username']) || empty($this->params['password']))) { if (!(empty($this->params['username']) || empty($this->params['password']))) {
$user = $this->userList->GetUser($this->params['username']); $user = $this->userList->GetUser($this->params['username']);
if ($user && ($this->params['password'] === $user->GetPassword())) { if ($user && ($this->params['password'] === $user->GetPassword())) {
$_SESSION['gitphpuser'] = $user->GetUsername(); $_SESSION['gitphpuser'] = $user->GetUsername();
if (!(isset($this->params['output']) && ($this->params['output'] == 'js'))) { if (!(isset($this->params['output']) && ($this->params['output'] == 'js'))) {
if (!empty($this->params['redirect'])) if (!empty($this->params['redirect']))
$this->headers[] = 'Location: ' . $this->params['redirect']; $this->headers[] = 'Location: ' . $this->params['redirect'];
else else
$this->headers[] = 'Location: ' . $this->router->GetUrl(array(), true); $this->headers[] = 'Location: ' . $this->router->GetUrl(array(), true);
  $this->DisableLogging();
} }
$this->loginSuccess = true; $this->loginSuccess = true;
} else { } else {
$this->loginSuccess = false; $this->loginSuccess = false;
} }
} }
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
if (!(isset($this->params['output']) && ($this->params['output'] == 'js'))) { if (!(isset($this->params['output']) && ($this->params['output'] == 'js'))) {
if ($this->loginSuccess === false) { if ($this->loginSuccess === false) {
if ($this->resource) { if ($this->resource) {
$this->tpl->assign('loginerror', $this->resource->translate('Invalid username or password')); $this->tpl->assign('loginerror', $this->resource->translate('Invalid username or password'));
} else { } else {
$this->tpl->assign('loginerror', 'Invalid username or password'); $this->tpl->assign('loginerror', 'Invalid username or password');
} }
} }
if (!empty($this->params['redirect'])) { if (!empty($this->params['redirect'])) {
$this->tpl->assign('redirect', $this->params['redirect']); $this->tpl->assign('redirect', $this->params['redirect']);
} else if (!empty($_SERVER['HTTP_REFERER'])) { } else if (!empty($_SERVER['HTTP_REFERER'])) {
$this->tpl->assign('redirect', $_SERVER['HTTP_REFERER']); $this->tpl->assign('redirect', $_SERVER['HTTP_REFERER']);
} }
} }
} }
   
/** /**
* Renders the output * Renders the output
*/ */
public function Render() public function Render()
{ {
if (isset($this->params['output']) && ($this->params['output'] == 'js')) { if (isset($this->params['output']) && ($this->params['output'] == 'js')) {
$result = array(); $result = array();
if ($this->loginSuccess === true) if ($this->loginSuccess === true)
$result['success'] = true; $result['success'] = true;
else { else {
$result['success'] = false; $result['success'] = false;
if ($this->loginSuccess === false) { if ($this->loginSuccess === false) {
if ($this->resource) { if ($this->resource) {
$result['message'] = $this->resource->translate('Invalid username or password'); $result['message'] = $this->resource->translate('Invalid username or password');
} else { } else {
$result['message'] = 'Invalid username or password'; $result['message'] = 'Invalid username or password';
} }
} }
} }
echo json_encode($result); echo json_encode($result);
return; return;
} }
   
if ($this->loginSuccess === true) if ($this->loginSuccess === true)
return; // logged in and redirected, don't render return; // logged in and redirected, don't render
   
return parent::Render(); return parent::Render();
} }
   
} }
   
<?php <?php
/** /**
* Controller for displaying a message page * Controller for displaying a message page
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Message extends GitPHP_ControllerBase class GitPHP_Controller_Message extends GitPHP_ControllerBase
{ {
/** /**
* Constructor * Constructor
*/ */
public function Initialize() public function Initialize()
{ {
try { try {
$this->InitializeConfig(); $this->InitializeConfig();
} catch (Exception $e) { } catch (Exception $e) {
} }
   
$this->InitializeResource(); $this->InitializeResource();
   
$this->InitializeUserList(); $this->InitializeUserList();
   
$this->InitializeGitExe(false); $this->InitializeGitExe(false);
   
try { try {
$this->InitializeProjectList(); $this->InitializeProjectList();
} catch (Exception $e) { } catch (Exception $e) {
} }
   
try { try {
$this->InitializeSmarty(); $this->InitializeSmarty();
} catch (Exception $e) { } catch (Exception $e) {
} }
   
if (!empty($this->params['project']) && $this->projectList) { if (!empty($this->params['project']) && $this->projectList) {
$project = $this->projectList->GetProject($this->params['project']); $project = $this->projectList->GetProject($this->params['project']);
if ($project) { if ($project) {
$this->project = $project->GetProject(); if ($this->userList && ($this->userList->GetCount() > 0)) {
  if ($project->UserCanAccess((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null))) {
  $this->project = $project->GetProject();
  }
  } else {
  $this->project = $project->GetProject();
  }
} }
} }
   
if (empty($this->params['hash'])) if (empty($this->params['hash']))
$this->params['hash'] = 'HEAD'; $this->params['hash'] = 'HEAD';
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
if ($this->project) if ($this->project)
return 'projectmessage.tpl'; return 'projectmessage.tpl';
return 'message.tpl'; return 'message.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return sha1(serialize($this->params['exception'])); return sha1(serialize($this->params['exception']));
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
// This isn't a real controller // This isn't a real controller
return ''; return '';
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
parent::LoadHeaders(); parent::LoadHeaders();
   
if (($this->params['exception'] instanceof GitPHP_MessageException) && ($this->params['exception']->StatusCode)) { if (($this->params['exception'] instanceof GitPHP_MessageException) && ($this->params['exception']->StatusCode)) {
$partialHeader = $this->StatusCodeHeader($this->params['exception']->StatusCode); $partialHeader = $this->StatusCodeHeader($this->params['exception']->StatusCode);
if (!empty($partialHeader)) { if (!empty($partialHeader)) {
if (substr(php_sapi_name(), 0, 8) == 'cgi-fcgi') { if (substr(php_sapi_name(), 0, 8) == 'cgi-fcgi') {
/* /*
* FastCGI requires a different header * FastCGI requires a different header
*/ */
$this->headers[] = 'Status: ' . $partialHeader; $this->headers[] = 'Status: ' . $partialHeader;
} else { } else {
$this->headers[] = 'HTTP/1.1 ' . $partialHeader; $this->headers[] = 'HTTP/1.1 ' . $partialHeader;
} }
} }
} }
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
$this->tpl->assign('message', $this->ExceptionToMessage($this->params['exception'])); $this->tpl->assign('message', $this->ExceptionToMessage($this->params['exception']));
if (($this->params['exception'] instanceof GitPHP_MessageException) && ($this->params['exception']->Error)) { if (($this->params['exception'] instanceof GitPHP_MessageException) && ($this->params['exception']->Error)) {
$this->tpl->assign('error', true); $this->tpl->assign('error', true);
} }
if ($this->project) { if ($this->project) {
try { try {
$co = $this->GetProject()->GetCommit($this->params['hash']); $co = $this->GetProject()->GetCommit($this->params['hash']);
if ($co) { if ($co) {
$this->tpl->assign('commit', $co); $this->tpl->assign('commit', $co);
} }
} catch (Exception $e) { } catch (Exception $e) {
} }
} }
} }
   
/** /**
* Gets the user-displayed message for an exception * Gets the user-displayed message for an exception
* *
* @param Exception $exception exception * @param Exception $exception exception
* @return string message * @return string message
*/ */
private function ExceptionToMessage($exception) private function ExceptionToMessage($exception)
{ {
if (!$exception) if (!$exception)
return; return;
   
if ($exception instanceof GitPHP_InvalidProjectParameterException) { if ($exception instanceof GitPHP_InvalidProjectParameterException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Invalid project %1$s'), $exception->Project); return sprintf($this->resource->translate('Invalid project %1$s'), $exception->Project);
return sprintf('Invalid project %1$s', $exception->Project); return sprintf('Invalid project %1$s', $exception->Project);
} }
if ($exception instanceof GitPHP_MissingProjectParameterException) { if ($exception instanceof GitPHP_MissingProjectParameterException) {
if ($this->resource) if ($this->resource)
return $this->resource->translate('Project is required'); return $this->resource->translate('Project is required');
return 'Project is required'; return 'Project is required';
} }
   
if ($exception instanceof GitPHP_SearchDisabledException) { if ($exception instanceof GitPHP_SearchDisabledException) {
if ($exception->FileSearch) { if ($exception->FileSearch) {
if ($this->resource) if ($this->resource)
return $this->resource->translate('File search has been disabled'); return $this->resource->translate('File search has been disabled');
return 'File search has been disabled'; return 'File search has been disabled';
} else { } else {
if ($this->resource) if ($this->resource)
return $this->resource->translate('Search has been disabled'); return $this->resource->translate('Search has been disabled');
return 'Search has been disabled'; return 'Search has been disabled';
} }
} }
   
if ($exception instanceof GitPHP_InvalidSearchTypeException) { if ($exception instanceof GitPHP_InvalidSearchTypeException) {
if ($this->resource) if ($this->resource)
return $this->resource->translate('Invalid search type'); return $this->resource->translate('Invalid search type');
return 'Invalid search type'; return 'Invalid search type';
} }
   
if ($exception instanceof GitPHP_SearchLengthException) { if ($exception instanceof GitPHP_SearchLengthException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->ngettext('You must enter search text of at least %1$d character', 'You must enter search text of at least %1$d characters', $exception->MinimumLength), $exception->MinimumLength); return sprintf($this->resource->ngettext('You must enter search text of at least %1$d character', 'You must enter search text of at least %1$d characters', $exception->MinimumLength), $exception->MinimumLength);
return sprintf($exception->MinimumLength == 1 ? 'You must enter search text of at least %1$d character' : 'You must enter search text of at least %1$d characters', $exception->MinimumLength); return sprintf($exception->MinimumLength == 1 ? 'You must enter search text of at least %1$d character' : 'You must enter search text of at least %1$d characters', $exception->MinimumLength);
} }
   
if ($exception instanceof GitPHP_InvalidHashException) { if ($exception instanceof GitPHP_InvalidHashException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Invalid hash %1$s'), $exception->Hash); return sprintf($this->resource->translate('Invalid hash %1$s'), $exception->Hash);
return sprintf('Invalid hash %1$s', $exception->Hash); return sprintf('Invalid hash %1$s', $exception->Hash);
} }
   
if ($exception instanceof GitPHP_InvalidGitExecutableException) { if ($exception instanceof GitPHP_InvalidGitExecutableException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Could not run the git executable "%1$s". You may need to set the "%2$s" config value.'), $exception->Executable, 'gitbin'); return sprintf($this->resource->translate('Could not run the git executable "%1$s". You may need to set the "%2$s" config value.'), $exception->Executable, 'gitbin');
return sprintf('Could not run the git executable "%1$s". You may need to set the "%2$s" config value.', $exception->Executable, 'gitbin'); return sprintf('Could not run the git executable "%1$s". You may need to set the "%2$s" config value.', $exception->Executable, 'gitbin');
} }
   
if ($exception instanceof GitPHP_MissingProjectrootException) { if ($exception instanceof GitPHP_MissingProjectrootException) {
if ($this->resource) if ($this->resource)
return $this->resource->translate('A projectroot must be set in the config'); return $this->resource->translate('A projectroot must be set in the config');
return 'A projectroot must be set in the config'; return 'A projectroot must be set in the config';
} }
   
if ($exception instanceof GitPHP_MissingMemcacheException) { if ($exception instanceof GitPHP_MissingMemcacheException) {
if ($this->resource) if ($this->resource)
return $this->resource->translate('The Memcached or Memcache PHP extension is required for Memcache support'); return $this->resource->translate('The Memcached or Memcache PHP extension is required for Memcache support');
return 'The Memcached or Memcache PHP extension is required for Memcache support'; return 'The Memcached or Memcache PHP extension is required for Memcache support';
} }
   
if (($exception instanceof GitPHP_InvalidDirectoryException) || ($exception instanceof GitPHP_InvalidDirectoryConfigurationException)) { if (($exception instanceof GitPHP_InvalidDirectoryException) || ($exception instanceof GitPHP_InvalidDirectoryConfigurationException)) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('%1$s is not a directory'), $exception->Directory); return sprintf($this->resource->translate('%1$s is not a directory'), $exception->Directory);
return sprintf('%1$s is not a directory', $exception->Directory); return sprintf('%1$s is not a directory', $exception->Directory);
} }
   
if ($exception instanceof GitPHP_InvalidFileException) { if ($exception instanceof GitPHP_InvalidFileException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('%1$s is not a file'), $exception->File); return sprintf($this->resource->translate('%1$s is not a file'), $exception->File);
return sprintf('%1$s is not a file', $exception->File); return sprintf('%1$s is not a file', $exception->File);
} }
   
if ($exception instanceof GitPHP_InvalidGitRepositoryException) { if ($exception instanceof GitPHP_InvalidGitRepositoryException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('%1$s is not a git repository'), $exception->Repository); return sprintf($this->resource->translate('%1$s is not a git repository'), $exception->Repository);
return sprintf('%1$s is not a git repository', $exception->Repository); return sprintf('%1$s is not a git repository', $exception->Repository);
} }
   
if ($exception instanceof GitPHP_ProjectListFileReadException) { if ($exception instanceof GitPHP_ProjectListFileReadException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Failed to open project list file %1$s'), $exception->File); return sprintf($this->resource->translate('Failed to open project list file %1$s'), $exception->File);
return sprintf('Failed to open project list file %1$s', $exception->File); return sprintf('Failed to open project list file %1$s', $exception->File);
} }
   
if ($exception instanceof GitPHP_DirectoryTraversalException) { if ($exception instanceof GitPHP_DirectoryTraversalException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('%1$s is attempting directory traversal'), $exception->Path); return sprintf($this->resource->translate('%1$s is attempting directory traversal'), $exception->Path);
return sprintf('%1$s is attempting directory traversal', $exception->Path); return sprintf('%1$s is attempting directory traversal', $exception->Path);
} }
   
if ($exception instanceof GitPHP_ProjectrootBoundException) { if ($exception instanceof GitPHP_ProjectrootBoundException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('%1$s is outside of the projectroot'), $exception->Path); return sprintf($this->resource->translate('%1$s is outside of the projectroot'), $exception->Path);
return sprintf('%1$s is outside of the projectroot', $exception->Path); return sprintf('%1$s is outside of the projectroot', $exception->Path);
} }
   
if ($exception instanceof GitPHP_InvalidConfigFileException) { if ($exception instanceof GitPHP_InvalidConfigFileException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Could not load config file %1$s'), $exception->File); return sprintf($this->resource->translate('Could not load config file %1$s'), $exception->File);
return sprintf('Could not load config file %1$s', $exception->File); return sprintf('Could not load config file %1$s', $exception->File);
} }
   
if ($exception instanceof GitPHP_AmbiguousHashException) { if ($exception instanceof GitPHP_AmbiguousHashException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Ambiguous abbreviated hash %1$s'), $exception->Hash); return sprintf($this->resource->translate('Ambiguous abbreviated hash %1$s'), $exception->Hash);
return sprintf('Ambiguous abbreviated hash %1$s', $exception->Hash); return sprintf('Ambiguous abbreviated hash %1$s', $exception->Hash);
} }
   
if ($exception instanceof GitPHP_DirectoryNotFoundException) { if ($exception instanceof GitPHP_DirectoryNotFoundException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('Directory %1$s not found'), $exception->Directory); return sprintf($this->resource->translate('Directory %1$s not found'), $exception->Directory);
   
return sprintf('Directory %1$s not found', $exception->Directory); return sprintf('Directory %1$s not found', $exception->Directory);
} }
   
if ($exception instanceof GitPHP_FileNotFoundException) { if ($exception instanceof GitPHP_FileNotFoundException) {
if ($this->resource) if ($this->resource)
return sprintf($this->resource->translate('File %1$s not found'), $exception->File); return sprintf($this->resource->translate('File %1$s not found'), $exception->File);
   
return sprintf('File %1$s not found', $exception->File); return sprintf('File %1$s not found', $exception->File);
} }
   
  if ($exception instanceof GitPHP_UnauthorizedProjectException) {
  if ($this->resource)
  return sprintf($this->resource->translate('You are not authorized to access project %1$s'), $exception->Project);
   
  return sprintf('You are not authorized to access project %1$s', $exception->Project);
  }
   
  if ($exception instanceof GitPHP_DisabledFunctionException) {
  if ($this->resource)
  return sprintf($this->resource->translate('Required function %1$s has been disabled'), $exception->Function);
   
  return sprintf('Required function %1$s has been disabled', $exception->Function);
  }
   
return $exception->getMessage(); return $exception->getMessage();
} }
   
/** /**
* Gets the header for an HTTP status code * Gets the header for an HTTP status code
* *
* @param integer $code status code * @param integer $code status code
* @return string header * @return string header
*/ */
private function StatusCodeHeader($code) private function StatusCodeHeader($code)
{ {
switch ($code) { switch ($code) {
  case 403:
  return '403 Forbidden';
  case 404:
  return '404 Not Found';
case 500: case 500:
return '500 Internal Server Error'; return '500 Internal Server Error';
} }
} }
   
} }
   
<?php <?php
/** /**
* Controller for displaying a project summary * Controller for displaying a project summary
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Project extends GitPHP_ControllerBase class GitPHP_Controller_Project extends GitPHP_ControllerBase
{ {
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
return 'project.tpl'; return 'project.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return ''; return '';
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('summary'); return $this->resource->translate('summary');
} }
return 'summary'; return 'summary';
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
  $log = GitPHP_DebugLog::GetInstance();
   
  $log->TimerStart();
$head = $this->GetProject()->GetHeadCommit(); $head = $this->GetProject()->GetHeadCommit();
$this->tpl->assign('head', $head); $this->tpl->assign('head', $head);
if (!$head) if (!$head)
$this->tpl->assign('enablesearch', false); $this->tpl->assign('enablesearch', false);
  $log->TimerStop('GetHeadCommit');
   
$compat = $this->GetProject()->GetCompat(); //$compat = $this->GetProject()->GetCompat();
$strategy = null; $strategy = null;
//if ($compat) { //if ($compat) {
$strategy = new GitPHP_LogLoad_Git($this->exe); $strategy = new GitPHP_LogLoad_Git($this->exe);
//} else { //} else {
// $strategy = new GitPHP_LogLoad_Raw(); // $strategy = new GitPHP_LogLoad_Raw();
//} //}
$revlist = new GitPHP_Log($this->GetProject(), $this->GetProject()->GetHeadCommit(), $strategy, 17); $revlist = new GitPHP_Log($this->GetProject(), $this->GetProject()->GetHeadCommit(), $strategy, 17);
   
if ($revlist->GetCount() > 16) { if ($revlist->GetCount() > 16) {
$this->tpl->assign('hasmorerevs', true); $this->tpl->assign('hasmorerevs', true);
$revlist->SetLimit(16); $revlist->SetLimit(16);
} }
$this->tpl->assign('revlist', $revlist); $this->tpl->assign('revlist', $revlist);
   
  $log->TimerStart();
$taglist = $this->GetProject()->GetTagList()->GetOrderedTags('-creatordate', 17); $taglist = $this->GetProject()->GetTagList()->GetOrderedTags('-creatordate', 17);
  $log->TimerStop('GetTagList');
if ($taglist) { if ($taglist) {
if (count($taglist) > 16) { if (count($taglist) > 16) {
$this->tpl->assign('hasmoretags', true); $this->tpl->assign('hasmoretags', true);
$taglist = array_slice($taglist, 0, 16); $taglist = array_slice($taglist, 0, 16);
} }
$this->tpl->assign('taglist', $taglist); $this->tpl->assign('taglist', $taglist);
} }
   
  $log->TimerStart();
$headlist = $this->GetProject()->GetHeadList()->GetOrderedHeads('-committerdate', 17); $headlist = $this->GetProject()->GetHeadList()->GetOrderedHeads('-committerdate', 17);
  $log->TimerStop('GetHeadList');
if ($headlist) { if ($headlist) {
if (count($headlist) > 17) { if (count($headlist) > 17) {
$this->tpl->assign('hasmoreheads', true); $this->tpl->assign('hasmoreheads', true);
$headlist = array_slice($headlist, 0, 16); $headlist = array_slice($headlist, 0, 16);
} }
$this->tpl->assign('headlist', $headlist); $this->tpl->assign('headlist', $headlist);
} }
} }
   
} }
   
<?php <?php
/** /**
* Controller for listing projects * Controller for listing projects
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_ProjectList extends GitPHP_ControllerBase class GitPHP_Controller_ProjectList extends GitPHP_ControllerBase
{ {
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
$this->multiProject = true; $this->multiProject = true;
   
parent::Initialize(); parent::Initialize();
   
  if ($this->userList && ($this->userList->GetCount() > 0)) {
  if (!$this->config->GetValue('showrestrictedprojects') || (isset($this->params['opml']) && ($this->params['opml'] === true)) || (isset($this->params['txt']) && ($this->params['txt'] === true))) {
  $this->projectList->FilterByUser((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null));
  }
  }
   
if (empty($this->params['sort'])) if (empty($this->params['sort']))
$this->params['sort'] = 'project'; $this->params['sort'] = 'project';
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
if (isset($this->params['opml']) && ($this->params['opml'] === true)) { if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
return 'opml.tpl'; return 'opml.tpl';
} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) { } else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
return 'projectindex.tpl'; return 'projectindex.tpl';
} }
return 'projectlist.tpl'; return 'projectlist.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
  $cachekey = (!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : '');
if (isset($this->params['opml']) && ($this->params['opml'] === true)) { if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
return ''; return $cachekey;
} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) { } else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
return ''; return $cachekey;
} }
return $this->params['sort'] . '|' . (isset($this->params['search']) ? $this->params['search'] : ''); $cachekey .= '|' . $this->params['sort'] . '|' . (isset($this->params['search']) ? $this->params['search'] : '');
  return $cachekey;
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if (isset($this->params['opml']) && ($this->params['opml'] === true)) { if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('opml'); return $this->resource->translate('opml');
} }
return 'opml'; return 'opml';
} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) { } else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('project index'); return $this->resource->translate('project index');
} }
return 'project index'; return 'project index';
} }
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('projects'); return $this->resource->translate('projects');
} }
return 'projects'; return 'projects';
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
if (isset($this->params['opml']) && ($this->params['opml'] === true)) { if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
$this->headers[] = "Content-type: text/xml; charset=UTF-8"; $this->headers[] = "Content-type: text/xml; charset=UTF-8";
$this->DisableLogging(); $this->DisableLogging();
$this->preserveWhitespace = true; $this->preserveWhitespace = true;
} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) { } else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
$this->headers[] = "Content-type: text/plain; charset=utf-8"; $this->headers[] = "Content-type: text/plain; charset=utf-8";
$this->headers[] = "Content-Disposition: inline; filename=\"index.aux\""; $this->headers[] = "Content-Disposition: inline; filename=\"index.aux\"";
$this->DisableLogging(); $this->DisableLogging();
} else { } else {
parent::LoadHeaders(); parent::LoadHeaders();
} }
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
$this->tpl->assign('sort', $this->params['sort']); $this->tpl->assign('sort', $this->params['sort']);
$this->projectList->Sort($this->params['sort']); $this->projectList->Sort($this->params['sort']);
   
if ((empty($this->params['opml']) || ($this->params['opml'] !== true)) && if ((empty($this->params['opml']) || ($this->params['opml'] !== true)) &&
(empty($this->params['txt']) || ($this->params['txt'] !== true)) && (empty($this->params['txt']) || ($this->params['txt'] !== true)) &&
(!empty($this->params['search']))) { (!empty($this->params['search']))) {
$this->tpl->assign('search', $this->params['search']); $this->tpl->assign('search', $this->params['search']);
$matches = $this->projectList->Filter($this->params['search']); $matches = $this->projectList->Filter($this->params['search']);
if (count($matches) > 0) { if (count($matches) > 0) {
$this->tpl->assign('projectlist', $matches); $this->tpl->assign('projectlist', $matches);
} }
} else { } else {
if ($this->projectList->Count() > 0) if ($this->projectList->Count() > 0)
$this->tpl->assign('projectlist', $this->projectList); $this->tpl->assign('projectlist', $this->projectList);
} }
} }
   
} }
   
<?php <?php
/** /**
* Controller for getting a snapshot * Controller for getting a snapshot
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
class GitPHP_Controller_Snapshot extends GitPHP_ControllerBase class GitPHP_Controller_Snapshot extends GitPHP_ControllerBase
{ {
   
/** /**
* Stores the archive object * Stores the archive object
* *
* @var GitPHP_Archive * @var GitPHP_Archive
*/ */
private $archive = null; private $archive = null;
   
/** /**
* Snapshot cache directory * Snapshot cache directory
* *
* @var string * @var string
*/ */
private $cacheDir = null; private $cacheDir = null;
   
/** /**
* Snapshot cached file path  
*  
* @var string  
*/  
private $cachedFile;  
   
/**  
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
$this->InitializeConfig(); $this->InitializeConfig();
   
$this->InitializeUserList(); $this->InitializeUserList();
   
$this->InitializeGitExe(); $this->InitializeGitExe();
   
$this->InitializeProjectList(); $this->InitializeProjectList();
   
  // HACK: this needs to be done early because the snapshot controller modifies the headers before opening the archive
  if (!GitPHP_Util::FunctionAllowed('popen'))
  throw new GitPHP_DisabledFunctionException('popen');
   
if (isset($this->params['project'])) { if (isset($this->params['project'])) {
$project = $this->projectList->GetProject($this->params['project']); $project = $this->projectList->GetProject($this->params['project']);
if (!$project) { if (!$project) {
throw new GitPHP_InvalidProjectParameterException($this->params['project']); throw new GitPHP_InvalidProjectParameterException($this->params['project']);
  }
  if ($this->userList && ($this->userList->GetCount() > 0)) {
  if (!$project->UserCanAccess((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null))) {
  throw new GitPHP_UnauthorizedProjectException($this->params['project']);
  }
} }
$this->project = $project->GetProject(); $this->project = $project->GetProject();
} }
   
if (!$this->project) { if (!$this->project) {
throw new GitPHP_MissingProjectParameterException(); throw new GitPHP_MissingProjectParameterException();
} }
   
$this->preserveWhitespace = true; $this->preserveWhitespace = true;
   
if (empty($this->params['format'])) if (empty($this->params['format']))
$this->params['format'] = $this->config->GetValue('compressformat'); $this->params['format'] = $this->config->GetValue('compressformat');
   
$this->InitializeArchive(); $this->InitializeArchive();
   
if ($this->config->GetValue('cache')) { if ($this->config->GetValue('cache')) {
   
$this->cacheDir = GITPHP_CACHEDIR . 'snapshots/'; $this->cacheDir = GITPHP_CACHEDIR . 'snapshots/';
   
if (file_exists($this->cacheDir)) { if (file_exists($this->cacheDir)) {
if (!is_dir($this->cacheDir)) { if (!is_dir($this->cacheDir)) {
throw new Exception($this->cacheDir . ' exists but is not a directory'); throw new Exception($this->cacheDir . ' exists but is not a directory');
} else if (!is_writable($this->cacheDir)) { } else if (!is_writable($this->cacheDir)) {
throw new Exception($this->cacheDir . ' is not writable'); throw new Exception($this->cacheDir . ' is not writable');
} }
} else { } else {
if (!mkdir($this->cacheDir, 0777)) if (!mkdir($this->cacheDir, 0777))
throw new Exception($this->cacheDir . ' could not be created'); throw new Exception($this->cacheDir . ' could not be created');
chmod($this->cacheDir, 0777); chmod($this->cacheDir, 0777);
} }
} }
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? $this->params['file'] : '') . '|' . (isset($this->params['prefix']) ? $this->params['prefix'] : '') . '|' . $this->params['format']; return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? $this->params['file'] : '') . '|' . (isset($this->params['prefix']) ? $this->params['prefix'] : '') . '|' . $this->params['format'];
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('snapshot'); return $this->resource->translate('snapshot');
} }
return 'snapshot'; return 'snapshot';
} }
   
/** /**
* Loads headers for this template * Loads headers for this template
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
$mimetype = $this->archive->GetMimeType(); $mimetype = $this->archive->GetMimeType();
if (!empty($mimetype)) if (!empty($mimetype))
$this->headers[] = 'Content-Type: ' . $mimetype; $this->headers[] = 'Content-Type: ' . $mimetype;
   
$this->headers[] = 'Content-Disposition: attachment; filename=' . $this->archive->GetFilename(); $this->headers[] = 'Content-Disposition: attachment; filename=' . $this->archive->GetFilename();
   
if ($this->config->GetValue('cache')) { if ($this->config->GetValue('cache')) {
$cachedfile = $this->cacheDir . $this->CachedSnapshotFile(); $cachedfile = $this->cacheDir . $this->CachedSnapshotFile();
if (is_readable($cachedfile)) { if (is_readable($cachedfile)) {
$size = filesize($cachedfile); $size = filesize($cachedfile);
if ($size !== false) if ($size !== false)
$this->headers[] = 'Content-Length: ' . $size; $this->headers[] = 'Content-Length: ' . $size;
} }
} }
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
} }
   
/** /**
* Render this controller * Render this controller
*/ */
public function Render() public function Render()
{ {
$cache = $this->config->GetValue('cache'); $cache = $this->config->GetValue('cache');
$cachedfile = null; $cachedfile = null;
   
if ($cache) { if ($cache) {
$cachedfile = $this->CachedSnapshotFile(); $cachedfile = $this->CachedSnapshotFile();
$cachedfilepath = $this->cacheDir . $cachedfile; $cachedfilepath = $this->cacheDir . $cachedfile;
if (is_readable($cachedfilepath)) { if (is_readable($cachedfilepath)) {
$cachehandle = fopen($cachedfilepath, 'rb'); $cachehandle = fopen($cachedfilepath, 'rb');
if ($cachehandle) { if ($cachehandle) {
while (!feof($cachehandle)) { while (!feof($cachehandle)) {
print fread($cachehandle, 1048576); print fread($cachehandle, 1048576);
flush(); flush();
} }
fclose($cachehandle); fclose($cachehandle);
return; return;
} }
} }
} }
   
if ($this->archive->Open()) { if ($this->archive->Open()) {
   
$tmpcachefile = null; $tmpcachefile = null;
$cachehandle = null; $cachehandle = null;
   
if ($cache && !empty($cachedfile)) { if ($cache && !empty($cachedfile)) {
// write cached file too // write cached file too
$pid = 0; $pid = 0;
if (function_exists('posix_getpid')) if (function_exists('posix_getpid'))
$pid = posix_getpid(); $pid = posix_getpid();
else else
$pid = rand(); $pid = rand();
   
$tmpcachefile = 'tmp-' . $pid . '-' . $cachedfile; $tmpcachefile = 'tmp-' . $pid . '-' . $cachedfile;
$cachehandle = fopen($this->cacheDir . $tmpcachefile, 'wb'); $cachehandle = fopen($this->cacheDir . $tmpcachefile, 'wb');
} }
   
while (($data = $this->archive->Read()) !== false) { while (($data = $this->archive->Read()) !== false) {
   
print $data; print $data;
flush(); flush();
   
if ($cache && $cachehandle) { if ($cache && $cachehandle) {
fwrite($cachehandle, $data); fwrite($cachehandle, $data);
} }
   
} }
   
$this->archive->Close(); $this->archive->Close();
   
if ($cachehandle) { if ($cachehandle) {
fclose($cachehandle); fclose($cachehandle);
sleep(1); sleep(1);
rename($this->cacheDir . $tmpcachefile, $this->cacheDir . $cachedfile); rename($this->cacheDir . $tmpcachefile, $this->cacheDir . $cachedfile);
} }
} }
} }
   
/** /**
* Initialize archive for reading * Initialize archive for reading
*/ */
private function InitializeArchive() private function InitializeArchive()
{ {
$strategy = null; $strategy = null;
if ($this->params['format'] == GITPHP_COMPRESS_TAR) { if ($this->params['format'] == GITPHP_COMPRESS_TAR) {
$strategy = new GitPHP_Archive_Tar(); $strategy = new GitPHP_Archive_Tar();
} else if ($this->params['format'] == GITPHP_COMPRESS_BZ2) { } else if ($this->params['format'] == GITPHP_COMPRESS_BZ2) {
$strategy = new GitPHP_Archive_Bzip2($this->config->GetValue('compresslevel')); $strategy = new GitPHP_Archive_Bzip2($this->config->GetValue('compresslevel'));
if (!$strategy->Valid()) if (!$strategy->Valid())
$strategy = new GitPHP_Archive_Tar(); $strategy = new GitPHP_Archive_Tar();
} else if ($this->params['format'] == GITPHP_COMPRESS_GZ) { } else if ($this->params['format'] == GITPHP_COMPRESS_GZ) {
$strategy = new GitPHP_Archive_Gzip($this->config->GetValue('compresslevel')); $strategy = new GitPHP_Archive_Gzip($this->config->GetValue('compresslevel'));
if (!$strategy->Valid()) if (!$strategy->Valid())
$strategy = new GitPHP_Archive_Tar(); $strategy = new GitPHP_Archive_Tar();
} else if ($this->params['format'] == GITPHP_COMPRESS_ZIP) { } else if ($this->params['format'] == GITPHP_COMPRESS_ZIP) {
$strategy = new GitPHP_Archive_Zip($this->config->GetValue('compresslevel')); $strategy = new GitPHP_Archive_Zip($this->config->GetValue('compresslevel'));
if (!$strategy->Valid()) if (!$strategy->Valid())
$strategy = new GitPHP_Archive_Tar(); $strategy = new GitPHP_Archive_Tar();
} }
$strategy->SetExe($this->exe); $strategy->SetExe($this->exe);
   
$this->archive = new GitPHP_Archive($this->GetProject(), null, $strategy, (isset($this->params['file']) ? $this->params['file'] : ''), (isset($this->params['prefix']) ? $this->params['prefix'] : '')); $this->archive = new GitPHP_Archive($this->GetProject(), null, $strategy, (isset($this->params['file']) ? $this->params['file'] : ''), (isset($this->params['prefix']) ? $this->params['prefix'] : ''));
$commit = null; $commit = null;
   
if (!isset($this->params['hash'])) if (!isset($this->params['hash']))
$commit = $this->GetProject()->GetHeadCommit(); $commit = $this->GetProject()->GetHeadCommit();
else else
$commit = $this->GetProject()->GetCommit($this->params['hash']); $commit = $this->GetProject()->GetCommit($this->params['hash']);
   
$this->archive->SetObject($commit); $this->archive->SetObject($commit);
} }
   
/** /**
* Gets the cached snapshot file name * Gets the cached snapshot file name
* *
* @return string cached file name * @return string cached file name
*/ */
private function CachedSnapshotFile() private function CachedSnapshotFile()
{ {
$key = ($this->archive->GetObject() ? $this->archive->GetObject()->GetHash() : '') . '|' . (isset($this->params['file']) ? $this->params['file'] : '') . '|' . (isset($this->params['prefix']) ? $this->params['prefix'] : ''); $key = ($this->archive->GetObject() ? $this->archive->GetObject()->GetHash() : '') . '|' . (isset($this->params['file']) ? $this->params['file'] : '') . '|' . (isset($this->params['prefix']) ? $this->params['prefix'] : '');
$cachefile = sha1($key) . '-' . $this->archive->GetFilename(); $cachefile = sha1($key) . '-' . $this->archive->GetFilename();
return $cachefile; return $cachefile;
} }
   
} }
   
<?php <?php
/** /**
* Custom exception when an ambiguous abbreviated hash is specified * Custom exception when an ambiguous abbreviated hash is specified
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Exception * @subpackage Exception
*/ */
class GitPHP_AmbiguousHashException extends GitPHP_MessageException class GitPHP_AmbiguousHashException extends GitPHP_MessageException
{ {
/** /**
* Hash * Hash
* *
* @var string * @var string
*/ */
public $Hash; public $Hash;
   
/** /**
* Constructor * Constructor
* *
* @param string $hash hash * @param string $hash hash
* @param string $message message * @param string $message message
* @param integer $code exception code * @param integer $code exception code
*/ */
public function __construct($hash, $message = '', $code = 0) public function __construct($hash, $message = '', $code = 0)
{ {
$this->Hash = $hash; $this->Hash = $hash;
if (empty($message)) if (empty($message))
$message = 'Ambiguous abbreviated hash %1$s'; $message = sprintf('Ambiguous abbreviated hash %1$s', $hash);
parent::__construct($message, true, 200, $code); parent::__construct($message, true, 200, $code);
} }
} }
   
  <?php
  /**
  * Custom exception when a required function is disabled
  *
  * @author Christopher Han <xiphux@gmail.com>
  * @copyright Copyright (c) 2012 Christopher Han
  * @package GitPHP
  * @subpackage Exception
  */
  class GitPHP_DisabledFunctionException extends GitPHP_MessageException
  {
  /**
  * Function
  *
  * @var string
  */
  public $Function;
 
  /**
  * Constructor
  *
  * @param string $function function
  * @param string $message message
  * @param integer $code exception code
  */
  public function __construct($function, $message = '', $code = 0)
  {
  $this->Function = $function;
  if (empty($message))
  $message = sprintf('Required function %1$s has been disabled', $function);
  parent::__construct($message, true, 500, $code);
  }
  }
 
  <?php
  /**
  * Custom exception when a user tries to access a project they don't have access to
  *
  * @author Christopher Han <xiphux@gmail.com>
  * @copyright Copyright (c) 2012 Christopher Han
  * @package GitPHP
  * @subpackage Exception
  */
  class GitPHP_UnauthorizedProjectException extends GitPHP_MessageException
  {
  /**
  * Specified project
  *
  * @var string
  */
  public $Project;
 
  /**
  * Constructor
  *
  * @param string $project project
  * @param string $message message
  * @param int $code code
  */
  public function __construct($project, $message = '', $code = 0)
  {
  $this->Project = $project;
  if (empty($message))
  $message = sprintf('You are not authorized to access project %1$s', $project);
  parent::__construct($message, true, 403, $code);
  }
  }
 
<?php <?php
/** /**
* Class to load a file's history * Class to load a file's history
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_FileHistory implements Iterator, GitPHP_Pagination_Interface class GitPHP_FileHistory implements Iterator, GitPHP_Pagination_Interface
{ {
/** /**
* The project * The project
* *
* @var GitPHP_Project * @var GitPHP_Project
*/ */
protected $project = null; protected $project = null;
   
/** /**
* History * History
* *
* @var GitPHP_FileDiff[] * @var GitPHP_FileDiff[]
*/ */
protected $history = array(); protected $history = array();
   
/** /**
* The path * The path
* *
* @var string * @var string
*/ */
protected $path; protected $path;
   
/** /**
* The limit of objects to load * The limit of objects to load
* *
* @var int * @var int
*/ */
protected $limit = 50; protected $limit = 50;
   
/** /**
* The number of objects to skip * The number of objects to skip
* *
* @var int * @var int
*/ */
protected $skip = 0; protected $skip = 0;
   
/** /**
* The hash to walk back from * The hash to walk back from
* *
* @var string * @var string
*/ */
protected $hash = false; protected $hash = false;
   
/** /**
* Whether data has been loaded * Whether data has been loaded
* *
* @var boolean * @var boolean
*/ */
protected $dataLoaded = false; protected $dataLoaded = false;
   
/** /**
* Executable * Executable
* *
* @var GitPHP_GitExe * @var GitPHP_GitExe
*/ */
protected $exe; protected $exe;
   
/** /**
* Constructor * Constructor
* *
* @param GitPHP_Project $project project * @param GitPHP_Project $project project
* @param string $path file path to trace history of * @param string $path file path to trace history of
* @param GitPHP_GitExe $exe git exe * @param GitPHP_GitExe $exe git exe
* @param GitPHP_Commit $head commit to start history from * @param GitPHP_Commit $head commit to start history from
* @param int $limit limit of revisions to walk * @param int $limit limit of revisions to walk
* @param int $skip number of revisions to skip * @param int $skip number of revisions to skip
*/ */
public function __construct($project, $path, $exe, $head = null, $limit = 0, $skip = 0) public function __construct($project, $path, $exe, $head = null, $limit = 0, $skip = 0)
{ {
if (!$project) { if (!$project) {
throw new Exception('Project is required'); throw new Exception('Project is required');
} }
   
$this->project = $project; $this->project = $project;
$this->limit = $limit; $this->limit = $limit;
$this->skip = $skip; $this->skip = $skip;
   
if (!$head) if (!$head)
$head = $this->project->GetHeadCommit(); $head = $this->project->GetHeadCommit();
   
if ($head) { if ($head) {
$this->hash = $head->GetHash(); $this->hash = $head->GetHash();
} }
if (empty($path)) if (empty($path))
throw new Exception('Path is required'); throw new Exception('Path is required');
   
if (!$exe) if (!$exe)
throw new Exception('Git exe is required'); throw new Exception('Git exe is required');
   
$this->path = $path; $this->path = $path;
   
$this->exe = $exe; $this->exe = $exe;
} }
   
/** /**
* Gets the path for this file history * Gets the path for this file history
* *
* @return string path * @return string path
*/ */
public function GetPath() public function GetPath()
{ {
return $this->path; return $this->path;
} }
   
/** /**
* Gets the project * Gets the project
* *
* @return GitPHP_Project project * @return GitPHP_Project project
*/ */
public function GetProject() public function GetProject()
{ {
return $this->project; return $this->project;
} }
   
/** /**
* Gets the count * Gets the count
* *
* @return int count * @return int count
*/ */
public function GetCount() public function GetCount()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return count($this->history); return count($this->history);
} }
   
/** /**
* Gets the limit * Gets the limit
* *
* @return int limit * @return int limit
*/ */
public function GetLimit() public function GetLimit()
{ {
return $this->limit; return $this->limit;
} }
   
/** /**
* Sets the limit * Sets the limit
* *
* @param int $limit limit * @param int $limit limit
*/ */
public function SetLimit($limit) public function SetLimit($limit)
{ {
if ($this->limit == $limit) if ($this->limit == $limit)
return; return;
   
if ($this->dataLoaded) { if ($this->dataLoaded) {
if (($limit < $this->limit) && ($limit > 0)) { if (($limit < $this->limit) && ($limit > 0)) {
/* want less data, just trim the array */ /* want less data, just trim the array */
$this->history = array_slice($this->history, 0, $limit); $this->history = array_slice($this->history, 0, $limit);
} else if (($limit > $this->limit) || ($limit < 1)) { } else if (($limit > $this->limit) || ($limit < 1)) {
/* want more data, have to reload */ /* want more data, have to reload */
$this->Clear(); $this->Clear();
} }
} }
   
$this->limit = $limit; $this->limit = $limit;
} }
   
/** /**
* Gets the skip number * Gets the skip number
* *
* @return int skip number * @return int skip number
*/ */
public function GetSkip() public function GetSkip()
{ {
return $this->skip; return $this->skip;
} }
   
/** /**
* Sets the skip number * Sets the skip number
* *
* @param int $skip skip number * @param int $skip skip number
*/ */
public function SetSkip($skip) public function SetSkip($skip)
{ {
if ($skip == $this->skip) if ($skip == $this->skip)
return; return;
   
if ($this->dataLoaded) { if ($this->dataLoaded) {
$this->Clear(); $this->Clear();
} }
   
$this->skip = $skip; $this->skip = $skip;
} }
   
/** /**
* Gets the head this log will walk from * Gets the head this log will walk from
* *
* @return GitPHP_Commit head commit * @return GitPHP_Commit head commit
*/ */
public function GetHead() public function GetHead()
{ {
return $this->project->GetCommit($this->hash); return $this->project->GetCommit($this->hash);
} }
   
/** /**
* Sets the head this log will walk from * Sets the head this log will walk from
* *
* @param GitPHP_Commit $head head commit * @param GitPHP_Commit $head head commit
*/ */
public function SetHead($head) public function SetHead($head)
{ {
if ($head) if ($head)
$this->SetHeadHash($head->GetHash()); $this->SetHeadHash($head->GetHash());
else else
$this->SetHeadHash(null); $this->SetHeadHash(null);
} }
   
/** /**
* Gets the head hash this log will walk from * Gets the head hash this log will walk from
* *
* @return string hash * @return string hash
*/ */
public function GetHeadHash() public function GetHeadHash()
{ {
return $this->hash; return $this->hash;
} }
   
/** /**
* Sets the head hash this log will walk from * Sets the head hash this log will walk from
* *
* @param string $hash head commit hash * @param string $hash head commit hash
*/ */
public function SetHeadHash($hash) public function SetHeadHash($hash)
{ {
if (empty($hash)) { if (empty($hash)) {
$head = $this->project->GetHeadCommit(); $head = $this->project->GetHeadCommit();
if ($head) if ($head)
$hash = $head->GetHash(); $hash = $head->GetHash();
} }
   
if ($hash != $this->hash) { if ($hash != $this->hash) {
$this->Clear(); $this->Clear();
$this->hash = $hash; $this->hash = $hash;
} }
} }
   
/** /**
* Loads the history data * Loads the history data
*/ */
protected function LoadData() protected function LoadData()
{ {
$this->dataLoaded = true; $this->dataLoaded = true;
   
$args = array(); $args = array();
$args[] = $this->hash; $args[] = $this->hash;
$args[] = '--no-merges'; $args[] = '--no-merges';
   
$canSkip = true; $canSkip = true;
if ($this->skip > 0) if ($this->skip > 0)
$canSkip = $this->exe->CanSkip(); $canSkip = $this->exe->CanSkip();
   
if ($canSkip) { if ($canSkip) {
if ($this->limit > 0) { if ($this->limit > 0) {
$args[] = '--max-count=' . $this->limit; $args[] = '--max-count=' . $this->limit;
} }
if ($this->skip > 0) { if ($this->skip > 0) {
$args[] = '--skip=' . $this->skip; $args[] = '--skip=' . $this->skip;
} }
} else { } else {
if ($this->limit > 0) { if ($this->limit > 0) {
$args[] = '--max-count=' . ($this->limit + $this->skip); $args[] = '--max-count=' . ($this->limit + $this->skip);
} }
} }
   
$args[] = '--'; $args[] = '--';
$args[] = $this->path; $args[] = $this->path;
$args[] = '|'; $args[] = '|';
$args[] = $this->exe->GetBinary(); $args[] = $this->exe->GetBinary();
$args[] = '--git-dir=' . $this->project->GetPath(); $args[] = '--git-dir=' . escapeshellarg($this->project->GetPath());
$args[] = GIT_DIFF_TREE; $args[] = GIT_DIFF_TREE;
$args[] = '-r'; $args[] = '-r';
$args[] = '--stdin'; $args[] = '--stdin';
$args[] = '--'; $args[] = '--';
$args[] = $this->path; $args[] = $this->path;
$historylines = explode("\n", $this->exe->Execute($this->project->GetPath(), GIT_REV_LIST, $args)); $historylines = explode("\n", $this->exe->Execute($this->project->GetPath(), GIT_REV_LIST, $args));
   
$commitHash = null; $commitHash = null;
foreach ($historylines as $line) { foreach ($historylines as $line) {
if (preg_match('/^([0-9a-fA-F]{40})/', $line, $regs)) { if (preg_match('/^([0-9a-fA-F]{40})/', $line, $regs)) {
$commitHash = $regs[1]; $commitHash = $regs[1];
} else if ($commitHash) { } else if ($commitHash) {
try { try {
$this->history[] = array('diffline' => $line, 'commithash' => $commitHash); $this->history[] = array('diffline' => $line, 'commithash' => $commitHash);
} catch (Exception $e) { } catch (Exception $e) {
} }
$commitHash = null; $commitHash = null;
} }
} }
   
if (($this->skip > 0) && (!$canSkip)) { if (($this->skip > 0) && (!$canSkip)) {
if ($this->limit > 0) { if ($this->limit > 0) {
$this->history = array_slice($this->history, $this->skip, $this->limit); $this->history = array_slice($this->history, $this->skip, $this->limit);
} else { } else {
$this->history = array_slice($this->history, $this->skip); $this->history = array_slice($this->history, $this->skip);
} }
} }
   
} }
   
/** /**
* Rewinds the iterator * Rewinds the iterator
*/ */
function rewind() function rewind()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return reset($this->history); return reset($this->history);
} }
   
/** /**
* Returns the current revision * Returns the current revision
* *
* @return GitPHP_Commit * @return GitPHP_Commit
*/ */
function current() function current()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
$data = current($this->history); $data = current($this->history);
   
if (!(empty($data['diffline']) || empty($data['commithash']))) { if (!(empty($data['diffline']) || empty($data['commithash']))) {
$history = $this->GetProject()->GetObjectManager()->GetFileDiff($data['diffline']); $history = $this->GetProject()->GetObjectManager()->GetFileDiff($data['diffline']);
$history->SetCommitHash($data['commithash']); $history->SetCommitHash($data['commithash']);
return $history; return $history;
} }
   
return null; return null;
} }
   
/** /**
* Returns the current key * Returns the current key
*/ */
function key() function key()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return key($this->history); return key($this->history);
} }
   
/** /**
* Advance the pointer * Advance the pointer
*/ */
function next() function next()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return next($this->history); return next($this->history);
} }
   
/** /**
* Test for a valid pointer * Test for a valid pointer
* *
* @return boolean * @return boolean
*/ */
function valid() function valid()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return key($this->history) !== null; return key($this->history) !== null;
} }
   
/** /**
* Clears the loaded data * Clears the loaded data
*/ */
public function Clear() public function Clear()
{ {
if (!$this->dataLoaded) if (!$this->dataLoaded)
return; return;
   
$this->history = array(); $this->history = array();
reset($this->history); reset($this->history);
   
$this->dataLoaded = false; $this->dataLoaded = false;
} }
} }
   
<?php <?php
/** /**
* Class to represent a file search * Class to represent a file search
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_FileSearch implements Iterator, GitPHP_Pagination_Interface class GitPHP_FileSearch implements Iterator, GitPHP_Pagination_Interface
{ {
/** /**
* The project * The project
* *
* @var GitPHP_Project * @var GitPHP_Project
*/ */
protected $project; protected $project;
   
/** /**
* Tree hash * Tree hash
* *
* @var string * @var string
*/ */
protected $treeHash; protected $treeHash;
   
/** /**
* Tree path * Tree path
* *
* @var string * @var string
*/ */
protected $treePath; protected $treePath;
   
/** /**
* The list of all results * The list of all results
* *
* @var GitPHP_FileSearchResult[] * @var GitPHP_FileSearchResult[]
*/ */
protected $allResults = array(); protected $allResults = array();
   
/** /**
* The paginated subset of results * The paginated subset of results
* *
* @var GitPHP_FileSearchResult[] * @var GitPHP_FileSearchResult[]
*/ */
protected $resultList; protected $resultList;
   
/** /**
* Search string * Search string
* *
* @var string * @var string
*/ */
protected $search; protected $search;
   
/** /**
* The limit of objects to load * The limit of objects to load
* *
* @var int * @var int
*/ */
protected $limit = 50; protected $limit = 50;
   
/** /**
* The number of objects to skip * The number of objects to skip
* *
* @var int * @var int
*/ */
protected $skip = 0; protected $skip = 0;
   
/** /**
* Whether data has been loaded * Whether data has been loaded
* *
* @var boolean * @var boolean
*/ */
protected $dataLoaded = false; protected $dataLoaded = false;
   
/** /**
* Executable * Executable
* *
* @var GitPHP_GitExe * @var GitPHP_GitExe
*/ */
protected $exe; protected $exe;
   
/** /**
* Constructor * Constructor
* *
* @param GitPHP_Project $project project * @param GitPHP_Project $project project
* @param GitPHP_Tree $tree tree to search * @param GitPHP_Tree $tree tree to search
* @param string $search string to search * @param string $search string to search
* @param GitPHP_GitExe $exe git executable * @param GitPHP_GitExe $exe git executable
* @param int $limit limit of results to return * @param int $limit limit of results to return
* @param int $skip number of results to skip * @param int $skip number of results to skip
*/ */
public function __construct($project, $tree, $search, $exe, $limit = 50, $skip = 0) public function __construct($project, $tree, $search, $exe, $limit = 50, $skip = 0)
{ {
if (!$project) { if (!$project) {
throw new Exception('Project is required'); throw new Exception('Project is required');
} }
   
if (!$tree) { if (!$tree) {
throw new Exception('Tree is required'); throw new Exception('Tree is required');
} }
   
if (empty($search)) { if (empty($search)) {
throw new Exception('Search is required'); throw new Exception('Search is required');
} }
   
if (!$exe) { if (!$exe) {
throw new Exception('Git exe is required'); throw new Exception('Git exe is required');
} }
   
$this->project = $project; $this->project = $project;
   
$this->treeHash = $tree->GetHash(); $this->treeHash = $tree->GetHash();
$this->treePath = $tree->GetPath(); $this->treePath = $tree->GetPath();
   
$this->search = $search; $this->search = $search;
   
$this->exe = $exe; $this->exe = $exe;
   
$this->limit = $limit; $this->limit = $limit;
$this->skip = $skip; $this->skip = $skip;
} }
   
/** /**
* Gets the tree for this search * Gets the tree for this search
* *
* @return GitPHP_Tree tree * @return GitPHP_Tree tree
*/ */
public function GetTree() public function GetTree()
{ {
$tree = $this->project->GetObjectManager()->GetTree($this->treeHash); $tree = $this->project->GetObjectManager()->GetTree($this->treeHash);
$tree->SetPath($this->treePath); $tree->SetPath($this->treePath);
return $tree; return $tree;
} }
   
/** /**
* Gets the search string * Gets the search string
* *
* @return string search * @return string search
*/ */
public function GetSearch() public function GetSearch()
{ {
return $this->search; return $this->search;
} }
   
/** /**
* Sets the search string * Sets the search string
* *
* @param string $search search * @param string $search search
*/ */
public function SetSearch($search) public function SetSearch($search)
{ {
if (empty($search)) if (empty($search))
return; return;
   
if ($this->search == $search) if ($this->search == $search)
return; return;
   
if ($this->dataLoaded) { if ($this->dataLoaded) {
$this->Clear(); $this->Clear();
} }
   
$this->search = $search; $this->search = $search;
} }
   
/** /**
* Gets the count of results * Gets the count of results
* *
* @return int count * @return int count
*/ */
public function GetCount() public function GetCount()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return count($this->resultList); return count($this->resultList);
} }
   
/** /**
* Gets the limit * Gets the limit
* *
* @return int limit * @return int limit
*/ */
public function GetLimit() public function GetLimit()
{ {
return $this->limit; return $this->limit;
} }
   
/** /**
* Sets the limit * Sets the limit
* *
* @param int $limit limit * @param int $limit limit
*/ */
public function SetLimit($limit) public function SetLimit($limit)
{ {
if ($this->limit == $limit) if ($this->limit == $limit)
return; return;
   
if ($this->dataLoaded) { if ($this->dataLoaded) {
if ($limit < $this->limit) { if ($limit < $this->limit) {
/* want less data, just trim the array */ /* want less data, just trim the array */
$this->resultList = array_slice($this->resultList, 0, $limit); $this->resultList = array_slice($this->resultList, 0, $limit);
} else { } else {
/* want more data, have to rebuild subset */ /* want more data, have to rebuild subset */
$this->UpdateSubset(); $this->UpdateSubset();
} }
} }
   
$this->limit = $limit; $this->limit = $limit;
} }
   
/** /**
* Gets the skip number * Gets the skip number
* *
* @return int skip number * @return int skip number
*/ */
public function GetSkip() public function GetSkip()
{ {
return $this->skip; return $this->skip;
} }
   
/** /**
* Sets the skip number * Sets the skip number
* *
* @param int $skip skip number * @param int $skip skip number
*/ */
public function SetSkip($skip) public function SetSkip($skip)
{ {
if ($skip == $this->skip) if ($skip == $this->skip)
return; return;
   
if ($this->dataLoaded) { if ($this->dataLoaded) {
$this->UpdateSubset(); $this->UpdateSubset();
} }
   
$this->skip = $skip; $this->skip = $skip;
} }
   
/** /**
* Rewinds the iterator * Rewinds the iterator
* *
* @return GitPHP_FileSearchResult * @return GitPHP_FileSearchResult
*/ */
function rewind() function rewind()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return reset($this->resultList); return reset($this->resultList);
} }
   
/** /**
* Returns the current revision * Returns the current revision
* *
* @return GitPHP_FileSearchResult * @return GitPHP_FileSearchResult
*/ */
function current() function current()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return current($this->resultList); return current($this->resultList);
} }
   
/** /**
* Returns the current key * Returns the current key
* *
* @return int * @return int
*/ */
function key() function key()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return key($this->resultList); return key($this->resultList);
} }
   
/** /**
* Advance the pointer * Advance the pointer
* *
* @return GitPHP_FileSearchResult|boolean * @return GitPHP_FileSearchResult|boolean
*/ */
function next() function next()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return next($this->resultList); return next($this->resultList);
} }
   
/** /**
* Test for a valid pointer * Test for a valid pointer
* *
* @return boolean * @return boolean
*/ */
function valid() function valid()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
return key($this->resultList) !== null; return key($this->resultList) !== null;
} }
   
/** /**
* Clears the results * Clears the results
*/ */
public function Clear() public function Clear()
{ {
if (!$this->dataLoaded) if (!$this->dataLoaded)
return; return;
   
$this->allResults = array(); $this->allResults = array();
$this->resultList = null; $this->resultList = null;
   
$this->dataLoaded = false; $this->dataLoaded = false;
} }
   
/** /**
* Load search data * Load search data
*/ */
private function LoadData() private function LoadData()
{ {
$this->dataLoaded = true; $this->dataLoaded = true;
   
$this->SearchFilenames(); $this->SearchFilenames();
   
$this->SearchFileContents(); $this->SearchFileContents();
   
ksort($this->allResults); ksort($this->allResults);
   
$this->UpdateSubset(); $this->UpdateSubset();
} }
   
/** /**
* Searches file names for matches * Searches file names for matches
*/ */
private function SearchFilenames() private function SearchFilenames()
{ {
$treePaths = $this->GetTree()->GetTreePaths(); $treePaths = $this->GetTree()->GetTreePaths();
   
foreach ($treePaths as $path => $hash) { foreach ($treePaths as $path => $hash) {
if (preg_match('/' . preg_quote($this->search, '/') . '/i', $path)) { if (preg_match('/' . preg_quote($this->search, '/') . '/i', $path)) {
if (!isset($this->allResults[$path])) { if (!isset($this->allResults[$path])) {
   
$obj = $this->project->GetObjectManager()->GetTree($hash); $obj = $this->project->GetObjectManager()->GetTree($hash);
$obj->SetPath($path); $obj->SetPath($path);
   
$this->allResults[$path] = new GitPHP_FileSearchResult($this->project, $obj, $path); $this->allResults[$path] = new GitPHP_FileSearchResult($this->project, $obj, $path);
} }
} }
} }
   
$blobPaths = $this->GetTree()->GetBlobPaths(); $blobPaths = $this->GetTree()->GetBlobPaths();
   
foreach ($blobPaths as $path => $hash) { foreach ($blobPaths as $path => $hash) {
if (preg_match('/' . preg_quote($this->search, '/') . '/i', $path)) { if (preg_match('/' . preg_quote($this->search, '/') . '/i', $path)) {
if (!isset($this->allResults[$path])) { if (!isset($this->allResults[$path])) {
   
$obj = $this->project->GetObjectManager()->GetBlob($hash); $obj = $this->project->GetObjectManager()->GetBlob($hash);
$obj->SetPath($path); $obj->SetPath($path);
$this->allResults[$path] = new GitPHP_FileSearchResult($this->project, $obj, $path); $this->allResults[$path] = new GitPHP_FileSearchResult($this->project, $obj, $path);
} }
} }
} }
} }
   
/** /**
* Searches file contents for matches * Searches file contents for matches
*/ */
private function SearchFileContents() private function SearchFileContents()
{ {
$args = array(); $args = array();
$args[] = '-I'; $args[] = '-I';
$args[] = '--full-name'; $args[] = '--full-name';
$args[] = '--ignore-case'; $args[] = '--ignore-case';
$args[] = '-n'; $args[] = '-n';
$args[] = '-e'; $args[] = '-e';
$args[] = '"' . addslashes($this->search) . '"'; $args[] = '"' . addslashes($this->search) . '"';
$args[] = $this->treeHash; $args[] = $this->treeHash;
   
$lines = explode("\n", $this->exe->Execute($this->project->GetPath(), GIT_GREP, $args)); $lines = explode("\n", $this->exe->Execute($this->project->GetPath(), GIT_GREP, $args));
   
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match('/^[^:]+:([^:]+):([0-9]+):(.+)$/', $line, $regs)) { if (preg_match('/^[^:]+:([^:]+):([0-9]+):(.+)$/', $line, $regs)) {
if (isset($this->allResults[$regs[1]])) { if (isset($this->allResults[$regs[1]])) {
   
$result = $this->allResults[$regs[1]]; $result = $this->allResults[$regs[1]];
$matchingLines = $result->GetMatchingLines(); $matchingLines = $result->GetMatchingLines();
$matchingLines[(int)($regs[2])] = trim($regs[3], "\n\r\0\x0B"); $matchingLines[(int)($regs[2])] = trim($regs[3], "\n\r\0\x0B");
$result->SetMatchingLines($matchingLines); $result->SetMatchingLines($matchingLines);
   
} else { } else {
$tree = $this->GetTree(); $tree = $this->GetTree();
$hash = $tree->PathToHash($regs[1]); $hash = $tree->PathToHash($regs[1]);
if ($hash) { if ($hash) {
$blob = $this->project->GetObjectManager()->GetBlob($hash); $blob = $this->project->GetObjectManager()->GetBlob($hash);
$blob->SetPath($regs[1]); $blob->SetPath($regs[1]);
$result = new GitPHP_FileSearchResult($this->project, $blob, $regs[1]); $result = new GitPHP_FileSearchResult($this->project, $blob, $regs[1]);
$matchingLines = array(); $matchingLines = array();
$matchingLines[(int)($regs[2])] = trim($regs[3], "\n\r\0\x0B"); $matchingLines[(int)($regs[2])] = trim($regs[3], "\n\r\0\x0B");
$result->SetMatchingLines($matchingLines); $result->SetMatchingLines($matchingLines);
$this->allResults[$regs[1]] = $result; $this->allResults[$regs[1]] = $result;
} }
   
} }
   
} }
} }
} }
   
/** /**
* Extracts the subset of results based on size limits * Extracts the subset of results based on size limits
*/ */
private function UpdateSubset() private function UpdateSubset()
{ {
if (!$this->dataLoaded) { if (!$this->dataLoaded) {
$this->LoadData(); $this->LoadData();
} }
   
$pos = 0; $pos = 0;
$count = 0;  
   
$this->resultList = array(); $this->resultList = array();
   
foreach ($this->allResults as $path => $result) { foreach ($this->allResults as $result) {
   
if ($pos++ < $this->skip) if ($pos++ < $this->skip)
continue; continue;
   
$this->resultList[] = $result; $this->resultList[] = $result;
   
if (count($this->resultList) >= $this->limit) if (count($this->resultList) >= $this->limit)
break; break;
} }
} }
   
} }
   
<?php <?php
/** /**
* Base class for all git objects that represent a filesystem item * Base class for all git objects that represent a filesystem item
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
abstract class GitPHP_FilesystemObject extends GitPHP_GitObject abstract class GitPHP_FilesystemObject extends GitPHP_GitObject
{ {
/** /**
* Unknown type * Unknown type
* *
* @var int * @var int
*/ */
const UnknownType = 0; const UnknownType = 0;
   
/** /**
* Directory type * Directory type
* *
* @var int * @var int
*/ */
const DirectoryType = 1; const DirectoryType = 1;
   
/** /**
* Symlink type * Symlink type
* *
* @var int * @var int
*/ */
const SymlinkType = 2; const SymlinkType = 2;
   
/** /**
* File type * File type
* *
* @var int * @var int
*/ */
const FileType = 3; const FileType = 3;
   
/** /**
* The object path * The object path
* *
* @var string * @var string
*/ */
protected $path = ''; protected $path = '';
   
/** /**
* The object mode * The object mode
* *
* @var string * @var string
*/ */
protected $mode; protected $mode;
   
/** /**
* The hash of the commit this object belongs to * The hash of the commit this object belongs to
* *
* @var string * @var string
*/ */
protected $commitHash; protected $commitHash;
   
/** /**
* The trees of this object's base path * The trees of this object's base path
* *
* @var array * @var array
*/ */
protected $pathTree; protected $pathTree;
   
/** /**
* Whether the trees of the object's base path have been read * Whether the trees of the object's base path have been read
* *
* @var boolean * @var boolean
*/ */
protected $pathTreeRead = false; protected $pathTreeRead = false;
   
/** /**
* Instantiates object * Instantiates object
* *
* @param GitPHP_Project $project the project * @param GitPHP_Project $project the project
* @param string $hash object hash * @param string $hash object hash
*/ */
public function __construct($project, $hash) public function __construct($project, $hash)
{ {
parent::__construct($project, $hash); parent::__construct($project, $hash);
} }
   
/** /**
* Gets the object name * Gets the object name
* *
* @return string name * @return string name
*/ */
public function GetName() public function GetName()
{ {
if (!empty($this->path)) if (!empty($this->path))
return GitPHP_Util::BaseName($this->path); return GitPHP_Util::BaseName($this->path);
   
return ''; return '';
} }
   
/** /**
* Gets the full path * Gets the full path
* *
* @return string path * @return string path
*/ */
public function GetPath() public function GetPath()
{ {
if (!empty($this->path)) if (!empty($this->path))
return $this->path; return $this->path;
   
return ''; return '';
} }
   
/** /**
* Sets the object path * Sets the object path
* *
* @param string $path object path * @param string $path object path
*/ */
public function SetPath($path) public function SetPath($path)
{ {
$this->path = $path; $this->path = $path;
} }
   
/** /**
* Gets the object mode * Gets the object mode
* *
* @return string mode * @return string mode
*/ */
public function GetMode() public function GetMode()
{ {
return $this->mode; return $this->mode;
} }
   
/** /**
* Gets the mode as a readable string * Gets the mode as a readable string
* *
* @return string mode string * @return string mode string
*/ */
public function GetModeString() public function GetModeString()
{ {
if (empty($this->mode)) if (empty($this->mode))
return ''; return '';
   
$mode = octdec($this->mode); $mode = octdec($this->mode);
   
/* /*
* Git doesn't store full ugo modes, * Git doesn't store full ugo modes,
* it only knows if something is a directory, * it only knows if something is a directory,
* symlink, or an executable or non-executable file * symlink, or an executable or non-executable file
*/ */
if (($mode & 0x4000) == 0x4000) if (($mode & 0x4000) == 0x4000)
return 'drwxr-xr-x'; return 'drwxr-xr-x';
else if (($mode & 0xA000) == 0xA000) else if (($mode & 0xA000) == 0xA000)
return 'lrwxrwxrwx'; return 'lrwxrwxrwx';
else if (($mode & 0x8000) == 0x8000) { else if (($mode & 0x8000) == 0x8000) {
if (($mode & 0x0040) == 0x0040) if (($mode & 0x0040) == 0x0040)
return '-rwxr-xr-x'; return '-rwxr-xr-x';
else else
return '-rw-r--r--'; return '-rw-r--r--';
} }
return '----------'; return '----------';
} }
   
/** /**
* Sets the object mode * Sets the object mode
* *
* @param string $mode tree mode * @param string $mode tree mode
*/ */
public function SetMode($mode) public function SetMode($mode)
{ {
$this->mode = $mode; $this->mode = $mode;
} }
   
/** /**
* Gets the commit this object belongs to * Gets the commit this object belongs to
* *
* @return GitPHP_Commit commit object * @return GitPHP_Commit commit object
*/ */
public function GetCommit() public function GetCommit()
{ {
return $this->GetProject()->GetCommit($this->commitHash); return $this->GetProject()->GetCommit($this->commitHash);
} }
   
/** /**
* Sets the commit this object belongs to * Sets the commit this object belongs to
* *
* @param GitPHP_Commit $commit commit object * @param GitPHP_Commit $commit commit object
*/ */
public function SetCommit($commit) public function SetCommit($commit)
{ {
if (!$commit) if (!$commit)
return; return;
   
$this->SetCommitHash($commit->GetHash()); $this->SetCommitHash($commit->GetHash());
} }
   
/** /**
* Sets the hash of the commit this object belongs to * Sets the hash of the commit this object belongs to
* *
* @param string $commitHash commit hash * @param string $commitHash commit hash
*/ */
public function SetCommitHash($commitHash) public function SetCommitHash($commitHash)
{ {
if (!preg_match('/^[0-9A-Fa-f]{40}$/', $commitHash)) if (!preg_match('/^[0-9A-Fa-f]{40}$/', $commitHash))
return; return;
   
$this->commitHash = $commitHash; $this->commitHash = $commitHash;
} }
   
/** /**
* Gets the objects of the base path * Gets the objects of the base path
* *
* @return GitPHP_Tree[] array of tree objects * @return GitPHP_Tree[] array of tree objects
*/ */
public function GetPathTree() public function GetPathTree()
{ {
if (!$this->pathTreeRead) if (!$this->pathTreeRead)
$this->ReadPathTree(); $this->ReadPathTree();
   
$pathTree = array(); $pathTree = array();
$usedTrees = array(); $usedTrees = array();
for ($i = 0; $i < count($this->pathTree); ++$i) { for ($i = 0; $i < count($this->pathTree); ++$i) {
$data = $this->pathTree[$i]; $data = $this->pathTree[$i];
   
if (isset($data['hash']) && !empty($data['hash'])) { if (isset($data['hash']) && !empty($data['hash'])) {
$tree = $this->GetProject()->GetObjectManager()->GetTree($data['hash']); $tree = $this->GetProject()->GetObjectManager()->GetTree($data['hash']);
if (isset($usedTrees[$data['hash']])) { if (isset($usedTrees[$data['hash']])) {
$tree = clone $obj; $tree = clone $tree;
} else { } else {
$usedTrees[$data['hash']] = 1; $usedTrees[$data['hash']] = 1;
} }
$tree->SetPath($data['path']); $tree->SetPath($data['path']);
$pathTree[] = $tree; $pathTree[] = $tree;
} }
} }
   
return $pathTree; return $pathTree;
} }
   
/** /**
* Reads the objects of the base path * Reads the objects of the base path
*/ */
private function ReadPathTree() private function ReadPathTree()
{ {
$this->pathTreeRead = true; $this->pathTreeRead = true;
   
if (empty($this->path)) { if (empty($this->path)) {
/* this is a top level tree, it has no subpath */ /* this is a top level tree, it has no subpath */
return; return;
} }
   
$path = $this->path; $path = $this->path;
   
$tree = $this->GetCommit()->GetTree(); $tree = $this->GetCommit()->GetTree();
   
while (($pos = strrpos($path, '/')) !== false) { while (($pos = strrpos($path, '/')) !== false) {
$path = substr($path, 0, $pos); $path = substr($path, 0, $pos);
$pathhash = $tree->PathToHash($path); $pathhash = $tree->PathToHash($path);
if (!empty($pathhash)) { if (!empty($pathhash)) {
$data = array(); $data = array();
$data['hash'] = $pathhash; $data['hash'] = $pathhash;
$data['path'] = $path; $data['path'] = $path;
$this->pathTree[] = $data; $this->pathTree[] = $data;
} }
} }
   
if (count($this->pathTree) > 0) { if (count($this->pathTree) > 0) {
$this->pathTree = array_reverse($this->pathTree); $this->pathTree = array_reverse($this->pathTree);
} }
} }
   
/** /**
* Gets a filesystem object type from its octal mode * Gets a filesystem object type from its octal mode
* *
* @param string $octMode octal mode * @param string $octMode octal mode
* @return int file type * @return int file type
*/ */
public static function ObjectType($octMode) public static function ObjectType($octMode)
{ {
$mode = octdec($octMode); $mode = octdec($octMode);
   
if (($mode & 0x4000) == 0x4000) { if (($mode & 0x4000) == 0x4000) {
return GitPHP_FilesystemObject::DirectoryType; return GitPHP_FilesystemObject::DirectoryType;
} else if (($mode & 0xA000) == 0xA000) { } else if (($mode & 0xA000) == 0xA000) {
return GitPHP_FilesystemObject::SymlinkType; return GitPHP_FilesystemObject::SymlinkType;
} else if (($mode & 0x8000) == 0x8000) { } else if (($mode & 0x8000) == 0x8000) {
return GitPHP_FilesystemObject::FileType; return GitPHP_FilesystemObject::FileType;
} }
   
return GitPHP_FilesystemObject::UnknownType; return GitPHP_FilesystemObject::UnknownType;
} }
   
/** /**
* Compares two objects by path * Compares two objects by path
* *
* @param GitPHP_FilesystemObject $a first object * @param GitPHP_FilesystemObject $a first object
* @param GitPHP_FilesystemObject $b second object * @param GitPHP_FilesystemObject $b second object
* @return integer comparison result * @return integer comparison result
*/ */
public static function ComparePath($a, $b) public static function ComparePath($a, $b)
{ {
return strcmp($a->GetPath(), $b->GetPath()); return strcmp($a->GetPath(), $b->GetPath());
} }
   
} }
   
<?php <?php
/** /**
* Parses Git configuration files * Parses Git configuration files
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2011 Christopher Han * @copyright Copyright (c) 2011 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_GitConfig class GitPHP_GitConfig
{ {
   
/** /**
* Default config value type (no conversion) * Default config value type (no conversion)
* *
* @var int * @var int
*/ */
const TypeDefault = 1; const TypeDefault = 1;
   
/** /**
* Integer config value type * Integer config value type
* *
* @var int * @var int
*/ */
const TypeInteger = 2; const TypeInteger = 2;
   
/** /**
* Boolean config value type * Boolean config value type
* *
* @var int * @var int
*/ */
const TypeBoolean = 3; const TypeBoolean = 3;
/** /**
* The config file path * The config file path
* *
* @var string * @var string
*/ */
protected $configPath = null; protected $configPath = null;
   
/** /**
* Whether the config file has been loaded * Whether the config file has been loaded
* *
* @var string * @var string
*/ */
protected $configRead = false; protected $configRead = false;
   
/** /**
* Config values * Config values
* *
* @var array * @var array
*/ */
protected $config = array(); protected $config = array();
   
/** /**
* Instantiates object * Instantiates object
* *
* @param string $configPath config file path * @param string $configPath config file path
*/ */
public function __construct($configPath) public function __construct($configPath)
{ {
if (!is_readable($configPath)) { if (!is_readable($configPath)) {
throw new Exception('Git config file not readable'); throw new Exception('Git config file not readable');
} }
   
$this->configPath = $configPath; $this->configPath = $configPath;
} }
   
/** /**
* Gets a config value * Gets a config value
* *
* @param string $key config key * @param string $key config key
* @param boolean $multiValue true to support multivalue config variables * @param boolean $multiValue true to support multivalue config variables
* @param int $forceType force interpretation as a certain type * @param int $forceType force interpretation as a certain type
* @return mixed config value * @return mixed config value
*/ */
public function GetValue($key, $multiValue = false, $forceType = null) public function GetValue($key, $multiValue = false, $forceType = null)
{ {
if (empty($key)) { if (empty($key)) {
return null; return null;
} }
   
if (!$this->configRead) if (!$this->configRead)
$this->LoadConfig(); $this->LoadConfig();
   
if (!isset($this->config[$key])) { if (!isset($this->config[$key])) {
return null; return null;
} }
   
$values = array(); $values = array();
   
$valueType = null; $valueType = null;
if ($forceType !== null) { if ($forceType !== null) {
$valueType = $forceType; $valueType = $forceType;
} else { } else {
$valueType = GitPHP_GitConfig::GetType($key); $valueType = GitPHP_GitConfig::GetType($key);
} }
   
if ($valueType != GitPHP_GitConfig::TypeDefault) { if ($valueType != GitPHP_GitConfig::TypeDefault) {
// type conversion // type conversion
foreach ($this->config[$key] as $value) { foreach ($this->config[$key] as $value) {
switch ($valueType) { switch ($valueType) {
case GitPHP_GitConfig::TypeInteger: case GitPHP_GitConfig::TypeInteger:
$value = intval($value); $value = intval($value);
break; break;
case GitPHP_GitConfig::TypeBoolean: case GitPHP_GitConfig::TypeBoolean:
$value = GitPHP_GitConfig::ToBool($value); $value = GitPHP_GitConfig::ToBool($value);
break; break;
} }
$values[] = $value; $values[] = $value;
} }
} else { } else {
$values = $this->config[$key]; $values = $this->config[$key];
} }
   
if ((count($values) == 1) || (!$multiValue)) { if (!$multiValue) {
// single value // single value
return $values[0]; return $values[0];
} else { } else {
// multivalue // multivalue
return $values; return $values;
} }
} }
   
/** /**
* Tests if a config value exists * Tests if a config value exists
* *
* @param string $key config key * @param string $key config key
* @return boolean true if value exists * @return boolean true if value exists
*/ */
public function HasValue($key) public function HasValue($key)
{ {
if (empty($key)) { if (empty($key)) {
return false; return false;
} }
   
if (!$this->configRead) if (!$this->configRead)
$this->LoadConfig(); $this->LoadConfig();
   
return isset($this->config[$key]); return isset($this->config[$key]);
} }
   
/** /**
* Loads the config data * Loads the config data
*/ */
private function LoadConfig() private function LoadConfig()
{ {
$this->configRead = true; $this->configRead = true;
   
$data = explode("\n", file_get_contents($this->configPath)); $data = explode("\n", file_get_contents($this->configPath));
   
$currentSection = ''; $currentSection = '';
$currentSetting = ''; $currentSetting = '';
foreach ($data as $line) { foreach ($data as $line) {
$trimmed = trim($line); $trimmed = trim($line);
if (empty($trimmed)) { if (empty($trimmed)) {
continue; continue;
} }
   
if (preg_match('/^\[(.+)\]$/', $trimmed, $regs)) { if (preg_match('/^\[(.+)\]$/', $trimmed, $regs)) {
// section // section
   
$currentSection = ''; $currentSection = '';
$currentSetting = ''; $currentSetting = '';
$trimmedSection = trim($regs[1]); $trimmedSection = trim($regs[1]);
if (preg_match('/^([0-9A-Za-z\.\-]+)( "(.+)")?$/', $trimmedSection, $subRegs)) { if (preg_match('/^([0-9A-Za-z\.\-]+)( "(.+)")?$/', $trimmedSection, $subRegs)) {
$currentSection = strtolower($subRegs[1]); $currentSection = strtolower($subRegs[1]);
if (!empty($subRegs[3])) { if (!empty($subRegs[3])) {
// subsection // subsection
$currentSection .= '.' . $subRegs[3]; $currentSection .= '.' . $subRegs[3];
} }
} }
} else if (!empty($currentSection)) { } else if (!empty($currentSection)) {
// variable // variable
   
$currentSetting .= $trimmed; $currentSetting .= $trimmed;
if (substr($trimmed, -1) === '\\') { if (substr($trimmed, -1) === '\\') {
// continued on next line // continued on next line
continue; continue;
} }
   
$key = ''; $key = '';
$value = null; $value = null;
   
$eq = strpos($currentSetting, '='); $eq = strpos($currentSetting, '=');
if ($eq !== false) { if ($eq !== false) {
// key value pair // key value pair
$key = GitPHP_GitConfig::Unescape(trim(substr($currentSetting, 0, $eq))); $key = GitPHP_GitConfig::Unescape(trim(substr($currentSetting, 0, $eq)));
$value = GitPHP_GitConfig::Unescape(trim(substr($currentSetting, $eq+1))); $value = GitPHP_GitConfig::Unescape(trim(substr($currentSetting, $eq+1)));
} else { } else {
// no equals is assumed true // no equals is assumed true
$key = GitPHP_GitConfig::Unescape($currentSetting); $key = GitPHP_GitConfig::Unescape($currentSetting);
$value = "true"; $value = "true";
} }
   
if (!empty($key)) { if (!empty($key)) {
$fullSetting = $currentSection . '.' . strtolower($key); $fullSetting = $currentSection . '.' . strtolower($key);
   
$this->config[$fullSetting][] = $value; $this->config[$fullSetting][] = $value;
} }
   
$currentSetting = ''; $currentSetting = '';
} }
} }
} }
   
/** /**
* Gets the default type of a config value * Gets the default type of a config value
* *
* @param string $key config key * @param string $key config key
* @return int config value type * @return int config value type
*/ */
protected static function GetType($key) protected static function GetType($key)
{ {
switch ($key) { switch ($key) {
case 'gitphp.compat': case 'gitphp.compat':
return GitPHP_GitConfig::TypeBoolean; return GitPHP_GitConfig::TypeBoolean;
case 'core.abbrev': case 'core.abbrev':
return GitPHP_GitConfig::TypeInteger; return GitPHP_GitConfig::TypeInteger;
} }
   
return GitPHP_GitConfig::TypeDefault; return GitPHP_GitConfig::TypeDefault;
} }
   
/** /**
* Interpret git config boolean values * Interpret git config boolean values
* *
* @param mixed $value value to interpret * @param mixed $value value to interpret
* @return boolean boolean interpretation * @return boolean boolean interpretation
*/ */
protected static function ToBool($value) protected static function ToBool($value)
{ {
// true/false // true/false
if (strncasecmp($value, 'true', 4) === 0) { if (strncasecmp($value, 'true', 4) === 0) {
return true; return true;
} }
if (strncasecmp($value, 'false', 5) === 0) { if (strncasecmp($value, 'false', 5) === 0) {
return false; return false;
} }
   
// on/off // on/off
if (strncasecmp($value, 'on', 2) === 0) { if (strncasecmp($value, 'on', 2) === 0) {
return true; return true;
} }
if (strncasecmp($value, 'off', 3) === 0) { if (strncasecmp($value, 'off', 3) === 0) {
return false; return false;
} }
   
// 1/0 is handled by normal type conversion // 1/0 is handled by normal type conversion
return (bool)$value; return (bool)$value;
} }
   
/** /**
* Parses escaped values and comments from git configs * Parses escaped values and comments from git configs
* *
* @param string $value value * @param string $value value
* @return string unescaped value * @return string unescaped value
*/ */
protected static function Unescape($value) protected static function Unescape($value)
{ {
if (strlen($value) == 0) { if (strlen($value) == 0) {
return ''; return '';
} }
   
if ((substr($value, 0, 1) === '"') && (substr($value, -1) === '"')) { if ((substr($value, 0, 1) === '"') && (substr($value, -1) === '"')) {
// escaped with quotes // escaped with quotes
$value = substr($value, 1, -1); $value = substr($value, 1, -1);
} else { } else {
// not quoted, strip comments // not quoted, strip comments
$value = preg_replace('/(#|;).*$/', '', $value); $value = preg_replace('/(#|;).*$/', '', $value);
} }
   
// replace backslashed chars // replace backslashed chars
$value = str_replace(array('\\\\', '\\"'), array('\\', '"'), $value); $value = str_replace(array('\\\\', '\\"'), array('\\', '"'), $value);
   
return $value; return $value;
} }
   
} }
   
   
<?php <?php
   
/** /**
* git cat-file constant * git cat-file constant
*/ */
define('GIT_CAT_FILE','cat-file'); define('GIT_CAT_FILE','cat-file');
   
/** /**
* git diff-tree constant * git diff-tree constant
*/ */
define('GIT_DIFF_TREE','diff-tree'); define('GIT_DIFF_TREE','diff-tree');
   
/** /**
* git ls-tree constant * git ls-tree constant
*/ */
define('GIT_LS_TREE','ls-tree'); define('GIT_LS_TREE','ls-tree');
   
/** /**
* git rev-list constant * git rev-list constant
*/ */
define('GIT_REV_LIST','rev-list'); define('GIT_REV_LIST','rev-list');
   
/** /**
* git rev-parse constant * git rev-parse constant
*/ */
define('GIT_REV_PARSE','rev-parse'); define('GIT_REV_PARSE','rev-parse');
   
/** /**
* git show-ref constant * git show-ref constant
*/ */
define('GIT_SHOW_REF','show-ref'); define('GIT_SHOW_REF','show-ref');
   
/** /**
* git archive constant * git archive constant
*/ */
define('GIT_ARCHIVE','archive'); define('GIT_ARCHIVE','archive');
   
/** /**
* git grep constant * git grep constant
*/ */
define('GIT_GREP','grep'); define('GIT_GREP','grep');
   
/** /**
* git blame constant * git blame constant
*/ */
define('GIT_BLAME','blame'); define('GIT_BLAME','blame');
   
/** /**
* git name-rev constant * git name-rev constant
*/ */
define('GIT_NAME_REV','name-rev'); define('GIT_NAME_REV','name-rev');
   
/** /**
* git for-each-ref constant * git for-each-ref constant
*/ */
define('GIT_FOR_EACH_REF','for-each-ref'); define('GIT_FOR_EACH_REF','for-each-ref');
   
/** /**
* Class to wrap git executable * Class to wrap git executable
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_GitExe implements GitPHP_Observable_Interface class GitPHP_GitExe implements GitPHP_Observable_Interface
{ {
   
/** /**
* The binary path * The binary path
* *
* @var string * @var string
*/ */
protected $binary; protected $binary;
   
/** /**
* The binary version * The binary version
* *
* @var string * @var string
*/ */
protected $version; protected $version;
   
/** /**
* Whether the version has been read * Whether the version has been read
* *
* @var boolean * @var boolean
*/ */
protected $versionRead = false; protected $versionRead = false;
   
/** /**
* Observers * Observers
* *
* @var GitPHP_Observer_Interface[] * @var GitPHP_Observer_Interface[]
*/ */
protected $observers = array(); protected $observers = array();
   
  /**
  * Whether the exec function is allowed by the install
  *
  * @var null|boolean
  */
  protected $execAllowed = null;
   
  /**
  * Whether the shell_exec function is allowed by the install
  *
  * @var null|boolean
  */
  protected $shellExecAllowed = null;
   
  /**
  * Whether the popen function is allowed by the install
  *
  * @var null|boolean
  */
  protected $popenAllowed = null;
   
  /**
  * Whether the proc_open function is allowed by the install
  *
  * @var null|boolean
  */
  protected $procOpenAllowed = null;
   
  /**
  * Whether or not caching function GetProcess is initialized
  *
  * @var null|boolean
  */
  protected $getProcessInitialized = false;
   
  /**
  * Processes spawned for batch object fetching
  *
  * @var array
  */
  protected static $processes = array();
   
/** /**
* Constructor * Constructor
* *
* @param string $binary path to git binary * @param string $binary path to git binary
*/ */
public function __construct($binary = '') public function __construct($binary = '')
{ {
if (empty($binary)) { if (empty($binary)) {
$binary = GitPHP_GitExe::DefaultBinary(); $binary = GitPHP_GitExe::DefaultBinary();
} }
$this->binary = $binary; $this->binary = $binary;
} }
   
/** /**
* Executes a command * Executes a command
* *
* @param string $projectPath path to project * @param string $projectPath path to project
* @param string $command the command to execute * @param string $command the command to execute
* @param string[] $args arguments * @param string[] $args arguments
* @return string result of command * @return string result of command
*/ */
public function Execute($projectPath, $command, $args) public function Execute($projectPath, $command, $args)
{ {
  if ($this->shellExecAllowed === null) {
  $this->shellExecAllowed = GitPHP_Util::FunctionAllowed('shell_exec');
  if (!$this->shellExecAllowed) {
  throw new GitPHP_DisabledFunctionException('shell_exec');
  }
  }
   
$fullCommand = $this->CreateCommand($projectPath, $command, $args); $fullCommand = $this->CreateCommand($projectPath, $command, $args);
   
$this->Log('Begin executing "' . $fullCommand . '"'); $this->Log('Execute', '', 'start');
   
$ret = shell_exec($fullCommand); $ret = shell_exec($fullCommand);
   
$this->Log('Finish executing "' . $fullCommand . '"' . $this->Log('Execute', $fullCommand . "\n\n" . $ret, 'stop');
"\nwith result: " . $ret);  
   
return $ret; return $ret;
  }
   
  protected function GetProcess($projectPath)
  {
  if (!$this->getProcessInitialized) {
  register_shutdown_function(array($this, 'DestroyAllProcesses'));
  $this->getProcessInitialized = true;
  }
   
  if (!isset(self::$processes[$projectPath])) {
  GitPHP_DebugLog::GetInstance()->TimerStart();
   
  $process = proc_open(
  $cmd = $this->CreateCommand($projectPath, GIT_CAT_FILE, array('--batch')),
  array(
  0 => array('pipe', 'r'),
  1 => array('pipe', 'w'),
  2 => array('file', GitPHP_Util::NullFile(), 'w'),
  ),
  $pipes
  );
   
  self::$processes[$projectPath] = array(
  'process' => $process,
  'pipes' => $pipes,
  );
   
  GitPHP_DebugLog::GetInstance()->TimerStop('proc_open', $cmd);
  }
   
  return self::$processes[$projectPath];
  }
   
  public function DestroyAllProcesses()
  {
  foreach (self::$processes as $projectPath => $process) {
  $this->DestroyProcess($projectPath);
  }
  }
   
  protected function DestroyProcess($projectPath)
  {
  $pipes = self::$processes[$projectPath]['pipes'];
  foreach ($pipes as $pipe) {
  fclose($pipe);
  }
  $process = self::$processes[$projectPath]['process'];
  proc_terminate($process);
  proc_close($process);
  unset(self::$processes[$projectPath]);
  }
   
  public function GetObjectData($projectPath, $hash)
  {
  if ($this->procOpenAllowed === null) {
  $this->procOpenAllowed = GitPHP_Util::FunctionAllowed('proc_open');
  if (!$this->procOpenAllowed) {
  throw new GitPHP_DisabledFunctionException('proc_open');
  }
  }
   
  $process = $this->GetProcess($projectPath);
  $pipes = $process['pipes'];
   
  $data = $hash . "\n";
  if (fwrite($pipes[0], $data) !== mb_orig_strlen($data)) {
  $this->DestroyProcess($projectPath);
  return false;
  }
  fflush($pipes[0]);
   
  $ln = rtrim(fgets($pipes[1]));
  if (!$ln) {
  $this->DestroyProcess($projectPath);
  return false;
  }
   
  $parts = explode(" ", rtrim($ln));
  if (count($parts) == 2 && $parts[1] == 'missing') {
  return false;
  } else if (count($parts) != 3) {
  $this->DestroyProcess($projectPath);
  return false;
  }
   
  list($hash, $type, $n) = $parts;
   
  $contents = '';
  while (mb_orig_strlen($contents) < $n) {
  $buf = fread($pipes[1], min(4096, $n - mb_orig_strlen($contents)));
  if ($buf === false) {
  $this->DestroyProcess($projectPath);
  return false;
  }
  $contents .= $buf;
  }
   
  if (fgetc($pipes[1]) != "\n") {
  $this->DestroyProcess($projectPath);
  return false;
  }
   
  return array(
  'hash' => $hash,
  'contents' => $contents,
  'type' => $type,
  );
} }
   
/** /**
* Opens a resource to a command * Opens a resource to a command
* *
* @param string $projectPath path to project * @param string $projectPath path to project
* @param string $command the command to execute * @param string $command the command to execute
* @param string[] $args arguments * @param string[] $args arguments
* @param string $mode process open mode * @param string $mode process open mode
* @return resource process handle * @return resource process handle
*/ */
public function Open($projectPath, $command, $args, $mode = 'r') public function Open($projectPath, $command, $args, $mode = 'r')
{ {
  if ($this->popenAllowed === null) {
  $this->popenAllowed = GitPHP_Util::FunctionAllowed('popen');
  if (!$this->popenAllowed) {
  throw new GitPHP_DisabledFunctionException('popen');
  }
  }
   
$fullCommand = $this->CreateCommand($projectPath, $command, $args); $fullCommand = $this->CreateCommand($projectPath, $command, $args);
   
return popen($fullCommand, $mode); return popen($fullCommand, $mode);
} }
   
/** /**
* Creates a command * Creates a command
* *
* @param string $projectPath path to project * @param string $projectPath path to project
* @param string $command the command to execute * @param string $command the command to execute
* @param string[] $args arguments * @param string[] $args arguments
* @return string full executable string * @return string full executable string
*/ */
protected function CreateCommand($projectPath, $command, $args) protected function CreateCommand($projectPath, $command, $args)
{ {
$gitDir = ''; $gitDir = '';
if (!empty($projectPath)) { if (!empty($projectPath)) {
$gitDir = '--git-dir=' . $projectPath; $gitDir = '--git-dir=' . escapeshellarg($projectPath);
} }
   
return $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args); return $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args);
} }
   
/** /**
* Gets the binary for this executable * Gets the binary for this executable
* *
* @return string binary * @return string binary
*/ */
public function GetBinary() public function GetBinary()
{ {
return $this->binary; return $this->binary;
} }
   
/** /**
* Gets the version of the git binary * Gets the version of the git binary
* *
* @return string version * @return string version
*/ */
public function GetVersion() public function GetVersion()
{ {
if (!$this->versionRead) if (!$this->versionRead)
$this->ReadVersion(); $this->ReadVersion();
   
return $this->version; return $this->version;
} }
   
/** /**
* Reads the git version * Reads the git version
*/ */
protected function ReadVersion() protected function ReadVersion()
{ {
  if ($this->shellExecAllowed === null) {
  $this->shellExecAllowed = GitPHP_Util::FunctionAllowed('shell_exec');
  if (!$this->shellExecAllowed) {
  throw new GitPHP_DisabledFunctionException('shell_exec');
  }
  }
   
$this->versionRead = true; $this->versionRead = true;
   
$this->version = ''; $this->version = '';
   
$versionCommand = $this->binary . ' --version'; $versionCommand = $this->binary . ' --version';
$ret = trim(shell_exec($versionCommand)); $ret = trim(shell_exec($versionCommand));
if (preg_match('/^git version ([0-9\.]+)$/i', $ret, $regs)) { if (preg_match('/^git version ([0-9\.]+)$/i', $ret, $regs)) {
$this->version = $regs[1]; $this->version = $regs[1];
} }
} }
   
/** /**
* Tests if this version of git can skip through the revision list * Tests if this version of git can skip through the revision list
* *
* @return boolean true if we can skip * @return boolean true if we can skip
*/ */
public function CanSkip() public function CanSkip()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* Skip only appears in git >= 1.5.0 */ /* Skip only appears in git >= 1.5.0 */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5))) {
return false; return false;
} }
} }
   
return true; return true;
} }
   
/** /**
* Tests if this version of git can show the size of a blob when listing a tree * Tests if this version of git can show the size of a blob when listing a tree
* *
* @return true if we can show sizes * @return true if we can show sizes
*/ */
public function CanShowSizeInTree() public function CanShowSizeInTree()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* /*
* ls-tree -l only appears in git 1.5.3 * ls-tree -l only appears in git 1.5.3
* (technically 1.5.3-rc0 but i'm not getting that fancy) * (technically 1.5.3-rc0 but i'm not getting that fancy)
*/ */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) {
return false; return false;
} }
} }
   
return true; return true;
   
} }
   
/** /**
* Tests if this version of git has the regexp tuning option to ignore regexp case * Tests if this version of git has the regexp tuning option to ignore regexp case
* *
* @return true if we can ignore regexp case * @return true if we can ignore regexp case
*/ */
public function CanIgnoreRegexpCase() public function CanIgnoreRegexpCase()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* /*
* regexp-ignore-case only appears in git 1.5.3 * regexp-ignore-case only appears in git 1.5.3
*/ */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) {
return false; return false;
} }
} }
   
return true; return true;
} }
   
/** /**
* Tests if this executable is valid * Tests if this executable is valid
* *
* @return boolean true if valid * @return boolean true if valid
*/ */
public function Valid() public function Valid()
{ {
  if ($this->execAllowed === null) {
  $this->execAllowed = GitPHP_Util::FunctionAllowed('exec');
  if (!$this->execAllowed) {
  throw new GitPHP_DisabledFunctionException('exec');
  }
  }
   
if (empty($this->binary)) if (empty($this->binary))
return false; return false;
   
$code = 0; $code = 0;
$out = exec($this->binary . ' --version', $tmp, $code); exec($this->binary . ' --version', $tmp, $code);
   
return $code == 0; return $code == 0;
} }
   
/** /**
* Add a new observer * Add a new observer
* *
* @param GitPHP_Observer_Interface $observer observer * @param GitPHP_Observer_Interface $observer observer
*/ */
public function AddObserver($observer) public function AddObserver($observer)
{ {
if (!$observer) if (!$observer)
return; return;
   
if (array_search($observer, $this->observers) !== false) if (array_search($observer, $this->observers) !== false)
return; return;
   
$this->observers[] = $observer; $this->observers[] = $observer;
} }
   
/** /**
* Remove an observer * Remove an observer
* *
* @param GitPHP_Observer_Interface $observer observer * @param GitPHP_Observer_Interface $observer observer
*/ */
public function RemoveObserver($observer) public function RemoveObserver($observer)
{ {
if (!$observer) if (!$observer)
return; return;
   
$key = array_search($observer, $this->observers); $key = array_search($observer, $this->observers);
   
if ($key === false) if ($key === false)
return; return;
   
unset($this->observers[$key]); unset($this->observers[$key]);
} }
   
/** /**
* Log an execution * Log an execution
* *
* @param string $message message * @param string $message message
*/ * @param string $msg_data message
private function Log($message) * @param string $type message
  */
  private function Log($message, $msg_data, $type)
{ {
if (empty($message)) if (empty($message))
return; return;
   
foreach ($this->observers as $observer) { foreach ($this->observers as $observer) {
$observer->ObjectChanged($this, GitPHP_Observer_Interface::LoggableChange, array($message)); $observer->ObjectChanged($this, GitPHP_Observer_Interface::LoggableChange, array($message, $msg_data, $type));
} }
} }
   
/** /**
* Gets the default binary for the platform * Gets the default binary for the platform
* *
* @return string binary * @return string binary
*/ */
public static function DefaultBinary() public static function DefaultBinary()
{ {
if (GitPHP_Util::IsWindows()) { if (GitPHP_Util::IsWindows()) {
// windows // windows
   
if (GitPHP_Util::Is64Bit()) { if (GitPHP_Util::Is64Bit()) {
// match x86_64 and x64 (64 bit) // match x86_64 and x64 (64 bit)
// C:\Program Files (x86)\Git\bin\git.exe // C:\Program Files (x86)\Git\bin\git.exe
return 'C:\\Progra~2\\Git\\bin\\git.exe'; return 'C:\\Progra~2\\Git\\bin\\git.exe';
} else { } else {
// 32 bit // 32 bit
// C:\Program Files\Git\bin\git.exe // C:\Program Files\Git\bin\git.exe
return 'C:\\Progra~1\\Git\\bin\\git.exe'; return 'C:\\Progra~1\\Git\\bin\\git.exe';
} }
} else { } else {
// *nix, just use PATH // *nix, just use PATH
return 'git'; return 'git';
} }
} }
   
} }
   
<?php <?php
/** /**
* Handles loading data from raw git objects * Handles loading data from raw git objects
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_GitObjectLoader class GitPHP_GitObjectLoader
{ {
/** /**
* The project * The project
* *
* @var GitPHP_Project * @var GitPHP_Project
*/ */
protected $project; protected $project;
   
/** /**
* The list of packs * The list of packs
* *
* @var GitPHP_Pack[] * @var GitPHP_Pack[]
*/ */
protected $packs = array(); protected $packs = array();
   
/** /**
* Whether packs have been read * Whether packs have been read
* *
* @var boolean * @var boolean
*/ */
protected $packsRead = false; protected $packsRead = false;
   
/** /**
* Constructor * Constructor
* *
* @param GitPHP_Project $project project * @param GitPHP_Project $project project
*/ */
public function __construct($project) public function __construct($project)
{ {
if (!$project) if (!$project)
throw new Exception('Project is required'); throw new Exception('Project is required');
   
$this->project = $project; $this->project = $project;
} }
   
/** /**
* Gets the project * Gets the project
* *
* @return GitPHP_Project project * @return GitPHP_Project project
*/ */
public function GetProject() public function GetProject()
{ {
return $this->project; return $this->project;
} }
   
/** /**
* Gets the raw content of an object * Gets the raw content of an object
* *
* @param string $hash object hash * @param string $hash object hash
* @param int $type returns the object type * @param int $type returns the object type
* @return string object data * @return string object data
*/ */
public function GetObject($hash, &$type = 0) public function GetObject($hash, &$type = 0)
{ {
if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) { if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) {
return false; return false;
} }
   
  if (GitPHP_DebugLog::GetInstance()->GetEnabled())
  $autolog = new GitPHP_DebugAutoLog();
   
// first check if it's unpacked // first check if it's unpacked
$path = $this->project->GetPath() . '/objects/' . substr($hash, 0, 2) . '/' . substr($hash, 2); $path = $this->project->GetPath() . '/objects/' . substr($hash, 0, 2) . '/' . substr($hash, 2);
if (file_exists($path)) { if (file_exists($path)) {
list($header, $data) = explode("\0", gzuncompress(file_get_contents($path)), 2); list($header, $data) = explode("\0", gzuncompress(file_get_contents($path)), 2);
sscanf($header, "%s %d", $typestr, $size); if (preg_match('/^([A-Za-z]+) /', $header, $typestr)) {
switch ($typestr) { switch ($typestr[1]) {
case 'commit': case 'commit':
$type = GitPHP_Pack::OBJ_COMMIT; $type = GitPHP_Pack::OBJ_COMMIT;
break; break;
case 'tree': case 'tree':
$type = GitPHP_Pack::OBJ_TREE; $type = GitPHP_Pack::OBJ_TREE;
break; break;
case 'blob': case 'blob':
$type = GitPHP_Pack::OBJ_BLOB; $type = GitPHP_Pack::OBJ_BLOB;
break; break;
case 'tag': case 'tag':
$type = GitPHP_Pack::OBJ_TAG; $type = GitPHP_Pack::OBJ_TAG;
break; break;
  }
} }
return $data; return $data;
} }
   
if (!$this->packsRead) { if (!$this->packsRead) {
$this->ReadPacks(); $this->ReadPacks();
} }
   
// then try packs // then try packs
foreach ($this->packs as $pack) { foreach ($this->packs as $pack) {
$data = $pack->GetObject($hash, $type); $data = $pack->GetObject($hash, $type);
if ($data !== false) { if ($data !== false) {
return $data; return $data;
} }
} }
   
return false; return false;
} }
   
/** /**
* Read the list of packs in the repository * Read the list of packs in the repository
*/ */
private function ReadPacks() private function ReadPacks()
{ {
$dh = opendir($this->project->GetPath() . '/objects/pack'); $dh = opendir($this->project->GetPath() . '/objects/pack');
if ($dh !== false) { if ($dh !== false) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
if (preg_match('/^pack-([0-9A-Fa-f]{40})\.idx$/', $file, $regs)) { if (preg_match('/^pack-([0-9A-Fa-f]{40})\.pack$/', $file, $regs)) {
$this->packs[] = new GitPHP_Pack($this->project, $regs[1], $this); $this->packs[] = new GitPHP_Pack($this->project, $regs[1], $this);
} }
} }
} }
$this->packsRead = true; $this->packsRead = true;
} }
   
/** /**
* Ensures a hash prefix is unique * Ensures a hash prefix is unique
* *
* @param string $hash full hash * @param string $hash full hash
* @param string $prefix abbreviated hash prefix * @param string $prefix abbreviated hash prefix
*/ */
public function EnsureUniqueHash($hash, $prefix) public function EnsureUniqueHash($hash, $prefix)
{ {
if (empty($hash) || empty($prefix)) if (empty($hash) || empty($prefix))
return null; return null;
   
if (!(preg_match('/[0-9A-Fa-f]{40}/', $hash))) if (!(preg_match('/[0-9A-Fa-f]{40}/', $hash)))
return $hash; return $hash;
   
if (preg_match('/[0-9A-Fa-f]{40}/', $prefix)) if (preg_match('/[0-9A-Fa-f]{40}/', $prefix))
return $prefix; return $prefix;
   
$hashMap = array(); $hashMap = array();
   
$matches = $this->FindHashObjects($prefix); $matches = $this->FindHashObjects($prefix);
foreach ($matches as $matchingHash) { foreach ($matches as $matchingHash) {
$hashMap[$matchingHash] = 1; $hashMap[$matchingHash] = 1;
} }
   
if (!$this->packsRead) { if (!$this->packsRead) {
$this->ReadPacks(); $this->ReadPacks();
} }
   
foreach ($this->packs as $pack) { foreach ($this->packs as $pack) {
$matches = $pack->FindHashes($prefix); $matches = $pack->FindHashes($prefix);
foreach ($matches as $matchingHash) { foreach ($matches as $matchingHash) {
$hashMap[$matchingHash] = 1; $hashMap[$matchingHash] = 1;
} }
} }
   
if (count($hashMap) == 0) { if (count($hashMap) == 0) {
return $hash; return $hash;
} }
   
if (count($hashMap) == 1) { if (count($hashMap) == 1) {
return $prefix; return $prefix;
} }
   
for ($len = strlen($prefix)+1; $len < 40; $len++) { for ($len = strlen($prefix)+1; $len < 40; $len++) {
$prefix = substr($hash, 0, $len); $prefix = substr($hash, 0, $len);
   
foreach ($hashMap as $matchingHash => $val) { foreach (array_keys($hashMap) as $matchingHash) {
if (substr_compare($matchingHash, $prefix, 0, $len) !== 0) { if (substr_compare($matchingHash, $prefix, 0, $len) !== 0) {
unset($hashMap[$matchingHash]); unset($hashMap[$matchingHash]);
} }
} }
   
if (count($hashMap) == 1) { if (count($hashMap) == 1) {
return $prefix; return $prefix;
} }
} }
   
return $hash; return $hash;
} }
   
/** /**
* Expands an abbreviated hash to the full hash * Expands an abbreviated hash to the full hash
* *
* @param string $abbrevHash abbreviated hash * @param string $abbrevHash abbreviated hash
* @return string full hash * @return string full hash
*/ */
public function ExpandHash($abbrevHash) public function ExpandHash($abbrevHash)
{ {
if (!(preg_match('/[0-9A-Fa-f]{4,39}/', $abbrevHash))) { if (!(preg_match('/[0-9A-Fa-f]{4,39}/', $abbrevHash))) {
return $abbrevHash; return $abbrevHash;
} }
   
$matches = $this->FindHashObjects($abbrevHash); $matches = $this->FindHashObjects($abbrevHash);
   
if (!$this->packsRead) { if (!$this->packsRead) {
$this->ReadPacks(); $this->ReadPacks();
} }
   
foreach ($this->packs as $pack) { foreach ($this->packs as $pack) {
$matches = array_merge($matches, $pack->FindHashes($abbrevHash)); $matches = array_merge($matches, $pack->FindHashes($abbrevHash));
} }
   
$matches = array_unique($matches); $matches = array_unique($matches);
   
if (count($matches) < 1) if (count($matches) < 1)
return $abbrevHash; return $abbrevHash;
   
if (count($matches) > 1) { if (count($matches) > 1) {
throw new GitPHP_AmbiguousHashException($abbrevHash); throw new GitPHP_AmbiguousHashException($abbrevHash);
} }
   
return $matches[0]; return $matches[0];
} }
   
/** /**
* Finds loose hash files matching a given prefix * Finds loose hash files matching a given prefix
* *
* @param string $prefix hash prefix * @param string $prefix hash prefix
* @return string[] array of hashes * @return string[] array of hashes
*/ */
private function FindHashObjects($prefix) private function FindHashObjects($prefix)
{ {
$matches = array(); $matches = array();
if (empty($prefix)) { if (empty($prefix)) {
return $matches; return $matches;
} }
   
$subdir = substr($prefix, 0, 2); $subdir = substr($prefix, 0, 2);
$fulldir = $this->project->GetPath() . '/objects/' . $subdir; $fulldir = $this->project->GetPath() . '/objects/' . $subdir;
if (!is_dir($fulldir)) { if (!is_dir($fulldir)) {
return $matches; return $matches;
} }
   
$prefixlen = strlen($prefix); $prefixlen = strlen($prefix);
$dh = opendir($fulldir); $dh = opendir($fulldir);
if ($dh !== false) { if ($dh !== false) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
$fullhash = $subdir . $file; $fullhash = $subdir . $file;
if (substr_compare($fullhash, $prefix, 0, $prefixlen) === 0) { if (substr_compare($fullhash, $prefix, 0, $prefixlen) === 0) {
$matches[] = $fullhash; $matches[] = $fullhash;
} }
} }
} }
return $matches; return $matches;
} }
   
} }
   
<?php <?php
/** /**
* Manages creating and caching git object classes * Manages creating and caching git object classes
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_GitObjectManager implements GitPHP_Observer_Interface class GitPHP_GitObjectManager implements GitPHP_Observer_Interface
{ {
/** /**
* The project * The project
* *
* @var GitPHP_Project * @var GitPHP_Project
*/ */
protected $project; protected $project;
   
/** /**
* Cache instance * Cache instance
* *
* @var GitPHP_Cache * @var GitPHP_Cache
*/ */
protected $cache = null; protected $cache = null;
   
/** /**
* MemoryCache instance * MemoryCache instance
* *
* @var GitPHP_MemoryCache * @var GitPHP_MemoryCache
*/ */
protected $memoryCache = null; protected $memoryCache = null;
   
/** /**
* Compatibility mode * Compatibility mode
* *
* @var boolean * @var boolean
*/ */
protected $compat = false; protected $compat = false;
   
/** /**
* Executable * Executable
* *
* @var GitPHP_GitExe * @var GitPHP_GitExe
*/ */
protected $exe; protected $exe;
   
/** /**
* Object loader * Object loader
* *
* @var GitPHP_GitObjectLoader * @var GitPHP_GitObjectLoader
*/ */
protected $objectLoader; protected $objectLoader;
   
/** /**
* Constructor * Constructor
* *
* @param GitPHP_Project $project project * @param GitPHP_Project $project project
*/ */
public function __construct($project) public function __construct($project)
{ {
if (!$project) if (!$project)
throw new Exception('Project is required'); throw new Exception('Project is required');
   
$this->project = $project; $this->project = $project;
} }
   
/** /**
* Set executable * Set executable
* *
* @param GitPHP_GitExe $exe executable * @param GitPHP_GitExe $exe executable
*/ */
public function SetExe($exe) public function SetExe($exe)
{ {
$this->exe = $exe; $this->exe = $exe;
} }
   
/** /**
* Set object loader * Set object loader
* *
* @param GitPHP_GitObjectLoader $objectLoader object loader * @param GitPHP_GitObjectLoader $objectLoader object loader
*/ */
public function SetObjectLoader($objectLoader) public function SetObjectLoader($objectLoader)
{ {
$this->objectLoader = $objectLoader; $this->objectLoader = $objectLoader;
} }
   
/** /**
* Get project * Get project
* *
* @return GitPHP_Project * @return GitPHP_Project
*/ */
public function GetProject() public function GetProject()
{ {
return $this->project; return $this->project;
} }
   
/** /**
* Gets the cache instance being used * Gets the cache instance being used
* *
* @return GitPHP_Cache|null cache instance * @return GitPHP_Cache|null cache instance
*/ */
public function GetCache() public function GetCache()
{ {
return $this->cache; return $this->cache;
} }
   
/** /**
* Set the cache instance to use * Set the cache instance to use
* *
* @param GitPHP_Cache|null $cache cache instance * @param GitPHP_Cache|null $cache cache instance
*/ */
public function SetCache($cache) public function SetCache($cache)
{ {
$this->cache = $cache; $this->cache = $cache;
} }
   
/** /**
* Gets the memory cache instance being used * Gets the memory cache instance being used
* *
* @return GitPHP_MemoryCache|null memory cache instance * @return GitPHP_MemoryCache|null memory cache instance
*/ */
public function GetMemoryCache() public function GetMemoryCache()
{ {
return $this->memoryCache; return $this->memoryCache;
} }
   
/** /**
* Sets the memory cache instance to use * Sets the memory cache instance to use
* *
* @param GitPHP_MemoryCache|null $memoryCache memory cache instance * @param GitPHP_MemoryCache|null $memoryCache memory cache instance
*/ */
public function SetMemoryCache($memoryCache) public function SetMemoryCache($memoryCache)
{ {
$this->memoryCache = $memoryCache; $this->memoryCache = $memoryCache;
} }
   
/** /**
* Gets the compatibility mode * Gets the compatibility mode
* *
* @return boolean * @return boolean
*/ */
public function GetCompat() public function GetCompat()
{ {
return $this->compat; return $this->compat;
} }
   
/** /**
* Sets the compatibility mode * Sets the compatibility mode
* *
* @param boolean $compat compatibility mode * @param boolean $compat compatibility mode
*/ */
public function SetCompat($compat) public function SetCompat($compat)
{ {
$this->compat = $compat; $this->compat = $compat;
} }
   
/** /**
* Get a commit * Get a commit
* *
* @param string $hash commit hash * @param string $hash commit hash
* @return GitPHP_Commit|null commit objet * @return GitPHP_Commit|null commit objet
*/ */
public function GetCommit($hash) public function GetCommit($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $hash)) { if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $hash)) {
$fullHash = $this->project->ExpandHash($hash); $fullHash = $this->project->ExpandHash($hash);
if ($fullHash == $hash) if ($fullHash == $hash)
throw new GitPHP_InvalidHashException($hash); throw new GitPHP_InvalidHashException($hash);
$hash = $fullHash; $hash = $fullHash;
} }
   
if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash))
return null; return null;
   
$key = GitPHP_Commit::CacheKey($this->project->GetProject(), $hash); $key = GitPHP_Commit::CacheKey($this->project->GetProject(), $hash);
   
$commit = null; $commit = null;
if ($this->memoryCache) if ($this->memoryCache)
$commit = $this->memoryCache->Get($key); $commit = $this->memoryCache->Get($key);
   
if (!$commit) { if (!$commit) {
   
if ($this->cache) { if ($this->cache) {
$commit = $this->cache->Get($key); $commit = $this->cache->Get($key);
} }
   
$strategy = null; $strategy = null;
if ($this->compat) { if ($this->compat) {
$strategy = new GitPHP_CommitLoad_Git($this->exe); $strategy = new GitPHP_CommitLoad_Git($this->exe);
} else { } else {
$strategy = new GitPHP_CommitLoad_Raw($this->objectLoader, $this->exe); $strategy = new GitPHP_CommitLoad_Raw($this->objectLoader, $this->exe);
} }
   
if ($commit) { if ($commit) {
$commit->SetProject($this->project); $commit->SetProject($this->project);
$commit->SetStrategy($strategy); $commit->SetStrategy($strategy);
} else { } else {
$commit = new GitPHP_Commit($this->project, $hash, $strategy); $commit = new GitPHP_Commit($this->project, $hash, $strategy);
} }
   
$commit->AddObserver($this); $commit->AddObserver($this);
   
if ($this->memoryCache) if ($this->memoryCache)
$this->memoryCache->Set($key, $commit); $this->memoryCache->Set($key, $commit);
   
} }
   
return $commit; return $commit;
} }
   
/** /**
* Gets a single tag * Gets a single tag
* *
* @param string $tag tag to find * @param string $tag tag to find
* @param string $hash hash of tag, if known * @param string $hash hash of tag, if known
* @return GitPHP_Tag tag object * @return GitPHP_Tag tag object
*/ */
public function GetTag($tag, $hash = '') public function GetTag($tag, $hash = '')
{ {
if (empty($tag)) if (empty($tag))
return null; return null;
   
$key = GitPHP_Tag::CacheKey($this->project->GetProject(), $tag); $key = GitPHP_Tag::CacheKey($this->project->GetProject(), $tag);
   
$tagObj = null; $tagObj = null;
if ($this->memoryCache) if ($this->memoryCache)
$tagObj = $this->memoryCache->Get($key); $tagObj = $this->memoryCache->Get($key);
   
if (!$tagObj) { if (!$tagObj) {
   
if ($this->cache) { if ($this->cache) {
$tagObj = $this->cache->Get($key); $tagObj = $this->cache->Get($key);
} }
   
$strategy = null; $strategy = null;
if ($this->compat) { if ($this->compat) {
$strategy = new GitPHP_TagLoad_Git($this->exe); $strategy = new GitPHP_TagLoad_Git($this->exe);
} else { } else {
$strategy = new GitPHP_TagLoad_Raw($this->objectLoader); $strategy = new GitPHP_TagLoad_Raw($this->objectLoader);
} }
   
if ($tagObj) { if ($tagObj) {
$tagObj->SetProject($this->project); $tagObj->SetProject($this->project);
$tagObj->SetStrategy($strategy); $tagObj->SetStrategy($strategy);
} else { } else {
$tagObj = new GitPHP_Tag($this->project, $tag, $strategy, $hash); $tagObj = new GitPHP_Tag($this->project, $tag, $strategy, $hash);
} }
   
$tagObj->AddObserver($this); $tagObj->AddObserver($this);
   
if ($this->memoryCache) if ($this->memoryCache)
$this->memoryCache->Set($key, $tagObj); $this->memoryCache->Set($key, $tagObj);
} }
   
return $tagObj; return $tagObj;
} }
   
/** /**
* Gets a single head * Gets a single head
* *
* @param string $head head to find * @param string $head head to find
* @param string $hash hash of head, if known * @param string $hash hash of head, if known
* @return GitPHP_Head head object * @return GitPHP_Head head object
*/ */
public function GetHead($head, $hash = '') public function GetHead($head, $hash = '')
{ {
if (empty($head)) if (empty($head))
return null; return null;
   
$key = GitPHP_Head::CacheKey($this->project->GetProject(), $head); $key = GitPHP_Head::CacheKey($this->project->GetProject(), $head);
   
$headObj = null; $headObj = null;
if ($this->memoryCache) if ($this->memoryCache)
$headObj = $this->memoryCache->Get($key); $headObj = $this->memoryCache->Get($key);
   
if (!$headObj) { if (!$headObj) {
$headObj = new GitPHP_Head($this->project, $head, $hash); $headObj = new GitPHP_Head($this->project, $head, $hash);
   
if ($this->memoryCache) if ($this->memoryCache)
$this->memoryCache->Set($key, $headObj); $this->memoryCache->Set($key, $headObj);
} }
   
return $headObj; return $headObj;
} }
   
/** /**
* Gets a blob * Gets a blob
* *
* @param string $hash blob hash * @param string $hash blob hash
* @return GitPHP_Blob blob object * @return GitPHP_Blob blob object
*/ */
public function GetBlob($hash) public function GetBlob($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $hash) && !$this->compat) { if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $hash) && !$this->compat) {
$fullHash = $this->project->ExpandHash($hash); $fullHash = $this->project->ExpandHash($hash);
if ($fullHash == $hash) if ($fullHash == $hash)
throw new GitPHP_InvalidHashException($hash); throw new GitPHP_InvalidHashException($hash);
$hash = $fullHash; $hash = $fullHash;
} }
   
if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash))
return null; return null;
   
$key = GitPHP_Blob::CacheKey($this->project->GetProject(), $hash); $key = GitPHP_Blob::CacheKey($this->project->GetProject(), $hash);
   
$blob = null; $blob = null;
if ($this->memoryCache) if ($this->memoryCache)
$blob = $this->memoryCache->Get($key); $blob = $this->memoryCache->Get($key);
   
if (!$blob) { if (!$blob) {
   
if ($this->cache) { if ($this->cache) {
$blob = $this->cache->Get($key); $blob = $this->cache->Get($key);
} }
   
$strategy = null; $strategy = null;
if ($this->compat) { if ($this->compat) {
$strategy = new GitPHP_BlobLoad_Git($this->exe); $strategy = new GitPHP_BlobLoad_Git($this->exe);
} else { } else {
$strategy = new GitPHP_BlobLoad_Raw($this->objectLoader); $strategy = new GitPHP_BlobLoad_Raw($this->objectLoader, $this->exe);
} }
   
if ($blob) { if ($blob) {
$blob->SetProject($this->project); $blob->SetProject($this->project);
$blob->SetStrategy($strategy); $blob->SetStrategy($strategy);
} else { } else {
$blob = new GitPHP_Blob($this->project, $hash, $strategy); $blob = new GitPHP_Blob($this->project, $hash, $strategy);
} }
   
$blob->AddObserver($this); $blob->AddObserver($this);
   
if ($this->memoryCache) if ($this->memoryCache)
$this->memoryCache->Set($key, $blob); $this->memoryCache->Set($key, $blob);
} }
   
return $blob; return $blob;
} }
   
/** /**
* Gets a tree * Gets a tree
* *
* @param string $hash tree hash * @param string $hash tree hash
* @return GitPHP_Tree tree object * @return GitPHP_Tree tree object
*/ */
public function GetTree($hash) public function GetTree($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $hash) && !$this->compat) { if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $hash) && !$this->compat) {
$fullHash = $this->project->ExpandHash($hash); $fullHash = $this->project->ExpandHash($hash);
if ($fullHash == $hash) if ($fullHash == $hash)
throw new GitPHP_InvalidHashException($hash); throw new GitPHP_InvalidHashException($hash);
$hash = $fullHash; $hash = $fullHash;
} }
   
if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash))
return null; return null;
   
$key = GitPHP_Tree::CacheKey($this->project->GetProject(), $hash); $key = GitPHP_Tree::CacheKey($this->project->GetProject(), $hash);
$tree = null; $tree = null;
if ($this->memoryCache) if ($this->memoryCache)
$tree = $this->memoryCache->Get($key); $tree = $this->memoryCache->Get($key);
   
if (!$tree) { if (!$tree) {
   
if ($this->cache) { if ($this->cache) {
$tree = $this->cache->Get($key); $tree = $this->cache->Get($key);
} }
   
$strategy = null; $strategy = null;
if ($this->compat) { if ($this->compat) {
$strategy = new GitPHP_TreeLoad_Git($this->exe); $strategy = new GitPHP_TreeLoad_Git($this->exe);
} else { } else {
$strategy = new GitPHP_TreeLoad_Raw($this->objectLoader, $this->exe); $strategy = new GitPHP_TreeLoad_Raw($this->objectLoader, $this->exe);
} }
if ($tree) { if ($tree) {
$tree->SetProject($this->project); $tree->SetProject($this->project);
$tree->SetStrategy($strategy); $tree->SetStrategy($strategy);
} else { } else {
$tree = new GitPHP_Tree($this->project, $hash, $strategy); $tree = new GitPHP_Tree($this->project, $hash, $strategy);
} }
   
$tree->AddObserver($this); $tree->AddObserver($this);
   
if ($this->memoryCache) if ($this->memoryCache)
$this->memoryCache->Set($key, $tree); $this->memoryCache->Set($key, $tree);
} }
   
return $tree; return $tree;
} }
   
/** /**
* Gets a file diff * Gets a file diff
* *
* @param string $fromHash source hash, can also be a diff-tree info line * @param string $fromHash source hash, can also be a diff-tree info line
* @param string $toHash target hash, required if $fromHash is a hash * @param string $toHash target hash, required if $fromHash is a hash
* @return GitPHP_FileDiff file diff object * @return GitPHP_FileDiff file diff object
*/ */
public function GetFileDiff($fromHash, $toHash = '') public function GetFileDiff($fromHash, $toHash = '')
{ {
if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $fromHash) && !$this->compat) { if (preg_match('/^[0-9A-Fa-f]{4,39}$/', $fromHash) && !$this->compat) {
$fullHash = $this->project->ExpandHash($fromHash); $fullHash = $this->project->ExpandHash($fromHash);
if ($fullHash == $fromHash) if ($fullHash == $fromHash)
throw new GitPHP_InvalidHashException($fromHash); throw new GitPHP_InvalidHashException($fromHash);
$fromHash = $fullHash; $fromHash = $fullHash;
} }
   
if (!empty($toHash) && preg_match('/^[0-9A-Fa-f]{4,39}$/', $toHash) && !$this->compat) { if (!empty($toHash) && preg_match('/^[0-9A-Fa-f]{4,39}$/', $toHash) && !$this->compat) {
$fullHash = $this->project->ExpandHash($toHash); $fullHash = $this->project->ExpandHash($toHash);
if ($fullHash == $toHash) if ($fullHash == $toHash)
throw new GitPHP_InvalidHashException($toHash); throw new GitPHP_InvalidHashException($toHash);
$toHash = $fullHash; $toHash = $fullHash;
} }
   
$fileDiff = new GitPHP_FileDiff($this->project, $fromHash, $toHash); $fileDiff = new GitPHP_FileDiff($this->project, $fromHash, $toHash);
$fileDiff->SetCache($this->cache); $fileDiff->SetCache($this->cache);
return $fileDiff; return $fileDiff;
} }
   
/** /**
* Notify that observable object changed * Notify that observable object changed
* *
* @param GitPHP_Observable_Interface $object object * @param GitPHP_Observable_Interface $object object
* @param int $changeType type of change * @param int $changeType type of change
* @param array $args argument array * @param array $args argument array
*/ */
public function ObjectChanged($object, $changeType, $args = array()) public function ObjectChanged($object, $changeType, $args = array())
{ {
if (!$object) if (!$object)
return; return;
   
if ($changeType !== GitPHP_Observer_Interface::CacheableDataChange) if ($changeType !== GitPHP_Observer_Interface::CacheableDataChange)
return; return;
   
if (!$this->cache) if (!$this->cache)
return; return;
   
if (!(($object instanceof GitPHP_Observable_Interface) && ($object instanceof GitPHP_Cacheable_Interface))) if (!(($object instanceof GitPHP_Observable_Interface) && ($object instanceof GitPHP_Cacheable_Interface)))
return; return;
   
$this->cache->Set($object->GetCacheKey(), $object); $this->cache->Set($object->GetCacheKey(), $object);
} }
   
} }
   
<?php <?php
/** /**
* Bzip2 archive creation class * Bzip2 archive creation class
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2012 Christopher Han * @copyright Copyright (c) 2012 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git\Archive * @subpackage Git\Archive
*/ */
class GitPHP_Archive_Bzip2 implements GitPHP_ArchiveStrategy_Interface class GitPHP_Archive_Bzip2 implements GitPHP_ArchiveStrategy_Interface
{ {
/** /**
* Executable * Executable
* *
* @var GitPHP_GitExe * @var GitPHP_GitExe
*/ */
protected $exe; protected $exe;
   
/** /**
* Process handle * Process handle
* *
* @var resource * @var resource
*/ */
protected $handle; protected $handle;
   
/** /**
* Compression level * Compression level
* *
* @var integer * @var integer
*/ */
protected $compressLevel; protected $compressLevel;
   
/** /**
* Constructor * Constructor
* *
* @param integer $compressLevel compression level * @param integer $compressLevel compression level
*/ */
public function __construct($compressLevel = null) public function __construct($compressLevel = null)
{ {
if (!(($compressLevel === null) || (is_int($compressLevel) && ($compressLevel >= 1) && ($compressLevel <= 9)))) if (!(($compressLevel === null) || (is_int($compressLevel) && ($compressLevel >= 1) && ($compressLevel <= 9))))
throw new Exception('Invalid compression level'); throw new Exception('Invalid compression level');
   
$this->compressLevel = $compressLevel; $this->compressLevel = $compressLevel;
} }
   
/** /**
* Set executable for this archive * Set executable for this archive
* *
* @param GitPHP_GitExe $exe git exe * @param GitPHP_GitExe $exe git exe
*/ */
public function SetExe($exe) public function SetExe($exe)
{ {
$this->exe = $exe; $this->exe = $exe;
} }
   
/** /**
* Open a descriptor for this archive * Open a descriptor for this archive
* *
* @param GitPHP_Archive $archive archive * @param GitPHP_Archive $archive archive
* @return boolean true on success * @return boolean true on success
*/ */
public function Open($archive) public function Open($archive)
{ {
if (!$archive) if (!$archive)
return false; return false;
   
if ($this->handle) { if ($this->handle) {
return true; return true;
} }
   
$args = array(); $args = array();
$args[] = '--format=tar'; $args[] = '--format=tar';
$args[] = '--prefix=' . $archive->GetPrefix(); $args[] = "--prefix='" . $archive->GetPrefix() . "'";
$args[] = $archive->GetObject()->GetHash(); $args[] = $archive->GetObject()->GetHash();
   
$this->handle = $this->exe->Open($archive->GetProject()->GetPath(), GIT_ARCHIVE, $args); $this->handle = $this->exe->Open($archive->GetProject()->GetPath(), GIT_ARCHIVE, $args);
   
return ($this->handle !== false); return ($this->handle !== false);
} }
   
/** /**
* Read a chunk of the archive data * Read a chunk of the archive data
* *
* @param int $size size of data to read * @param int $size size of data to read
* @return string|boolean archive data or false * @return string|boolean archive data or false
*/ */
public function Read($size = 1048576) public function Read($size = 1048576)
{ {
if (!$this->handle) if (!$this->handle)
return false; return false;
   
if (feof($this->handle)) if (feof($this->handle))
return false; r