Add option to show restricted projects as disabled
Add option to show restricted projects as disabled

<?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 * feedfilter
* Sets a regular expression to use to filter commits out * Sets a regular expression to use to filter commits out
* of the project atom/rss feed. Commits that have a * of the project atom/rss feed. Commits that have a
* commit message matching this pattern will be excluded. * commit message matching this pattern will be excluded.
* For example, '/GIT_SILENT/' will exclude any commit * For example, '/GIT_SILENT/' will exclude any commit
* with the string GIT_SILENT in the commit message. * with the string GIT_SILENT in the commit message.
*/ */
//$gitphp_conf['feedfilter'] = '/GIT_SILENT/'; //$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;
   
   
/* /*
* 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;
} }
   
<?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 ($this->userList && ($this->userList->GetCount() > 0)) {  
$this->projectList->FilterByUser((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null));  
}  
} }
   
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 ($this->userList && ($this->userList->GetCount() > 0)) {
if (!$project->UserCanAccess((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null))) { if (!$project->UserCanAccess((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null))) {
throw new GitPHP_UnauthorizedProjectException($this->params['project']); 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($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']); $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 = new GitPHP_DebugLog($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->GetValue('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 = 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());
} }
} }
} }
} }
   
/** /**
* 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();
   
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()) if ($this->log && $this->log->GetBenchmark())
$this->log->Log("Data load begin"); $this->log->Log("Data load begin");
$this->LoadCommonData(); $this->LoadCommonData();
$this->LoadData(); $this->LoadData();
if ($this->log && $this->log->GetBenchmark()) if ($this->log && $this->log->GetBenchmark())
$this->log->Log("Data load end"); $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()) if ($this->log && $this->log->GetBenchmark())
$this->log->Log("Smarty render begin"); $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()) if ($this->log && $this->log->GetBenchmark())
$this->log->Log("Smarty render end"); $this->log->Log("Smarty render end");
   
$this->tpl->clearAllAssign(); $this->tpl->clearAllAssign();
   
if ($this->log && $this->projectList) if ($this->log && $this->projectList)
$this->log->Log('MemoryCache count: ' . $this->projectList->GetMemoryCache()->GetCount()); $this->log->Log('MemoryCache count: ' . $this->projectList->GetMemoryCache()->GetCount());
} }
   
/** /**
* 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 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'] : ''); $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 $cachekey; return $cachekey;
} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) { } else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
return $cachekey; return $cachekey;
} }
$cachekey .= '|' . $this->params['sort'] . '|' . (isset($this->params['search']) ? $this->params['search'] : ''); $cachekey .= '|' . $this->params['sort'] . '|' . (isset($this->params['search']) ? $this->params['search'] : '');
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 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);
} }
} }
   
} }
   
/* /*
* GitPHP javascript project search * GitPHP javascript project search
* *
* Live search of project list * Live search of project list
* *
* @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 Javascript * @subpackage Javascript
*/ */
   
define(["jquery", 'modules/resources', 'modules/hassearchreset'], define(["jquery", 'modules/resources', 'modules/hassearchreset'],
function($, resources, hassearchreset) { function($, resources, hassearchreset) {
   
var table = null; var table = null;
var searchPanel = null; var searchPanel = null;
var msgContainer = null; var msgContainer = null;
   
var currentSearch = ''; var currentSearch = '';
var searchTimer = null; var searchTimer = null;
   
var self = null; var self = null;
   
var clearSearch = function() { var clearSearch = function() {
searchPanel.find('img searchSpinner').show(); searchPanel.find('img searchSpinner').show();
searchPanel.find('input.projectSearchBox').val(''); searchPanel.find('input.projectSearchBox').val('');
currentSearch = ''; currentSearch = '';
search(''); search('');
searchPanel.find('img.searchSpinner').hide(); searchPanel.find('img.searchSpinner').hide();
return false; return false;
}; };
   
var doSearch = function() { var doSearch = function() {
var newSearch = searchPanel.find('input.projectSearchBox').val().toLowerCase(); var newSearch = searchPanel.find('input.projectSearchBox').val().toLowerCase();
if (newSearch != currentSearch) { if (newSearch != currentSearch) {
searchPanel.find('img.searchSpinner').show(); searchPanel.find('img.searchSpinner').show();
if (searchTimer != null) { if (searchTimer != null) {
clearTimeout(searchTimer); clearTimeout(searchTimer);
} }
currentSearch = newSearch; currentSearch = newSearch;
searchTimer = setTimeout(function() { searchTimer = setTimeout(function() {
self.search(newSearch); self.search(newSearch);
searchPanel.find('img.searchSpinner').hide(); searchPanel.find('img.searchSpinner').hide();
}, 500); }, 500);
} }
}; };
   
function bindEvents() { function bindEvents() {
searchPanel.find('form').keypress(function(e) { searchPanel.find('form').keypress(function(e) {
if (e.which == 13) { if (e.which == 13) {
return false; return false;
} }
}); });
if (table.find('tr.projectRow').size() > 0) { if (table.find('tr.projectRow').size() > 0) {
if (!hassearchreset()) { if (!hassearchreset()) {
searchPanel.find('a.clearSearch').click(clearSearch); searchPanel.find('a.clearSearch').click(clearSearch);
} }
searchPanel.find('input.projectSearchBox').keyup(doSearch).bind('input paste', doSearch); searchPanel.find('input.projectSearchBox').keyup(doSearch).bind('input paste', doSearch);
} }
} }
   
function searchRow(row, searchString) { function searchRow(row, searchString) {
var projectName = row.find('td.projectName a').text(); var projectName = row.find('td.projectName span').text();
if ((projectName.length > 0) && (projectName.toLowerCase().indexOf(searchString) != -1)) { if ((projectName.length > 0) && (projectName.toLowerCase().indexOf(searchString) != -1)) {
return true; return true;
} }
   
var projectDesc = row.find('td.projectDescription a').text(); var projectDesc = row.find('td.projectDescription span').text();
if ((projectDesc.length > 0) && (projectDesc.toLowerCase().indexOf(searchString) != -1)) { if ((projectDesc.length > 0) && (projectDesc.toLowerCase().indexOf(searchString) != -1)) {
return true; return true;
} }
   
var projectOwner = row.find('td.projectOwner em').text(); var projectOwner = row.find('td.projectOwner em').text();
if ((projectOwner.length > 0) && (projectOwner.toLowerCase().indexOf(searchString) != -1)) { if ((projectOwner.length > 0) && (projectOwner.toLowerCase().indexOf(searchString) != -1)) {
return true; return true;
} }
   
return false; return false;
} }
   
function noMatchesMessage(show, searchString) { function noMatchesMessage(show, searchString) {
if (show) { if (show) {
if (!msgContainer) { if (!msgContainer) {
msgContainer = jQuery(document.createElement('div')); msgContainer = jQuery(document.createElement('div'));
msgContainer.addClass('message'); msgContainer.addClass('message');
msgContainer.appendTo(table); msgContainer.appendTo(table);
} }
   
var msg = resources.NoMatchesFound.replace(new RegExp('%1'), searchString); var msg = resources.NoMatchesFound.replace(new RegExp('%1'), searchString);
msgContainer.text(msg); msgContainer.text(msg);
   
msgContainer.show(); msgContainer.show();
} else { } else {
if (msgContainer) { if (msgContainer) {
msgContainer.hide(); msgContainer.hide();
} }
} }
} }
   
var search = function(searchString) { var search = function(searchString) {
clearTimeout(searchTimer); clearTimeout(searchTimer);
searchTimer = null; searchTimer = null;
   
if (!hassearchreset()) { if (!hassearchreset()) {
if (searchString.length == 0) { if (searchString.length == 0) {
searchPanel.find('a.clearSearch').hide(); searchPanel.find('a.clearSearch').hide();
} else { } else {
searchPanel.find('a.clearSearch').show(); searchPanel.find('a.clearSearch').show();
} }
} }
   
var hasMatch = false; var hasMatch = false;
var visibleCategories = []; var visibleCategories = [];
   
// search each project // search each project
table.find('tr.projectRow').each(function() { table.find('tr.projectRow').each(function() {
var jThis = $(this); var jThis = $(this);
if (searchString.length < 1) { if (searchString.length < 1) {
jThis.show(); jThis.show();
hasMatch = true; hasMatch = true;
return; return;
} }
if (searchRow(jThis, searchString)) { if (searchRow(jThis, searchString)) {
jThis.show(); jThis.show();
hasMatch = true; hasMatch = true;
var category = jThis.data('category'); var category = jThis.data('category');
if (category && (jQuery.inArray(category, visibleCategories) == -1)) { if (category && (jQuery.inArray(category, visibleCategories) == -1)) {
visibleCategories.push(category); visibleCategories.push(category);
} }
} else { } else {
jThis.hide(); jThis.hide();
} }
}); });
   
// show categories that have matching projects // show categories that have matching projects
table.find('tr.categoryRow').each(function() { table.find('tr.categoryRow').each(function() {
var jThis = $(this); var jThis = $(this);
if (searchString.length < 1) { if (searchString.length < 1) {
jThis.show(); jThis.show();
return; return;
} }
var category = jThis.children('th.categoryName').text(); var category = jThis.children('th.categoryName').text();
if (category.length > 0) { if (category.length > 0) {
if (jQuery.inArray(category, visibleCategories) !== -1) { if (jQuery.inArray(category, visibleCategories) !== -1) {
jThis.show(); jThis.show();
} else { } else {
jThis.hide(); jThis.hide();
} }
} }
}); });
   
if (hasMatch) { if (hasMatch) {
noMatchesMessage(false); noMatchesMessage(false);
table.find('tr.projectHeader').show(); table.find('tr.projectHeader').show();
} else { } else {
noMatchesMessage(true, searchString) noMatchesMessage(true, searchString)
table.find('tr.projectHeader').hide(); table.find('tr.projectHeader').hide();
} }
}; };
   
var init = function(tableElem, searchPanelElem) { var init = function(tableElem, searchPanelElem) {
table = tableElem; table = tableElem;
searchPanel = searchPanelElem; searchPanel = searchPanelElem;
self = this; self = this;
   
// store project categories // store project categories
var category = ""; var category = "";
table.find('tr').each(function() { table.find('tr').each(function() {
var jThis = $(this); var jThis = $(this);
if (jThis.hasClass('categoryRow')) { if (jThis.hasClass('categoryRow')) {
category = jThis.children('th.categoryName').text(); category = jThis.children('th.categoryName').text();
} else if (jThis.hasClass('projectRow')) { } else if (jThis.hasClass('projectRow')) {
if (category.length > 0) { if (category.length > 0) {
jThis.data('category', category); jThis.data('category', category);
} }
} }
}); });
bindEvents(); bindEvents();
}; };
   
return { return {
init: init, init: init,
search: search search: search
} }
} }
); );
   
{* {*
* projectlist.tpl * projectlist.tpl
* gitphp: A PHP git repository browser * gitphp: A PHP git repository browser
* Component: Project list template * Component: Project list template
* *
* Copyright (C) 2009 Christopher Han <xiphux@gmail.com> * Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
*} *}
{extends file='main.tpl'} {extends file='main.tpl'}
   
{block name=javascript} {block name=javascript}
require.deps = ['projectlist']; require.deps = ['projectlist'];
{if file_exists('js/projectlist.min.js')} {if file_exists('js/projectlist.min.js')}
require.paths.projectlist = "projectlist.min"; require.paths.projectlist = "projectlist.min";
{/if} {/if}
{/block} {/block}
   
{block name=main} {block name=main}
   
<div class="index_header"> <div class="index_header">
{if file_exists('templates/hometext.tpl') } {if file_exists('templates/hometext.tpl') }
{include file='hometext.tpl'} {include file='hometext.tpl'}
{else} {else}
{* default header *} {* default header *}
<p> <p>
git source code archive git source code archive
</p> </p>
{/if} {/if}
</div> </div>
   
<div class="projectSearch"> <div class="projectSearch">
<form method="get" action="{geturl}" id="projectSearchForm" enctype="application/x-www-form-urlencoded"> <form method="get" action="{geturl}" id="projectSearchForm" enctype="application/x-www-form-urlencoded">
{t}Search projects{/t}: <input type="search" name="s" class="projectSearchBox" {if $search}value="{$search}"{/if} /> <a href="{geturl}" class="clearSearch" {if !$search}style="display: none;"{/if}>X</a> {if $javascript}<img src="images/search-loader.gif" class="searchSpinner" style="display: none;" alt="{t}Loading…{/t}" />{/if} {t}Search projects{/t}: <input type="search" name="s" class="projectSearchBox" {if $search}value="{$search}"{/if} /> <a href="{geturl}" class="clearSearch" {if !$search}style="display: none;"{/if}>X</a> {if $javascript}<img src="images/search-loader.gif" class="searchSpinner" style="display: none;" alt="{t}Loading…{/t}" />{/if}
</form> </form>
</div> </div>
   
<table class="projectList"> <table class="projectList">
{foreach name=projects from=$projectlist item=proj} {foreach name=projects from=$projectlist item=proj}
{if $smarty.foreach.projects.first} {if $smarty.foreach.projects.first}
{* Header *} {* Header *}
<tr class="projectHeader"> <tr class="projectHeader">
{if $sort == "project"} {if $sort == "project"}
<th>{t}Project{/t}</th> <th>{t}Project{/t}</th>
{else} {else}
<th><a class="header" href="{geturl sort=project}">{t}Project{/t}</a></th> <th><a class="header" href="{geturl sort=project}">{t}Project{/t}</a></th>
{/if} {/if}
{if $sort == "descr"} {if $sort == "descr"}
<th>{t}Description{/t}</th> <th>{t}Description{/t}</th>
{else} {else}
<th><a class="header" href="{geturl sort=descr}">{t}Description{/t}</a></th> <th><a class="header" href="{geturl sort=descr}">{t}Description{/t}</a></th>
{/if} {/if}
{if $sort == "owner"} {if $sort == "owner"}
<th>{t}Owner{/t}</th> <th>{t}Owner{/t}</th>
{else} {else}
<th><a class="header" href="{geturl sort=owner}">{t}Owner{/t}</a></th> <th><a class="header" href="{geturl sort=owner}">{t}Owner{/t}</a></th>
{/if} {/if}
{if $sort == "age"} {if $sort == "age"}
<th>{t}Last Change{/t}</th> <th>{t}Last Change{/t}</th>
{else} {else}
<th><a class="header" href="{geturl sort=age}">{t}Last Change{/t}</a></th> <th><a class="header" href="{geturl sort=age}">{t}Last Change{/t}</a></th>
{/if} {/if}
<th>{t}Actions{/t}</th> <th>{t}Actions{/t}</th>
</tr> </tr>
{/if} {/if}
   
{if $currentcategory != $proj->GetCategory()} {if $currentcategory != $proj->GetCategory()}
{assign var=currentcategory value=$proj->GetCategory()} {assign var=currentcategory value=$proj->GetCategory()}
{if $currentcategory != ''} {if $currentcategory != ''}
<tr class="light categoryRow"> <tr class="light categoryRow">
<th class="categoryName">{$currentcategory}</th> <th class="categoryName">{$currentcategory}</th>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
</tr> </tr>
{/if} {/if}
{/if} {/if}
   
<tr class="{cycle values="light,dark"} projectRow"> <tr class="{cycle values="light,dark"} projectRow {if $loginenabled && !$proj->UserCanAccess($loggedinuser)}disabled{/if}">
<td class="projectName"> <td class="projectName">
<a href="{geturl project=$proj}" class="list {if $currentcategory != ''}indent{/if}">{$proj->GetProject()}</a> {if !$loginenabled || $proj->UserCanAccess($loggedinuser)}
  <a href="{geturl project=$proj}" class="list {if $currentcategory != ''}indent{/if}"><span>{$proj->GetProject()}</span></a>
  {else}
  <span {if $currentcategory != ''}class="indent"{/if}>{$proj->GetProject()}</span>
  {/if}
</td> </td>
<td class="projectDescription"><a href="{geturl project=$proj}" class="list">{$proj->GetDescription()|escape}</a></td> <td class="projectDescription">
  {if !$loginenabled || $proj->UserCanAccess($loggedinuser)}
  <a href="{geturl project=$proj}" class="list"><span>{$proj->GetDescription()|escape}</span></a>
  {else}
  <span>{$proj->GetDescription()|escape}</span>
  {/if}
  </td>
<td class="projectOwner"><em>{$proj->GetOwner()|escape:'html'}</em></td> <td class="projectOwner"><em>{$proj->GetOwner()|escape:'html'}</em></td>
{assign var=projecthead value=$proj->GetHeadCommit()} {assign var=projecthead value=$proj->GetHeadCommit()}
<td class="projectAge"> <td class="projectAge">
{if $projecthead} {if $projecthead}
{if $proj->GetAge() < 7200} {* 60*60*2, or 2 hours *} {if $proj->GetAge() < 7200} {* 60*60*2, or 2 hours *}
<span class="agehighlight"><strong><em><time datetime="{$proj->GetEpoch()|date_format:"%Y-%m-%dT%H:%M:%S+00:00"}">{agestring age=$proj->GetAge()}</time></em></strong></span> <span class="agehighlight"><strong><em><time datetime="{$proj->GetEpoch()|date_format:"%Y-%m-%dT%H:%M:%S+00:00"}">{agestring age=$proj->GetAge()}</time></em></strong></span>
{elseif $proj->GetAge() < 172800} {* 60*60*24*2, or 2 days *} {elseif $proj->GetAge() < 172800} {* 60*60*24*2, or 2 days *}
<span class="agehighlight"><em><time datetime="{$proj->GetEpoch()|date_format:"%Y-%m-%dT%H:%M:%S+00:00"}">{agestring age=$proj->GetAge()}</time></em></span> <span class="agehighlight"><em><time datetime="{$proj->GetEpoch()|date_format:"%Y-%m-%dT%H:%M:%S+00:00"}">{agestring age=$proj->GetAge()}</time></em></span>
{else} {else}
<em><time datetime="{$proj->GetEpoch()|date_format:"%Y-%m-%dT%H:%M:%S+00:00"}">{agestring age=$proj->GetAge()}</time></em> <em><time datetime="{$proj->GetEpoch()|date_format:"%Y-%m-%dT%H:%M:%S+00:00"}">{agestring age=$proj->GetAge()}</time></em>
{/if} {/if}
{else} {else}
<em class="empty">{t}No commits{/t}</em> <em class="empty">{t}No commits{/t}</em>
{/if} {/if}
</td> </td>
<td class="link"> <td class="link">
  {if !$loginenabled || $proj->UserCanAccess($loggedinuser)}
<a href="{geturl project=$proj}">{t}summary{/t}</a> <a href="{geturl project=$proj}">{t}summary{/t}</a>
{if $projecthead} {if $projecthead}
| |
<a href="{geturl project=$proj action=shortlog}">{t}shortlog{/t}</a> | <a href="{geturl project=$proj action=shortlog}">{t}shortlog{/t}</a> |
<a href="{geturl project=$proj action=log}">{t}log{/t}</a> | <a href="{geturl project=$proj action=log}">{t}log{/t}</a> |
<a href="{geturl project=$proj action=tree}">{t}tree{/t}</a> | <a href="{geturl project=$proj action=tree}">{t}tree{/t}</a> |
<a href="{geturl project=$proj action=snapshot hash=HEAD}" class="snapshotTip">{t}snapshot{/t}</a> <a href="{geturl project=$proj action=snapshot hash=HEAD}" class="snapshotTip">{t}snapshot{/t}</a>
{/if} {/if}
  {/if}
</td> </td>
</tr> </tr>
{foreachelse} {foreachelse}
{if $search} {if $search}
<div class="message">{t 1=$search}No matches found for "%1"{/t}</div> <div class="message">{t 1=$search}No matches found for "%1"{/t}</div>
{else} {else}
<div class="message">{t}No projects found{/t}</div> <div class="message">{t}No projects found{/t}</div>
{/if} {/if}
{/foreach} {/foreach}
   
</table> </table>
   
{/block} {/block}
   
{block name=footer} {block name=footer}
<a href="{geturl action=opml}" class="rss_logo">{t}OPML{/t}</a> <a href="{geturl action=opml}" class="rss_logo">{t}OPML{/t}</a>
<a href="{geturl action=projectindex}" class="rss_logo">{t}TXT{/t}</a> <a href="{geturl action=projectindex}" class="rss_logo">{t}TXT{/t}</a>
{/block} {/block}
   
   
comments