From: Artem Daniliants
Date: Fri, 5 Mar 2010 11:30:48 +0000 (+0200)
Subject: Initial Kohana install
X-Git-Tag: v0.1~78
X-Git-Url: http://git.maemo.org/git/?p=speedfreak;a=commitdiff_plain;h=b6e210d698acb6872a1b1badccc2d83ad9b635c7
Initial Kohana install
---
diff --git a/Server/.htaccess b/Server/.htaccess
new file mode 100644
index 0000000..b79e25f
--- /dev/null
+++ b/Server/.htaccess
@@ -0,0 +1,15 @@
+# Turn on URL rewriting
+RewriteEngine On
+
+# Installation directory
+RewriteBase /
+
+# Protect application and system files from being viewed
+RewriteRule ^(application|modules|system) - [F,L]
+
+# Allow any files or directories that exist to be displayed directly
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+
+# Rewrite all other URLs to index.php/URL
+RewriteRule .* index.php/$0 [PT,L]
diff --git a/Server/application/config/config.php b/Server/application/config/config.php
new file mode 100644
index 0000000..8c45192
--- /dev/null
+++ b/Server/application/config/config.php
@@ -0,0 +1,125 @@
+Examples:\n";
+ echo "\n";
+
+ foreach ($examples as $method)
+ {
+ if ($method == __FUNCTION__)
+ continue;
+
+ echo ''.html::anchor('examples/'.$method, $method)." \n";
+ }
+
+ echo " \n";
+ echo ''.Kohana::lang('core.stats_footer')."
\n";
+ }
+
+ /**
+ * Demonstrates how to archive a directory. First enable the archive module
+ */
+ //public function archive($build = FALSE)
+ //{
+ // if ($build === 'build')
+ // {
+ // // Load archive
+ // $archive = new Archive('zip');
+
+ // // Download the application/views directory
+ // $archive->add(APPPATH.'views/', 'app_views/', TRUE);
+
+ // // Download the built archive
+ // $archive->download('test.zip');
+ // }
+ // else
+ // {
+ // echo html::anchor(Router::$current_uri.'/build', 'Download views');
+ // }
+ //}
+
+ /**
+ * Demonstrates how to parse RSS feeds by using DOMDocument.
+ */
+ function rss()
+ {
+ // Parse an external atom feed
+ $feed = feed::parse('http://dev.kohanaphp.com/projects/kohana2/activity.atom');
+
+ // Show debug info
+ echo Kohana::debug($feed);
+
+ echo Kohana::lang('core.stats_footer');
+ }
+
+ /**
+ * Demonstrates the Session library and using session data.
+ */
+ function session()
+ {
+ // Gets the singleton instance of the Session library
+ $s = Session::instance();
+
+ echo 'SESSID: '.session_id()." \n";
+
+ echo ''.print_r($_SESSION, TRUE)." \n";
+
+ echo ' {execution_time} seconds';
+ }
+
+ /**
+ * Demonstrates how to use the form helper with the Validation libraryfor file uploads .
+ */
+ function form()
+ {
+ // Anything submitted?
+ if ($_POST)
+ {
+ // Merge the globals into our validation object.
+ $post = Validation::factory(array_merge($_POST, $_FILES));
+
+ // Ensure upload helper is correctly configured, config/upload.php contains default entries.
+ // Uploads can be required or optional, but should be valid
+ $post->add_rules('imageup1', 'upload::required', 'upload::valid', 'upload::type[gif,jpg,png]', 'upload::size[1M]');
+ $post->add_rules('imageup2', 'upload::required', 'upload::valid', 'upload::type[gif,jpg,png]', 'upload::size[1M]');
+
+ // Alternative syntax for multiple file upload validation rules
+ //$post->add_rules('imageup.*', 'upload::required', 'upload::valid', 'upload::type[gif,jpg,png]', 'upload::size[1M]');
+
+ if ($post->validate() )
+ {
+ // It worked!
+ // Move (and rename) the files from php upload folder to configured application folder
+ upload::save('imageup1');
+ upload::save('imageup2');
+ echo 'Validation successfull, check your upload folder!';
+ }
+ else
+ {
+ // You got validation errors
+ echo 'validation errors: '.var_export($post->errors(), TRUE).'
';
+ echo Kohana::debug($post);
+ }
+ }
+
+ // Display the form
+ echo form::open('examples/form', array('enctype' => 'multipart/form-data'));
+ echo form::label('imageup', 'Image Uploads').': ';
+ // Use discrete upload fields
+ // Alternative syntax for multiple file uploads
+ // echo form::upload('imageup[]').' ';
+
+ echo form::upload('imageup1').' ';
+ echo form::upload('imageup2').' ';
+ echo form::submit('upload', 'Upload!');
+ echo form::close();
+
+ }
+
+ /**
+ * Demontrates how to use the Validation library to validate an arbitrary array.
+ */
+ function validation()
+ {
+ // To demonstrate Validation being able to validate any array, I will
+ // be using a pre-built array. When you load validation with no arguments
+ // it will default to validating the POST array.
+ $data = array
+ (
+ 'user' => 'hello',
+ 'pass' => 'bigsecret',
+ 'reme' => '1'
+ );
+
+ $validation = new Validation($data);
+
+ $validation->add_rules('user', 'required', 'length[1,12]')->pre_filter('trim', 'user');
+ $validation->add_rules('pass', 'required')->post_filter('sha1', 'pass');
+ $validation->add_rules('reme', 'required');
+
+ $result = $validation->validate();
+
+ var_dump($validation->errors());
+ var_dump($validation->as_array());
+
+ // Yay!
+ echo '{execution_time} ALL DONE!';
+ }
+
+ /**
+ * Demontrates how to use the Captcha library.
+ */
+ public function captcha()
+ {
+ // Look at the counters for valid and invalid
+ // responses in the Session Profiler.
+ new Profiler;
+
+ // Load Captcha library, you can supply the name
+ // of the config group you would like to use.
+ $captcha = new Captcha;
+
+ // Ban bots (that accept session cookies) after 50 invalid responses.
+ // Be careful not to ban real people though! Set the threshold high enough.
+ if ($captcha->invalid_count() > 49)
+ exit('Bye! Stupid bot.');
+
+ // Form submitted
+ if ($_POST)
+ {
+ // Captcha::valid() is a static method that can be used as a Validation rule also.
+ if (Captcha::valid($this->input->post('captcha_response')))
+ {
+ echo 'Good answer!
';
+ }
+ else
+ {
+ echo 'Wrong answer!
';
+ }
+
+ // Validate other fields here
+ }
+
+ // Show form
+ echo form::open();
+ echo 'Other form fields here...
';
+
+ // Don't show Captcha anymore after the user has given enough valid
+ // responses. The "enough" count is set in the captcha config.
+ if ( ! $captcha->promoted())
+ {
+ echo '';
+ echo $captcha->render(); // Shows the Captcha challenge (image/riddle/etc)
+ echo '
';
+ echo form::input('captcha_response');
+ }
+ else
+ {
+ echo 'You have been promoted to human.
';
+ }
+
+ // Close form
+ echo form::submit(array('value' => 'Check'));
+ echo form::close();
+ }
+
+ /**
+ * Demonstrates the features of the Database library.
+ *
+ * Table Structure:
+ * CREATE TABLE `pages` (
+ * `id` mediumint( 9 ) NOT NULL AUTO_INCREMENT ,
+ * `page_name` varchar( 100 ) NOT NULL ,
+ * `title` varchar( 255 ) NOT NULL ,
+ * `content` longtext NOT NULL ,
+ * `menu` tinyint( 1 ) NOT NULL default '0',
+ * `filename` varchar( 255 ) NOT NULL ,
+ * `order` mediumint( 9 ) NOT NULL ,
+ * `date` int( 11 ) NOT NULL ,
+ * `child_of` mediumint( 9 ) NOT NULL default '0',
+ * PRIMARY KEY ( `id` ) ,
+ * UNIQUE KEY `filename` ( `filename` )
+ * ) ENGINE = MYISAM DEFAULT CHARSET = utf8 PACK_KEYS =0;
+ *
+ */
+ function database()
+ {
+ $db = new Database;
+
+ $table = 'pages';
+ echo 'Does the '.$table.' table exist? ';
+ if ($db->table_exists($table))
+ {
+ echo 'YES! Lets do some work =)
';
+
+ $query = $db->select('DISTINCT pages.*')->from($table)->get();
+ echo $db->last_query();
+ echo 'Iterate through the result: ';
+ foreach ($query as $item)
+ {
+ echo ''.$item->title.'
';
+ }
+ echo 'Numrows using count(): '.count($query).' ';
+ echo 'Table Listing:'.print_r($db->list_tables(), TRUE).' ';
+
+ echo 'Try Query Binding with objects: ';
+ $sql = 'SELECT * FROM '.$table.' WHERE id = ?';
+ $query = $db->query($sql, array(1));
+ echo ''.$db->last_query().'
';
+ $query->result(TRUE);
+ foreach ($query as $item)
+ {
+ echo ''.print_r($item, true).' ';
+ }
+
+ echo 'Try Query Binding with arrays (returns both associative and numeric because I pass MYSQL_BOTH to result(): ';
+ $sql = 'SELECT * FROM '.$table.' WHERE id = ?';
+ $query = $db->query($sql, array(1));
+ echo ''.$db->last_query().'
';
+ $query->result(FALSE, MYSQL_BOTH);
+ foreach ($query as $item)
+ {
+ echo ''.print_r($item, true).' ';
+ }
+
+ echo 'Look, we can also manually advance the result pointer! ';
+ $query = $db->select('title')->from($table)->get();
+ echo 'First:'.print_r($query->current(), true).' ';
+ $query->next();
+ echo 'Second:'.print_r($query->current(), true).' ';
+ $query->next();
+ echo 'Third:'.print_r($query->current(), true).' ';
+ echo 'And we can reset it to the beginning: ';
+ $query->rewind();
+ echo 'Rewound:'.print_r($query->current(), true).' ';
+
+ echo 'Number of rows using count_records(): '.$db->count_records('pages').'
';
+ }
+ else
+ {
+ echo 'NO! The '.$table.' table doesn\'t exist, so we can\'t continue =( ';
+ }
+ echo " \n";
+ echo 'done in {execution_time} seconds';
+ }
+
+ /**
+ * Demonstrates how to use the Pagination library and Pagination styles.
+ */
+ function pagination()
+ {
+ $pagination = new Pagination(array(
+ // Base_url will default to the current URI
+ // 'base_url' => 'welcome/pagination_example/page/x',
+
+ // The URI segment (integer) in which the pagination number can be found
+ // The URI segment (string) that precedes the pagination number (aka "label")
+ 'uri_segment' => 'page',
+
+ // You could also use the query string for pagination instead of the URI segments
+ // Just set this to the $_GET key that contains the page number
+ // 'query_string' => 'page',
+
+ // The total items to paginate through (probably need to use a database COUNT query here)
+ 'total_items' => 254,
+
+ // The amount of items you want to display per page
+ 'items_per_page' => 10,
+
+ // The pagination style: classic (default), digg, extended or punbb
+ // Easily add your own styles to views/pagination and point to the view name here
+ 'style' => 'classic',
+
+ // If there is only one page, completely hide all pagination elements
+ // Pagination->render() will return an empty string
+ 'auto_hide' => TRUE,
+ ));
+
+ // Just echo to display the links (__toString() rocks!)
+ echo 'Classic style: '.$pagination;
+
+ // You can also use the render() method and pick a style on the fly if you want
+ echo ' Digg style: ', $pagination->render('digg');
+ echo ' Extended style: ', $pagination->render('extended');
+ echo ' PunBB style: ', $pagination->render('punbb');
+ echo 'done in {execution_time} seconds';
+ }
+
+ /**
+ * Demonstrates the User_Agent library.
+ */
+ function user_agent()
+ {
+ foreach (array('agent', 'browser', 'version') as $key)
+ {
+ echo $key.': '.Kohana::user_agent($key).' '."\n";
+ }
+
+ echo " \n";
+ echo 'done in {execution_time} seconds';
+ }
+
+ /**
+ * Demonstrates the Payment library.
+ */
+ /*function payment()
+ {
+ $credit_card = new Payment;
+
+ // You can also pass the driver name to the library to use multiple ones:
+ $credit_card = new Payment('Paypal');
+ $credit_card = new Payment('Authorize');
+
+ // You can specify one parameter at a time:
+ $credit_card->login = 'this';
+ $credit_card->first_name = 'Jeremy';
+ $credit_card->last_name = 'Bush';
+ $credit_card->card_num = '1234567890';
+ $credit_card->exp_date = '0910';
+ $credit_card->amount = '478.41';
+
+ // Or you can also set fields with an array and the method:
+ $credit_card->set_fields(array('login' => 'test',
+ 'first_name' => 'Jeremy',
+ 'last_name' => 'Bush',
+ 'card_num' => '1234567890',
+ 'exp_date' => '0910',
+ 'amount' => '487.41'));
+
+ echo ''.print_r($credit_card, true).' ';
+
+ echo 'Success? ';
+ echo ($response = $credit_card->process() == TRUE) ? 'YES!' : $response;
+ }*/
+
+ function calendar()
+ {
+ $profiler = new Profiler;
+
+ $calendar = new Calendar($this->input->get('month', date('m')), $this->input->get('year', date('Y')));
+ $calendar->attach($calendar->event()
+ ->condition('year', 2008)
+ ->condition('month', 8)
+ ->condition('day', 8)
+ ->output(html::anchor('http://forum.kohanaphp.com/comments.php?DiscussionID=275', 'Learning about Kohana Calendar')));
+
+ echo $calendar->render();
+ }
+
+ /**
+ * Demonstrates how to use the Image libarary..
+ */
+ function image()
+ {
+ // For testing only, save the new image in DOCROOT
+ $dir = realpath(DOCROOT);
+
+ // Original Image filename
+ $image = DOCROOT.'kohana.png';
+
+ // Create an instance of Image, with file
+ // The orginal image is not affected
+ $image = new Image($image);
+
+ // Most methods are chainable
+ // Resize the image, crop the center left
+ $image->resize(200, 100)->crop(150, 50, 'center', 'left');
+
+ // Display image in browser.
+ // Keep the actions, to be applied when saving the image.
+ $image->render($keep_actions = TRUE);
+
+ // Save the image, as a jpeg
+ // Here the actions will be discarded, by default.
+ $image->save($dir.'/mypic_thumb.jpg');
+
+ //echo Kohana::debug($image);
+ }
+
+ /**
+ * Demonstrates how to use vendor software with Kohana.
+ */
+ function vendor()
+ {
+ // Let's do a little Markdown shall we.
+ $br = "\n\n";
+ $output = '#Marked Down!#'.$br;
+ $output .= 'This **_markup_** is created *on-the-fly*, by ';
+ $output .= '[php-markdown-extra](http://michelf.com/projects/php-markdown/extra)'.$br;
+ $output .= 'It\'s *great* for user & writing about ``'.$br;
+ $output .= 'It\'s also good at footnotes :-) [^1]'.$br;
+ $output .= '[^1]: A footnote.';
+
+ // looks in system/vendor for Markdown.php
+ require Kohana::find_file('vendor', 'Markdown');
+
+ echo Markdown($output);
+
+ echo 'done in {execution_time} seconds';
+ }
+} // End Examples
diff --git a/Server/application/controllers/welcome.php b/Server/application/controllers/welcome.php
new file mode 100644
index 0000000..8941953
--- /dev/null
+++ b/Server/application/controllers/welcome.php
@@ -0,0 +1,54 @@
+template->content = new View('welcome_content');
+
+ // You can assign anything variable to a view by using standard OOP
+ // methods. In my welcome view, the $title variable will be assigned
+ // the value I give it here.
+ $this->template->title = 'Welcome to Kohana!';
+
+ // An array of links to display. Assiging variables to views is completely
+ // asyncronous. Variables can be set in any order, and can be any type
+ // of data, including objects.
+ $this->template->content->links = array
+ (
+ 'Home Page' => 'http://kohanaphp.com/',
+ 'Documentation' => 'http://docs.kohanaphp.com/',
+ 'Forum' => 'http://forum.kohanaphp.com/',
+ 'License' => 'Kohana License.html',
+ 'Donate' => 'http://kohanaphp.com/donate',
+ );
+ }
+
+ public function __call($method, $arguments)
+ {
+ // Disable auto-rendering
+ $this->auto_render = FALSE;
+
+ // By defining a __call method, all pages routed to this controller
+ // that result in 404 errors will be handled by this method, instead of
+ // being displayed as "Page Not Found" errors.
+ echo 'This text is generated by __call. If you expected the index page, you need to use: welcome/index/'.substr(Router::$current_uri, 8);
+ }
+
+} // End Welcome Controller
\ No newline at end of file
diff --git a/Server/application/views/welcome_content.php b/Server/application/views/welcome_content.php
new file mode 100644
index 0000000..0adcab6
--- /dev/null
+++ b/Server/application/views/welcome_content.php
@@ -0,0 +1,15 @@
+
+
+
This is the default Kohana index page. You may also access this page as
.
+
+
+ To change what gets displayed for this page, edit application/controllers/welcome.php
.
+ To change this text, edit application/views/welcome_content.php
.
+
+
+
+
\ No newline at end of file
diff --git a/Server/index.php b/Server/index.php
new file mode 100644
index 0000000..4217a28
--- /dev/null
+++ b/Server/index.php
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+Kohana Installation
+
+
+
+
+
+
+Environment Tests
+
+The following tests have been run to determine if Kohana will work in your environment. If any of the tests have failed, consult the documentation for more information on how to correct the problem.
+
+
+
+
+
+PHP Version
+=')): ?>
+
+
+Kohana requires PHP 5.2 or newer, this version is .
+
+
+
+System Directory
+
+
+
+The configured system
directory does not exist or does not contain required files.
+
+
+
+Application Directory
+
+
+
+The configured application
directory does not exist or does not contain required files.
+
+
+
+Modules Directory
+
+
+
+The configured modules
directory does not exist or does not contain required files.
+
+
+
+PCRE UTF-8
+
+PCRE support is missing.
+
+PCRE has not been compiled with UTF-8 support.
+
+PCRE has not been compiled with Unicode property support.
+
+Pass
+
+
+
+Reflection Enabled
+
+Pass
+
+PHP reflection is either not loaded or not compiled in.
+
+
+
+Filters Enabled
+
+Pass
+
+The filter extension is either not loaded or not compiled in.
+
+
+
+Iconv Extension Loaded
+
+Pass
+
+The iconv extension is not loaded.
+
+
+
+
+SPL Enabled
+
+Pass
+
+SPL is not enabled.
+
+
+
+
+
+Mbstring Not Overloaded
+
+The mbstring extension is overloading PHP's native string functions.
+
+Pass
+
+
+
+
+XML support
+
+PHP is compiled without XML support, thus lacking support for utf8_encode()
/utf8_decode()
.
+
+Pass
+
+
+
+
+URI Determination
+
+Pass
+
+Neither $_SERVER['REQUEST_URI']
or $_SERVER['PHP_SELF']
is available.
+
+
+
+
+
+
+
+
Kohana may not work correctly with your environment.
+
+
Your environment passed all requirements. Remove or rename the install
file now.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Server/system/config/cache.php b/Server/system/config/cache.php
new file mode 100644
index 0000000..ccd3da4
--- /dev/null
+++ b/Server/system/config/cache.php
@@ -0,0 +1,32 @@
+ File cache is fast and reliable, but requires many filesystem lookups.
+ * > Database cache can be used to cache items remotely, but is slower.
+ * > Memcache is very high performance, but prevents cache tags from being used.
+ *
+ * params - Driver parameters, specific to each driver.
+ *
+ * lifetime - Default lifetime of caches in seconds. By default caches are stored for
+ * thirty minutes. Specific lifetime can also be set when creating a new cache.
+ * Setting this to 0 will never automatically delete caches.
+ *
+ * requests - Average number of cache requests that will processed before all expired
+ * caches are deleted. This is commonly referred to as "garbage collection".
+ * Setting this to 0 or a negative number will disable automatic garbage collection.
+ */
+$config['default'] = array
+(
+ 'driver' => 'file',
+ 'params' => APPPATH.'cache',
+ 'lifetime' => 1800,
+ 'requests' => 1000
+);
diff --git a/Server/system/config/cache_memcache.php b/Server/system/config/cache_memcache.php
new file mode 100644
index 0000000..43d8f20
--- /dev/null
+++ b/Server/system/config/cache_memcache.php
@@ -0,0 +1,20 @@
+ '127.0.0.1',
+ 'port' => 11211,
+ 'persistent' => FALSE,
+ )
+);
+
+/**
+ * Enable cache data compression.
+ */
+$config['compression'] = FALSE;
diff --git a/Server/system/config/cache_sqlite.php b/Server/system/config/cache_sqlite.php
new file mode 100644
index 0000000..818b893
--- /dev/null
+++ b/Server/system/config/cache_sqlite.php
@@ -0,0 +1,10 @@
+ 'basic',
+ 'width' => 150,
+ 'height' => 50,
+ 'complexity' => 4,
+ 'background' => '',
+ 'fontpath' => SYSPATH.'fonts/',
+ 'fonts' => array('DejaVuSerif.ttf'),
+ 'promote' => FALSE,
+);
\ No newline at end of file
diff --git a/Server/system/config/cookie.php b/Server/system/config/cookie.php
new file mode 100644
index 0000000..b6ddfe4
--- /dev/null
+++ b/Server/system/config/cookie.php
@@ -0,0 +1,32 @@
+ array
+ (
+ 'length' => '13,14,15,16,17,18,19',
+ 'prefix' => '',
+ 'luhn' => TRUE
+ ),
+ 'american express' => array
+ (
+ 'length' => '15',
+ 'prefix' => '3[47]',
+ 'luhn' => TRUE
+ ),
+ 'diners club' => array
+ (
+ 'length' => '14,16',
+ 'prefix' => '36|55|30[0-5]',
+ 'luhn' => TRUE
+ ),
+ 'discover' => array
+ (
+ 'length' => '16',
+ 'prefix' => '6(?:5|011)',
+ 'luhn' => TRUE,
+ ),
+ 'jcb' => array
+ (
+ 'length' => '15,16',
+ 'prefix' => '3|1800|2131',
+ 'luhn' => TRUE
+ ),
+ 'maestro' => array
+ (
+ 'length' => '16,18',
+ 'prefix' => '50(?:20|38)|6(?:304|759)',
+ 'luhn' => TRUE
+ ),
+ 'mastercard' => array
+ (
+ 'length' => '16',
+ 'prefix' => '5[1-5]',
+ 'luhn' => TRUE
+ ),
+ 'visa' => array
+ (
+ 'length' => '13,16',
+ 'prefix' => '4',
+ 'luhn' => TRUE
+ ),
+);
\ No newline at end of file
diff --git a/Server/system/config/database.php b/Server/system/config/database.php
new file mode 100644
index 0000000..6519156
--- /dev/null
+++ b/Server/system/config/database.php
@@ -0,0 +1,45 @@
+ 'mysql://dbuser:secret@localhost/kohana'
+ * character_set - Database character set
+ * table_prefix - Database table prefix
+ * object - Enable or disable object results
+ * cache - Enable or disable query caching
+ * escape - Enable automatic query builder escaping
+ */
+$config['default'] = array
+(
+ 'benchmark' => TRUE,
+ 'persistent' => FALSE,
+ 'connection' => array
+ (
+ 'type' => 'mysql',
+ 'user' => 'dbuser',
+ 'pass' => 'p@ssw0rd',
+ 'host' => 'localhost',
+ 'port' => FALSE,
+ 'socket' => FALSE,
+ 'database' => 'kohana'
+ ),
+ 'character_set' => 'utf8',
+ 'table_prefix' => '',
+ 'object' => TRUE,
+ 'cache' => FALSE,
+ 'escape' => TRUE
+);
\ No newline at end of file
diff --git a/Server/system/config/email.php b/Server/system/config/email.php
new file mode 100644
index 0000000..c768367
--- /dev/null
+++ b/Server/system/config/email.php
@@ -0,0 +1,22 @@
+ 'K0H@NA+PHP_7hE-SW!FtFraM3w0R|<',
+ 'mode' => MCRYPT_MODE_NOFB,
+ 'cipher' => MCRYPT_RIJNDAEL_128
+);
diff --git a/Server/system/config/http.php b/Server/system/config/http.php
new file mode 100644
index 0000000..3c4a86a
--- /dev/null
+++ b/Server/system/config/http.php
@@ -0,0 +1,19 @@
+ 'children',
+ 'clothes' => 'clothing',
+ 'man' => 'men',
+ 'movie' => 'movies',
+ 'person' => 'people',
+ 'woman' => 'women',
+ 'mouse' => 'mice',
+ 'goose' => 'geese',
+ 'ox' => 'oxen',
+ 'leaf' => 'leaves',
+ 'course' => 'courses',
+ 'size' => 'sizes',
+);
diff --git a/Server/system/config/locale.php b/Server/system/config/locale.php
new file mode 100644
index 0000000..3a26882
--- /dev/null
+++ b/Server/system/config/locale.php
@@ -0,0 +1,16 @@
+ array('text/h323'),
+ '7z' => array('application/x-7z-compressed'),
+ 'abw' => array('application/x-abiword'),
+ 'acx' => array('application/internet-property-stream'),
+ 'ai' => array('application/postscript'),
+ 'aif' => array('audio/x-aiff'),
+ 'aifc' => array('audio/x-aiff'),
+ 'aiff' => array('audio/x-aiff'),
+ 'asf' => array('video/x-ms-asf'),
+ 'asr' => array('video/x-ms-asf'),
+ 'asx' => array('video/x-ms-asf'),
+ 'atom' => array('application/atom+xml'),
+ 'avi' => array('video/avi', 'video/msvideo', 'video/x-msvideo'),
+ 'bin' => array('application/octet-stream','application/macbinary'),
+ 'bmp' => array('image/bmp'),
+ 'c' => array('text/x-csrc'),
+ 'c++' => array('text/x-c++src'),
+ 'cab' => array('application/x-cab'),
+ 'cc' => array('text/x-c++src'),
+ 'cda' => array('application/x-cdf'),
+ 'class' => array('application/octet-stream'),
+ 'cpp' => array('text/x-c++src'),
+ 'cpt' => array('application/mac-compactpro'),
+ 'csh' => array('text/x-csh'),
+ 'css' => array('text/css'),
+ 'csv' => array('text/x-comma-separated-values', 'application/vnd.ms-excel', 'text/comma-separated-values', 'text/csv'),
+ 'dbk' => array('application/docbook+xml'),
+ 'dcr' => array('application/x-director'),
+ 'deb' => array('application/x-debian-package'),
+ 'diff' => array('text/x-diff'),
+ 'dir' => array('application/x-director'),
+ 'divx' => array('video/divx'),
+ 'dll' => array('application/octet-stream', 'application/x-msdos-program'),
+ 'dmg' => array('application/x-apple-diskimage'),
+ 'dms' => array('application/octet-stream'),
+ 'doc' => array('application/msword'),
+ 'dvi' => array('application/x-dvi'),
+ 'dxr' => array('application/x-director'),
+ 'eml' => array('message/rfc822'),
+ 'eps' => array('application/postscript'),
+ 'evy' => array('application/envoy'),
+ 'exe' => array('application/x-msdos-program', 'application/octet-stream'),
+ 'fla' => array('application/octet-stream'),
+ 'flac' => array('application/x-flac'),
+ 'flc' => array('video/flc'),
+ 'fli' => array('video/fli'),
+ 'flv' => array('video/x-flv'),
+ 'gif' => array('image/gif'),
+ 'gtar' => array('application/x-gtar'),
+ 'gz' => array('application/x-gzip'),
+ 'h' => array('text/x-chdr'),
+ 'h++' => array('text/x-c++hdr'),
+ 'hh' => array('text/x-c++hdr'),
+ 'hpp' => array('text/x-c++hdr'),
+ 'hqx' => array('application/mac-binhex40'),
+ 'hs' => array('text/x-haskell'),
+ 'htm' => array('text/html'),
+ 'html' => array('text/html'),
+ 'ico' => array('image/x-icon'),
+ 'ics' => array('text/calendar'),
+ 'iii' => array('application/x-iphone'),
+ 'ins' => array('application/x-internet-signup'),
+ 'iso' => array('application/x-iso9660-image'),
+ 'isp' => array('application/x-internet-signup'),
+ 'jar' => array('application/java-archive'),
+ 'java' => array('application/x-java-applet'),
+ 'jpe' => array('image/jpeg', 'image/pjpeg'),
+ 'jpeg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpg' => array('image/jpeg', 'image/pjpeg'),
+ 'js' => array('application/x-javascript'),
+ 'json' => array('application/json'),
+ 'latex' => array('application/x-latex'),
+ 'lha' => array('application/octet-stream'),
+ 'log' => array('text/plain', 'text/x-log'),
+ 'lzh' => array('application/octet-stream'),
+ 'm4a' => array('audio/mpeg'),
+ 'm4p' => array('video/mp4v-es'),
+ 'm4v' => array('video/mp4'),
+ 'man' => array('application/x-troff-man'),
+ 'mdb' => array('application/x-msaccess'),
+ 'midi' => array('audio/midi'),
+ 'mid' => array('audio/midi'),
+ 'mif' => array('application/vnd.mif'),
+ 'mka' => array('audio/x-matroska'),
+ 'mkv' => array('video/x-matroska'),
+ 'mov' => array('video/quicktime'),
+ 'movie' => array('video/x-sgi-movie'),
+ 'mp2' => array('audio/mpeg'),
+ 'mp3' => array('audio/mpeg'),
+ 'mp4' => array('application/mp4','audio/mp4','video/mp4'),
+ 'mpa' => array('video/mpeg'),
+ 'mpe' => array('video/mpeg'),
+ 'mpeg' => array('video/mpeg'),
+ 'mpg' => array('video/mpeg'),
+ 'mpg4' => array('video/mp4'),
+ 'mpga' => array('audio/mpeg'),
+ 'mpp' => array('application/vnd.ms-project'),
+ 'mpv' => array('video/x-matroska'),
+ 'mpv2' => array('video/mpeg'),
+ 'ms' => array('application/x-troff-ms'),
+ 'msg' => array('application/msoutlook','application/x-msg'),
+ 'msi' => array('application/x-msi'),
+ 'nws' => array('message/rfc822'),
+ 'oda' => array('application/oda'),
+ 'odb' => array('application/vnd.oasis.opendocument.database'),
+ 'odc' => array('application/vnd.oasis.opendocument.chart'),
+ 'odf' => array('application/vnd.oasis.opendocument.forumla'),
+ 'odg' => array('application/vnd.oasis.opendocument.graphics'),
+ 'odi' => array('application/vnd.oasis.opendocument.image'),
+ 'odm' => array('application/vnd.oasis.opendocument.text-master'),
+ 'odp' => array('application/vnd.oasis.opendocument.presentation'),
+ 'ods' => array('application/vnd.oasis.opendocument.spreadsheet'),
+ 'odt' => array('application/vnd.oasis.opendocument.text'),
+ 'oga' => array('audio/ogg'),
+ 'ogg' => array('application/ogg'),
+ 'ogv' => array('video/ogg'),
+ 'otg' => array('application/vnd.oasis.opendocument.graphics-template'),
+ 'oth' => array('application/vnd.oasis.opendocument.web'),
+ 'otp' => array('application/vnd.oasis.opendocument.presentation-template'),
+ 'ots' => array('application/vnd.oasis.opendocument.spreadsheet-template'),
+ 'ott' => array('application/vnd.oasis.opendocument.template'),
+ 'p' => array('text/x-pascal'),
+ 'pas' => array('text/x-pascal'),
+ 'patch' => array('text/x-diff'),
+ 'pbm' => array('image/x-portable-bitmap'),
+ 'pdf' => array('application/pdf', 'application/x-download'),
+ 'php' => array('application/x-httpd-php'),
+ 'php3' => array('application/x-httpd-php'),
+ 'php4' => array('application/x-httpd-php'),
+ 'php5' => array('application/x-httpd-php'),
+ 'phps' => array('application/x-httpd-php-source'),
+ 'phtml' => array('application/x-httpd-php'),
+ 'pl' => array('text/x-perl'),
+ 'pm' => array('text/x-perl'),
+ 'png' => array('image/png', 'image/x-png'),
+ 'po' => array('text/x-gettext-translation'),
+ 'pot' => array('application/vnd.ms-powerpoint'),
+ 'pps' => array('application/vnd.ms-powerpoint'),
+ 'ppt' => array('application/powerpoint'),
+ 'ps' => array('application/postscript'),
+ 'psd' => array('application/x-photoshop', 'image/x-photoshop'),
+ 'pub' => array('application/x-mspublisher'),
+ 'py' => array('text/x-python'),
+ 'qt' => array('video/quicktime'),
+ 'ra' => array('audio/x-realaudio'),
+ 'ram' => array('audio/x-realaudio', 'audio/x-pn-realaudio'),
+ 'rar' => array('application/rar'),
+ 'rgb' => array('image/x-rgb'),
+ 'rm' => array('audio/x-pn-realaudio'),
+ 'rpm' => array('audio/x-pn-realaudio-plugin', 'application/x-redhat-package-manager'),
+ 'rss' => array('application/rss+xml'),
+ 'rtf' => array('text/rtf'),
+ 'rtx' => array('text/richtext'),
+ 'rv' => array('video/vnd.rn-realvideo'),
+ 'sea' => array('application/octet-stream'),
+ 'sh' => array('text/x-sh'),
+ 'shtml' => array('text/html'),
+ 'sit' => array('application/x-stuffit'),
+ 'smi' => array('application/smil'),
+ 'smil' => array('application/smil'),
+ 'so' => array('application/octet-stream'),
+ 'src' => array('application/x-wais-source'),
+ 'svg' => array('image/svg+xml'),
+ 'swf' => array('application/x-shockwave-flash'),
+ 't' => array('application/x-troff'),
+ 'tar' => array('application/x-tar'),
+ 'tcl' => array('text/x-tcl'),
+ 'tex' => array('application/x-tex'),
+ 'text' => array('text/plain'),
+ 'texti' => array('application/x-texinfo'),
+ 'textinfo' => array('application/x-texinfo'),
+ 'tgz' => array('application/x-tar'),
+ 'tif' => array('image/tiff'),
+ 'tiff' => array('image/tiff'),
+ 'torrent' => array('application/x-bittorrent'),
+ 'tr' => array('application/x-troff'),
+ 'tsv' => array('text/tab-separated-values'),
+ 'txt' => array('text/plain'),
+ 'wav' => array('audio/x-wav'),
+ 'wax' => array('audio/x-ms-wax'),
+ 'wbxml' => array('application/wbxml'),
+ 'wm' => array('video/x-ms-wm'),
+ 'wma' => array('audio/x-ms-wma'),
+ 'wmd' => array('application/x-ms-wmd'),
+ 'wmlc' => array('application/wmlc'),
+ 'wmv' => array('video/x-ms-wmv', 'application/octet-stream'),
+ 'wmx' => array('video/x-ms-wmx'),
+ 'wmz' => array('application/x-ms-wmz'),
+ 'word' => array('application/msword', 'application/octet-stream'),
+ 'wp5' => array('application/wordperfect5.1'),
+ 'wpd' => array('application/vnd.wordperfect'),
+ 'wvx' => array('video/x-ms-wvx'),
+ 'xbm' => array('image/x-xbitmap'),
+ 'xcf' => array('image/xcf'),
+ 'xhtml' => array('application/xhtml+xml'),
+ 'xht' => array('application/xhtml+xml'),
+ 'xl' => array('application/excel', 'application/vnd.ms-excel'),
+ 'xla' => array('application/excel', 'application/vnd.ms-excel'),
+ 'xlc' => array('application/excel', 'application/vnd.ms-excel'),
+ 'xlm' => array('application/excel', 'application/vnd.ms-excel'),
+ 'xls' => array('application/excel', 'application/vnd.ms-excel'),
+ 'xlt' => array('application/excel', 'application/vnd.ms-excel'),
+ 'xml' => array('text/xml'),
+ 'xof' => array('x-world/x-vrml'),
+ 'xpm' => array('image/x-xpixmap'),
+ 'xsl' => array('text/xml'),
+ 'xvid' => array('video/x-xvid'),
+ 'xwd' => array('image/x-xwindowdump'),
+ 'z' => array('application/x-compress'),
+ 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed')
+);
diff --git a/Server/system/config/pagination.php b/Server/system/config/pagination.php
new file mode 100644
index 0000000..808fc31
--- /dev/null
+++ b/Server/system/config/pagination.php
@@ -0,0 +1,25 @@
+ 'pagination',
+ 'style' => 'classic',
+ 'uri_segment' => 3,
+ 'query_string' => '',
+ 'items_per_page' => 20,
+ 'auto_hide' => FALSE,
+);
diff --git a/Server/system/config/profiler.php b/Server/system/config/profiler.php
new file mode 100644
index 0000000..98ab5a4
--- /dev/null
+++ b/Server/system/config/profiler.php
@@ -0,0 +1,8 @@
+ array('type' => 'int', 'max' => 127),
+ 'smallint' => array('type' => 'int', 'max' => 32767),
+ 'mediumint' => array('type' => 'int', 'max' => 8388607),
+ 'int' => array('type' => 'int', 'max' => 2147483647),
+ 'integer' => array('type' => 'int', 'max' => 2147483647),
+ 'bigint' => array('type' => 'int', 'max' => 9223372036854775807),
+ 'float' => array('type' => 'float'),
+ 'float unsigned' => array('type' => 'float', 'min' => 0),
+ 'boolean' => array('type' => 'boolean'),
+ 'time' => array('type' => 'string', 'format' => '00:00:00'),
+ 'time with time zone' => array('type' => 'string'),
+ 'date' => array('type' => 'string', 'format' => '0000-00-00'),
+ 'year' => array('type' => 'string', 'format' => '0000'),
+ 'datetime' => array('type' => 'string', 'format' => '0000-00-00 00:00:00'),
+ 'timestamp with time zone' => array('type' => 'string'),
+ 'char' => array('type' => 'string', 'exact' => TRUE),
+ 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE),
+ 'varchar' => array('type' => 'string'),
+ 'varbinary' => array('type' => 'string', 'binary' => TRUE),
+ 'blob' => array('type' => 'string', 'binary' => TRUE),
+ 'text' => array('type' => 'string')
+);
+
+// DOUBLE
+$config['double'] = $config['double precision'] = $config['decimal'] = $config['real'] = $config['numeric'] = $config['float'];
+$config['double unsigned'] = $config['float unsigned'];
+
+// BIT
+$config['bit'] = $config['boolean'];
+
+// TIMESTAMP
+$config['timestamp'] = $config['timestamp without time zone'] = $config['datetime'];
+
+// ENUM
+$config['enum'] = $config['set'] = $config['varchar'];
+
+// TEXT
+$config['tinytext'] = $config['mediumtext'] = $config['longtext'] = $config['text'];
+
+// BLOB
+$config['tsvector'] = $config['tinyblob'] = $config['mediumblob'] = $config['longblob'] = $config['clob'] = $config['bytea'] = $config['blob'];
+
+// CHARACTER
+$config['character'] = $config['char'];
+$config['character varying'] = $config['varchar'];
+
+// TIME
+$config['time without time zone'] = $config['time'];
diff --git a/Server/system/config/upload.php b/Server/system/config/upload.php
new file mode 100644
index 0000000..df26a2d
--- /dev/null
+++ b/Server/system/config/upload.php
@@ -0,0 +1,17 @@
+ 'Windows Vista',
+ 'windows nt 5.2' => 'Windows 2003',
+ 'windows nt 5.0' => 'Windows 2000',
+ 'windows nt 5.1' => 'Windows XP',
+ 'windows nt 4.0' => 'Windows NT',
+ 'winnt4.0' => 'Windows NT',
+ 'winnt 4.0' => 'Windows NT',
+ 'winnt' => 'Windows NT',
+ 'windows 98' => 'Windows 98',
+ 'win98' => 'Windows 98',
+ 'windows 95' => 'Windows 95',
+ 'win95' => 'Windows 95',
+ 'windows' => 'Unknown Windows OS',
+ 'os x' => 'Mac OS X',
+ 'intel mac' => 'Intel Mac',
+ 'ppc mac' => 'PowerPC Mac',
+ 'powerpc' => 'PowerPC',
+ 'ppc' => 'PowerPC',
+ 'cygwin' => 'Cygwin',
+ 'linux' => 'Linux',
+ 'debian' => 'Debian',
+ 'openvms' => 'OpenVMS',
+ 'sunos' => 'Sun Solaris',
+ 'amiga' => 'Amiga',
+ 'beos' => 'BeOS',
+ 'apachebench' => 'ApacheBench',
+ 'freebsd' => 'FreeBSD',
+ 'netbsd' => 'NetBSD',
+ 'bsdi' => 'BSDi',
+ 'openbsd' => 'OpenBSD',
+ 'os/2' => 'OS/2',
+ 'warp' => 'OS/2',
+ 'aix' => 'AIX',
+ 'irix' => 'Irix',
+ 'osf' => 'DEC OSF',
+ 'hp-ux' => 'HP-UX',
+ 'hurd' => 'GNU/Hurd',
+ 'unix' => 'Unknown Unix OS',
+);
+
+// The order of this array should NOT be changed. Many browsers return
+// multiple browser types so we want to identify the sub-type first.
+$config['browser'] = array
+(
+ 'Opera' => 'Opera',
+ 'MSIE' => 'Internet Explorer',
+ 'Internet Explorer' => 'Internet Explorer',
+ 'Shiira' => 'Shiira',
+ 'Firefox' => 'Firefox',
+ 'Chimera' => 'Chimera',
+ 'Phoenix' => 'Phoenix',
+ 'Firebird' => 'Firebird',
+ 'Camino' => 'Camino',
+ 'Netscape' => 'Netscape',
+ 'OmniWeb' => 'OmniWeb',
+ 'Chrome' => 'Chrome',
+ 'Safari' => 'Safari',
+ 'Konqueror' => 'Konqueror',
+ 'Epiphany' => 'Epiphany',
+ 'Galeon' => 'Galeon',
+ 'Mozilla' => 'Mozilla',
+ 'icab' => 'iCab',
+ 'lynx' => 'Lynx',
+ 'links' => 'Links',
+ 'hotjava' => 'HotJava',
+ 'amaya' => 'Amaya',
+ 'IBrowse' => 'IBrowse',
+);
+
+$config['mobile'] = array
+(
+ 'mobileexplorer' => 'Mobile Explorer',
+ 'openwave' => 'Open Wave',
+ 'opera mini' => 'Opera Mini',
+ 'operamini' => 'Opera Mini',
+ 'elaine' => 'Palm',
+ 'palmsource' => 'Palm',
+ 'digital paths' => 'Palm',
+ 'avantgo' => 'Avantgo',
+ 'xiino' => 'Xiino',
+ 'palmscape' => 'Palmscape',
+ 'nokia' => 'Nokia',
+ 'ericsson' => 'Ericsson',
+ 'blackBerry' => 'BlackBerry',
+ 'motorola' => 'Motorola',
+ 'iphone' => 'iPhone',
+ 'android' => 'Android',
+);
+
+// There are hundreds of bots but these are the most common.
+$config['robot'] = array
+(
+ 'googlebot' => 'Googlebot',
+ 'msnbot' => 'MSNBot',
+ 'slurp' => 'Inktomi Slurp',
+ 'yahoo' => 'Yahoo',
+ 'askjeeves' => 'AskJeeves',
+ 'fastcrawler' => 'FastCrawler',
+ 'infoseek' => 'InfoSeek Robot 1.0',
+ 'lycos' => 'Lycos',
+);
\ No newline at end of file
diff --git a/Server/system/config/view.php b/Server/system/config/view.php
new file mode 100644
index 0000000..6bed22e
--- /dev/null
+++ b/Server/system/config/view.php
@@ -0,0 +1,17 @@
+" />
+ *
+ * $Id: captcha.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Captcha
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class Captcha_Controller extends Controller {
+
+ public function __call($method, $args)
+ {
+ // Output the Captcha challenge resource (no html)
+ // Pull the config group name from the URL
+ Captcha::factory($this->uri->segment(2))->render(FALSE);
+ }
+
+} // End Captcha_Controller
\ No newline at end of file
diff --git a/Server/system/controllers/template.php b/Server/system/controllers/template.php
new file mode 100644
index 0000000..34d1a22
--- /dev/null
+++ b/Server/system/controllers/template.php
@@ -0,0 +1,54 @@
+template = new View($this->template);
+
+ if ($this->auto_render == TRUE)
+ {
+ // Render the template immediately after the controller method
+ Event::add('system.post_controller', array($this, '_render'));
+ }
+ }
+
+ /**
+ * Render the loaded template.
+ */
+ public function _render()
+ {
+ if ($this->auto_render == TRUE)
+ {
+ // Render the template when the class is destroyed
+ $this->template->render(TRUE);
+ }
+ }
+
+} // End Template_Controller
\ No newline at end of file
diff --git a/Server/system/core/Benchmark.php b/Server/system/core/Benchmark.php
new file mode 100644
index 0000000..ce230f1
--- /dev/null
+++ b/Server/system/core/Benchmark.php
@@ -0,0 +1,125 @@
+ microtime(TRUE),
+ 'stop' => FALSE,
+ 'memory_start' => self::memory_usage(),
+ 'memory_stop' => FALSE
+ );
+
+ array_unshift(self::$marks[$name], $mark);
+ }
+
+ /**
+ * Set a benchmark stop point.
+ *
+ * @param string benchmark name
+ * @return void
+ */
+ public static function stop($name)
+ {
+ if (isset(self::$marks[$name]) AND self::$marks[$name][0]['stop'] === FALSE)
+ {
+ self::$marks[$name][0]['stop'] = microtime(TRUE);
+ self::$marks[$name][0]['memory_stop'] = self::memory_usage();
+ }
+ }
+
+ /**
+ * Get the elapsed time between a start and stop.
+ *
+ * @param string benchmark name, TRUE for all
+ * @param integer number of decimal places to count to
+ * @return array
+ */
+ public static function get($name, $decimals = 4)
+ {
+ if ($name === TRUE)
+ {
+ $times = array();
+ $names = array_keys(self::$marks);
+
+ foreach ($names as $name)
+ {
+ // Get each mark recursively
+ $times[$name] = self::get($name, $decimals);
+ }
+
+ // Return the array
+ return $times;
+ }
+
+ if ( ! isset(self::$marks[$name]))
+ return FALSE;
+
+ if (self::$marks[$name][0]['stop'] === FALSE)
+ {
+ // Stop the benchmark to prevent mis-matched results
+ self::stop($name);
+ }
+
+ // Return a string version of the time between the start and stop points
+ // Properly reading a float requires using number_format or sprintf
+ $time = $memory = 0;
+ for ($i = 0; $i < count(self::$marks[$name]); $i++)
+ {
+ $time += self::$marks[$name][$i]['stop'] - self::$marks[$name][$i]['start'];
+ $memory += self::$marks[$name][$i]['memory_stop'] - self::$marks[$name][$i]['memory_start'];
+ }
+
+ return array
+ (
+ 'time' => number_format($time, $decimals),
+ 'memory' => $memory,
+ 'count' => count(self::$marks[$name])
+ );
+ }
+
+ /**
+ * Returns the current memory usage. This is only possible if the
+ * memory_get_usage function is supported in PHP.
+ *
+ * @return integer
+ */
+ private static function memory_usage()
+ {
+ static $func;
+
+ if ($func === NULL)
+ {
+ // Test if memory usage can be seen
+ $func = function_exists('memory_get_usage');
+ }
+
+ return $func ? memory_get_usage() : 0;
+ }
+
+} // End Benchmark
diff --git a/Server/system/core/Bootstrap.php b/Server/system/core/Bootstrap.php
new file mode 100644
index 0000000..1334190
--- /dev/null
+++ b/Server/system/core/Bootstrap.php
@@ -0,0 +1,58 @@
+ $event_callback)
+ {
+ if ($callback === $event_callback)
+ {
+ unset(self::$events[$name][$i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Execute all of the callbacks attached to an event.
+ *
+ * @param string event name
+ * @param array data can be processed as Event::$data by the callbacks
+ * @return void
+ */
+ public static function run($name, & $data = NULL)
+ {
+ if ( ! empty(self::$events[$name]))
+ {
+ // So callbacks can access Event::$data
+ self::$data =& $data;
+ $callbacks = self::get($name);
+
+ foreach ($callbacks as $callback)
+ {
+ call_user_func($callback);
+ }
+
+ // Do this to prevent data from getting 'stuck'
+ $clear_data = '';
+ self::$data =& $clear_data;
+ }
+
+ // The event has been run!
+ self::$has_run[$name] = $name;
+ }
+
+ /**
+ * Check if a given event has been run.
+ *
+ * @param string event name
+ * @return boolean
+ */
+ public static function has_run($name)
+ {
+ return isset(self::$has_run[$name]);
+ }
+
+} // End Event
\ No newline at end of file
diff --git a/Server/system/core/Kohana.php b/Server/system/core/Kohana.php
new file mode 100644
index 0000000..c934b12
--- /dev/null
+++ b/Server/system/core/Kohana.php
@@ -0,0 +1,1788 @@
+ 1,
+ 'alert' => 2,
+ 'info' => 3,
+ 'debug' => 4,
+ );
+
+ // Internal caches and write status
+ private static $internal_cache = array();
+ private static $write_cache;
+ private static $internal_cache_path;
+ private static $internal_cache_key;
+ private static $internal_cache_encrypt;
+
+ /**
+ * Sets up the PHP environment. Adds error/exception handling, output
+ * buffering, and adds an auto-loading method for loading classes.
+ *
+ * This method is run immediately when this file is loaded, and is
+ * benchmarked as environment_setup.
+ *
+ * For security, this function also destroys the $_REQUEST global variable.
+ * Using the proper global (GET, POST, COOKIE, etc) is inherently more secure.
+ * The recommended way to fetch a global variable is using the Input library.
+ * @see http://www.php.net/globals
+ *
+ * @return void
+ */
+ public static function setup()
+ {
+ static $run;
+
+ // This function can only be run once
+ if ($run === TRUE)
+ return;
+
+ // Start the environment setup benchmark
+ Benchmark::start(SYSTEM_BENCHMARK.'_environment_setup');
+
+ // Define Kohana error constant
+ define('E_KOHANA', 42);
+
+ // Define 404 error constant
+ define('E_PAGE_NOT_FOUND', 43);
+
+ // Define database error constant
+ define('E_DATABASE_ERROR', 44);
+
+ if (self::$cache_lifetime = self::config('core.internal_cache'))
+ {
+ // Are we using encryption for caches?
+ self::$internal_cache_encrypt = self::config('core.internal_cache_encrypt');
+
+ if(self::$internal_cache_encrypt===TRUE)
+ {
+ self::$internal_cache_key = self::config('core.internal_cache_key');
+
+ // Be sure the key is of acceptable length for the mcrypt algorithm used
+ self::$internal_cache_key = substr(self::$internal_cache_key, 0, 24);
+ }
+
+ // Set the directory to be used for the internal cache
+ if ( ! self::$internal_cache_path = self::config('core.internal_cache_path'))
+ {
+ self::$internal_cache_path = APPPATH.'cache/';
+ }
+
+ // Load cached configuration and language files
+ self::$internal_cache['configuration'] = self::cache('configuration', self::$cache_lifetime);
+ self::$internal_cache['language'] = self::cache('language', self::$cache_lifetime);
+
+ // Load cached file paths
+ self::$internal_cache['find_file_paths'] = self::cache('find_file_paths', self::$cache_lifetime);
+
+ // Enable cache saving
+ Event::add('system.shutdown', array(__CLASS__, 'internal_cache_save'));
+ }
+
+ // Disable notices and "strict" errors
+ $ER = error_reporting(~E_NOTICE & ~E_STRICT);
+
+ // Set the user agent
+ self::$user_agent = ( ! empty($_SERVER['HTTP_USER_AGENT']) ? trim($_SERVER['HTTP_USER_AGENT']) : '');
+
+ if (function_exists('date_default_timezone_set'))
+ {
+ $timezone = self::config('locale.timezone');
+
+ // Set default timezone, due to increased validation of date settings
+ // which cause massive amounts of E_NOTICEs to be generated in PHP 5.2+
+ date_default_timezone_set(empty($timezone) ? date_default_timezone_get() : $timezone);
+ }
+
+ // Restore error reporting
+ error_reporting($ER);
+
+ // Start output buffering
+ ob_start(array(__CLASS__, 'output_buffer'));
+
+ // Save buffering level
+ self::$buffer_level = ob_get_level();
+
+ // Set autoloader
+ spl_autoload_register(array('Kohana', 'auto_load'));
+
+ // Set error handler
+ set_error_handler(array('Kohana', 'exception_handler'));
+
+ // Set exception handler
+ set_exception_handler(array('Kohana', 'exception_handler'));
+
+ // Send default text/html UTF-8 header
+ header('Content-Type: text/html; charset=UTF-8');
+
+ // Load locales
+ $locales = self::config('locale.language');
+
+ // Make first locale UTF-8
+ $locales[0] .= '.UTF-8';
+
+ // Set locale information
+ self::$locale = setlocale(LC_ALL, $locales);
+
+ if (self::$configuration['core']['log_threshold'] > 0)
+ {
+ // Set the log directory
+ self::log_directory(self::$configuration['core']['log_directory']);
+
+ // Enable log writing at shutdown
+ register_shutdown_function(array(__CLASS__, 'log_save'));
+ }
+
+ // Enable Kohana routing
+ Event::add('system.routing', array('Router', 'find_uri'));
+ Event::add('system.routing', array('Router', 'setup'));
+
+ // Enable Kohana controller initialization
+ Event::add('system.execute', array('Kohana', 'instance'));
+
+ // Enable Kohana 404 pages
+ Event::add('system.404', array('Kohana', 'show_404'));
+
+ // Enable Kohana output handling
+ Event::add('system.shutdown', array('Kohana', 'shutdown'));
+
+ if (self::config('core.enable_hooks') === TRUE)
+ {
+ // Find all the hook files
+ $hooks = self::list_files('hooks', TRUE);
+
+ foreach ($hooks as $file)
+ {
+ // Load the hook
+ include $file;
+ }
+ }
+
+ // Setup is complete, prevent it from being run again
+ $run = TRUE;
+
+ // Stop the environment setup routine
+ Benchmark::stop(SYSTEM_BENCHMARK.'_environment_setup');
+ }
+
+ /**
+ * Loads the controller and initializes it. Runs the pre_controller,
+ * post_controller_constructor, and post_controller events. Triggers
+ * a system.404 event when the route cannot be mapped to a controller.
+ *
+ * This method is benchmarked as controller_setup and controller_execution.
+ *
+ * @return object instance of controller
+ */
+ public static function & instance()
+ {
+ if (self::$instance === NULL)
+ {
+ Benchmark::start(SYSTEM_BENCHMARK.'_controller_setup');
+
+ // Include the Controller file
+ require Router::$controller_path;
+
+ try
+ {
+ // Start validation of the controller
+ $class = new ReflectionClass(ucfirst(Router::$controller).'_Controller');
+ }
+ catch (ReflectionException $e)
+ {
+ // Controller does not exist
+ Event::run('system.404');
+ }
+
+ if ($class->isAbstract() OR (IN_PRODUCTION AND $class->getConstant('ALLOW_PRODUCTION') == FALSE))
+ {
+ // Controller is not allowed to run in production
+ Event::run('system.404');
+ }
+
+ // Run system.pre_controller
+ Event::run('system.pre_controller');
+
+ // Create a new controller instance
+ $controller = $class->newInstance();
+
+ // Controller constructor has been executed
+ Event::run('system.post_controller_constructor');
+
+ try
+ {
+ // Load the controller method
+ $method = $class->getMethod(Router::$method);
+
+ // Method exists
+ if (Router::$method[0] === '_')
+ {
+ // Do not allow access to hidden methods
+ Event::run('system.404');
+ }
+
+ if ($method->isProtected() or $method->isPrivate())
+ {
+ // Do not attempt to invoke protected methods
+ throw new ReflectionException('protected controller method');
+ }
+
+ // Default arguments
+ $arguments = Router::$arguments;
+ }
+ catch (ReflectionException $e)
+ {
+ // Use __call instead
+ $method = $class->getMethod('__call');
+
+ // Use arguments in __call format
+ $arguments = array(Router::$method, Router::$arguments);
+ }
+
+ // Stop the controller setup benchmark
+ Benchmark::stop(SYSTEM_BENCHMARK.'_controller_setup');
+
+ // Start the controller execution benchmark
+ Benchmark::start(SYSTEM_BENCHMARK.'_controller_execution');
+
+ // Execute the controller method
+ $method->invokeArgs($controller, $arguments);
+
+ // Controller method has been executed
+ Event::run('system.post_controller');
+
+ // Stop the controller execution benchmark
+ Benchmark::stop(SYSTEM_BENCHMARK.'_controller_execution');
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Get all include paths. APPPATH is the first path, followed by module
+ * paths in the order they are configured, follow by the SYSPATH.
+ *
+ * @param boolean re-process the include paths
+ * @return array
+ */
+ public static function include_paths($process = FALSE)
+ {
+ if ($process === TRUE)
+ {
+ // Add APPPATH as the first path
+ self::$include_paths = array(APPPATH);
+
+ foreach (self::$configuration['core']['modules'] as $path)
+ {
+ if ($path = str_replace('\\', '/', realpath($path)))
+ {
+ // Add a valid path
+ self::$include_paths[] = $path.'/';
+ }
+ }
+
+ // Add SYSPATH as the last path
+ self::$include_paths[] = SYSPATH;
+ }
+
+ return self::$include_paths;
+ }
+
+ /**
+ * Get a config item or group.
+ *
+ * @param string item name
+ * @param boolean force a forward slash (/) at the end of the item
+ * @param boolean is the item required?
+ * @return mixed
+ */
+ public static function config($key, $slash = FALSE, $required = TRUE)
+ {
+ if (self::$configuration === NULL)
+ {
+ // Load core configuration
+ self::$configuration['core'] = self::config_load('core');
+
+ // Re-parse the include paths
+ self::include_paths(TRUE);
+ }
+
+ // Get the group name from the key
+ $group = explode('.', $key, 2);
+ $group = $group[0];
+
+ if ( ! isset(self::$configuration[$group]))
+ {
+ // Load the configuration group
+ self::$configuration[$group] = self::config_load($group, $required);
+ }
+
+ // Get the value of the key string
+ $value = self::key_string(self::$configuration, $key);
+
+ if ($slash === TRUE AND is_string($value) AND $value !== '')
+ {
+ // Force the value to end with "/"
+ $value = rtrim($value, '/').'/';
+ }
+
+ return $value;
+ }
+
+ /**
+ * Sets a configuration item, if allowed.
+ *
+ * @param string config key string
+ * @param string config value
+ * @return boolean
+ */
+ public static function config_set($key, $value)
+ {
+ // Do this to make sure that the config array is already loaded
+ self::config($key);
+
+ if (substr($key, 0, 7) === 'routes.')
+ {
+ // Routes cannot contain sub keys due to possible dots in regex
+ $keys = explode('.', $key, 2);
+ }
+ else
+ {
+ // Convert dot-noted key string to an array
+ $keys = explode('.', $key);
+ }
+
+ // Used for recursion
+ $conf =& self::$configuration;
+ $last = count($keys) - 1;
+
+ foreach ($keys as $i => $k)
+ {
+ if ($i === $last)
+ {
+ $conf[$k] = $value;
+ }
+ else
+ {
+ $conf =& $conf[$k];
+ }
+ }
+
+ if ($key === 'core.modules')
+ {
+ // Reprocess the include paths
+ self::include_paths(TRUE);
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Load a config file.
+ *
+ * @param string config filename, without extension
+ * @param boolean is the file required?
+ * @return array
+ */
+ public static function config_load($name, $required = TRUE)
+ {
+ if ($name === 'core')
+ {
+ // Load the application configuration file
+ require APPPATH.'config/config'.EXT;
+
+ if ( ! isset($config['site_domain']))
+ {
+ // Invalid config file
+ die('Your Kohana application configuration file is not valid.');
+ }
+
+ return $config;
+ }
+
+ if (isset(self::$internal_cache['configuration'][$name]))
+ return self::$internal_cache['configuration'][$name];
+
+ // Load matching configs
+ $configuration = array();
+
+ if ($files = self::find_file('config', $name, $required))
+ {
+ foreach ($files as $file)
+ {
+ require $file;
+
+ if (isset($config) AND is_array($config))
+ {
+ // Merge in configuration
+ $configuration = array_merge($configuration, $config);
+ }
+ }
+ }
+
+ if ( ! isset(self::$write_cache['configuration']))
+ {
+ // Cache has changed
+ self::$write_cache['configuration'] = TRUE;
+ }
+
+ return self::$internal_cache['configuration'][$name] = $configuration;
+ }
+
+ /**
+ * Clears a config group from the cached configuration.
+ *
+ * @param string config group
+ * @return void
+ */
+ public static function config_clear($group)
+ {
+ // Remove the group from config
+ unset(self::$configuration[$group], self::$internal_cache['configuration'][$group]);
+
+ if ( ! isset(self::$write_cache['configuration']))
+ {
+ // Cache has changed
+ self::$write_cache['configuration'] = TRUE;
+ }
+ }
+
+ /**
+ * Add a new message to the log.
+ *
+ * @param string type of message
+ * @param string message text
+ * @return void
+ */
+ public static function log($type, $message)
+ {
+ if (self::$log_levels[$type] <= self::$configuration['core']['log_threshold'])
+ {
+ $message = array(date('Y-m-d H:i:s P'), $type, $message);
+
+ // Run the system.log event
+ Event::run('system.log', $message);
+
+ self::$log[] = $message;
+ }
+ }
+
+ /**
+ * Save all currently logged messages.
+ *
+ * @return void
+ */
+ public static function log_save()
+ {
+ if (empty(self::$log) OR self::$configuration['core']['log_threshold'] < 1)
+ return;
+
+ // Filename of the log
+ $filename = self::log_directory().date('Y-m-d').'.log'.EXT;
+
+ if ( ! is_file($filename))
+ {
+ // Write the SYSPATH checking header
+ file_put_contents($filename,
+ ''.PHP_EOL.PHP_EOL);
+
+ // Prevent external writes
+ chmod($filename, 0644);
+ }
+
+ // Messages to write
+ $messages = array();
+
+ do
+ {
+ // Load the next mess
+ list ($date, $type, $text) = array_shift(self::$log);
+
+ // Add a new message line
+ $messages[] = $date.' --- '.$type.': '.$text;
+ }
+ while ( ! empty(self::$log));
+
+ // Write messages to log file
+ file_put_contents($filename, implode(PHP_EOL, $messages).PHP_EOL, FILE_APPEND);
+ }
+
+ /**
+ * Get or set the logging directory.
+ *
+ * @param string new log directory
+ * @return string
+ */
+ public static function log_directory($dir = NULL)
+ {
+ static $directory;
+
+ if ( ! empty($dir))
+ {
+ // Get the directory path
+ $dir = realpath($dir);
+
+ if (is_dir($dir) AND is_writable($dir))
+ {
+ // Change the log directory
+ $directory = str_replace('\\', '/', $dir).'/';
+ }
+ else
+ {
+ // Log directory is invalid
+ throw new Kohana_Exception('core.log_dir_unwritable', $dir);
+ }
+ }
+
+ return $directory;
+ }
+
+ /**
+ * Load data from a simple cache file. This should only be used internally,
+ * and is NOT a replacement for the Cache library.
+ *
+ * @param string unique name of cache
+ * @param integer expiration in seconds
+ * @return mixed
+ */
+ public static function cache($name, $lifetime)
+ {
+ if ($lifetime > 0)
+ {
+ $path = self::$internal_cache_path.'kohana_'.$name;
+
+ if (is_file($path))
+ {
+ // Check the file modification time
+ if ((time() - filemtime($path)) < $lifetime)
+ {
+ // Cache is valid! Now, do we need to decrypt it?
+ if(self::$internal_cache_encrypt===TRUE)
+ {
+ $data = file_get_contents($path);
+
+ $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
+ $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
+
+ $decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, self::$internal_cache_key, $data, MCRYPT_MODE_ECB, $iv);
+
+ $cache = unserialize($decrypted_text);
+
+ // If the key changed, delete the cache file
+ if(!$cache)
+ unlink($path);
+
+ // If cache is false (as above) return NULL, otherwise, return the cache
+ return ($cache ? $cache : NULL);
+ }
+ else
+ {
+ return unserialize(file_get_contents($path));
+ }
+ }
+ else
+ {
+ // Cache is invalid, delete it
+ unlink($path);
+ }
+ }
+ }
+
+ // No cache found
+ return NULL;
+ }
+
+ /**
+ * Save data to a simple cache file. This should only be used internally, and
+ * is NOT a replacement for the Cache library.
+ *
+ * @param string cache name
+ * @param mixed data to cache
+ * @param integer expiration in seconds
+ * @return boolean
+ */
+ public static function cache_save($name, $data, $lifetime)
+ {
+ if ($lifetime < 1)
+ return FALSE;
+
+ $path = self::$internal_cache_path.'kohana_'.$name;
+
+ if ($data === NULL)
+ {
+ // Delete cache
+ return (is_file($path) and unlink($path));
+ }
+ else
+ {
+ // Using encryption? Encrypt the data when we write it
+ if(self::$internal_cache_encrypt===TRUE)
+ {
+ // Encrypt and write data to cache file
+ $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
+ $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
+
+ // Serialize and encrypt!
+ $encrypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, self::$internal_cache_key, serialize($data), MCRYPT_MODE_ECB, $iv);
+
+ return (bool) file_put_contents($path, $encrypted_text);
+ }
+ else
+ {
+ // Write data to cache file
+ return (bool) file_put_contents($path, serialize($data));
+ }
+ }
+ }
+
+ /**
+ * Kohana output handler. Called during ob_clean, ob_flush, and their variants.
+ *
+ * @param string current output buffer
+ * @return string
+ */
+ public static function output_buffer($output)
+ {
+ // Could be flushing, so send headers first
+ if ( ! Event::has_run('system.send_headers'))
+ {
+ // Run the send_headers event
+ Event::run('system.send_headers');
+ }
+
+ self::$output = $output;
+
+ // Set and return the final output
+ return self::$output;
+ }
+
+ /**
+ * Closes all open output buffers, either by flushing or cleaning, and stores the Kohana
+ * output buffer for display during shutdown.
+ *
+ * @param boolean disable to clear buffers, rather than flushing
+ * @return void
+ */
+ public static function close_buffers($flush = TRUE)
+ {
+ if (ob_get_level() >= self::$buffer_level)
+ {
+ // Set the close function
+ $close = ($flush === TRUE) ? 'ob_end_flush' : 'ob_end_clean';
+
+ while (ob_get_level() > self::$buffer_level)
+ {
+ // Flush or clean the buffer
+ $close();
+ }
+
+ // Store the Kohana output buffer
+ ob_end_clean();
+ }
+ }
+
+ /**
+ * Triggers the shutdown of Kohana by closing the output buffer, runs the system.display event.
+ *
+ * @return void
+ */
+ public static function shutdown()
+ {
+ // Close output buffers
+ self::close_buffers(TRUE);
+
+ // Run the output event
+ Event::run('system.display', self::$output);
+
+ // Render the final output
+ self::render(self::$output);
+ }
+
+ /**
+ * Inserts global Kohana variables into the generated output and prints it.
+ *
+ * @param string final output that will displayed
+ * @return void
+ */
+ public static function render($output)
+ {
+ if (self::config('core.render_stats') === TRUE)
+ {
+ // Fetch memory usage in MB
+ $memory = function_exists('memory_get_usage') ? (memory_get_usage() / 1024 / 1024) : 0;
+
+ // Fetch benchmark for page execution time
+ $benchmark = Benchmark::get(SYSTEM_BENCHMARK.'_total_execution');
+
+ // Replace the global template variables
+ $output = str_replace(
+ array
+ (
+ '{kohana_version}',
+ '{kohana_codename}',
+ '{execution_time}',
+ '{memory_usage}',
+ '{included_files}',
+ ),
+ array
+ (
+ KOHANA_VERSION,
+ KOHANA_CODENAME,
+ $benchmark['time'],
+ number_format($memory, 2).'MB',
+ count(get_included_files()),
+ ),
+ $output
+ );
+ }
+
+ if ($level = self::config('core.output_compression') AND ini_get('output_handler') !== 'ob_gzhandler' AND (int) ini_get('zlib.output_compression') === 0)
+ {
+ if ($level < 1 OR $level > 9)
+ {
+ // Normalize the level to be an integer between 1 and 9. This
+ // step must be done to prevent gzencode from triggering an error
+ $level = max(1, min($level, 9));
+ }
+
+ if (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
+ {
+ $compress = 'gzip';
+ }
+ elseif (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE)
+ {
+ $compress = 'deflate';
+ }
+ }
+
+ if (isset($compress) AND $level > 0)
+ {
+ switch ($compress)
+ {
+ case 'gzip':
+ // Compress output using gzip
+ $output = gzencode($output, $level);
+ break;
+ case 'deflate':
+ // Compress output using zlib (HTTP deflate)
+ $output = gzdeflate($output, $level);
+ break;
+ }
+
+ // This header must be sent with compressed content to prevent
+ // browser caches from breaking
+ header('Vary: Accept-Encoding');
+
+ // Send the content encoding header
+ header('Content-Encoding: '.$compress);
+
+ // Sending Content-Length in CGI can result in unexpected behavior
+ if (stripos(PHP_SAPI, 'cgi') === FALSE)
+ {
+ header('Content-Length: '.strlen($output));
+ }
+ }
+
+ echo $output;
+ }
+
+ /**
+ * Displays a 404 page.
+ *
+ * @throws Kohana_404_Exception
+ * @param string URI of page
+ * @param string custom template
+ * @return void
+ */
+ public static function show_404($page = FALSE, $template = FALSE)
+ {
+ throw new Kohana_404_Exception($page, $template);
+ }
+
+ /**
+ * Dual-purpose PHP error and exception handler. Uses the kohana_error_page
+ * view to display the message.
+ *
+ * @param integer|object exception object or error code
+ * @param string error message
+ * @param string filename
+ * @param integer line number
+ * @return void
+ */
+ public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL)
+ {
+ try
+ {
+ // PHP errors have 5 args, always
+ $PHP_ERROR = (func_num_args() === 5);
+
+ // Test to see if errors should be displayed
+ if ($PHP_ERROR AND (error_reporting() & $exception) === 0)
+ return;
+
+ // This is useful for hooks to determine if a page has an error
+ self::$has_error = TRUE;
+
+ // Error handling will use exactly 5 args, every time
+ if ($PHP_ERROR)
+ {
+ $code = $exception;
+ $type = 'PHP Error';
+ $template = 'kohana_error_page';
+ }
+ else
+ {
+ $code = $exception->getCode();
+ $type = get_class($exception);
+ $message = $exception->getMessage();
+ $file = $exception->getFile();
+ $line = $exception->getLine();
+ $template = ($exception instanceof Kohana_Exception) ? $exception->getTemplate() : 'kohana_error_page';
+ }
+
+ if (is_numeric($code))
+ {
+ $codes = self::lang('errors');
+
+ if ( ! empty($codes[$code]))
+ {
+ list($level, $error, $description) = $codes[$code];
+ }
+ else
+ {
+ $level = 1;
+ $error = $PHP_ERROR ? 'Unknown Error' : get_class($exception);
+ $description = '';
+ }
+ }
+ else
+ {
+ // Custom error message, this will never be logged
+ $level = 5;
+ $error = $code;
+ $description = '';
+ }
+
+ // Remove the DOCROOT from the path, as a security precaution
+ $file = str_replace('\\', '/', realpath($file));
+ $file = preg_replace('|^'.preg_quote(DOCROOT).'|', '', $file);
+
+ if ($level <= self::$configuration['core']['log_threshold'])
+ {
+ // Log the error
+ self::log('error', self::lang('core.uncaught_exception', $type, $message, $file, $line));
+ }
+
+ if ($PHP_ERROR)
+ {
+ $description = self::lang('errors.'.E_RECOVERABLE_ERROR);
+ $description = is_array($description) ? $description[2] : '';
+
+ if ( ! headers_sent())
+ {
+ // Send the 500 header
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ }
+ else
+ {
+ if (method_exists($exception, 'sendHeaders') AND ! headers_sent())
+ {
+ // Send the headers if they have not already been sent
+ $exception->sendHeaders();
+ }
+ }
+
+ // Close all output buffers except for Kohana
+ while (ob_get_level() > self::$buffer_level)
+ {
+ ob_end_clean();
+ }
+
+ // Test if display_errors is on
+ if (self::$configuration['core']['display_errors'] === TRUE)
+ {
+ if ( ! IN_PRODUCTION AND $line != FALSE)
+ {
+ // Remove the first entry of debug_backtrace(), it is the exception_handler call
+ $trace = $PHP_ERROR ? array_slice(debug_backtrace(), 1) : $exception->getTrace();
+
+ // Beautify backtrace
+ $trace = self::backtrace($trace);
+ }
+
+ // Load the error
+ require self::find_file('views', empty($template) ? 'kohana_error_page' : $template);
+ }
+ else
+ {
+ // Get the i18n messages
+ $error = self::lang('core.generic_error');
+ $message = self::lang('core.errors_disabled', url::site(), url::site(Router::$current_uri));
+
+ // Load the errors_disabled view
+ require self::find_file('views', 'kohana_error_disabled');
+ }
+
+ if ( ! Event::has_run('system.shutdown'))
+ {
+ // Run the shutdown even to ensure a clean exit
+ Event::run('system.shutdown');
+ }
+
+ // Turn off error reporting
+ error_reporting(0);
+ exit;
+ }
+ catch (Exception $e)
+ {
+ if (IN_PRODUCTION)
+ {
+ die('Fatal Error');
+ }
+ else
+ {
+ die('Fatal Error: '.$e->getMessage().' File: '.$e->getFile().' Line: '.$e->getLine());
+ }
+ }
+ }
+
+ /**
+ * Provides class auto-loading.
+ *
+ * @throws Kohana_Exception
+ * @param string name of class
+ * @return bool
+ */
+ public static function auto_load($class)
+ {
+ if (class_exists($class, FALSE))
+ return TRUE;
+
+ if (($suffix = strrpos($class, '_')) > 0)
+ {
+ // Find the class suffix
+ $suffix = substr($class, $suffix + 1);
+ }
+ else
+ {
+ // No suffix
+ $suffix = FALSE;
+ }
+
+ if ($suffix === 'Core')
+ {
+ $type = 'libraries';
+ $file = substr($class, 0, -5);
+ }
+ elseif ($suffix === 'Controller')
+ {
+ $type = 'controllers';
+ // Lowercase filename
+ $file = strtolower(substr($class, 0, -11));
+ }
+ elseif ($suffix === 'Model')
+ {
+ $type = 'models';
+ // Lowercase filename
+ $file = strtolower(substr($class, 0, -6));
+ }
+ elseif ($suffix === 'Driver')
+ {
+ $type = 'libraries/drivers';
+ $file = str_replace('_', '/', substr($class, 0, -7));
+ }
+ else
+ {
+ // This could be either a library or a helper, but libraries must
+ // always be capitalized, so we check if the first character is
+ // uppercase. If it is, we are loading a library, not a helper.
+ $type = ($class[0] < 'a') ? 'libraries' : 'helpers';
+ $file = $class;
+ }
+
+ if ($filename = self::find_file($type, $file))
+ {
+ // Load the class
+ require $filename;
+ }
+ else
+ {
+ // The class could not be found
+ return FALSE;
+ }
+
+ if ($filename = self::find_file($type, self::$configuration['core']['extension_prefix'].$class))
+ {
+ // Load the class extension
+ require $filename;
+ }
+ elseif ($suffix !== 'Core' AND class_exists($class.'_Core', FALSE))
+ {
+ // Class extension to be evaluated
+ $extension = 'class '.$class.' extends '.$class.'_Core { }';
+
+ // Start class analysis
+ $core = new ReflectionClass($class.'_Core');
+
+ if ($core->isAbstract())
+ {
+ // Make the extension abstract
+ $extension = 'abstract '.$extension;
+ }
+
+ // Transparent class extensions are handled using eval. This is
+ // a disgusting hack, but it gets the job done.
+ eval($extension);
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Find a resource file in a given directory. Files will be located according
+ * to the order of the include paths. Configuration and i18n files will be
+ * returned in reverse order.
+ *
+ * @throws Kohana_Exception if file is required and not found
+ * @param string directory to search in
+ * @param string filename to look for (without extension)
+ * @param boolean file required
+ * @param string file extension
+ * @return array if the type is config, i18n or l10n
+ * @return string if the file is found
+ * @return FALSE if the file is not found
+ */
+ public static function find_file($directory, $filename, $required = FALSE, $ext = FALSE)
+ {
+ // NOTE: This test MUST be not be a strict comparison (===), or empty
+ // extensions will be allowed!
+ if ($ext == '')
+ {
+ // Use the default extension
+ $ext = EXT;
+ }
+ else
+ {
+ // Add a period before the extension
+ $ext = '.'.$ext;
+ }
+
+ // Search path
+ $search = $directory.'/'.$filename.$ext;
+
+ if (isset(self::$internal_cache['find_file_paths'][$search]))
+ return self::$internal_cache['find_file_paths'][$search];
+
+ // Load include paths
+ $paths = self::$include_paths;
+
+ // Nothing found, yet
+ $found = NULL;
+
+ if ($directory === 'config' OR $directory === 'i18n')
+ {
+ // Search in reverse, for merging
+ $paths = array_reverse($paths);
+
+ foreach ($paths as $path)
+ {
+ if (is_file($path.$search))
+ {
+ // A matching file has been found
+ $found[] = $path.$search;
+ }
+ }
+ }
+ else
+ {
+ foreach ($paths as $path)
+ {
+ if (is_file($path.$search))
+ {
+ // A matching file has been found
+ $found = $path.$search;
+
+ // Stop searching
+ break;
+ }
+ }
+ }
+
+ if ($found === NULL)
+ {
+ if ($required === TRUE)
+ {
+ // Directory i18n key
+ $directory = 'core.'.inflector::singular($directory);
+
+ // If the file is required, throw an exception
+ throw new Kohana_Exception('core.resource_not_found', self::lang($directory), $filename);
+ }
+ else
+ {
+ // Nothing was found, return FALSE
+ $found = FALSE;
+ }
+ }
+
+ if ( ! isset(self::$write_cache['find_file_paths']))
+ {
+ // Write cache at shutdown
+ self::$write_cache['find_file_paths'] = TRUE;
+ }
+
+ return self::$internal_cache['find_file_paths'][$search] = $found;
+ }
+
+ /**
+ * Lists all files and directories in a resource path.
+ *
+ * @param string directory to search
+ * @param boolean list all files to the maximum depth?
+ * @param string full path to search (used for recursion, *never* set this manually)
+ * @return array filenames and directories
+ */
+ public static function list_files($directory, $recursive = FALSE, $path = FALSE)
+ {
+ $files = array();
+
+ if ($path === FALSE)
+ {
+ $paths = array_reverse(self::include_paths());
+
+ foreach ($paths as $path)
+ {
+ // Recursively get and merge all files
+ $files = array_merge($files, self::list_files($directory, $recursive, $path.$directory));
+ }
+ }
+ else
+ {
+ $path = rtrim($path, '/').'/';
+
+ if (is_readable($path))
+ {
+ $items = (array) glob($path.'*');
+
+ if ( ! empty($items))
+ {
+ foreach ($items as $index => $item)
+ {
+ $files[] = $item = str_replace('\\', '/', $item);
+
+ // Handle recursion
+ if (is_dir($item) AND $recursive == TRUE)
+ {
+ // Filename should only be the basename
+ $item = pathinfo($item, PATHINFO_BASENAME);
+
+ // Append sub-directory search
+ $files = array_merge($files, self::list_files($directory, TRUE, $path.$item));
+ }
+ }
+ }
+ }
+ }
+
+ return $files;
+ }
+
+ /**
+ * Fetch an i18n language item.
+ *
+ * @param string language key to fetch
+ * @param array additional information to insert into the line
+ * @return string i18n language string, or the requested key if the i18n item is not found
+ */
+ public static function lang($key, $args = array())
+ {
+ // Extract the main group from the key
+ $group = explode('.', $key, 2);
+ $group = $group[0];
+
+ // Get locale name
+ $locale = self::config('locale.language.0');
+
+ if ( ! isset(self::$internal_cache['language'][$locale][$group]))
+ {
+ // Messages for this group
+ $messages = array();
+
+ if ($files = self::find_file('i18n', $locale.'/'.$group))
+ {
+ foreach ($files as $file)
+ {
+ include $file;
+
+ // Merge in configuration
+ if ( ! empty($lang) AND is_array($lang))
+ {
+ foreach ($lang as $k => $v)
+ {
+ $messages[$k] = $v;
+ }
+ }
+ }
+ }
+
+ if ( ! isset(self::$write_cache['language']))
+ {
+ // Write language cache
+ self::$write_cache['language'] = TRUE;
+ }
+
+ self::$internal_cache['language'][$locale][$group] = $messages;
+ }
+
+ // Get the line from cache
+ $line = self::key_string(self::$internal_cache['language'][$locale], $key);
+
+ if ($line === NULL)
+ {
+ self::log('error', 'Missing i18n entry '.$key.' for language '.$locale);
+
+ // Return the key string as fallback
+ return $key;
+ }
+
+ if (is_string($line) AND func_num_args() > 1)
+ {
+ $args = array_slice(func_get_args(), 1);
+
+ // Add the arguments into the line
+ $line = vsprintf($line, is_array($args[0]) ? $args[0] : $args);
+ }
+
+ return $line;
+ }
+
+ /**
+ * Returns the value of a key, defined by a 'dot-noted' string, from an array.
+ *
+ * @param array array to search
+ * @param string dot-noted string: foo.bar.baz
+ * @return string if the key is found
+ * @return void if the key is not found
+ */
+ public static function key_string($array, $keys)
+ {
+ if (empty($array))
+ return NULL;
+
+ // Prepare for loop
+ $keys = explode('.', $keys);
+
+ do
+ {
+ // Get the next key
+ $key = array_shift($keys);
+
+ if (isset($array[$key]))
+ {
+ if (is_array($array[$key]) AND ! empty($keys))
+ {
+ // Dig down to prepare the next loop
+ $array = $array[$key];
+ }
+ else
+ {
+ // Requested key was found
+ return $array[$key];
+ }
+ }
+ else
+ {
+ // Requested key is not set
+ break;
+ }
+ }
+ while ( ! empty($keys));
+
+ return NULL;
+ }
+
+ /**
+ * Sets values in an array by using a 'dot-noted' string.
+ *
+ * @param array array to set keys in (reference)
+ * @param string dot-noted string: foo.bar.baz
+ * @return mixed fill value for the key
+ * @return void
+ */
+ public static function key_string_set( & $array, $keys, $fill = NULL)
+ {
+ if (is_object($array) AND ($array instanceof ArrayObject))
+ {
+ // Copy the array
+ $array_copy = $array->getArrayCopy();
+
+ // Is an object
+ $array_object = TRUE;
+ }
+ else
+ {
+ if ( ! is_array($array))
+ {
+ // Must always be an array
+ $array = (array) $array;
+ }
+
+ // Copy is a reference to the array
+ $array_copy =& $array;
+ }
+
+ if (empty($keys))
+ return $array;
+
+ // Create keys
+ $keys = explode('.', $keys);
+
+ // Create reference to the array
+ $row =& $array_copy;
+
+ for ($i = 0, $end = count($keys) - 1; $i <= $end; $i++)
+ {
+ // Get the current key
+ $key = $keys[$i];
+
+ if ( ! isset($row[$key]))
+ {
+ if (isset($keys[$i + 1]))
+ {
+ // Make the value an array
+ $row[$key] = array();
+ }
+ else
+ {
+ // Add the fill key
+ $row[$key] = $fill;
+ }
+ }
+ elseif (isset($keys[$i + 1]))
+ {
+ // Make the value an array
+ $row[$key] = (array) $row[$key];
+ }
+
+ // Go down a level, creating a new row reference
+ $row =& $row[$key];
+ }
+
+ if (isset($array_object))
+ {
+ // Swap the array back in
+ $array->exchangeArray($array_copy);
+ }
+ }
+
+ /**
+ * Retrieves current user agent information:
+ * keys: browser, version, platform, mobile, robot, referrer, languages, charsets
+ * tests: is_browser, is_mobile, is_robot, accept_lang, accept_charset
+ *
+ * @param string key or test name
+ * @param string used with "accept" tests: user_agent(accept_lang, en)
+ * @return array languages and charsets
+ * @return string all other keys
+ * @return boolean all tests
+ */
+ public static function user_agent($key = 'agent', $compare = NULL)
+ {
+ static $info;
+
+ // Return the raw string
+ if ($key === 'agent')
+ return self::$user_agent;
+
+ if ($info === NULL)
+ {
+ // Parse the user agent and extract basic information
+ $agents = self::config('user_agents');
+
+ foreach ($agents as $type => $data)
+ {
+ foreach ($data as $agent => $name)
+ {
+ if (stripos(self::$user_agent, $agent) !== FALSE)
+ {
+ if ($type === 'browser' AND preg_match('|'.preg_quote($agent).'[^0-9.]*+([0-9.][0-9.a-z]*)|i', self::$user_agent, $match))
+ {
+ // Set the browser version
+ $info['version'] = $match[1];
+ }
+
+ // Set the agent name
+ $info[$type] = $name;
+ break;
+ }
+ }
+ }
+ }
+
+ if (empty($info[$key]))
+ {
+ switch ($key)
+ {
+ case 'is_robot':
+ case 'is_browser':
+ case 'is_mobile':
+ // A boolean result
+ $return = ! empty($info[substr($key, 3)]);
+ break;
+ case 'languages':
+ $return = array();
+ if ( ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
+ {
+ if (preg_match_all('/[-a-z]{2,}/', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])), $matches))
+ {
+ // Found a result
+ $return = $matches[0];
+ }
+ }
+ break;
+ case 'charsets':
+ $return = array();
+ if ( ! empty($_SERVER['HTTP_ACCEPT_CHARSET']))
+ {
+ if (preg_match_all('/[-a-z0-9]{2,}/', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET'])), $matches))
+ {
+ // Found a result
+ $return = $matches[0];
+ }
+ }
+ break;
+ case 'referrer':
+ if ( ! empty($_SERVER['HTTP_REFERER']))
+ {
+ // Found a result
+ $return = trim($_SERVER['HTTP_REFERER']);
+ }
+ break;
+ }
+
+ // Cache the return value
+ isset($return) and $info[$key] = $return;
+ }
+
+ if ( ! empty($compare))
+ {
+ // The comparison must always be lowercase
+ $compare = strtolower($compare);
+
+ switch ($key)
+ {
+ case 'accept_lang':
+ // Check if the lange is accepted
+ return in_array($compare, self::user_agent('languages'));
+ break;
+ case 'accept_charset':
+ // Check if the charset is accepted
+ return in_array($compare, self::user_agent('charsets'));
+ break;
+ default:
+ // Invalid comparison
+ return FALSE;
+ break;
+ }
+ }
+
+ // Return the key, if set
+ return isset($info[$key]) ? $info[$key] : NULL;
+ }
+
+ /**
+ * Quick debugging of any variable. Any number of parameters can be set.
+ *
+ * @return string
+ */
+ public static function debug()
+ {
+ if (func_num_args() === 0)
+ return;
+
+ // Get params
+ $params = func_get_args();
+ $output = array();
+
+ foreach ($params as $var)
+ {
+ $output[] = '('.gettype($var).') '.html::specialchars(print_r($var, TRUE)).' ';
+ }
+
+ return implode("\n", $output);
+ }
+
+ /**
+ * Displays nice backtrace information.
+ * @see http://php.net/debug_backtrace
+ *
+ * @param array backtrace generated by an exception or debug_backtrace
+ * @return string
+ */
+ public static function backtrace($trace)
+ {
+ if ( ! is_array($trace))
+ return;
+
+ // Final output
+ $output = array();
+
+ foreach ($trace as $entry)
+ {
+ $temp = '';
+
+ if (isset($entry['file']))
+ {
+ $temp .= self::lang('core.error_file_line', preg_replace('!^'.preg_quote(DOCROOT).'!', '', $entry['file']), $entry['line']);
+ }
+
+ $temp .= '';
+
+ if (isset($entry['class']))
+ {
+ // Add class and call type
+ $temp .= $entry['class'].$entry['type'];
+ }
+
+ // Add function
+ $temp .= $entry['function'].'( ';
+
+ // Add function args
+ if (isset($entry['args']) AND is_array($entry['args']))
+ {
+ // Separator starts as nothing
+ $sep = '';
+
+ while ($arg = array_shift($entry['args']))
+ {
+ if (is_string($arg) AND is_file($arg))
+ {
+ // Remove docroot from filename
+ $arg = preg_replace('!^'.preg_quote(DOCROOT).'!', '', $arg);
+ }
+
+ $temp .= $sep.html::specialchars(print_r($arg, TRUE));
+
+ // Change separator to a comma
+ $sep = ', ';
+ }
+ }
+
+ $temp .= ' ) ';
+
+ $output[] = $temp;
+ }
+
+ return ''.implode("\n", $output).' ';
+ }
+
+ /**
+ * Saves the internal caches: configuration, include paths, etc.
+ *
+ * @return boolean
+ */
+ public static function internal_cache_save()
+ {
+ if ( ! is_array(self::$write_cache))
+ return FALSE;
+
+ // Get internal cache names
+ $caches = array_keys(self::$write_cache);
+
+ // Nothing written
+ $written = FALSE;
+
+ foreach ($caches as $cache)
+ {
+ if (isset(self::$internal_cache[$cache]))
+ {
+ // Write the cache file
+ self::cache_save($cache, self::$internal_cache[$cache], self::$configuration['core']['internal_cache']);
+
+ // A cache has been written
+ $written = TRUE;
+ }
+ }
+
+ return $written;
+ }
+
+} // End Kohana
+
+/**
+ * Creates a generic i18n exception.
+ */
+class Kohana_Exception extends Exception {
+
+ // Template file
+ protected $template = 'kohana_error_page';
+
+ // Header
+ protected $header = FALSE;
+
+ // Error code
+ protected $code = E_KOHANA;
+
+ /**
+ * Set exception message.
+ *
+ * @param string i18n language key for the message
+ * @param array addition line parameters
+ */
+ public function __construct($error)
+ {
+ $args = array_slice(func_get_args(), 1);
+
+ // Fetch the error message
+ $message = Kohana::lang($error, $args);
+
+ if ($message === $error OR empty($message))
+ {
+ // Unable to locate the message for the error
+ $message = 'Unknown Exception: '.$error;
+ }
+
+ // Sets $this->message the proper way
+ parent::__construct($message);
+ }
+
+ /**
+ * Magic method for converting an object to a string.
+ *
+ * @return string i18n message
+ */
+ public function __toString()
+ {
+ return (string) $this->message;
+ }
+
+ /**
+ * Fetch the template name.
+ *
+ * @return string
+ */
+ public function getTemplate()
+ {
+ return $this->template;
+ }
+
+ /**
+ * Sends an Internal Server Error header.
+ *
+ * @return void
+ */
+ public function sendHeaders()
+ {
+ // Send the 500 header
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+
+} // End Kohana Exception
+
+/**
+ * Creates a custom exception.
+ */
+class Kohana_User_Exception extends Kohana_Exception {
+
+ /**
+ * Set exception title and message.
+ *
+ * @param string exception title string
+ * @param string exception message string
+ * @param string custom error template
+ */
+ public function __construct($title, $message, $template = FALSE)
+ {
+ Exception::__construct($message);
+
+ $this->code = $title;
+
+ if ($template !== FALSE)
+ {
+ $this->template = $template;
+ }
+ }
+
+} // End Kohana PHP Exception
+
+/**
+ * Creates a Page Not Found exception.
+ */
+class Kohana_404_Exception extends Kohana_Exception {
+
+ protected $code = E_PAGE_NOT_FOUND;
+
+ /**
+ * Set internal properties.
+ *
+ * @param string URL of page
+ * @param string custom error template
+ */
+ public function __construct($page = FALSE, $template = FALSE)
+ {
+ if ($page === FALSE)
+ {
+ // Construct the page URI using Router properties
+ $page = Router::$current_uri.Router::$url_suffix.Router::$query_string;
+ }
+
+ Exception::__construct(Kohana::lang('core.page_not_found', $page));
+
+ $this->template = $template;
+ }
+
+ /**
+ * Sends "File Not Found" headers, to emulate server behavior.
+ *
+ * @return void
+ */
+ public function sendHeaders()
+ {
+ // Send the 404 header
+ header('HTTP/1.1 404 File Not Found');
+ }
+
+} // End Kohana 404 Exception
diff --git a/Server/system/core/utf8.php b/Server/system/core/utf8.php
new file mode 100644
index 0000000..9f20f42
--- /dev/null
+++ b/Server/system/core/utf8.php
@@ -0,0 +1,743 @@
+PCRE has not been compiled with UTF-8 support. '.
+ 'See PCRE Pattern Modifiers '.
+ 'for more information. This application cannot be run without UTF-8 support.',
+ E_USER_ERROR
+ );
+}
+
+if ( ! extension_loaded('iconv'))
+{
+ trigger_error
+ (
+ 'The iconv extension is not loaded. '.
+ 'Without iconv, strings cannot be properly translated to UTF-8 from user input. '.
+ 'This application cannot be run without UTF-8 support.',
+ E_USER_ERROR
+ );
+}
+
+if (extension_loaded('mbstring') AND (ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING))
+{
+ trigger_error
+ (
+ 'The mbstring extension is overloading PHP\'s native string functions. '.
+ 'Disable this by setting mbstring.func_overload to 0, 1, 4 or 5 in php.ini or a .htaccess file.'.
+ 'This application cannot be run without UTF-8 support.',
+ E_USER_ERROR
+ );
+}
+
+// Check PCRE support for Unicode properties such as \p and \X.
+$ER = error_reporting(0);
+define('PCRE_UNICODE_PROPERTIES', (bool) preg_match('/^\pL$/u', 'ñ'));
+error_reporting($ER);
+
+// SERVER_UTF8 ? use mb_* functions : use non-native functions
+if (extension_loaded('mbstring'))
+{
+ mb_internal_encoding('UTF-8');
+ define('SERVER_UTF8', TRUE);
+}
+else
+{
+ define('SERVER_UTF8', FALSE);
+}
+
+// Convert all global variables to UTF-8.
+$_GET = utf8::clean($_GET);
+$_POST = utf8::clean($_POST);
+$_COOKIE = utf8::clean($_COOKIE);
+$_SERVER = utf8::clean($_SERVER);
+
+if (PHP_SAPI == 'cli')
+{
+ // Convert command line arguments
+ $_SERVER['argv'] = utf8::clean($_SERVER['argv']);
+}
+
+final class utf8 {
+
+ // Called methods
+ static $called = array();
+
+ /**
+ * Recursively cleans arrays, objects, and strings. Removes ASCII control
+ * codes and converts to UTF-8 while silently discarding incompatible
+ * UTF-8 characters.
+ *
+ * @param string string to clean
+ * @return string
+ */
+ public static function clean($str)
+ {
+ if (is_array($str) OR is_object($str))
+ {
+ foreach ($str as $key => $val)
+ {
+ // Recursion!
+ $str[self::clean($key)] = self::clean($val);
+ }
+ }
+ elseif (is_string($str) AND $str !== '')
+ {
+ // Remove control characters
+ $str = self::strip_ascii_ctrl($str);
+
+ if ( ! self::is_ascii($str))
+ {
+ // Disable notices
+ $ER = error_reporting(~E_NOTICE);
+
+ // iconv is expensive, so it is only used when needed
+ $str = iconv('UTF-8', 'UTF-8//IGNORE', $str);
+
+ // Turn notices back on
+ error_reporting($ER);
+ }
+ }
+
+ return $str;
+ }
+
+ /**
+ * Tests whether a string contains only 7bit ASCII bytes. This is used to
+ * determine when to use native functions or UTF-8 functions.
+ *
+ * @param string string to check
+ * @return bool
+ */
+ public static function is_ascii($str)
+ {
+ return ! preg_match('/[^\x00-\x7F]/S', $str);
+ }
+
+ /**
+ * Strips out device control codes in the ASCII range.
+ *
+ * @param string string to clean
+ * @return string
+ */
+ public static function strip_ascii_ctrl($str)
+ {
+ return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $str);
+ }
+
+ /**
+ * Strips out all non-7bit ASCII bytes.
+ *
+ * @param string string to clean
+ * @return string
+ */
+ public static function strip_non_ascii($str)
+ {
+ return preg_replace('/[^\x00-\x7F]+/S', '', $str);
+ }
+
+ /**
+ * Replaces special/accented UTF-8 characters by ASCII-7 'equivalents'.
+ *
+ * @author Andreas Gohr
+ *
+ * @param string string to transliterate
+ * @param integer -1 lowercase only, +1 uppercase only, 0 both cases
+ * @return string
+ */
+ public static function transliterate_to_ascii($str, $case = 0)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _transliterate_to_ascii($str, $case);
+ }
+
+ /**
+ * Returns the length of the given string.
+ * @see http://php.net/strlen
+ *
+ * @param string string being measured for length
+ * @return integer
+ */
+ public static function strlen($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strlen($str);
+ }
+
+ /**
+ * Finds position of first occurrence of a UTF-8 string.
+ * @see http://php.net/strlen
+ *
+ * @author Harry Fuecks
+ *
+ * @param string haystack
+ * @param string needle
+ * @param integer offset from which character in haystack to start searching
+ * @return integer position of needle
+ * @return boolean FALSE if the needle is not found
+ */
+ public static function strpos($str, $search, $offset = 0)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strpos($str, $search, $offset);
+ }
+
+ /**
+ * Finds position of last occurrence of a char in a UTF-8 string.
+ * @see http://php.net/strrpos
+ *
+ * @author Harry Fuecks
+ *
+ * @param string haystack
+ * @param string needle
+ * @param integer offset from which character in haystack to start searching
+ * @return integer position of needle
+ * @return boolean FALSE if the needle is not found
+ */
+ public static function strrpos($str, $search, $offset = 0)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strrpos($str, $search, $offset);
+ }
+
+ /**
+ * Returns part of a UTF-8 string.
+ * @see http://php.net/substr
+ *
+ * @author Chris Smith
+ *
+ * @param string input string
+ * @param integer offset
+ * @param integer length limit
+ * @return string
+ */
+ public static function substr($str, $offset, $length = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _substr($str, $offset, $length);
+ }
+
+ /**
+ * Replaces text within a portion of a UTF-8 string.
+ * @see http://php.net/substr_replace
+ *
+ * @author Harry Fuecks
+ *
+ * @param string input string
+ * @param string replacement string
+ * @param integer offset
+ * @return string
+ */
+ public static function substr_replace($str, $replacement, $offset, $length = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _substr_replace($str, $replacement, $offset, $length);
+ }
+
+ /**
+ * Makes a UTF-8 string lowercase.
+ * @see http://php.net/strtolower
+ *
+ * @author Andreas Gohr
+ *
+ * @param string mixed case string
+ * @return string
+ */
+ public static function strtolower($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strtolower($str);
+ }
+
+ /**
+ * Makes a UTF-8 string uppercase.
+ * @see http://php.net/strtoupper
+ *
+ * @author Andreas Gohr
+ *
+ * @param string mixed case string
+ * @return string
+ */
+ public static function strtoupper($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strtoupper($str);
+ }
+
+ /**
+ * Makes a UTF-8 string's first character uppercase.
+ * @see http://php.net/ucfirst
+ *
+ * @author Harry Fuecks
+ *
+ * @param string mixed case string
+ * @return string
+ */
+ public static function ucfirst($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _ucfirst($str);
+ }
+
+ /**
+ * Makes the first character of every word in a UTF-8 string uppercase.
+ * @see http://php.net/ucwords
+ *
+ * @author Harry Fuecks
+ *
+ * @param string mixed case string
+ * @return string
+ */
+ public static function ucwords($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _ucwords($str);
+ }
+
+ /**
+ * Case-insensitive UTF-8 string comparison.
+ * @see http://php.net/strcasecmp
+ *
+ * @author Harry Fuecks
+ *
+ * @param string string to compare
+ * @param string string to compare
+ * @return integer less than 0 if str1 is less than str2
+ * @return integer greater than 0 if str1 is greater than str2
+ * @return integer 0 if they are equal
+ */
+ public static function strcasecmp($str1, $str2)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strcasecmp($str1, $str2);
+ }
+
+ /**
+ * Returns a string or an array with all occurrences of search in subject (ignoring case).
+ * replaced with the given replace value.
+ * @see http://php.net/str_ireplace
+ *
+ * @note It's not fast and gets slower if $search and/or $replace are arrays.
+ * @author Harry Fuecks
+ *
+ * @param string input string
+ * @param string needle
+ * @return string matched substring if found
+ * @return boolean FALSE if the substring was not found
+ */
+ public static function stristr($str, $search)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _stristr($str, $search);
+ }
+
+ /**
+ * Finds the length of the initial segment matching mask.
+ * @see http://php.net/strspn
+ *
+ * @author Harry Fuecks
+ *
+ * @param string input string
+ * @param string mask for search
+ * @param integer start position of the string to examine
+ * @param integer length of the string to examine
+ * @return integer length of the initial segment that contains characters in the mask
+ */
+ public static function strspn($str, $mask, $offset = NULL, $length = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strspn($str, $mask, $offset, $length);
+ }
+
+ /**
+ * Finds the length of the initial segment not matching mask.
+ * @see http://php.net/strcspn
+ *
+ * @author Harry Fuecks
+ *
+ * @param string input string
+ * @param string mask for search
+ * @param integer start position of the string to examine
+ * @param integer length of the string to examine
+ * @return integer length of the initial segment that contains characters not in the mask
+ */
+ public static function strcspn($str, $mask, $offset = NULL, $length = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strcspn($str, $mask, $offset, $length);
+ }
+
+ /**
+ * Pads a UTF-8 string to a certain length with another string.
+ * @see http://php.net/str_pad
+ *
+ * @author Harry Fuecks
+ *
+ * @param string input string
+ * @param integer desired string length after padding
+ * @param string string to use as padding
+ * @param string padding type: STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH
+ * @return string
+ */
+ public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_type = STR_PAD_RIGHT)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _str_pad($str, $final_str_length, $pad_str, $pad_type);
+ }
+
+ /**
+ * Converts a UTF-8 string to an array.
+ * @see http://php.net/str_split
+ *
+ * @author Harry Fuecks
+ *
+ * @param string input string
+ * @param integer maximum length of each chunk
+ * @return array
+ */
+ public static function str_split($str, $split_length = 1)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _str_split($str, $split_length);
+ }
+
+ /**
+ * Reverses a UTF-8 string.
+ * @see http://php.net/strrev
+ *
+ * @author Harry Fuecks
+ *
+ * @param string string to be reversed
+ * @return string
+ */
+ public static function strrev($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _strrev($str);
+ }
+
+ /**
+ * Strips whitespace (or other UTF-8 characters) from the beginning and
+ * end of a string.
+ * @see http://php.net/trim
+ *
+ * @author Andreas Gohr
+ *
+ * @param string input string
+ * @param string string of characters to remove
+ * @return string
+ */
+ public static function trim($str, $charlist = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _trim($str, $charlist);
+ }
+
+ /**
+ * Strips whitespace (or other UTF-8 characters) from the beginning of a string.
+ * @see http://php.net/ltrim
+ *
+ * @author Andreas Gohr
+ *
+ * @param string input string
+ * @param string string of characters to remove
+ * @return string
+ */
+ public static function ltrim($str, $charlist = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _ltrim($str, $charlist);
+ }
+
+ /**
+ * Strips whitespace (or other UTF-8 characters) from the end of a string.
+ * @see http://php.net/rtrim
+ *
+ * @author Andreas Gohr
+ *
+ * @param string input string
+ * @param string string of characters to remove
+ * @return string
+ */
+ public static function rtrim($str, $charlist = NULL)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _rtrim($str, $charlist);
+ }
+
+ /**
+ * Returns the unicode ordinal for a character.
+ * @see http://php.net/ord
+ *
+ * @author Harry Fuecks
+ *
+ * @param string UTF-8 encoded character
+ * @return integer
+ */
+ public static function ord($chr)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _ord($chr);
+ }
+
+ /**
+ * Takes an UTF-8 string and returns an array of ints representing the Unicode characters.
+ * Astral planes are supported i.e. the ints in the output can be > 0xFFFF.
+ * Occurrances of the BOM are ignored. Surrogates are not allowed.
+ *
+ * The Original Code is Mozilla Communicator client code.
+ * The Initial Developer of the Original Code is Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer.
+ * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/.
+ * Slight modifications to fit with phputf8 library by Harry Fuecks .
+ *
+ * @param string UTF-8 encoded string
+ * @return array unicode code points
+ * @return boolean FALSE if the string is invalid
+ */
+ public static function to_unicode($str)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _to_unicode($str);
+ }
+
+ /**
+ * Takes an array of ints representing the Unicode characters and returns a UTF-8 string.
+ * Astral planes are supported i.e. the ints in the input can be > 0xFFFF.
+ * Occurrances of the BOM are ignored. Surrogates are not allowed.
+ *
+ * The Original Code is Mozilla Communicator client code.
+ * The Initial Developer of the Original Code is Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer.
+ * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/.
+ * Slight modifications to fit with phputf8 library by Harry Fuecks .
+ *
+ * @param array unicode code points representing a string
+ * @return string utf8 string of characters
+ * @return boolean FALSE if a code point cannot be found
+ */
+ public static function from_unicode($arr)
+ {
+ if ( ! isset(self::$called[__FUNCTION__]))
+ {
+ require SYSPATH.'core/utf8/'.__FUNCTION__.EXT;
+
+ // Function has been called
+ self::$called[__FUNCTION__] = TRUE;
+ }
+
+ return _from_unicode($arr);
+ }
+
+} // End utf8
\ No newline at end of file
diff --git a/Server/system/core/utf8/from_unicode.php b/Server/system/core/utf8/from_unicode.php
new file mode 100644
index 0000000..66c6742
--- /dev/null
+++ b/Server/system/core/utf8/from_unicode.php
@@ -0,0 +1,68 @@
+= 0) AND ($arr[$k] <= 0x007f))
+ {
+ echo chr($arr[$k]);
+ }
+ // 2 byte sequence
+ elseif ($arr[$k] <= 0x07ff)
+ {
+ echo chr(0xc0 | ($arr[$k] >> 6));
+ echo chr(0x80 | ($arr[$k] & 0x003f));
+ }
+ // Byte order mark (skip)
+ elseif ($arr[$k] == 0xFEFF)
+ {
+ // nop -- zap the BOM
+ }
+ // Test for illegal surrogates
+ elseif ($arr[$k] >= 0xD800 AND $arr[$k] <= 0xDFFF)
+ {
+ // Found a surrogate
+ trigger_error('utf8::from_unicode: Illegal surrogate at index: '.$k.', value: '.$arr[$k], E_USER_WARNING);
+ return FALSE;
+ }
+ // 3 byte sequence
+ elseif ($arr[$k] <= 0xffff)
+ {
+ echo chr(0xe0 | ($arr[$k] >> 12));
+ echo chr(0x80 | (($arr[$k] >> 6) & 0x003f));
+ echo chr(0x80 | ($arr[$k] & 0x003f));
+ }
+ // 4 byte sequence
+ elseif ($arr[$k] <= 0x10ffff)
+ {
+ echo chr(0xf0 | ($arr[$k] >> 18));
+ echo chr(0x80 | (($arr[$k] >> 12) & 0x3f));
+ echo chr(0x80 | (($arr[$k] >> 6) & 0x3f));
+ echo chr(0x80 | ($arr[$k] & 0x3f));
+ }
+ // Out of range
+ else
+ {
+ trigger_error('utf8::from_unicode: Codepoint out of Unicode range at index: '.$k.', value: '.$arr[$k], E_USER_WARNING);
+ return FALSE;
+ }
+ }
+
+ $result = ob_get_contents();
+ ob_end_clean();
+ return $result;
+}
diff --git a/Server/system/core/utf8/ltrim.php b/Server/system/core/utf8/ltrim.php
new file mode 100644
index 0000000..556fe07
--- /dev/null
+++ b/Server/system/core/utf8/ltrim.php
@@ -0,0 +1,22 @@
+= 0 AND $ord0 <= 127)
+ {
+ return $ord0;
+ }
+
+ if ( ! isset($chr[1]))
+ {
+ trigger_error('Short sequence - at least 2 bytes expected, only 1 seen', E_USER_WARNING);
+ return FALSE;
+ }
+
+ $ord1 = ord($chr[1]);
+
+ if ($ord0 >= 192 AND $ord0 <= 223)
+ {
+ return ($ord0 - 192) * 64 + ($ord1 - 128);
+ }
+
+ if ( ! isset($chr[2]))
+ {
+ trigger_error('Short sequence - at least 3 bytes expected, only 2 seen', E_USER_WARNING);
+ return FALSE;
+ }
+
+ $ord2 = ord($chr[2]);
+
+ if ($ord0 >= 224 AND $ord0 <= 239)
+ {
+ return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128);
+ }
+
+ if ( ! isset($chr[3]))
+ {
+ trigger_error('Short sequence - at least 4 bytes expected, only 3 seen', E_USER_WARNING);
+ return FALSE;
+ }
+
+ $ord3 = ord($chr[3]);
+
+ if ($ord0 >= 240 AND $ord0 <= 247)
+ {
+ return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2-128) * 64 + ($ord3 - 128);
+ }
+
+ if ( ! isset($chr[4]))
+ {
+ trigger_error('Short sequence - at least 5 bytes expected, only 4 seen', E_USER_WARNING);
+ return FALSE;
+ }
+
+ $ord4 = ord($chr[4]);
+
+ if ($ord0 >= 248 AND $ord0 <= 251)
+ {
+ return ($ord0 - 248) * 16777216 + ($ord1-128) * 262144 + ($ord2 - 128) * 4096 + ($ord3 - 128) * 64 + ($ord4 - 128);
+ }
+
+ if ( ! isset($chr[5]))
+ {
+ trigger_error('Short sequence - at least 6 bytes expected, only 5 seen', E_USER_WARNING);
+ return FALSE;
+ }
+
+ if ($ord0 >= 252 AND $ord0 <= 253)
+ {
+ return ($ord0 - 252) * 1073741824 + ($ord1 - 128) * 16777216 + ($ord2 - 128) * 262144 + ($ord3 - 128) * 4096 + ($ord4 - 128) * 64 + (ord($chr[5]) - 128);
+ }
+
+ if ($ord0 >= 254 AND $ord0 <= 255)
+ {
+ trigger_error('Invalid UTF-8 with surrogate ordinal '.$ord0, E_USER_WARNING);
+ return FALSE;
+ }
+}
\ No newline at end of file
diff --git a/Server/system/core/utf8/rtrim.php b/Server/system/core/utf8/rtrim.php
new file mode 100644
index 0000000..efa0e19
--- /dev/null
+++ b/Server/system/core/utf8/rtrim.php
@@ -0,0 +1,22 @@
+ $val)
+ {
+ $str[$key] = utf8::str_ireplace($search, $replace, $val, $count);
+ }
+ return $str;
+ }
+
+ if (is_array($search))
+ {
+ $keys = array_keys($search);
+
+ foreach ($keys as $k)
+ {
+ if (is_array($replace))
+ {
+ if (array_key_exists($k, $replace))
+ {
+ $str = utf8::str_ireplace($search[$k], $replace[$k], $str, $count);
+ }
+ else
+ {
+ $str = utf8::str_ireplace($search[$k], '', $str, $count);
+ }
+ }
+ else
+ {
+ $str = utf8::str_ireplace($search[$k], $replace, $str, $count);
+ }
+ }
+ return $str;
+ }
+
+ $search = utf8::strtolower($search);
+ $str_lower = utf8::strtolower($str);
+
+ $total_matched_strlen = 0;
+ $i = 0;
+
+ while (preg_match('/(.*?)'.preg_quote($search, '/').'/s', $str_lower, $matches))
+ {
+ $matched_strlen = strlen($matches[0]);
+ $str_lower = substr($str_lower, $matched_strlen);
+
+ $offset = $total_matched_strlen + strlen($matches[1]) + ($i * (strlen($replace) - 1));
+ $str = substr_replace($str, $replace, $offset, strlen($search));
+
+ $total_matched_strlen += $matched_strlen;
+ $i++;
+ }
+
+ $count += $i;
+ return $str;
+}
diff --git a/Server/system/core/utf8/str_pad.php b/Server/system/core/utf8/str_pad.php
new file mode 100644
index 0000000..aab4ccc
--- /dev/null
+++ b/Server/system/core/utf8/str_pad.php
@@ -0,0 +1,54 @@
+0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062,
+ 0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101,
+ 0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3,
+ 0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C,
+ 0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F,
+ 0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F,
+ 0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3,
+ 0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B,
+ 0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9,
+ 0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D,
+ 0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4,
+ 0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165,
+ 0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157,
+ 0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119,
+ 0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129,
+ 0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448,
+ 0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075,
+ 0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A,
+ 0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC,
+ 0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0,
+ 0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D,
+ 0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0,
+ 0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5,
+ 0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA,
+ 0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065,
+ 0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F,
+ 0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068,
+ 0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6,
+ 0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457,
+ 0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5,
+ 0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6,
+ 0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071,
+ 0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458,
+ 0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE,
+ 0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127,
+ 0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C,
+ 0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F,
+ 0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB,
+ 0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441,
+ 0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B,
+ 0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103,
+ 0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9,
+ 0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123,
+ );
+ }
+
+ $uni = utf8::to_unicode($str);
+
+ if ($uni === FALSE)
+ return FALSE;
+
+ for ($i = 0, $c = count($uni); $i < $c; $i++)
+ {
+ if (isset($UTF8_UPPER_TO_LOWER[$uni[$i]]))
+ {
+ $uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]];
+ }
+ }
+
+ return utf8::from_unicode($uni);
+}
\ No newline at end of file
diff --git a/Server/system/core/utf8/strtoupper.php b/Server/system/core/utf8/strtoupper.php
new file mode 100644
index 0000000..f3ded73
--- /dev/null
+++ b/Server/system/core/utf8/strtoupper.php
@@ -0,0 +1,84 @@
+0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042,
+ 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100,
+ 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393,
+ 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C,
+ 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F,
+ 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E,
+ 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3,
+ 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A,
+ 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9,
+ 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C,
+ 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4,
+ 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164,
+ 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156,
+ 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118,
+ 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128,
+ 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428,
+ 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055,
+ 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A,
+ 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC,
+ 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0,
+ 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D,
+ 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0,
+ 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5,
+ 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA,
+ 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045,
+ 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F,
+ 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048,
+ 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6,
+ 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407,
+ 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395,
+ 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396,
+ 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051,
+ 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408,
+ 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F,
+ 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126,
+ 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C,
+ 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E,
+ 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB,
+ 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421,
+ 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A,
+ 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102,
+ 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9,
+ 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122,
+ );
+ }
+
+ $uni = utf8::to_unicode($str);
+
+ if ($uni === FALSE)
+ return FALSE;
+
+ for ($i = 0, $c = count($uni); $i < $c; $i++)
+ {
+ if (isset($UTF8_LOWER_TO_UPPER[$uni[$i]]))
+ {
+ $uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]];
+ }
+ }
+
+ return utf8::from_unicode($uni);
+}
\ No newline at end of file
diff --git a/Server/system/core/utf8/substr.php b/Server/system/core/utf8/substr.php
new file mode 100644
index 0000000..daf66b8
--- /dev/null
+++ b/Server/system/core/utf8/substr.php
@@ -0,0 +1,75 @@
+= $strlen OR ($length < 0 AND $length <= $offset - $strlen))
+ return '';
+
+ // Whole string
+ if ($offset == 0 AND ($length === NULL OR $length >= $strlen))
+ return $str;
+
+ // Build regex
+ $regex = '^';
+
+ // Create an offset expression
+ if ($offset > 0)
+ {
+ // PCRE repeating quantifiers must be less than 65536, so repeat when necessary
+ $x = (int) ($offset / 65535);
+ $y = (int) ($offset % 65535);
+ $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}';
+ $regex .= ($y == 0) ? '' : '.{'.$y.'}';
+ }
+
+ // Create a length expression
+ if ($length === NULL)
+ {
+ $regex .= '(.*)'; // No length set, grab it all
+ }
+ // Find length from the left (positive length)
+ elseif ($length > 0)
+ {
+ // Reduce length so that it can't go beyond the end of the string
+ $length = min($strlen - $offset, $length);
+
+ $x = (int) ($length / 65535);
+ $y = (int) ($length % 65535);
+ $regex .= '(';
+ $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}';
+ $regex .= '.{'.$y.'})';
+ }
+ // Find length from the right (negative length)
+ else
+ {
+ $x = (int) (-$length / 65535);
+ $y = (int) (-$length % 65535);
+ $regex .= '(.*)';
+ $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}';
+ $regex .= '.{'.$y.'}';
+ }
+
+ preg_match('/'.$regex.'/us', $str, $matches);
+ return $matches[1];
+}
\ No newline at end of file
diff --git a/Server/system/core/utf8/substr_replace.php b/Server/system/core/utf8/substr_replace.php
new file mode 100644
index 0000000..45e2d2a
--- /dev/null
+++ b/Server/system/core/utf8/substr_replace.php
@@ -0,0 +1,22 @@
+ 0x10FFFF))
+ {
+ trigger_error('utf8::to_unicode: Illegal sequence or codepoint in UTF-8 at byte '.$i, E_USER_WARNING);
+ return FALSE;
+ }
+
+ if (0xFEFF != $mUcs4)
+ {
+ // BOM is legal but we don't want to output it
+ $out[] = $mUcs4;
+ }
+
+ // Initialize UTF-8 cache
+ $mState = 0;
+ $mUcs4 = 0;
+ $mBytes = 1;
+ }
+ }
+ else
+ {
+ // ((0xC0 & (*in) != 0x80) AND (mState != 0))
+ // Incomplete multi-octet sequence
+ trigger_error('utf8::to_unicode: Incomplete multi-octet sequence in UTF-8 at byte '.$i, E_USER_WARNING);
+ return FALSE;
+ }
+ }
+ }
+
+ return $out;
+}
\ No newline at end of file
diff --git a/Server/system/core/utf8/transliterate_to_ascii.php b/Server/system/core/utf8/transliterate_to_ascii.php
new file mode 100644
index 0000000..07461fb
--- /dev/null
+++ b/Server/system/core/utf8/transliterate_to_ascii.php
@@ -0,0 +1,77 @@
+ 'a', 'ô' => 'o', 'Ä' => 'd', 'á¸' => 'f', 'ë' => 'e', 'Å¡' => 's', 'Æ¡' => 'o',
+ 'Ã' => 'ss', 'Ä' => 'a', 'Å' => 'r', 'È' => 't', 'Å' => 'n', 'Ä' => 'a', 'Ä·' => 'k',
+ 'Å' => 's', 'ỳ' => 'y', 'Å' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'á¹' => 'p', 'ó' => 'o',
+ 'ú' => 'u', 'Ä' => 'e', 'é' => 'e', 'ç' => 'c', 'áº' => 'w', 'Ä' => 'c', 'õ' => 'o',
+ 'ṡ' => 's', 'ø' => 'o', 'Ä£' => 'g', 'ŧ' => 't', 'È' => 's', 'Ä' => 'e', 'Ä' => 'c',
+ 'Å' => 's', 'î' => 'i', 'ű' => 'u', 'Ä' => 'c', 'Ä' => 'e', 'ŵ' => 'w', 'ṫ' => 't',
+ 'Å«' => 'u', 'Ä' => 'c', 'ö' => 'o', 'è' => 'e', 'Å·' => 'y', 'Ä
' => 'a', 'Å' => 'l',
+ 'ų' => 'u', 'ů' => 'u', 'Å' => 's', 'Ä' => 'g', 'ļ' => 'l', 'Æ' => 'f', 'ž' => 'z',
+ 'áº' => 'w', 'á¸' => 'b', 'Ã¥' => 'a', 'ì' => 'i', 'ï' => 'i', 'á¸' => 'd', 'Å¥' => 't',
+ 'Å' => 'r', 'ä' => 'a', 'Ã' => 'i', 'Å' => 'r', 'ê' => 'e', 'ü' => 'u', 'ò' => 'o',
+ 'Ä' => 'e', 'ñ' => 'n', 'Å' => 'n', 'Ä¥' => 'h', 'Ä' => 'g', 'Ä' => 'd', 'ĵ' => 'j',
+ 'ÿ' => 'y', 'Å©' => 'u', 'Å' => 'u', 'Æ°' => 'u', 'Å£' => 't', 'ý' => 'y', 'Å' => 'o',
+ 'â' => 'a', 'ľ' => 'l', 'áº
' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g',
+ 'á¹' => 'm', 'Å' => 'o', 'Ä©' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a',
+ 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'Ä' => 'e',
+ );
+ }
+
+ $str = str_replace(
+ array_keys($UTF8_LOWER_ACCENTS),
+ array_values($UTF8_LOWER_ACCENTS),
+ $str
+ );
+ }
+
+ if ($case >= 0)
+ {
+ if ($UTF8_UPPER_ACCENTS === NULL)
+ {
+ $UTF8_UPPER_ACCENTS = array(
+ 'Ã' => 'A', 'Ã' => 'O', 'Ä' => 'D', 'á¸' => 'F', 'Ã' => 'E', 'Å ' => 'S', 'Æ ' => 'O',
+ 'Ä' => 'A', 'Å' => 'R', 'È' => 'T', 'Å' => 'N', 'Ä' => 'A', 'Ķ' => 'K', 'Ä' => 'E',
+ 'Å' => 'S', 'Ỳ' => 'Y', 'Å
' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'á¹' => 'P', 'Ã' => 'O',
+ 'Ã' => 'U', 'Ä' => 'E', 'Ã' => 'E', 'Ã' => 'C', 'áº' => 'W', 'Ä' => 'C', 'Ã' => 'O',
+ 'á¹ ' => 'S', 'Ã' => 'O', 'Ä¢' => 'G', 'Ŧ' => 'T', 'È' => 'S', 'Ä' => 'E', 'Ä' => 'C',
+ 'Å' => 'S', 'Ã' => 'I', 'Å°' => 'U', 'Ä' => 'C', 'Ä' => 'E', 'Å´' => 'W', 'Ṫ' => 'T',
+ 'Ū' => 'U', 'Ä' => 'C', 'Ã' => 'O', 'Ã' => 'E', 'Ŷ' => 'Y', 'Ä' => 'A', 'Å' => 'L',
+ 'Ų' => 'U', 'Å®' => 'U', 'Å' => 'S', 'Ä' => 'G', 'Ä»' => 'L', 'Æ' => 'F', 'Ž' => 'Z',
+ 'áº' => 'W', 'á¸' => 'B', 'Ã
' => 'A', 'Ã' => 'I', 'Ã' => 'I', 'á¸' => 'D', 'Ť' => 'T',
+ 'Å' => 'R', 'Ã' => 'A', 'Ã' => 'I', 'Å' => 'R', 'Ã' => 'E', 'Ã' => 'U', 'Ã' => 'O',
+ 'Ä' => 'E', 'Ã' => 'N', 'Å' => 'N', 'Ĥ' => 'H', 'Ä' => 'G', 'Ä' => 'D', 'Ä´' => 'J',
+ 'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Å¢' => 'T', 'Ã' => 'Y', 'Å' => 'O',
+ 'Ã' => 'A', 'Ľ' => 'L', 'áº' => 'W', 'Å»' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ä ' => 'G',
+ 'á¹' => 'M', 'Å' => 'O', 'Ĩ' => 'I', 'Ã' => 'U', 'Ä®' => 'I', 'Ź' => 'Z', 'Ã' => 'A',
+ 'Ã' => 'U', 'Ã' => 'Th', 'Ã' => 'Dh', 'Ã' => 'Ae',
+ );
+ }
+
+ $str = str_replace(
+ array_keys($UTF8_UPPER_ACCENTS),
+ array_values($UTF8_UPPER_ACCENTS),
+ $str
+ );
+ }
+
+ return $str;
+}
\ No newline at end of file
diff --git a/Server/system/core/utf8/trim.php b/Server/system/core/utf8/trim.php
new file mode 100644
index 0000000..7434102
--- /dev/null
+++ b/Server/system/core/utf8/trim.php
@@ -0,0 +1,17 @@
+ $value)
+ {
+ $value = ($keep_keys === TRUE) ? $value : array_values($value);
+ foreach ($value as $k => $v)
+ {
+ $new_array[$k][$key] = $v;
+ }
+ }
+
+ return $new_array;
+ }
+
+ /**
+ * Removes a key from an array and returns the value.
+ *
+ * @param string key to return
+ * @param array array to work on
+ * @return mixed value of the requested array key
+ */
+ public static function remove($key, & $array)
+ {
+ if ( ! array_key_exists($key, $array))
+ return NULL;
+
+ $val = $array[$key];
+ unset($array[$key]);
+
+ return $val;
+ }
+
+
+ /**
+ * Extract one or more keys from an array. Each key given after the first
+ * argument (the array) will be extracted. Keys that do not exist in the
+ * search array will be NULL in the extracted data.
+ *
+ * @param array array to search
+ * @param string key name
+ * @return array
+ */
+ public static function extract(array $search, $keys)
+ {
+ // Get the keys, removing the $search array
+ $keys = array_slice(func_get_args(), 1);
+
+ $found = array();
+ foreach ($keys as $key)
+ {
+ if (isset($search[$key]))
+ {
+ $found[$key] = $search[$key];
+ }
+ else
+ {
+ $found[$key] = NULL;
+ }
+ }
+
+ return $found;
+ }
+
+ /**
+ * Because PHP does not have this function.
+ *
+ * @param array array to unshift
+ * @param string key to unshift
+ * @param mixed value to unshift
+ * @return array
+ */
+ public static function unshift_assoc( array & $array, $key, $val)
+ {
+ $array = array_reverse($array, TRUE);
+ $array[$key] = $val;
+ $array = array_reverse($array, TRUE);
+
+ return $array;
+ }
+
+ /**
+ * Because PHP does not have this function, and array_walk_recursive creates
+ * references in arrays and is not truly recursive.
+ *
+ * @param mixed callback to apply to each member of the array
+ * @param array array to map to
+ * @return array
+ */
+ public static function map_recursive($callback, array $array)
+ {
+ foreach ($array as $key => $val)
+ {
+ // Map the callback to the key
+ $array[$key] = is_array($val) ? arr::map_recursive($callback, $val) : call_user_func($callback, $val);
+ }
+
+ return $array;
+ }
+
+ /**
+ * @param mixed $needle the value to search for
+ * @param array $haystack an array of values to search in
+ * @param boolean $sort sort the array now
+ * @return integer|FALSE the index of the match or FALSE when not found
+ */
+ public static function binary_search($needle, $haystack, $sort = FALSE)
+ {
+ if ($sort)
+ {
+ sort($haystack);
+ }
+
+ $high = count($haystack) - 1;
+ $low = 0;
+
+ while ($low <= $high)
+ {
+ $mid = ($low + $high) >> 1;
+
+ if ($haystack[$mid] < $needle)
+ {
+ $low = $mid + 1;
+ }
+ elseif ($haystack[$mid] > $needle)
+ {
+ $high = $mid - 1;
+ }
+ else
+ {
+ return $mid;
+ }
+ }
+
+ return FALSE;
+ }
+
+
+ /**
+ * Emulates array_merge_recursive, but appends numeric keys and replaces
+ * associative keys, instead of appending all keys.
+ *
+ * @param array any number of arrays
+ * @return array
+ */
+ public static function merge()
+ {
+ $total = func_num_args();
+
+ $result = array();
+ for ($i = 0; $i < $total; $i++)
+ {
+ foreach (func_get_arg($i) as $key => $val)
+ {
+ if (isset($result[$key]))
+ {
+ if (is_array($val))
+ {
+ // Arrays are merged recursively
+ $result[$key] = arr::merge($result[$key], $val);
+ }
+ elseif (is_int($key))
+ {
+ // Indexed arrays are appended
+ array_push($result, $val);
+ }
+ else
+ {
+ // Associative arrays are replaced
+ $result[$key] = $val;
+ }
+ }
+ else
+ {
+ // New values are added
+ $result[$key] = $val;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Overwrites an array with values from input array(s).
+ * Non-existing keys will not be appended!
+ *
+ * @param array key array
+ * @param array input array(s) that will overwrite key array values
+ * @return array
+ */
+ public static function overwrite($array1, $array2)
+ {
+ foreach (array_intersect_key($array2, $array1) as $key => $value)
+ {
+ $array1[$key] = $value;
+ }
+
+ if (func_num_args() > 2)
+ {
+ foreach (array_slice(func_get_args(), 2) as $array2)
+ {
+ foreach (array_intersect_key($array2, $array1) as $key => $value)
+ {
+ $array1[$key] = $value;
+ }
+ }
+ }
+
+ return $array1;
+ }
+
+ /**
+ * Fill an array with a range of numbers.
+ *
+ * @param integer stepping
+ * @param integer ending number
+ * @return array
+ */
+ public static function range($step = 10, $max = 100)
+ {
+ if ($step < 1)
+ return array();
+
+ $array = array();
+ for ($i = $step; $i <= $max; $i += $step)
+ {
+ $array[$i] = $i;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Recursively convert an array to an object.
+ *
+ * @param array array to convert
+ * @return object
+ */
+ public static function to_object(array $array, $class = 'stdClass')
+ {
+ $object = new $class;
+
+ foreach ($array as $key => $value)
+ {
+ if (is_array($value))
+ {
+ // Convert the array to an object
+ $value = arr::to_object($value, $class);
+ }
+
+ // Add the value to the object
+ $object->{$key} = $value;
+ }
+
+ return $object;
+ }
+
+} // End arr
diff --git a/Server/system/helpers/cookie.php b/Server/system/helpers/cookie.php
new file mode 100644
index 0000000..901b6d8
--- /dev/null
+++ b/Server/system/helpers/cookie.php
@@ -0,0 +1,84 @@
+cookie($name, $default, $xss_clean);
+ }
+
+ /**
+ * Nullify and unset a cookie.
+ *
+ * @param string cookie name
+ * @param string URL path
+ * @param string URL domain
+ * @return boolean
+ */
+ public static function delete($name, $path = NULL, $domain = NULL)
+ {
+ if ( ! isset($_COOKIE[$name]))
+ return FALSE;
+
+ // Delete the cookie from globals
+ unset($_COOKIE[$name]);
+
+ // Sets the cookie value to an empty string, and the expiration to 24 hours ago
+ return cookie::set($name, '', -86400, $path, $domain, FALSE, FALSE);
+ }
+
+} // End cookie
\ No newline at end of file
diff --git a/Server/system/helpers/date.php b/Server/system/helpers/date.php
new file mode 100644
index 0000000..7d5a9ab
--- /dev/null
+++ b/Server/system/helpers/date.php
@@ -0,0 +1,405 @@
+> 1);
+ }
+
+ /**
+ * Converts a DOS timestamp to UNIX format.
+ *
+ * @param integer DOS timestamp
+ * @return integer
+ */
+ public static function dos2unix($timestamp = FALSE)
+ {
+ $sec = 2 * ($timestamp & 0x1f);
+ $min = ($timestamp >> 5) & 0x3f;
+ $hrs = ($timestamp >> 11) & 0x1f;
+ $day = ($timestamp >> 16) & 0x1f;
+ $mon = ($timestamp >> 21) & 0x0f;
+ $year = ($timestamp >> 25) & 0x7f;
+
+ return mktime($hrs, $min, $sec, $mon, $day, $year + 1980);
+ }
+
+ /**
+ * Returns the offset (in seconds) between two time zones.
+ * @see http://php.net/timezones
+ *
+ * @param string timezone that to find the offset of
+ * @param string|boolean timezone used as the baseline
+ * @return integer
+ */
+ public static function offset($remote, $local = TRUE)
+ {
+ static $offsets;
+
+ // Default values
+ $remote = (string) $remote;
+ $local = ($local === TRUE) ? date_default_timezone_get() : (string) $local;
+
+ // Cache key name
+ $cache = $remote.$local;
+
+ if (empty($offsets[$cache]))
+ {
+ // Create timezone objects
+ $remote = new DateTimeZone($remote);
+ $local = new DateTimeZone($local);
+
+ // Create date objects from timezones
+ $time_there = new DateTime('now', $remote);
+ $time_here = new DateTime('now', $local);
+
+ // Find the offset
+ $offsets[$cache] = $remote->getOffset($time_there) - $local->getOffset($time_here);
+ }
+
+ return $offsets[$cache];
+ }
+
+ /**
+ * Number of seconds in a minute, incrementing by a step.
+ *
+ * @param integer amount to increment each step by, 1 to 30
+ * @param integer start value
+ * @param integer end value
+ * @return array A mirrored (foo => foo) array from 1-60.
+ */
+ public static function seconds($step = 1, $start = 0, $end = 60)
+ {
+ // Always integer
+ $step = (int) $step;
+
+ $seconds = array();
+
+ for ($i = $start; $i < $end; $i += $step)
+ {
+ $seconds[$i] = ($i < 10) ? '0'.$i : $i;
+ }
+
+ return $seconds;
+ }
+
+ /**
+ * Number of minutes in an hour, incrementing by a step.
+ *
+ * @param integer amount to increment each step by, 1 to 30
+ * @return array A mirrored (foo => foo) array from 1-60.
+ */
+ public static function minutes($step = 5)
+ {
+ // Because there are the same number of minutes as seconds in this set,
+ // we choose to re-use seconds(), rather than creating an entirely new
+ // function. Shhhh, it's cheating! ;) There are several more of these
+ // in the following methods.
+ return date::seconds($step);
+ }
+
+ /**
+ * Number of hours in a day.
+ *
+ * @param integer amount to increment each step by
+ * @param boolean use 24-hour time
+ * @param integer the hour to start at
+ * @return array A mirrored (foo => foo) array from start-12 or start-23.
+ */
+ public static function hours($step = 1, $long = FALSE, $start = NULL)
+ {
+ // Default values
+ $step = (int) $step;
+ $long = (bool) $long;
+ $hours = array();
+
+ // Set the default start if none was specified.
+ if ($start === NULL)
+ {
+ $start = ($long === FALSE) ? 1 : 0;
+ }
+
+ $hours = array();
+
+ // 24-hour time has 24 hours, instead of 12
+ $size = ($long === TRUE) ? 23 : 12;
+
+ for ($i = $start; $i <= $size; $i += $step)
+ {
+ $hours[$i] = $i;
+ }
+
+ return $hours;
+ }
+
+ /**
+ * Returns AM or PM, based on a given hour.
+ *
+ * @param integer number of the hour
+ * @return string
+ */
+ public static function ampm($hour)
+ {
+ // Always integer
+ $hour = (int) $hour;
+
+ return ($hour > 11) ? 'PM' : 'AM';
+ }
+
+ /**
+ * Adjusts a non-24-hour number into a 24-hour number.
+ *
+ * @param integer hour to adjust
+ * @param string AM or PM
+ * @return string
+ */
+ public static function adjust($hour, $ampm)
+ {
+ $hour = (int) $hour;
+ $ampm = strtolower($ampm);
+
+ switch ($ampm)
+ {
+ case 'am':
+ if ($hour == 12)
+ $hour = 0;
+ break;
+ case 'pm':
+ if ($hour < 12)
+ $hour += 12;
+ break;
+ }
+
+ return sprintf('%02s', $hour);
+ }
+
+ /**
+ * Number of days in month.
+ *
+ * @param integer number of month
+ * @param integer number of year to check month, defaults to the current year
+ * @return array A mirrored (foo => foo) array of the days.
+ */
+ public static function days($month, $year = FALSE)
+ {
+ static $months;
+
+ // Always integers
+ $month = (int) $month;
+ $year = (int) $year;
+
+ // Use the current year by default
+ $year = ($year == FALSE) ? date('Y') : $year;
+
+ // We use caching for months, because time functions are used
+ if (empty($months[$year][$month]))
+ {
+ $months[$year][$month] = array();
+
+ // Use date to find the number of days in the given month
+ $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1;
+
+ for ($i = 1; $i < $total; $i++)
+ {
+ $months[$year][$month][$i] = $i;
+ }
+ }
+
+ return $months[$year][$month];
+ }
+
+ /**
+ * Number of months in a year
+ *
+ * @return array A mirrored (foo => foo) array from 1-12.
+ */
+ public static function months()
+ {
+ return date::hours();
+ }
+
+ /**
+ * Returns an array of years between a starting and ending year.
+ * Uses the current year +/- 5 as the max/min.
+ *
+ * @param integer starting year
+ * @param integer ending year
+ * @return array
+ */
+ public static function years($start = FALSE, $end = FALSE)
+ {
+ // Default values
+ $start = ($start === FALSE) ? date('Y') - 5 : (int) $start;
+ $end = ($end === FALSE) ? date('Y') + 5 : (int) $end;
+
+ $years = array();
+
+ // Add one, so that "less than" works
+ $end += 1;
+
+ for ($i = $start; $i < $end; $i++)
+ {
+ $years[$i] = $i;
+ }
+
+ return $years;
+ }
+
+ /**
+ * Returns time difference between two timestamps, in human readable format.
+ *
+ * @param integer timestamp
+ * @param integer timestamp, defaults to the current time
+ * @param string formatting string
+ * @return string|array
+ */
+ public static function timespan($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+ {
+ // Array with the output formats
+ $output = preg_split('/[^a-z]+/', strtolower((string) $output));
+
+ // Invalid output
+ if (empty($output))
+ return FALSE;
+
+ // Make the output values into keys
+ extract(array_flip($output), EXTR_SKIP);
+
+ // Default values
+ $time1 = max(0, (int) $time1);
+ $time2 = empty($time2) ? time() : max(0, (int) $time2);
+
+ // Calculate timespan (seconds)
+ $timespan = abs($time1 - $time2);
+
+ // All values found using Google Calculator.
+ // Years and months do not match the formula exactly, due to leap years.
+
+ // Years ago, 60 * 60 * 24 * 365
+ isset($years) and $timespan -= 31556926 * ($years = (int) floor($timespan / 31556926));
+
+ // Months ago, 60 * 60 * 24 * 30
+ isset($months) and $timespan -= 2629744 * ($months = (int) floor($timespan / 2629743.83));
+
+ // Weeks ago, 60 * 60 * 24 * 7
+ isset($weeks) and $timespan -= 604800 * ($weeks = (int) floor($timespan / 604800));
+
+ // Days ago, 60 * 60 * 24
+ isset($days) and $timespan -= 86400 * ($days = (int) floor($timespan / 86400));
+
+ // Hours ago, 60 * 60
+ isset($hours) and $timespan -= 3600 * ($hours = (int) floor($timespan / 3600));
+
+ // Minutes ago, 60
+ isset($minutes) and $timespan -= 60 * ($minutes = (int) floor($timespan / 60));
+
+ // Seconds ago, 1
+ isset($seconds) and $seconds = $timespan;
+
+ // Remove the variables that cannot be accessed
+ unset($timespan, $time1, $time2);
+
+ // Deny access to these variables
+ $deny = array_flip(array('deny', 'key', 'difference', 'output'));
+
+ // Return the difference
+ $difference = array();
+ foreach ($output as $key)
+ {
+ if (isset($$key) AND ! isset($deny[$key]))
+ {
+ // Add requested key to the output
+ $difference[$key] = $$key;
+ }
+ }
+
+ // Invalid output formats string
+ if (empty($difference))
+ return FALSE;
+
+ // If only one output format was asked, don't put it in an array
+ if (count($difference) === 1)
+ return current($difference);
+
+ // Return array
+ return $difference;
+ }
+
+ /**
+ * Returns time difference between two timestamps, in the format:
+ * N year, N months, N weeks, N days, N hours, N minutes, and N seconds ago
+ *
+ * @param integer timestamp
+ * @param integer timestamp, defaults to the current time
+ * @param string formatting string
+ * @return string
+ */
+ public static function timespan_string($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+ {
+ if ($difference = date::timespan($time1, $time2, $output) AND is_array($difference))
+ {
+ // Determine the key of the last item in the array
+ $last = end($difference);
+ $last = key($difference);
+
+ $span = array();
+ foreach ($difference as $name => $amount)
+ {
+ if ($amount === 0)
+ {
+ // Skip empty amounts
+ continue;
+ }
+
+ // Add the amount to the span
+ $span[] = ($name === $last ? ' and ' : ', ').$amount.' '.($amount === 1 ? inflector::singular($name) : $name);
+ }
+
+ // If the difference is less than 60 seconds, remove the preceding and.
+ if (count($span) === 1)
+ {
+ $span[0] = ltrim($span[0], 'and ');
+ }
+
+ // Replace difference by making the span into a string
+ $difference = trim(implode('', $span), ',');
+ }
+ elseif (is_int($difference))
+ {
+ // Single-value return
+ $difference = $difference.' '.($difference === 1 ? inflector::singular($output) : $output);
+ }
+
+ return $difference;
+ }
+
+} // End date
\ No newline at end of file
diff --git a/Server/system/helpers/download.php b/Server/system/helpers/download.php
new file mode 100644
index 0000000..49fed42
--- /dev/null
+++ b/Server/system/helpers/download.php
@@ -0,0 +1,105 @@
+setUsername($config['options']['username']);
+ empty($config['options']['password']) or $connection->setPassword($config['options']['password']);
+
+ if ( ! empty($config['options']['auth']))
+ {
+ // Get the class name and params
+ list ($class, $params) = arr::callback_string($config['options']['auth']);
+
+ if ($class === 'PopB4Smtp')
+ {
+ // Load the PopB4Smtp class manually, due to its odd filename
+ require Kohana::find_file('vendor', 'swift/Swift/Authenticator/$PopB4Smtp$');
+ }
+
+ // Prepare the class name for auto-loading
+ $class = 'Swift_Authenticator_'.$class;
+
+ // Attach the authenticator
+ $connection->attachAuthenticator(($params === NULL) ? new $class : new $class($params[0]));
+ }
+
+ // Set the timeout to 5 seconds
+ $connection->setTimeout(empty($config['options']['timeout']) ? 5 : (int) $config['options']['timeout']);
+ break;
+ case 'sendmail':
+ // Create a sendmail connection
+ $connection = new Swift_Connection_Sendmail
+ (
+ empty($config['options']) ? Swift_Connection_Sendmail::AUTO_DETECT : $config['options']
+ );
+
+ // Set the timeout to 5 seconds
+ $connection->setTimeout(5);
+ break;
+ default:
+ // Use the native connection
+ $connection = new Swift_Connection_NativeMail($config['options']);
+ break;
+ }
+
+ // Create the SwiftMailer instance
+ return email::$mail = new Swift($connection);
+ }
+
+ /**
+ * Send an email message.
+ *
+ * @param string|array recipient email (and name), or an array of To, Cc, Bcc names
+ * @param string|array sender email (and name)
+ * @param string message subject
+ * @param string message body
+ * @param boolean send email as HTML
+ * @return integer number of emails sent
+ */
+ public static function send($to, $from, $subject, $message, $html = FALSE)
+ {
+ // Connect to SwiftMailer
+ (email::$mail === NULL) and email::connect();
+
+ // Determine the message type
+ $html = ($html === TRUE) ? 'text/html' : 'text/plain';
+
+ // Create the message
+ $message = new Swift_Message($subject, $message, $html, '8bit', 'utf-8');
+
+ if (is_string($to))
+ {
+ // Single recipient
+ $recipients = new Swift_Address($to);
+ }
+ elseif (is_array($to))
+ {
+ if (isset($to[0]) AND isset($to[1]))
+ {
+ // Create To: address set
+ $to = array('to' => $to);
+ }
+
+ // Create a list of recipients
+ $recipients = new Swift_RecipientList;
+
+ foreach ($to as $method => $set)
+ {
+ if ( ! in_array($method, array('to', 'cc', 'bcc')))
+ {
+ // Use To: by default
+ $method = 'to';
+ }
+
+ // Create method name
+ $method = 'add'.ucfirst($method);
+
+ if (is_array($set))
+ {
+ // Add a recipient with name
+ $recipients->$method($set[0], $set[1]);
+ }
+ else
+ {
+ // Add a recipient without name
+ $recipients->$method($set);
+ }
+ }
+ }
+
+ if (is_string($from))
+ {
+ // From without a name
+ $from = new Swift_Address($from);
+ }
+ elseif (is_array($from))
+ {
+ // From with a name
+ $from = new Swift_Address($from[0], $from[1]);
+ }
+
+ return email::$mail->send($message, $recipients, $from);
+ }
+
+} // End email
\ No newline at end of file
diff --git a/Server/system/helpers/expires.php b/Server/system/helpers/expires.php
new file mode 100644
index 0000000..c43cc0c
--- /dev/null
+++ b/Server/system/helpers/expires.php
@@ -0,0 +1,111 @@
+ 0)
+ {
+ // Re-send headers
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $mod_time));
+ header('Expires: '.gmdate('D, d M Y H:i:s T', time() + $mod_time_diff));
+ header('Cache-Control: max-age='.$mod_time_diff);
+ header('Status: 304 Not Modified', TRUE, 304);
+
+ // Prevent any output
+ Event::add('system.display', array('expires', 'prevent_output'));
+
+ // Exit to prevent other output
+ exit;
+ }
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Check headers already created to not step on download or Img_lib's feet
+ *
+ * @return boolean
+ */
+ public static function check_headers()
+ {
+ foreach (headers_list() as $header)
+ {
+ if ((session_cache_limiter() == '' AND stripos($header, 'Last-Modified:') === 0)
+ OR stripos($header, 'Expires:') === 0)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Prevent any output from being displayed. Executed during system.display.
+ *
+ * @return void
+ */
+ public static function prevent_output()
+ {
+ Kohana::$output = '';
+ }
+
+} // End expires
\ No newline at end of file
diff --git a/Server/system/helpers/feed.php b/Server/system/helpers/feed.php
new file mode 100644
index 0000000..74bb2f6
--- /dev/null
+++ b/Server/system/helpers/feed.php
@@ -0,0 +1,122 @@
+channel) ? $feed->xpath('//item') : $feed->entry;
+
+ $i = 0;
+ $items = array();
+
+ foreach ($feed as $item)
+ {
+ if ($limit > 0 AND $i++ === $limit)
+ break;
+
+ $items[] = (array) $item;
+ }
+
+ return $items;
+ }
+
+ /**
+ * Creates a feed from the given parameters.
+ *
+ * @param array feed information
+ * @param array items to add to the feed
+ * @param string define which format to use
+ * @param string define which encoding to use
+ * @return string
+ */
+ public static function create($info, $items, $format = 'rss2', $encoding = 'UTF-8')
+ {
+ $info += array('title' => 'Generated Feed', 'link' => '', 'generator' => 'KohanaPHP');
+
+ $feed = ' ';
+ $feed = simplexml_load_string($feed);
+
+ foreach ($info as $name => $value)
+ {
+ if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value)))
+ {
+ // Convert timestamps to RFC 822 formatted dates
+ $value = date(DATE_RFC822, $value);
+ }
+ elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE)
+ {
+ // Convert URIs to URLs
+ $value = url::site($value, 'http');
+ }
+
+ // Add the info to the channel
+ $feed->channel->addChild($name, $value);
+ }
+
+ foreach ($items as $item)
+ {
+ // Add the item to the channel
+ $row = $feed->channel->addChild('item');
+
+ foreach ($item as $name => $value)
+ {
+ if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value)))
+ {
+ // Convert timestamps to RFC 822 formatted dates
+ $value = date(DATE_RFC822, $value);
+ }
+ elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE)
+ {
+ // Convert URIs to URLs
+ $value = url::site($value, 'http');
+ }
+
+ // Add the info to the row
+ $row->addChild($name, $value);
+ }
+ }
+
+ return $feed->asXML();
+ }
+
+} // End feed
\ No newline at end of file
diff --git a/Server/system/helpers/file.php b/Server/system/helpers/file.php
new file mode 100644
index 0000000..b1b7174
--- /dev/null
+++ b/Server/system/helpers/file.php
@@ -0,0 +1,186 @@
+'."\n";
+
+ // Add hidden fields immediate after opening tag
+ empty($hidden) or $form .= form::hidden($hidden);
+
+ return $form;
+ }
+
+ /**
+ * Generates an opening HTML form tag that can be used for uploading files.
+ *
+ * @param string form action attribute
+ * @param array extra attributes
+ * @param array hidden fields to be created immediately after the form tag
+ * @return string
+ */
+ public static function open_multipart($action = NULL, $attr = array(), $hidden = array())
+ {
+ // Set multi-part form type
+ $attr['enctype'] = 'multipart/form-data';
+
+ return form::open($action, $attr, $hidden);
+ }
+
+ /**
+ * Generates a fieldset opening tag.
+ *
+ * @param array html attributes
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function open_fieldset($data = NULL, $extra = '')
+ {
+ return ''."\n";
+ }
+
+ /**
+ * Generates a fieldset closing tag.
+ *
+ * @return string
+ */
+ public static function close_fieldset()
+ {
+ return ' '."\n";
+ }
+
+ /**
+ * Generates a legend tag for use with a fieldset.
+ *
+ * @param string legend text
+ * @param array HTML attributes
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function legend($text = '', $data = NULL, $extra = '')
+ {
+ return ''.$text.' '."\n";
+ }
+
+ /**
+ * Generates hidden form fields.
+ * You can pass a simple key/value string or an associative array with multiple values.
+ *
+ * @param string|array input name (string) or key/value pairs (array)
+ * @param string input value, if using an input name
+ * @return string
+ */
+ public static function hidden($data, $value = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array
+ (
+ $data => $value
+ );
+ }
+
+ $input = '';
+ foreach ($data as $name => $value)
+ {
+ $attr = array
+ (
+ 'type' => 'hidden',
+ 'name' => $name,
+ 'value' => $value
+ );
+
+ $input .= form::input($attr)."\n";
+ }
+
+ return $input;
+ }
+
+ /**
+ * Creates an HTML form input tag. Defaults to a text type.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function input($data, $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ // Type and value are required attributes
+ $data += array
+ (
+ 'type' => 'text',
+ 'value' => $value
+ );
+
+ return ' ';
+ }
+
+ /**
+ * Creates a HTML form password input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function password($data, $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'password';
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form upload input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function upload($data, $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'file';
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form textarea tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @param boolean encode existing entities
+ * @return string
+ */
+ public static function textarea($data, $value = '', $extra = '', $double_encode = TRUE)
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ // Use the value from $data if possible, or use $value
+ $value = isset($data['value']) ? $data['value'] : $value;
+
+ // Value is not part of the attributes
+ unset($data['value']);
+
+ return '';
+ }
+
+ /**
+ * Creates an HTML form select tag, or "dropdown menu".
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param array select options, when using a name
+ * @param string|array option key(s) that should be selected by default
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function dropdown($data, $options = NULL, $selected = NULL, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+ else
+ {
+ if (isset($data['options']))
+ {
+ // Use data options
+ $options = $data['options'];
+ }
+
+ if (isset($data['selected']))
+ {
+ // Use data selected
+ $selected = $data['selected'];
+ }
+ }
+
+ if (is_array($selected))
+ {
+ // Multi-select box
+ $data['multiple'] = 'multiple';
+ }
+ else
+ {
+ // Single selection (but converted to an array)
+ $selected = array($selected);
+ }
+
+ $input = ''."\n";
+ foreach ((array) $options as $key => $val)
+ {
+ // Key should always be a string
+ $key = (string) $key;
+
+ if (is_array($val))
+ {
+ $input .= ''."\n";
+ foreach ($val as $inner_key => $inner_val)
+ {
+ // Inner key should always be a string
+ $inner_key = (string) $inner_key;
+
+ $sel = in_array($inner_key, $selected) ? ' selected="selected"' : '';
+ $input .= ''.$inner_val.' '."\n";
+ }
+ $input .= ' '."\n";
+ }
+ else
+ {
+ $sel = in_array($key, $selected) ? ' selected="selected"' : '';
+ $input .= ''.$val.' '."\n";
+ }
+ }
+ $input .= ' ';
+
+ return $input;
+ }
+
+ /**
+ * Creates an HTML form checkbox input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param boolean make the checkbox checked by default
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function checkbox($data, $value = '', $checked = FALSE, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'checkbox';
+
+ if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE))
+ {
+ $data['checked'] = 'checked';
+ }
+ else
+ {
+ unset($data['checked']);
+ }
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form radio input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param boolean make the radio selected by default
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function radio($data = '', $value = '', $checked = FALSE, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'radio';
+
+ if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE))
+ {
+ $data['checked'] = 'checked';
+ }
+ else
+ {
+ unset($data['checked']);
+ }
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form submit input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function submit($data = '', $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ if (empty($data['name']))
+ {
+ // Remove the name if it is empty
+ unset($data['name']);
+ }
+
+ $data['type'] = 'submit';
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form button input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function button($data = '', $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ if (empty($data['name']))
+ {
+ // Remove the name if it is empty
+ unset($data['name']);
+ }
+
+ if (isset($data['value']) AND empty($value))
+ {
+ $value = arr::remove('value', $data);
+ }
+
+ return ''.$value.' ';
+ }
+
+ /**
+ * Closes an open form tag.
+ *
+ * @param string string to be attached after the closing tag
+ * @return string
+ */
+ public static function close($extra = '')
+ {
+ return ''."\n".$extra;
+ }
+
+ /**
+ * Creates an HTML form label tag.
+ *
+ * @param string|array label "for" name or an array of HTML attributes
+ * @param string label text or HTML
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function label($data = '', $text = NULL, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ if (is_string($data))
+ {
+ // Specify the input this label is for
+ $data = array('for' => $data);
+ }
+ else
+ {
+ // No input specified
+ $data = array();
+ }
+ }
+
+ if ($text === NULL AND isset($data['for']))
+ {
+ // Make the text the human-readable input name
+ $text = ucwords(inflector::humanize($data['for']));
+ }
+
+ return ''.$text.' ';
+ }
+
+ /**
+ * Sorts a key/value array of HTML attributes, putting form attributes first,
+ * and returns an attribute string.
+ *
+ * @param array HTML attributes array
+ * @return string
+ */
+ public static function attributes($attr, $type = NULL)
+ {
+ if (empty($attr))
+ return '';
+
+ if (isset($attr['name']) AND empty($attr['id']) AND strpos($attr['name'], '[') === FALSE)
+ {
+ if ($type === NULL AND ! empty($attr['type']))
+ {
+ // Set the type by the attributes
+ $type = $attr['type'];
+ }
+
+ switch ($type)
+ {
+ case 'text':
+ case 'textarea':
+ case 'password':
+ case 'select':
+ case 'checkbox':
+ case 'file':
+ case 'image':
+ case 'button':
+ case 'submit':
+ // Only specific types of inputs use name to id matching
+ $attr['id'] = $attr['name'];
+ break;
+ }
+ }
+
+ $order = array
+ (
+ 'action',
+ 'method',
+ 'type',
+ 'id',
+ 'name',
+ 'value',
+ 'src',
+ 'size',
+ 'maxlength',
+ 'rows',
+ 'cols',
+ 'accept',
+ 'tabindex',
+ 'accesskey',
+ 'align',
+ 'alt',
+ 'title',
+ 'class',
+ 'style',
+ 'selected',
+ 'checked',
+ 'readonly',
+ 'disabled'
+ );
+
+ $sorted = array();
+ foreach ($order as $key)
+ {
+ if (isset($attr[$key]))
+ {
+ // Move the attribute to the sorted array
+ $sorted[$key] = $attr[$key];
+
+ // Remove the attribute from unsorted array
+ unset($attr[$key]);
+ }
+ }
+
+ // Combine the sorted and unsorted attributes and create an HTML string
+ return html::attributes(array_merge($sorted, $attr));
+ }
+
+} // End form
\ No newline at end of file
diff --git a/Server/system/helpers/format.php b/Server/system/helpers/format.php
new file mode 100644
index 0000000..fb8a029
--- /dev/null
+++ b/Server/system/helpers/format.php
@@ -0,0 +1,66 @@
+='))
+ {
+ $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8', FALSE);
+ }
+ else
+ {
+ $str = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $str);
+ $str = str_replace(array('<', '>', '\'', '"'), array('<', '>', ''', '"'), $str);
+ }
+ }
+
+ return $str;
+ }
+
+ /**
+ * Perform a html::specialchars() with additional URL specific encoding.
+ *
+ * @param string string to convert
+ * @param boolean encode existing entities
+ * @return string
+ */
+ public static function specialurlencode($str, $double_encode = TRUE)
+ {
+ return str_replace(' ', '%20', html::specialchars($str, $double_encode));
+ }
+
+ /**
+ * Create HTML link anchors.
+ *
+ * @param string URL or URI string
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @param string non-default protocol, eg: https
+ * @param boolean option to escape the title that is output
+ * @return string
+ */
+ public static function anchor($uri, $title = NULL, $attributes = NULL, $protocol = NULL, $escape_title = FALSE)
+ {
+ if ($uri === '')
+ {
+ $site_url = url::base(FALSE);
+ }
+ elseif (strpos($uri, '#') === 0)
+ {
+ // This is an id target link, not a URL
+ $site_url = $uri;
+ }
+ elseif (strpos($uri, '://') === FALSE)
+ {
+ $site_url = url::site($uri, $protocol);
+ }
+ else
+ {
+ if (html::$windowed_urls === TRUE AND empty($attributes['target']))
+ {
+ $attributes['target'] = '_blank';
+ }
+
+ $site_url = $uri;
+ }
+
+ return
+ // Parsed URL
+ ''
+ // Title empty? Use the parsed URL
+ .($escape_title ? html::specialchars((($title === NULL) ? $site_url : $title), FALSE) : (($title === NULL) ? $site_url : $title)).' ';
+ }
+
+ /**
+ * Creates an HTML anchor to a file.
+ *
+ * @param string name of file to link to
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @param string non-default protocol, eg: ftp
+ * @return string
+ */
+ public static function file_anchor($file, $title = NULL, $attributes = NULL, $protocol = NULL)
+ {
+ return
+ // Base URL + URI = full URL
+ ''
+ // Title empty? Use the filename part of the URI
+ .(($title === NULL) ? end(explode('/', $file)) : $title) .' ';
+ }
+
+ /**
+ * Similar to anchor, but with the protocol parameter first.
+ *
+ * @param string link protocol
+ * @param string URI or URL to link to
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @return string
+ */
+ public static function panchor($protocol, $uri, $title = NULL, $attributes = FALSE)
+ {
+ return html::anchor($uri, $title, $attributes, $protocol);
+ }
+
+ /**
+ * Create an array of anchors from an array of link/title pairs.
+ *
+ * @param array link/title pairs
+ * @return array
+ */
+ public static function anchor_array(array $array)
+ {
+ $anchors = array();
+ foreach ($array as $link => $title)
+ {
+ // Create list of anchors
+ $anchors[] = html::anchor($link, $title);
+ }
+ return $anchors;
+ }
+
+ /**
+ * Generates an obfuscated version of an email address.
+ *
+ * @param string email address
+ * @return string
+ */
+ public static function email($email)
+ {
+ $safe = '';
+ foreach (str_split($email) as $letter)
+ {
+ switch (($letter === '@') ? rand(1, 2) : rand(1, 3))
+ {
+ // HTML entity code
+ case 1: $safe .= ''.ord($letter).';'; break;
+ // Hex character code
+ case 2: $safe .= ''.dechex(ord($letter)).';'; break;
+ // Raw (no) encoding
+ case 3: $safe .= $letter;
+ }
+ }
+
+ return $safe;
+ }
+
+ /**
+ * Creates an email anchor.
+ *
+ * @param string email address to send to
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @return string
+ */
+ public static function mailto($email, $title = NULL, $attributes = NULL)
+ {
+ if (empty($email))
+ return $title;
+
+ // Remove the subject or other parameters that do not need to be encoded
+ if (strpos($email, '?') !== FALSE)
+ {
+ // Extract the parameters from the email address
+ list ($email, $params) = explode('?', $email, 2);
+
+ // Make the params into a query string, replacing spaces
+ $params = '?'.str_replace(' ', '%20', $params);
+ }
+ else
+ {
+ // No parameters
+ $params = '';
+ }
+
+ // Obfuscate email address
+ $safe = html::email($email);
+
+ // Title defaults to the encoded email address
+ empty($title) and $title = $safe;
+
+ // Parse attributes
+ empty($attributes) or $attributes = html::attributes($attributes);
+
+ // Encoded start of the href="" is a static encoded version of 'mailto:'
+ return ''.$title.' ';
+ }
+
+ /**
+ * Generate a "breadcrumb" list of anchors representing the URI.
+ *
+ * @param array segments to use as breadcrumbs, defaults to using Router::$segments
+ * @return string
+ */
+ public static function breadcrumb($segments = NULL)
+ {
+ empty($segments) and $segments = Router::$segments;
+
+ $array = array();
+ while ($segment = array_pop($segments))
+ {
+ $array[] = html::anchor
+ (
+ // Complete URI for the URL
+ implode('/', $segments).'/'.$segment,
+ // Title for the current segment
+ ucwords(inflector::humanize($segment))
+ );
+ }
+
+ // Retrun the array of all the segments
+ return array_reverse($array);
+ }
+
+ /**
+ * Creates a meta tag.
+ *
+ * @param string|array tag name, or an array of tags
+ * @param string tag "content" value
+ * @return string
+ */
+ public static function meta($tag, $value = NULL)
+ {
+ if (is_array($tag))
+ {
+ $tags = array();
+ foreach ($tag as $t => $v)
+ {
+ // Build each tag and add it to the array
+ $tags[] = html::meta($t, $v);
+ }
+
+ // Return all of the tags as a string
+ return implode("\n", $tags);
+ }
+
+ // Set the meta attribute value
+ $attr = in_array(strtolower($tag), Kohana::config('http.meta_equiv')) ? 'http-equiv' : 'name';
+
+ return ' ';
+ }
+
+ /**
+ * Creates a stylesheet link.
+ *
+ * @param string|array filename, or array of filenames to match to array of medias
+ * @param string|array media type of stylesheet, or array to match filenames
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function stylesheet($style, $media = FALSE, $index = FALSE)
+ {
+ return html::link($style, 'stylesheet', 'text/css', '.css', $media, $index);
+ }
+
+ /**
+ * Creates a link tag.
+ *
+ * @param string|array filename
+ * @param string|array relationship
+ * @param string|array mimetype
+ * @param string specifies suffix of the file
+ * @param string|array specifies on what device the document will be displayed
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function link($href, $rel, $type, $suffix = FALSE, $media = FALSE, $index = FALSE)
+ {
+ $compiled = '';
+
+ if (is_array($href))
+ {
+ foreach ($href as $_href)
+ {
+ $_rel = is_array($rel) ? array_shift($rel) : $rel;
+ $_type = is_array($type) ? array_shift($type) : $type;
+ $_media = is_array($media) ? array_shift($media) : $media;
+
+ $compiled .= html::link($_href, $_rel, $_type, $suffix, $_media, $index);
+ }
+ }
+ else
+ {
+ if (strpos($href, '://') === FALSE)
+ {
+ // Make the URL absolute
+ $href = url::base($index).$href;
+ }
+
+ $length = strlen($suffix);
+
+ if ( $length > 0 AND substr_compare($href, $suffix, -$length, $length, FALSE) !== 0)
+ {
+ // Add the defined suffix
+ $href .= $suffix;
+ }
+
+ $attr = array
+ (
+ 'rel' => $rel,
+ 'type' => $type,
+ 'href' => $href,
+ );
+
+ if ( ! empty($media))
+ {
+ // Add the media type to the attributes
+ $attr['media'] = $media;
+ }
+
+ $compiled = ' ';
+ }
+
+ return $compiled."\n";
+ }
+
+ /**
+ * Creates a script link.
+ *
+ * @param string|array filename
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function script($script, $index = FALSE)
+ {
+ $compiled = '';
+
+ if (is_array($script))
+ {
+ foreach ($script as $name)
+ {
+ $compiled .= html::script($name, $index);
+ }
+ }
+ else
+ {
+ if (strpos($script, '://') === FALSE)
+ {
+ // Add the suffix only when it's not already present
+ $script = url::base((bool) $index).$script;
+ }
+
+ if (substr_compare($script, '.js', -3, 3, FALSE) !== 0)
+ {
+ // Add the javascript suffix
+ $script .= '.js';
+ }
+
+ $compiled = '';
+ }
+
+ return $compiled."\n";
+ }
+
+ /**
+ * Creates a image link.
+ *
+ * @param string image source, or an array of attributes
+ * @param string|array image alt attribute, or an array of attributes
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function image($src = NULL, $alt = NULL, $index = FALSE)
+ {
+ // Create attribute list
+ $attributes = is_array($src) ? $src : array('src' => $src);
+
+ if (is_array($alt))
+ {
+ $attributes += $alt;
+ }
+ elseif ( ! empty($alt))
+ {
+ // Add alt to attributes
+ $attributes['alt'] = $alt;
+ }
+
+ if (strpos($attributes['src'], '://') === FALSE)
+ {
+ // Make the src attribute into an absolute URL
+ $attributes['src'] = url::base($index).$attributes['src'];
+ }
+
+ return ' ';
+ }
+
+ /**
+ * Compiles an array of HTML attributes into an attribute string.
+ *
+ * @param string|array array of attributes
+ * @return string
+ */
+ public static function attributes($attrs)
+ {
+ if (empty($attrs))
+ return '';
+
+ if (is_string($attrs))
+ return ' '.$attrs;
+
+ $compiled = '';
+ foreach ($attrs as $key => $val)
+ {
+ $compiled .= ' '.$key.'="'.html::specialchars($val).'"';
+ }
+
+ return $compiled;
+ }
+
+} // End html
diff --git a/Server/system/helpers/inflector.php b/Server/system/helpers/inflector.php
new file mode 100644
index 0000000..1e4fee2
--- /dev/null
+++ b/Server/system/helpers/inflector.php
@@ -0,0 +1,193 @@
+ 1)
+ return $str;
+
+ // Cache key name
+ $key = 'singular_'.$str.$count;
+
+ if (isset(inflector::$cache[$key]))
+ return inflector::$cache[$key];
+
+ if (inflector::uncountable($str))
+ return inflector::$cache[$key] = $str;
+
+ if (empty(inflector::$irregular))
+ {
+ // Cache irregular words
+ inflector::$irregular = Kohana::config('inflector.irregular');
+ }
+
+ if ($irregular = array_search($str, inflector::$irregular))
+ {
+ $str = $irregular;
+ }
+ elseif (preg_match('/[sxz]es$/', $str) OR preg_match('/[^aeioudgkprt]hes$/', $str))
+ {
+ // Remove "es"
+ $str = substr($str, 0, -2);
+ }
+ elseif (preg_match('/[^aeiou]ies$/', $str))
+ {
+ $str = substr($str, 0, -3).'y';
+ }
+ elseif (substr($str, -1) === 's' AND substr($str, -2) !== 'ss')
+ {
+ $str = substr($str, 0, -1);
+ }
+
+ return inflector::$cache[$key] = $str;
+ }
+
+ /**
+ * Makes a singular word plural.
+ *
+ * @param string word to pluralize
+ * @return string
+ */
+ public static function plural($str, $count = NULL)
+ {
+ // Remove garbage
+ $str = strtolower(trim($str));
+
+ if (is_string($count))
+ {
+ // Convert to integer when using a digit string
+ $count = (int) $count;
+ }
+
+ // Do nothing with singular
+ if ($count === 1)
+ return $str;
+
+ // Cache key name
+ $key = 'plural_'.$str.$count;
+
+ if (isset(inflector::$cache[$key]))
+ return inflector::$cache[$key];
+
+ if (inflector::uncountable($str))
+ return inflector::$cache[$key] = $str;
+
+ if (empty(inflector::$irregular))
+ {
+ // Cache irregular words
+ inflector::$irregular = Kohana::config('inflector.irregular');
+ }
+
+ if (isset(inflector::$irregular[$str]))
+ {
+ $str = inflector::$irregular[$str];
+ }
+ elseif (preg_match('/[sxz]$/', $str) OR preg_match('/[^aeioudgkprt]h$/', $str))
+ {
+ $str .= 'es';
+ }
+ elseif (preg_match('/[^aeiou]y$/', $str))
+ {
+ // Change "y" to "ies"
+ $str = substr_replace($str, 'ies', -1);
+ }
+ else
+ {
+ $str .= 's';
+ }
+
+ // Set the cache and return
+ return inflector::$cache[$key] = $str;
+ }
+
+ /**
+ * Makes a phrase camel case.
+ *
+ * @param string phrase to camelize
+ * @return string
+ */
+ public static function camelize($str)
+ {
+ $str = 'x'.strtolower(trim($str));
+ $str = ucwords(preg_replace('/[\s_]+/', ' ', $str));
+
+ return substr(str_replace(' ', '', $str), 1);
+ }
+
+ /**
+ * Makes a phrase underscored instead of spaced.
+ *
+ * @param string phrase to underscore
+ * @return string
+ */
+ public static function underscore($str)
+ {
+ return preg_replace('/\s+/', '_', trim($str));
+ }
+
+ /**
+ * Makes an underscored or dashed phrase human-reable.
+ *
+ * @param string phrase to make human-reable
+ * @return string
+ */
+ public static function humanize($str)
+ {
+ return preg_replace('/[_-]+/', ' ', trim($str));
+ }
+
+} // End inflector
\ No newline at end of file
diff --git a/Server/system/helpers/num.php b/Server/system/helpers/num.php
new file mode 100644
index 0000000..3eb5d5a
--- /dev/null
+++ b/Server/system/helpers/num.php
@@ -0,0 +1,26 @@
+ 0);
+ }
+
+ /**
+ * Compare the q values for given array of content types and return the one with the highest value.
+ * If items are found to have the same q value, the first one encountered in the given array wins.
+ * If all items in the given array have a q value of 0, FALSE is returned.
+ *
+ * @param array content types
+ * @param boolean set to TRUE to disable wildcard checking
+ * @return mixed string mime type with highest q value, FALSE if none of the given types are accepted
+ */
+ public static function preferred_accept($types, $explicit_check = FALSE)
+ {
+ // Initialize
+ $mime_types = array();
+ $max_q = 0;
+ $preferred = FALSE;
+
+ // Load q values for all given content types
+ foreach (array_unique($types) as $type)
+ {
+ $mime_types[$type] = request::accepts_at_quality($type, $explicit_check);
+ }
+
+ // Look for the highest q value
+ foreach ($mime_types as $type => $q)
+ {
+ if ($q > $max_q)
+ {
+ $max_q = $q;
+ $preferred = $type;
+ }
+ }
+
+ return $preferred;
+ }
+
+ /**
+ * Returns quality factor at which the client accepts content type.
+ *
+ * @param string content type (e.g. "image/jpg", "jpg")
+ * @param boolean set to TRUE to disable wildcard checking
+ * @return integer|float
+ */
+ public static function accepts_at_quality($type = NULL, $explicit_check = FALSE)
+ {
+ request::parse_accept_header();
+
+ // Normalize type
+ $type = strtolower((string) $type);
+
+ // General content type (e.g. "jpg")
+ if (strpos($type, '/') === FALSE)
+ {
+ // Don't accept anything by default
+ $q = 0;
+
+ // Look up relevant mime types
+ foreach ((array) Kohana::config('mimes.'.$type) as $type)
+ {
+ $q2 = request::accepts_at_quality($type, $explicit_check);
+ $q = ($q2 > $q) ? $q2 : $q;
+ }
+
+ return $q;
+ }
+
+ // Content type with subtype given (e.g. "image/jpg")
+ $type = explode('/', $type, 2);
+
+ // Exact match
+ if (isset(request::$accept_types[$type[0]][$type[1]]))
+ return request::$accept_types[$type[0]][$type[1]];
+
+ // Wildcard match (if not checking explicitly)
+ if ($explicit_check === FALSE AND isset(request::$accept_types[$type[0]]['*']))
+ return request::$accept_types[$type[0]]['*'];
+
+ // Catch-all wildcard match (if not checking explicitly)
+ if ($explicit_check === FALSE AND isset(request::$accept_types['*']['*']))
+ return request::$accept_types['*']['*'];
+
+ // Content type not accepted
+ return 0;
+ }
+
+ /**
+ * Parses client's HTTP Accept request header, and builds array structure representing it.
+ *
+ * @return void
+ */
+ protected static function parse_accept_header()
+ {
+ // Run this function just once
+ if (request::$accept_types !== NULL)
+ return;
+
+ // Initialize accept_types array
+ request::$accept_types = array();
+
+ // No HTTP Accept header found
+ if (empty($_SERVER['HTTP_ACCEPT']))
+ {
+ // Accept everything
+ request::$accept_types['*']['*'] = 1;
+ return;
+ }
+
+ // Remove linebreaks and parse the HTTP Accept header
+ foreach (explode(',', str_replace(array("\r", "\n"), '', $_SERVER['HTTP_ACCEPT'])) as $accept_entry)
+ {
+ // Explode each entry in content type and possible quality factor
+ $accept_entry = explode(';', trim($accept_entry), 2);
+
+ // Explode each content type (e.g. "text/html")
+ $type = explode('/', $accept_entry[0], 2);
+
+ // Skip invalid content types
+ if ( ! isset($type[1]))
+ continue;
+
+ // Assume a default quality factor of 1 if no custom q value found
+ $q = (isset($accept_entry[1]) AND preg_match('~\bq\s*+=\s*+([.0-9]+)~', $accept_entry[1], $match)) ? (float) $match[1] : 1;
+
+ // Populate accept_types array
+ if ( ! isset(request::$accept_types[$type[0]][$type[1]]) OR $q > request::$accept_types[$type[0]][$type[1]])
+ {
+ request::$accept_types[$type[0]][$type[1]] = $q;
+ }
+ }
+ }
+
+} // End request
\ No newline at end of file
diff --git a/Server/system/helpers/security.php b/Server/system/helpers/security.php
new file mode 100644
index 0000000..cd48d2e
--- /dev/null
+++ b/Server/system/helpers/security.php
@@ -0,0 +1,47 @@
+xss_clean($str);
+ }
+
+ /**
+ * Remove image tags from a string.
+ *
+ * @param string string to sanitize
+ * @return string
+ */
+ public static function strip_image_tags($str)
+ {
+ return preg_replace('# \s]*)["\']?[^>]*)?>#is', '$1', $str);
+ }
+
+ /**
+ * Remove PHP tags from a string.
+ *
+ * @param string string to sanitize
+ * @return string
+ */
+ public static function encode_php_tags($str)
+ {
+ return str_replace(array('', '?>'), array('<?', '?>'), $str);
+ }
+
+} // End security
\ No newline at end of file
diff --git a/Server/system/helpers/text.php b/Server/system/helpers/text.php
new file mode 100644
index 0000000..d0e573e
--- /dev/null
+++ b/Server/system/helpers/text.php
@@ -0,0 +1,410 @@
+ 1)
+ {
+ if (ctype_alpha($str))
+ {
+ // Add a random digit
+ $str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57));
+ }
+ elseif (ctype_digit($str))
+ {
+ // Add a random letter
+ $str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90));
+ }
+ }
+
+ return $str;
+ }
+
+ /**
+ * Reduces multiple slashes in a string to single slashes.
+ *
+ * @param string string to reduce slashes of
+ * @return string
+ */
+ public static function reduce_slashes($str)
+ {
+ return preg_replace('#(? $badword)
+ {
+ $badwords[$key] = str_replace('\*', '\S*?', preg_quote((string) $badword));
+ }
+
+ $regex = '('.implode('|', $badwords).')';
+
+ if ($replace_partial_words == TRUE)
+ {
+ // Just using \b isn't sufficient when we need to replace a badword that already contains word boundaries itself
+ $regex = '(?<=\b|\s|^)'.$regex.'(?=\b|\s|$)';
+ }
+
+ $regex = '!'.$regex.'!ui';
+
+ if (utf8::strlen($replacement) == 1)
+ {
+ $regex .= 'e';
+ return preg_replace($regex, 'str_repeat($replacement, utf8::strlen(\'$1\'))', $str);
+ }
+
+ return preg_replace($regex, $replacement, $str);
+ }
+
+ /**
+ * Finds the text that is similar between a set of words.
+ *
+ * @param array words to find similar text of
+ * @return string
+ */
+ public static function similar(array $words)
+ {
+ // First word is the word to match against
+ $word = current($words);
+
+ for ($i = 0, $max = strlen($word); $i < $max; ++$i)
+ {
+ foreach ($words as $w)
+ {
+ // Once a difference is found, break out of the loops
+ if ( ! isset($w[$i]) OR $w[$i] !== $word[$i])
+ break 2;
+ }
+ }
+
+ // Return the similar text
+ return substr($word, 0, $i);
+ }
+
+ /**
+ * Converts text email addresses and anchors into links.
+ *
+ * @param string text to auto link
+ * @return string
+ */
+ public static function auto_link($text)
+ {
+ // Auto link emails first to prevent problems with "www.domain.com@example.com"
+ return text::auto_link_urls(text::auto_link_emails($text));
+ }
+
+ /**
+ * Converts text anchors into links.
+ *
+ * @param string text to auto link
+ * @return string
+ */
+ public static function auto_link_urls($text)
+ {
+ // Finds all http/https/ftp/ftps links that are not part of an existing html anchor
+ if (preg_match_all('~\b(?)(?:ht|f)tps?://\S+(?:/|\b)~i', $text, $matches))
+ {
+ foreach ($matches[0] as $match)
+ {
+ // Replace each link with an anchor
+ $text = str_replace($match, html::anchor($match), $text);
+ }
+ }
+
+ // Find all naked www.links.com (without http://)
+ if (preg_match_all('~\b(?|58;)(?!\.)[-+_a-z0-9.]++(? and markup to text. Basically nl2br() on steroids.
+ *
+ * @param string subject
+ * @return string
+ */
+ public static function auto_p($str)
+ {
+ // Trim whitespace
+ if (($str = trim($str)) === '')
+ return '';
+
+ // Standardize newlines
+ $str = str_replace(array("\r\n", "\r"), "\n", $str);
+
+ // Trim whitespace on each line
+ $str = preg_replace('~^[ \t]+~m', '', $str);
+ $str = preg_replace('~[ \t]+$~m', '', $str);
+
+ // The following regexes only need to be executed if the string contains html
+ if ($html_found = (strpos($str, '<') !== FALSE))
+ {
+ // Elements that should not be surrounded by p tags
+ $no_p = '(?:p|div|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))';
+
+ // Put at least two linebreaks before and after $no_p elements
+ $str = preg_replace('~^<'.$no_p.'[^>]*+>~im', "\n$0", $str);
+ $str = preg_replace('~'.$no_p.'\s*+>$~im', "$0\n", $str);
+ }
+
+ // Do the magic!
+ $str = '
'.trim($str).'
';
+ $str = preg_replace('~\n{2,}~', "
\n\n", $str);
+
+ // The following regexes only need to be executed if the string contains html
+ if ($html_found !== FALSE)
+ {
+ // Remove p tags around $no_p elements
+ $str = preg_replace('~
(?=?'.$no_p.'[^>]*+>)~i', '', $str);
+ $str = preg_replace('~(?'.$no_p.'[^>]*+>)
~i', '$1', $str);
+ }
+
+ // Convert single linebreaks to
+ $str = preg_replace('~(?\n", $str);
+
+ return $str;
+ }
+
+ /**
+ * Returns human readable sizes.
+ * @see Based on original functions written by:
+ * @see Aidan Lister: http://aidanlister.com/repos/v/function.size_readable.php
+ * @see Quentin Zervaas: http://www.phpriot.com/d/code/strings/filesize-format/
+ *
+ * @param integer size in bytes
+ * @param string a definitive unit
+ * @param string the return string format
+ * @param boolean whether to use SI prefixes or IEC
+ * @return string
+ */
+ public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE)
+ {
+ // Format string
+ $format = ($format === NULL) ? '%01.2f %s' : (string) $format;
+
+ // IEC prefixes (binary)
+ if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE)
+ {
+ $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
+ $mod = 1024;
+ }
+ // SI prefixes (decimal)
+ else
+ {
+ $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
+ $mod = 1000;
+ }
+
+ // Determine unit to use
+ if (($power = array_search((string) $force_unit, $units)) === FALSE)
+ {
+ $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0;
+ }
+
+ return sprintf($format, $bytes / pow($mod, $power), $units[$power]);
+ }
+
+ /**
+ * Prevents widow words by inserting a non-breaking space between the last two words.
+ * @see http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin
+ *
+ * @param string string to remove widows from
+ * @return string
+ */
+ public static function widont($str)
+ {
+ $str = rtrim($str);
+ $space = strrpos($str, ' ');
+
+ if ($space !== FALSE)
+ {
+ $str = substr($str, 0, $space).' '.substr($str, $space + 1);
+ }
+
+ return $str;
+ }
+
+} // End text
\ No newline at end of file
diff --git a/Server/system/helpers/upload.php b/Server/system/helpers/upload.php
new file mode 100644
index 0000000..422e9e8
--- /dev/null
+++ b/Server/system/helpers/upload.php
@@ -0,0 +1,162 @@
+ 'Refresh',
+ '300' => 'Multiple Choices',
+ '301' => 'Moved Permanently',
+ '302' => 'Found',
+ '303' => 'See Other',
+ '304' => 'Not Modified',
+ '305' => 'Use Proxy',
+ '307' => 'Temporary Redirect'
+ );
+
+ // Validate the method and default to 302
+ $method = isset($codes[$method]) ? (string) $method : '302';
+
+ if ($method === '300')
+ {
+ $uri = (array) $uri;
+
+ $output = '';
+ foreach ($uri as $link)
+ {
+ $output .= ''.html::anchor($link).' ';
+ }
+ $output .= ' ';
+
+ // The first URI will be used for the Location header
+ $uri = $uri[0];
+ }
+ else
+ {
+ $output = ''.html::anchor($uri).'
';
+ }
+
+ // Run the redirect event
+ Event::run('system.redirect', $uri);
+
+ if (strpos($uri, '://') === FALSE)
+ {
+ // HTTP headers expect absolute URLs
+ $uri = url::site($uri, request::protocol());
+ }
+
+ if ($method === 'refresh')
+ {
+ header('Refresh: 0; url='.$uri);
+ }
+ else
+ {
+ header('HTTP/1.1 '.$method.' '.$codes[$method]);
+ header('Location: '.$uri);
+ }
+
+ // We are about to exit, so run the send_headers event
+ Event::run('system.send_headers');
+
+ exit(''.$method.' - '.$codes[$method].' '.$output);
+ }
+
+} // End url
\ No newline at end of file
diff --git a/Server/system/helpers/valid.php b/Server/system/helpers/valid.php
new file mode 100644
index 0000000..8a3583b
--- /dev/null
+++ b/Server/system/helpers/valid.php
@@ -0,0 +1,338 @@
+= 0; $i -= 2)
+ {
+ // Add up every 2nd digit, starting from the right
+ $checksum += $number[$i];
+ }
+
+ for ($i = $length - 2; $i >= 0; $i -= 2)
+ {
+ // Add up every 2nd digit doubled, starting from the right
+ $double = $number[$i] * 2;
+
+ // Subtract 9 from the double where value is greater than 10
+ $checksum += ($double >= 10) ? $double - 9 : $double;
+ }
+
+ // If the checksum is a multiple of 10, the number is valid
+ return ($checksum % 10 === 0);
+ }
+
+ /**
+ * Checks if a phone number is valid.
+ *
+ * @param string phone number to check
+ * @return boolean
+ */
+ public static function phone($number, $lengths = NULL)
+ {
+ if ( ! is_array($lengths))
+ {
+ $lengths = array(7,10,11);
+ }
+
+ // Remove all non-digit characters from the number
+ $number = preg_replace('/\D+/', '', $number);
+
+ // Check if the number is within range
+ return in_array(strlen($number), $lengths);
+ }
+
+ /**
+ * Tests if a string is a valid date string.
+ *
+ * @param string date to check
+ * @return boolean
+ */
+ public static function date($str)
+ {
+ return (strtotime($str) !== FALSE);
+ }
+
+ /**
+ * Checks whether a string consists of alphabetical characters only.
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function alpha($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^\pL++$/uD', (string) $str)
+ : ctype_alpha((string) $str);
+ }
+
+ /**
+ * Checks whether a string consists of alphabetical characters and numbers only.
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function alpha_numeric($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^[\pL\pN]++$/uD', (string) $str)
+ : ctype_alnum((string) $str);
+ }
+
+ /**
+ * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only.
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function alpha_dash($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^[-\pL\pN_]++$/uD', (string) $str)
+ : (bool) preg_match('/^[-a-z0-9_]++$/iD', (string) $str);
+ }
+
+ /**
+ * Checks whether a string consists of digits only (no dots or dashes).
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function digit($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^\pN++$/uD', (string) $str)
+ : ctype_digit((string) $str);
+ }
+
+ /**
+ * Checks whether a string is a valid number (negative and decimal numbers allowed).
+ *
+ * @see Uses locale conversion to allow decimal point to be locale specific.
+ * @see http://www.php.net/manual/en/function.localeconv.php
+ *
+ * @param string input string
+ * @return boolean
+ */
+ public static function numeric($str)
+ {
+ // Use localeconv to set the decimal_point value: Usually a comma or period.
+ $locale = localeconv();
+ return (bool) preg_match('/^-?[0-9'.$locale['decimal_point'].']++$/D', (string) $str);
+ }
+
+ /**
+ * Checks whether a string is a valid text. Letters, numbers, whitespace,
+ * dashes, periods, and underscores are allowed.
+ *
+ * @param string text to check
+ * @return boolean
+ */
+ public static function standard_text($str)
+ {
+ // pL matches letters
+ // pN matches numbers
+ // pZ matches whitespace
+ // pPc matches underscores
+ // pPd matches dashes
+ // pPo matches normal puncuation
+ return (bool) preg_match('/^[\pL\pN\pZ\p{Pc}\p{Pd}\p{Po}]++$/uD', (string) $str);
+ }
+
+ /**
+ * Checks if a string is a proper decimal format. The format array can be
+ * used to specify a decimal length, or a number and decimal length, eg:
+ * array(2) would force the number to have 2 decimal places, array(4,2)
+ * would force the number to have 4 digits and 2 decimal places.
+ *
+ * @param string input string
+ * @param array decimal format: y or x,y
+ * @return boolean
+ */
+ public static function decimal($str, $format = NULL)
+ {
+ // Create the pattern
+ $pattern = '/^[0-9]%s\.[0-9]%s$/';
+
+ if ( ! empty($format))
+ {
+ if (count($format) > 1)
+ {
+ // Use the format for number and decimal length
+ $pattern = sprintf($pattern, '{'.$format[0].'}', '{'.$format[1].'}');
+ }
+ elseif (count($format) > 0)
+ {
+ // Use the format as decimal length
+ $pattern = sprintf($pattern, '+', '{'.$format[0].'}');
+ }
+ }
+ else
+ {
+ // No format
+ $pattern = sprintf($pattern, '+', '+');
+ }
+
+ return (bool) preg_match($pattern, (string) $str);
+ }
+
+} // End valid
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/cache.php b/Server/system/i18n/en_US/cache.php
new file mode 100644
index 0000000..bef0279
--- /dev/null
+++ b/Server/system/i18n/en_US/cache.php
@@ -0,0 +1,10 @@
+ 'The %s group is not defined in your configuration.',
+ 'extension_not_loaded' => 'The %s PHP extension must be loaded to use this driver.',
+ 'unwritable' => 'The configured storage location, %s, is not writable.',
+ 'resources' => 'Caching of resources is impossible, because resources cannot be serialized.',
+ 'driver_error' => '%s',
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/calendar.php b/Server/system/i18n/en_US/calendar.php
new file mode 100644
index 0000000..21dad22
--- /dev/null
+++ b/Server/system/i18n/en_US/calendar.php
@@ -0,0 +1,59 @@
+ 'Su',
+ 'mo' => 'Mo',
+ 'tu' => 'Tu',
+ 'we' => 'We',
+ 'th' => 'Th',
+ 'fr' => 'Fr',
+ 'sa' => 'Sa',
+
+ // Short day names
+ 'sun' => 'Sun',
+ 'mon' => 'Mon',
+ 'tue' => 'Tue',
+ 'wed' => 'Wed',
+ 'thu' => 'Thu',
+ 'fri' => 'Fri',
+ 'sat' => 'Sat',
+
+ // Long day names
+ 'sunday' => 'Sunday',
+ 'monday' => 'Monday',
+ 'tuesday' => 'Tuesday',
+ 'wednesday' => 'Wednesday',
+ 'thursday' => 'Thursday',
+ 'friday' => 'Friday',
+ 'saturday' => 'Saturday',
+
+ // Short month names
+ 'jan' => 'Jan',
+ 'feb' => 'Feb',
+ 'mar' => 'Mar',
+ 'apr' => 'Apr',
+ 'may' => 'May',
+ 'jun' => 'Jun',
+ 'jul' => 'Jul',
+ 'aug' => 'Aug',
+ 'sep' => 'Sep',
+ 'oct' => 'Oct',
+ 'nov' => 'Nov',
+ 'dec' => 'Dec',
+
+ // Long month names
+ 'january' => 'January',
+ 'february' => 'February',
+ 'march' => 'March',
+ 'april' => 'April',
+ 'mayl' => 'May',
+ 'june' => 'June',
+ 'july' => 'July',
+ 'august' => 'August',
+ 'september' => 'September',
+ 'october' => 'October',
+ 'november' => 'November',
+ 'december' => 'December'
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/captcha.php b/Server/system/i18n/en_US/captcha.php
new file mode 100644
index 0000000..6040471
--- /dev/null
+++ b/Server/system/i18n/en_US/captcha.php
@@ -0,0 +1,33 @@
+ 'The specified file, %s, was not found. Please verify that files exist by using file_exists() before using them.',
+ 'requires_GD2' => 'The Captcha library requires GD2 with FreeType support. Please see http://php.net/gd_info for more information.',
+
+ // Words of varying length for the Captcha_Word_Driver to pick from
+ // Note: use only alphanumeric characters
+ 'words' => array
+ (
+ 'cd', 'tv', 'it', 'to', 'be', 'or',
+ 'sun', 'car', 'dog', 'bed', 'kid', 'egg',
+ 'bike', 'tree', 'bath', 'roof', 'road', 'hair',
+ 'hello', 'world', 'earth', 'beard', 'chess', 'water',
+ 'barber', 'bakery', 'banana', 'market', 'purple', 'writer',
+ 'america', 'release', 'playing', 'working', 'foreign', 'general',
+ 'aircraft', 'computer', 'laughter', 'alphabet', 'kangaroo', 'spelling',
+ 'architect', 'president', 'cockroach', 'encounter', 'terrorism', 'cylinders',
+ ),
+
+ // Riddles for the Captcha_Riddle_Driver to pick from
+ // Note: use only alphanumeric characters
+ 'riddles' => array
+ (
+ array('Do you hate spam? (yes or no)', 'yes'),
+ array('Are you a robot? (yes or no)', 'no'),
+ array('Fire is... (hot or cold)', 'hot'),
+ array('The season after fall is...', 'winter'),
+ array('Which day of the week is it today?', strftime('%A')),
+ array('Which month of the year are we in?', strftime('%B')),
+ ),
+);
diff --git a/Server/system/i18n/en_US/core.php b/Server/system/i18n/en_US/core.php
new file mode 100644
index 0000000..9711b7c
--- /dev/null
+++ b/Server/system/i18n/en_US/core.php
@@ -0,0 +1,34 @@
+ 'There can be only one instance of Kohana per page request',
+ 'uncaught_exception' => 'Uncaught %s: %s in file %s on line %s',
+ 'invalid_method' => 'Invalid method %s called in %s',
+ 'invalid_property' => 'The %s property does not exist in the %s class.',
+ 'log_dir_unwritable' => 'The log directory is not writable: %s',
+ 'resource_not_found' => 'The requested %s, %s, could not be found',
+ 'invalid_filetype' => 'The requested filetype, .%s, is not allowed in your view configuration file',
+ 'view_set_filename' => 'You must set the the view filename before calling render',
+ 'no_default_route' => 'Please set a default route in config/routes.php',
+ 'no_controller' => 'Kohana was not able to determine a controller to process this request: %s',
+ 'page_not_found' => 'The page you requested, %s, could not be found.',
+ 'stats_footer' => 'Loaded in {execution_time} seconds, using {memory_usage} of memory. Generated by Kohana v{kohana_version}.',
+ 'error_file_line' => '%s [%s]: ',
+ 'stack_trace' => 'Stack Trace',
+ 'generic_error' => 'Unable to Complete Request',
+ 'errors_disabled' => 'You can go to the home page or try again .',
+
+ // Drivers
+ 'driver_implements' => 'The %s driver for the %s library must implement the %s interface',
+ 'driver_not_found' => 'The %s driver for the %s library could not be found',
+
+ // Resource names
+ 'config' => 'config file',
+ 'controller' => 'controller',
+ 'helper' => 'helper',
+ 'library' => 'library',
+ 'driver' => 'driver',
+ 'model' => 'model',
+ 'view' => 'view',
+);
diff --git a/Server/system/i18n/en_US/database.php b/Server/system/i18n/en_US/database.php
new file mode 100644
index 0000000..172e5c9
--- /dev/null
+++ b/Server/system/i18n/en_US/database.php
@@ -0,0 +1,15 @@
+ 'The %s group is not defined in your configuration.',
+ 'error' => 'There was an SQL error: %s',
+ 'connection' => 'There was an error connecting to the database: %s',
+ 'invalid_dsn' => 'The DSN you supplied is not valid: %s',
+ 'must_use_set' => 'You must set a SET clause for your query.',
+ 'must_use_where' => 'You must set a WHERE clause for your query.',
+ 'must_use_table' => 'You must set a database table for your query.',
+ 'table_not_found' => 'Table %s does not exist in your database.',
+ 'not_implemented' => 'The method you called, %s, is not supported by this driver.',
+ 'result_read_only' => 'Query results are read only.'
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/encrypt.php b/Server/system/i18n/en_US/encrypt.php
new file mode 100644
index 0000000..9afd620
--- /dev/null
+++ b/Server/system/i18n/en_US/encrypt.php
@@ -0,0 +1,8 @@
+ 'The %s group is not defined in your configuration.',
+ 'requires_mcrypt' => 'To use the Encrypt library, mcrypt must be enabled in your PHP installation',
+ 'no_encryption_key' => 'To use the Encrypt library, you must set an encryption key in your config file'
+);
diff --git a/Server/system/i18n/en_US/errors.php b/Server/system/i18n/en_US/errors.php
new file mode 100644
index 0000000..83341a2
--- /dev/null
+++ b/Server/system/i18n/en_US/errors.php
@@ -0,0 +1,16 @@
+ array( 1, 'Framework Error', 'Please check the Kohana documentation for information about the following error.'),
+ E_PAGE_NOT_FOUND => array( 1, 'Page Not Found', 'The requested page was not found. It may have moved, been deleted, or archived.'),
+ E_DATABASE_ERROR => array( 1, 'Database Error', 'A database error occurred while performing the requested procedure. Please review the database error below for more information.'),
+ E_RECOVERABLE_ERROR => array( 1, 'Recoverable Error', 'An error was detected which prevented the loading of this page. If this problem persists, please contact the website administrator.'),
+ E_ERROR => array( 1, 'Fatal Error', ''),
+ E_USER_ERROR => array( 1, 'Fatal Error', ''),
+ E_PARSE => array( 1, 'Syntax Error', ''),
+ E_WARNING => array( 1, 'Warning Message', ''),
+ E_USER_WARNING => array( 1, 'Warning Message', ''),
+ E_STRICT => array( 2, 'Strict Mode Error', ''),
+ E_NOTICE => array( 2, 'Runtime Message', ''),
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/event.php b/Server/system/i18n/en_US/event.php
new file mode 100644
index 0000000..282a0a2
--- /dev/null
+++ b/Server/system/i18n/en_US/event.php
@@ -0,0 +1,7 @@
+ 'Attempt to attach invalid subject %s to %s failed: Subjects must extend the Event_Subject class',
+ 'invalid_observer' => 'Attempt to attach invalid observer %s to %s failed: Observers must extend the Event_Observer class',
+);
diff --git a/Server/system/i18n/en_US/image.php b/Server/system/i18n/en_US/image.php
new file mode 100644
index 0000000..9f18493
--- /dev/null
+++ b/Server/system/i18n/en_US/image.php
@@ -0,0 +1,33 @@
+ 'The Image library requires the getimagesize() PHP function, which is not available in your installation.',
+ 'unsupported_method' => 'Your configured driver does not support the %s image transformation.',
+ 'file_not_found' => 'The specified image, %s, was not found. Please verify that images exist by using file_exists() before manipulating them.',
+ 'type_not_allowed' => 'The specified image, %s, is not an allowed image type.',
+ 'invalid_width' => 'The width you specified, %s, is not valid.',
+ 'invalid_height' => 'The height you specified, %s, is not valid.',
+ 'invalid_dimensions' => 'The dimensions specified for %s are not valid.',
+ 'invalid_master' => 'The master dimension specified is not valid.',
+ 'invalid_flip' => 'The flip direction specified is not valid.',
+ 'directory_unwritable' => 'The specified directory, %s, is not writable.',
+
+ // ImageMagick specific messages
+ 'imagemagick' => array
+ (
+ 'not_found' => 'The ImageMagick directory specified does not contain a required program, %s.',
+ ),
+
+ // GraphicsMagick specific messages
+ 'graphicsmagick' => array
+ (
+ 'not_found' => 'The GraphicsMagick directory specified does not contain a required program, %s.',
+ ),
+
+ // GD specific messages
+ 'gd' => array
+ (
+ 'requires_v2' => 'The Image library requires GD2. Please see http://php.net/gd_info for more information.',
+ ),
+);
diff --git a/Server/system/i18n/en_US/orm.php b/Server/system/i18n/en_US/orm.php
new file mode 100644
index 0000000..3c5720b
--- /dev/null
+++ b/Server/system/i18n/en_US/orm.php
@@ -0,0 +1,3 @@
+ 'The %s group is not defined in your pagination configuration.',
+ 'page' => 'page',
+ 'pages' => 'pages',
+ 'item' => 'item',
+ 'items' => 'items',
+ 'of' => 'of',
+ 'first' => 'first',
+ 'last' => 'last',
+ 'previous' => 'previous',
+ 'next' => 'next',
+);
diff --git a/Server/system/i18n/en_US/profiler.php b/Server/system/i18n/en_US/profiler.php
new file mode 100644
index 0000000..a39c2f5
--- /dev/null
+++ b/Server/system/i18n/en_US/profiler.php
@@ -0,0 +1,15 @@
+ 'Benchmarks',
+ 'post_data' => 'Post Data',
+ 'no_post' => 'No post data',
+ 'session_data' => 'Session Data',
+ 'no_session' => 'No session data',
+ 'queries' => 'Database Queries',
+ 'no_queries' => 'No queries',
+ 'no_database' => 'Database not loaded',
+ 'cookie_data' => 'Cookie Data',
+ 'no_cookie' => 'No cookie data',
+);
diff --git a/Server/system/i18n/en_US/session.php b/Server/system/i18n/en_US/session.php
new file mode 100644
index 0000000..ee781c6
--- /dev/null
+++ b/Server/system/i18n/en_US/session.php
@@ -0,0 +1,6 @@
+ 'The session_name, %s, is invalid. It must contain only alphanumeric characters and underscores. Also at least one letter must be present.',
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/swift.php b/Server/system/i18n/en_US/swift.php
new file mode 100644
index 0000000..249c4a8
--- /dev/null
+++ b/Server/system/i18n/en_US/swift.php
@@ -0,0 +1,6 @@
+ 'An error occurred while sending the email message.'
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/upload.php b/Server/system/i18n/en_US/upload.php
new file mode 100644
index 0000000..7f6e216
--- /dev/null
+++ b/Server/system/i18n/en_US/upload.php
@@ -0,0 +1,6 @@
+ 'The upload destination folder, %s, does not appear to be writable.',
+);
\ No newline at end of file
diff --git a/Server/system/i18n/en_US/validation.php b/Server/system/i18n/en_US/validation.php
new file mode 100644
index 0000000..d98e548
--- /dev/null
+++ b/Server/system/i18n/en_US/validation.php
@@ -0,0 +1,41 @@
+ 'Invalid validation rule used: %s',
+ 'i18n_array' => 'The %s i18n key must be an array to be used with the in_lang rule',
+ 'not_callable' => 'Callback %s used for Validation is not callable',
+
+ // General errors
+ 'unknown_error' => 'Unknown validation error while validating the %s field.',
+ 'required' => 'The %s field is required.',
+ 'min_length' => 'The %s field must be at least %d characters long.',
+ 'max_length' => 'The %s field must be %d characters or fewer.',
+ 'exact_length' => 'The %s field must be exactly %d characters.',
+ 'in_array' => 'The %s field must be selected from the options listed.',
+ 'matches' => 'The %s field must match the %s field.',
+ 'valid_url' => 'The %s field must contain a valid URL.',
+ 'valid_email' => 'The %s field must contain a valid email address.',
+ 'valid_ip' => 'The %s field must contain a valid IP address.',
+ 'valid_type' => 'The %s field must only contain %s characters.',
+ 'range' => 'The %s field must be between specified ranges.',
+ 'regex' => 'The %s field does not match accepted input.',
+ 'depends_on' => 'The %s field depends on the %s field.',
+
+ // Upload errors
+ 'user_aborted' => 'The %s file was aborted during upload.',
+ 'invalid_type' => 'The %s file is not an allowed file type.',
+ 'max_size' => 'The %s file you uploaded was too large. The maximum size allowed is %s.',
+ 'max_width' => 'The %s file you uploaded was too big. The maximum allowed width is %spx.',
+ 'max_height' => 'The %s file you uploaded was too big. The maximum allowed height is %spx.',
+ 'min_width' => 'The %s file you uploaded was too small. The minimum allowed width is %spx.',
+ 'min_height' => 'The %s file you uploaded was too small. The minimum allowed height is %spx.',
+
+ // Field types
+ 'alpha' => 'alphabetical',
+ 'alpha_numeric' => 'alphabetical and numeric',
+ 'alpha_dash' => 'alphabetical, dash, and underscore',
+ 'digit' => 'digit',
+ 'numeric' => 'numeric',
+);
diff --git a/Server/system/libraries/Cache.php b/Server/system/libraries/Cache.php
new file mode 100644
index 0000000..8a02a90
--- /dev/null
+++ b/Server/system/libraries/Cache.php
@@ -0,0 +1,208 @@
+config = $config;
+
+ // Set driver name
+ $driver = 'Cache_'.ucfirst($this->config['driver']).'_Driver';
+
+ // Load the driver
+ if ( ! Kohana::auto_load($driver))
+ throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this));
+
+ // Initialize the driver
+ $this->driver = new $driver($this->config['params']);
+
+ // Validate the driver
+ if ( ! ($this->driver instanceof Cache_Driver))
+ throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Cache_Driver');
+
+ Kohana::log('debug', 'Cache Library initialized');
+
+ if (Cache::$loaded !== TRUE)
+ {
+ $this->config['requests'] = (int) $this->config['requests'];
+
+ if ($this->config['requests'] > 0 AND mt_rand(1, $this->config['requests']) === 1)
+ {
+ // Do garbage collection
+ $this->driver->delete_expired();
+
+ Kohana::log('debug', 'Cache: Expired caches deleted.');
+ }
+
+ // Cache has been loaded once
+ Cache::$loaded = TRUE;
+ }
+ }
+
+ /**
+ * Fetches a cache by id. NULL is returned when a cache item is not found.
+ *
+ * @param string cache id
+ * @return mixed cached data or NULL
+ */
+ public function get($id)
+ {
+ // Sanitize the ID
+ $id = $this->sanitize_id($id);
+
+ return $this->driver->get($id);
+ }
+
+ /**
+ * Fetches all of the caches for a given tag. An empty array will be
+ * returned when no matching caches are found.
+ *
+ * @param string cache tag
+ * @return array all cache items matching the tag
+ */
+ public function find($tag)
+ {
+ return $this->driver->find($tag);
+ }
+
+ /**
+ * Set a cache item by id. Tags may also be added and a custom lifetime
+ * can be set. Non-string data is automatically serialized.
+ *
+ * @param string unique cache id
+ * @param mixed data to cache
+ * @param array|string tags for this item
+ * @param integer number of seconds until the cache expires
+ * @return boolean
+ */
+ function set($id, $data, $tags = NULL, $lifetime = NULL)
+ {
+ if (is_resource($data))
+ throw new Kohana_Exception('cache.resources');
+
+ // Sanitize the ID
+ $id = $this->sanitize_id($id);
+
+ if ($lifetime === NULL)
+ {
+ // Get the default lifetime
+ $lifetime = $this->config['lifetime'];
+ }
+
+ return $this->driver->set($id, $data, (array) $tags, $lifetime);
+ }
+
+ /**
+ * Delete a cache item by id.
+ *
+ * @param string cache id
+ * @return boolean
+ */
+ public function delete($id)
+ {
+ // Sanitize the ID
+ $id = $this->sanitize_id($id);
+
+ return $this->driver->delete($id);
+ }
+
+ /**
+ * Delete all cache items with a given tag.
+ *
+ * @param string cache tag name
+ * @return boolean
+ */
+ public function delete_tag($tag)
+ {
+ return $this->driver->delete($tag, TRUE);
+ }
+
+ /**
+ * Delete ALL cache items items.
+ *
+ * @return boolean
+ */
+ public function delete_all()
+ {
+ return $this->driver->delete(TRUE);
+ }
+
+ /**
+ * Replaces troublesome characters with underscores.
+ *
+ * @param string cache id
+ * @return string
+ */
+ protected function sanitize_id($id)
+ {
+ // Change slashes and spaces to underscores
+ return str_replace(array('/', '\\', ' '), '_', $id);
+ }
+
+} // End Cache
diff --git a/Server/system/libraries/Calendar.php b/Server/system/libraries/Calendar.php
new file mode 100644
index 0000000..193a6fb
--- /dev/null
+++ b/Server/system/libraries/Calendar.php
@@ -0,0 +1,362 @@
+ 3) ? '%A' : '%a';
+
+ // Days of the week
+ $days = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
+
+ if (Calendar::$start_monday === TRUE)
+ {
+ // Push Sunday to the end of the days
+ array_push($days, array_shift($days));
+ }
+
+ if (strpos(Kohana::config('locale.language.0'), 'en') !== 0)
+ {
+ // This is a bit awkward, but it works properly and is reliable
+ foreach ($days as $i => $day)
+ {
+ // Convert the English names to i18n names
+ $days[$i] = strftime($format, strtotime($day));
+ }
+ }
+
+ if (is_int($length) OR ctype_digit($length))
+ {
+ foreach ($days as $i => $day)
+ {
+ // Shorten the days to the expected length
+ $days[$i] = utf8::substr($day, 0, $length);
+ }
+ }
+
+ return $days;
+ }
+
+ /**
+ * Create a new Calendar instance. A month and year can be specified.
+ * By default, the current month and year are used.
+ *
+ * @param integer month number
+ * @param integer year number
+ * @return object
+ */
+ public static function factory($month = NULL, $year = NULL)
+ {
+ return new Calendar($month, $year);
+ }
+
+ /**
+ * Create a new Calendar instance. A month and year can be specified.
+ * By default, the current month and year are used.
+ *
+ * @param integer month number
+ * @param integer year number
+ * @return void
+ */
+ public function __construct($month = NULL, $year = NULL)
+ {
+ empty($month) and $month = date('n'); // Current month
+ empty($year) and $year = date('Y'); // Current year
+
+ // Set the month and year
+ $this->month = (int) $month;
+ $this->year = (int) $year;
+
+ if (Calendar::$start_monday === TRUE)
+ {
+ // Week starts on Monday
+ $this->week_start = 1;
+ }
+ }
+
+ /**
+ * Allows fetching the current month and year.
+ *
+ * @param string key to get
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ if ($key === 'month' OR $key === 'year')
+ {
+ return $this->$key;
+ }
+ }
+
+ /**
+ * Calendar_Event factory method.
+ *
+ * @param string unique name for the event
+ * @return object Calendar_Event
+ */
+ public function event($name = NULL)
+ {
+ return new Calendar_Event($this);
+ }
+
+ /**
+ * Calendar_Event factory method.
+ *
+ * @chainable
+ * @param string standard event type
+ * @return object
+ */
+ public function standard($name)
+ {
+ switch ($name)
+ {
+ case 'today':
+ // Add an event for the current day
+ $this->attach($this->event()->condition('timestamp', strtotime('today'))->add_class('today'));
+ break;
+ case 'prev-next':
+ // Add an event for padding days
+ $this->attach($this->event()->condition('current', FALSE)->add_class('prev-next'));
+ break;
+ case 'holidays':
+ // Base event
+ $event = $this->event()->condition('current', TRUE)->add_class('holiday');
+
+ // Attach New Years
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 1)->condition('day', 1));
+
+ // Attach Valentine's Day
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 2)->condition('day', 14));
+
+ // Attach St. Patrick's Day
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 3)->condition('day', 17));
+
+ // Attach Easter
+ $holiday = clone $event;
+ $this->attach($holiday->condition('easter', TRUE));
+
+ // Attach Memorial Day
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 5)->condition('day_of_week', 1)->condition('last_occurrence', TRUE));
+
+ // Attach Independance Day
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 7)->condition('day', 4));
+
+ // Attach Labor Day
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 9)->condition('day_of_week', 1)->condition('occurrence', 1));
+
+ // Attach Halloween
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 10)->condition('day', 31));
+
+ // Attach Thanksgiving
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 11)->condition('day_of_week', 4)->condition('occurrence', 4));
+
+ // Attach Christmas
+ $holiday = clone $event;
+ $this->attach($holiday->condition('month', 12)->condition('day', 25));
+ break;
+ case 'weekends':
+ // Weekend events
+ $this->attach($this->event()->condition('weekend', TRUE)->add_class('weekend'));
+ break;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns an array for use with a view. The array contains an array for
+ * each week. Each week contains 7 arrays, with a day number and status:
+ * TRUE if the day is in the month, FALSE if it is padding.
+ *
+ * @return array
+ */
+ public function weeks()
+ {
+ // First day of the month as a timestamp
+ $first = mktime(1, 0, 0, $this->month, 1, $this->year);
+
+ // Total number of days in this month
+ $total = (int) date('t', $first);
+
+ // Last day of the month as a timestamp
+ $last = mktime(1, 0, 0, $this->month, $total, $this->year);
+
+ // Make the month and week empty arrays
+ $month = $week = array();
+
+ // Number of days added. When this reaches 7, start a new week
+ $days = 0;
+ $week_number = 1;
+
+ if (($w = (int) date('w', $first) - $this->week_start) < 0)
+ {
+ $w = 6;
+ }
+
+ if ($w > 0)
+ {
+ // Number of days in the previous month
+ $n = (int) date('t', mktime(1, 0, 0, $this->month - 1, 1, $this->year));
+
+ // i = number of day, t = number of days to pad
+ for ($i = $n - $w + 1, $t = $w; $t > 0; $t--, $i++)
+ {
+ // Notify the listeners
+ $this->notify(array($this->month - 1, $i, $this->year, $week_number, FALSE));
+
+ // Add previous month padding days
+ $week[] = array($i, FALSE, $this->observed_data);
+ $days++;
+ }
+ }
+
+ // i = number of day
+ for ($i = 1; $i <= $total; $i++)
+ {
+ if ($days % 7 === 0)
+ {
+ // Start a new week
+ $month[] = $week;
+ $week = array();
+
+ $week_number++;
+ }
+
+ // Notify the listeners
+ $this->notify(array($this->month, $i, $this->year, $week_number, TRUE));
+
+ // Add days to this month
+ $week[] = array($i, TRUE, $this->observed_data);
+ $days++;
+ }
+
+ if (($w = (int) date('w', $last) - $this->week_start) < 0)
+ {
+ $w = 6;
+ }
+
+ if ($w >= 0)
+ {
+ // i = number of day, t = number of days to pad
+ for ($i = 1, $t = 6 - $w; $t > 0; $t--, $i++)
+ {
+ // Notify the listeners
+ $this->notify(array($this->month + 1, $i, $this->year, $week_number, FALSE));
+
+ // Add next month padding days
+ $week[] = array($i, FALSE, $this->observed_data);
+ }
+ }
+
+ if ( ! empty($week))
+ {
+ // Append the remaining days
+ $month[] = $week;
+ }
+
+ return $month;
+ }
+
+ /**
+ * Adds new data from an observer. All event data contains and array of CSS
+ * classes and an array of output messages.
+ *
+ * @param array observer data.
+ * @return void
+ */
+ public function add_data(array $data)
+ {
+ // Add new classes
+ $this->observed_data['classes'] += $data['classes'];
+
+ if ( ! empty($data['output']))
+ {
+ // Only add output if it's not empty
+ $this->observed_data['output'][] = $data['output'];
+ }
+ }
+
+ /**
+ * Resets the observed data and sends a notify to all attached events.
+ *
+ * @param array UNIX timestamp
+ * @return void
+ */
+ public function notify($data)
+ {
+ // Reset observed data
+ $this->observed_data = array
+ (
+ 'classes' => array(),
+ 'output' => array(),
+ );
+
+ // Send a notify
+ parent::notify($data);
+ }
+
+ /**
+ * Convert the calendar to HTML using the kohana_calendar view.
+ *
+ * @return string
+ */
+ public function render()
+ {
+ $view = new View('kohana_calendar', array
+ (
+ 'month' => $this->month,
+ 'year' => $this->year,
+ 'weeks' => $this->weeks(),
+ ));
+
+ return $view->render();
+ }
+
+ /**
+ * Magically convert this object to a string, the rendered calendar.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render();
+ }
+
+} // End Calendar
\ No newline at end of file
diff --git a/Server/system/libraries/Calendar_Event.php b/Server/system/libraries/Calendar_Event.php
new file mode 100644
index 0000000..ba9b5ad
--- /dev/null
+++ b/Server/system/libraries/Calendar_Event.php
@@ -0,0 +1,307 @@
+conditions[$key]);
+ }
+ else
+ {
+ if ($key === 'callback')
+ {
+ // Do nothing
+ }
+ elseif (in_array($key, $this->booleans))
+ {
+ // Make the value boolean
+ $value = (bool) $value;
+ }
+ else
+ {
+ // Make the value an int
+ $value = (int) $value;
+ }
+
+ $this->conditions[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a CSS class for this event. This can be called multiple times.
+ *
+ * @chainable
+ * @param string CSS class name
+ * @return object
+ */
+ public function add_class($class)
+ {
+ $this->classes[$class] = $class;
+
+ return $this;
+ }
+
+ /**
+ * Remove a CSS class for this event. This can be called multiple times.
+ *
+ * @chainable
+ * @param string CSS class name
+ * @return object
+ */
+ public function remove_class($class)
+ {
+ unset($this->classes[$class]);
+
+ return $this;
+ }
+
+ /**
+ * Set HTML output for this event.
+ *
+ * @chainable
+ * @param string HTML output
+ * @return object
+ */
+ public function output($str)
+ {
+ $this->output = $str;
+
+ return $this;
+ }
+
+ /**
+ * Add a CSS class for this event. This can be called multiple times.
+ *
+ * @chainable
+ * @param string CSS class name
+ * @return object
+ */
+ public function notify($data)
+ {
+ // Split the date and current status
+ list ($month, $day, $year, $week, $current) = $data;
+
+ // Get a timestamp for the day
+ $timestamp = mktime(0, 0, 0, $month, $day, $year);
+
+ // Date conditionals
+ $condition = array
+ (
+ 'timestamp' => (int) $timestamp,
+ 'day' => (int) date('j', $timestamp),
+ 'week' => (int) $week,
+ 'month' => (int) date('n', $timestamp),
+ 'year' => (int) date('Y', $timestamp),
+ 'day_of_week' => (int) date('w', $timestamp),
+ 'current' => (bool) $current,
+ );
+
+ // Tested conditions
+ $tested = array();
+
+ foreach ($condition as $key => $value)
+ {
+ // Timestamps need to be handled carefully
+ if($key === 'timestamp' AND isset($this->conditions['timestamp']))
+ {
+ // This adds 23 hours, 59 minutes and 59 seconds to today's timestamp, as 24 hours
+ // is classed as a new day
+ $next_day = $timestamp + 86399;
+
+ if($this->conditions['timestamp'] < $timestamp OR $this->conditions['timestamp'] > $next_day)
+ return FALSE;
+ }
+ // Test basic conditions first
+ elseif (isset($this->conditions[$key]) AND $this->conditions[$key] !== $value)
+ return FALSE;
+
+ // Condition has been tested
+ $tested[$key] = TRUE;
+ }
+
+ if (isset($this->conditions['weekend']))
+ {
+ // Weekday vs Weekend
+ $condition['weekend'] = ($condition['day_of_week'] === 0 OR $condition['day_of_week'] === 6);
+ }
+
+ if (isset($this->conditions['first_day']))
+ {
+ // First day of month
+ $condition['first_day'] = ($condition['day'] === 1);
+ }
+
+ if (isset($this->conditions['last_day']))
+ {
+ // Last day of month
+ $condition['last_day'] = ($condition['day'] === (int) date('t', $timestamp));
+ }
+
+ if (isset($this->conditions['occurrence']))
+ {
+ // Get the occurance of the current day
+ $condition['occurrence'] = $this->day_occurrence($timestamp);
+ }
+
+ if (isset($this->conditions['last_occurrence']))
+ {
+ // Test if the next occurance of this date is next month
+ $condition['last_occurrence'] = ((int) date('n', $timestamp + 604800) !== $condition['month']);
+ }
+
+ if (isset($this->conditions['easter']))
+ {
+ if ($condition['month'] === 3 OR $condition['month'] === 4)
+ {
+ // This algorithm is from Practical Astronomy With Your Calculator, 2nd Edition by Peter
+ // Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in
+ // 1876. This algorithm has also been published in the 1922 book General Astronomy by
+ // Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page
+ // 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus.
+
+ $a = $condition['year'] % 19;
+ $b = (int) ($condition['year'] / 100);
+ $c = $condition['year'] % 100;
+ $d = (int) ($b / 4);
+ $e = $b % 4;
+ $f = (int) (($b + 8) / 25);
+ $g = (int) (($b - $f + 1) / 3);
+ $h = (19 * $a + $b - $d - $g + 15) % 30;
+ $i = (int) ($c / 4);
+ $k = $c % 4;
+ $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7;
+ $m = (int) (($a + 11 * $h + 22 * $l) / 451);
+ $p = ($h + $l - 7 * $m + 114) % 31;
+
+ $month = (int) (($h + $l - 7 * $m + 114) / 31);
+ $day = $p + 1;
+
+ $condition['easter'] = ($condition['month'] === $month AND $condition['day'] === $day);
+ }
+ else
+ {
+ // Easter can only happen in March or April
+ $condition['easter'] = FALSE;
+ }
+ }
+
+ if (isset($this->conditions['callback']))
+ {
+ // Use a callback to determine validity
+ $condition['callback'] = call_user_func($this->conditions['callback'], $condition, $this);
+ }
+
+ $conditions = array_diff_key($this->conditions, $tested);
+
+ foreach ($conditions as $key => $value)
+ {
+ if ($key === 'callback')
+ {
+ // Callbacks are tested on a TRUE/FALSE basis
+ $value = TRUE;
+ }
+
+ // Test advanced conditions
+ if ($condition[$key] !== $value)
+ return FALSE;
+ }
+
+ $this->caller->add_data(array
+ (
+ 'classes' => $this->classes,
+ 'output' => $this->output,
+ ));
+ }
+
+ /**
+ * Find the week day occurrence for a specific timestamp. The occurrence is
+ * relative to the current month. For example, the second Saturday of any
+ * given month will return "2" as the occurrence. This is used in combination
+ * with the "occurrence" condition.
+ *
+ * @param integer UNIX timestamp
+ * @return integer
+ */
+ protected function day_occurrence($timestamp)
+ {
+ // Get the current month for the timestamp
+ $month = date('m', $timestamp);
+
+ // Default occurrence is one
+ $occurrence = 1;
+
+ // Reduce the timestamp by one week for each loop. This has the added
+ // benefit of preventing an infinite loop.
+ while ($timestamp -= 604800)
+ {
+ if (date('m', $timestamp) !== $month)
+ {
+ // Once the timestamp has gone into the previous month, the
+ // proper occurrence has been found.
+ return $occurrence;
+ }
+
+ // Increment the occurrence
+ $occurrence++;
+ }
+ }
+
+} // End Calendar Event
diff --git a/Server/system/libraries/Captcha.php b/Server/system/libraries/Captcha.php
new file mode 100644
index 0000000..f5f2b46
--- /dev/null
+++ b/Server/system/libraries/Captcha.php
@@ -0,0 +1,279 @@
+ 'basic',
+ 'width' => 150,
+ 'height' => 50,
+ 'complexity' => 4,
+ 'background' => '',
+ 'fontpath' => '',
+ 'fonts' => array(),
+ 'promote' => FALSE,
+ );
+
+ /**
+ * Singleton instance of Captcha.
+ *
+ * @return object
+ */
+ public static function instance()
+ {
+ // Create the instance if it does not exist
+ empty(Captcha::$instance) and new Captcha;
+
+ return Captcha::$instance;
+ }
+
+ /**
+ * Constructs and returns a new Captcha object.
+ *
+ * @param string config group name
+ * @return object
+ */
+ public static function factory($group = NULL)
+ {
+ return new Captcha($group);
+ }
+
+ /**
+ * Constructs a new Captcha object.
+ *
+ * @throws Kohana_Exception
+ * @param string config group name
+ * @return void
+ */
+ public function __construct($group = NULL)
+ {
+ // Create a singleton instance once
+ empty(Captcha::$instance) and Captcha::$instance = $this;
+
+ // No config group name given
+ if ( ! is_string($group))
+ {
+ $group = 'default';
+ }
+
+ // Load and validate config group
+ if ( ! is_array($config = Kohana::config('captcha.'.$group)))
+ throw new Kohana_Exception('captcha.undefined_group', $group);
+
+ // All captcha config groups inherit default config group
+ if ($group !== 'default')
+ {
+ // Load and validate default config group
+ if ( ! is_array($default = Kohana::config('captcha.default')))
+ throw new Kohana_Exception('captcha.undefined_group', 'default');
+
+ // Merge config group with default config group
+ $config += $default;
+ }
+
+ // Assign config values to the object
+ foreach ($config as $key => $value)
+ {
+ if (array_key_exists($key, Captcha::$config))
+ {
+ Captcha::$config[$key] = $value;
+ }
+ }
+
+ // Store the config group name as well, so the drivers can access it
+ Captcha::$config['group'] = $group;
+
+ // If using a background image, check if it exists
+ if ( ! empty($config['background']))
+ {
+ Captcha::$config['background'] = str_replace('\\', '/', realpath($config['background']));
+
+ if ( ! is_file(Captcha::$config['background']))
+ throw new Kohana_Exception('captcha.file_not_found', Captcha::$config['background']);
+ }
+
+ // If using any fonts, check if they exist
+ if ( ! empty($config['fonts']))
+ {
+ Captcha::$config['fontpath'] = str_replace('\\', '/', realpath($config['fontpath'])).'/';
+
+ foreach ($config['fonts'] as $font)
+ {
+ if ( ! is_file(Captcha::$config['fontpath'].$font))
+ throw new Kohana_Exception('captcha.file_not_found', Captcha::$config['fontpath'].$font);
+ }
+ }
+
+ // Set driver name
+ $driver = 'Captcha_'.ucfirst($config['style']).'_Driver';
+
+ // Load the driver
+ if ( ! Kohana::auto_load($driver))
+ throw new Kohana_Exception('core.driver_not_found', $config['style'], get_class($this));
+
+ // Initialize the driver
+ $this->driver = new $driver;
+
+ // Validate the driver
+ if ( ! ($this->driver instanceof Captcha_Driver))
+ throw new Kohana_Exception('core.driver_implements', $config['style'], get_class($this), 'Captcha_Driver');
+
+ Kohana::log('debug', 'Captcha Library initialized');
+ }
+
+ /**
+ * Validates a Captcha response and updates response counter.
+ *
+ * @param string captcha response
+ * @return boolean
+ */
+ public static function valid($response)
+ {
+ // Maximum one count per page load
+ static $counted;
+
+ // User has been promoted, always TRUE and don't count anymore
+ if (Captcha::instance()->promoted())
+ return TRUE;
+
+ // Challenge result
+ $result = (bool) Captcha::instance()->driver->valid($response);
+
+ // Increment response counter
+ if ($counted !== TRUE)
+ {
+ $counted = TRUE;
+
+ // Valid response
+ if ($result === TRUE)
+ {
+ Captcha::instance()->valid_count(Session::instance()->get('captcha_valid_count') + 1);
+ }
+ // Invalid response
+ else
+ {
+ Captcha::instance()->invalid_count(Session::instance()->get('captcha_invalid_count') + 1);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Gets or sets the number of valid Captcha responses for this session.
+ *
+ * @param integer new counter value
+ * @param boolean trigger invalid counter (for internal use only)
+ * @return integer counter value
+ */
+ public function valid_count($new_count = NULL, $invalid = FALSE)
+ {
+ // Pick the right session to use
+ $session = ($invalid === TRUE) ? 'captcha_invalid_count' : 'captcha_valid_count';
+
+ // Update counter
+ if ($new_count !== NULL)
+ {
+ $new_count = (int) $new_count;
+
+ // Reset counter = delete session
+ if ($new_count < 1)
+ {
+ Session::instance()->delete($session);
+ }
+ // Set counter to new value
+ else
+ {
+ Session::instance()->set($session, (int) $new_count);
+ }
+
+ // Return new count
+ return (int) $new_count;
+ }
+
+ // Return current count
+ return (int) Session::instance()->get($session);
+ }
+
+ /**
+ * Gets or sets the number of invalid Captcha responses for this session.
+ *
+ * @param integer new counter value
+ * @return integer counter value
+ */
+ public function invalid_count($new_count = NULL)
+ {
+ return $this->valid_count($new_count, TRUE);
+ }
+
+ /**
+ * Resets the Captcha response counters and removes the count sessions.
+ *
+ * @return void
+ */
+ public function reset_count()
+ {
+ $this->valid_count(0);
+ $this->valid_count(0, TRUE);
+ }
+
+ /**
+ * Checks whether user has been promoted after having given enough valid responses.
+ *
+ * @param integer valid response count threshold
+ * @return boolean
+ */
+ public function promoted($threshold = NULL)
+ {
+ // Promotion has been disabled
+ if (Captcha::$config['promote'] === FALSE)
+ return FALSE;
+
+ // Use the config threshold
+ if ($threshold === NULL)
+ {
+ $threshold = Captcha::$config['promote'];
+ }
+
+ // Compare the valid response count to the threshold
+ return ($this->valid_count() >= $threshold);
+ }
+
+ /**
+ * Returns or outputs the Captcha challenge.
+ *
+ * @param boolean TRUE to output html, e.g.
+ * @return mixed html string or void
+ */
+ public function render($html = TRUE)
+ {
+ return $this->driver->render($html);
+ }
+
+ /**
+ * Magically outputs the Captcha challenge.
+ *
+ * @return mixed
+ */
+ public function __toString()
+ {
+ return $this->render();
+ }
+
+} // End Captcha Class
\ No newline at end of file
diff --git a/Server/system/libraries/Controller.php b/Server/system/libraries/Controller.php
new file mode 100644
index 0000000..2f64c21
--- /dev/null
+++ b/Server/system/libraries/Controller.php
@@ -0,0 +1,86 @@
+uri = URI::instance();
+
+ // Input should always be available
+ $this->input = Input::instance();
+ }
+
+ /**
+ * Handles methods that do not exist.
+ *
+ * @param string method name
+ * @param array arguments
+ * @return void
+ */
+ public function __call($method, $args)
+ {
+ // Default to showing a 404 page
+ Event::run('system.404');
+ }
+
+ /**
+ * Includes a View within the controller scope.
+ *
+ * @param string view filename
+ * @param array array of view variables
+ * @return string
+ */
+ public function _kohana_load_view($kohana_view_filename, $kohana_input_data)
+ {
+ if ($kohana_view_filename == '')
+ return;
+
+ // Buffering on
+ ob_start();
+
+ // Import the view variables to local namespace
+ extract($kohana_input_data, EXTR_SKIP);
+
+ // Views are straight HTML pages with embedded PHP, so importing them
+ // this way insures that $this can be accessed as if the user was in
+ // the controller, which gives the easiest access to libraries in views
+ try
+ {
+ include $kohana_view_filename;
+ }
+ catch (Exception $e)
+ {
+ ob_end_clean();
+ throw $e;
+ }
+
+ // Fetch the output and close the buffer
+ return ob_get_clean();
+ }
+
+} // End Controller Class
\ No newline at end of file
diff --git a/Server/system/libraries/Database.php b/Server/system/libraries/Database.php
new file mode 100644
index 0000000..6267f63
--- /dev/null
+++ b/Server/system/libraries/Database.php
@@ -0,0 +1,1444 @@
+ TRUE,
+ 'persistent' => FALSE,
+ 'connection' => '',
+ 'character_set' => 'utf8',
+ 'table_prefix' => '',
+ 'object' => TRUE,
+ 'cache' => FALSE,
+ 'escape' => TRUE,
+ );
+
+ // Database driver object
+ protected $driver;
+ protected $link;
+
+ // Un-compiled parts of the SQL query
+ protected $select = array();
+ protected $set = array();
+ protected $from = array();
+ protected $join = array();
+ protected $where = array();
+ protected $orderby = array();
+ protected $order = array();
+ protected $groupby = array();
+ protected $having = array();
+ protected $distinct = FALSE;
+ protected $limit = FALSE;
+ protected $offset = FALSE;
+ protected $last_query = '';
+
+ // Stack of queries for push/pop
+ protected $query_history = array();
+
+ /**
+ * Returns a singleton instance of Database.
+ *
+ * @param mixed configuration array or DSN
+ * @return Database_Core
+ */
+ public static function & instance($name = 'default', $config = NULL)
+ {
+ if ( ! isset(Database::$instances[$name]))
+ {
+ // Create a new instance
+ Database::$instances[$name] = new Database($config === NULL ? $name : $config);
+ }
+
+ return Database::$instances[$name];
+ }
+
+ /**
+ * Returns the name of a given database instance.
+ *
+ * @param Database instance of Database
+ * @return string
+ */
+ public static function instance_name(Database $db)
+ {
+ return array_search($db, Database::$instances, TRUE);
+ }
+
+ /**
+ * Sets up the database configuration, loads the Database_Driver.
+ *
+ * @throws Kohana_Database_Exception
+ */
+ public function __construct($config = array())
+ {
+ if (empty($config))
+ {
+ // Load the default group
+ $config = Kohana::config('database.default');
+ }
+ elseif (is_array($config) AND count($config) > 0)
+ {
+ if ( ! array_key_exists('connection', $config))
+ {
+ $config = array('connection' => $config);
+ }
+ }
+ elseif (is_string($config))
+ {
+ // The config is a DSN string
+ if (strpos($config, '://') !== FALSE)
+ {
+ $config = array('connection' => $config);
+ }
+ // The config is a group name
+ else
+ {
+ $name = $config;
+
+ // Test the config group name
+ if (($config = Kohana::config('database.'.$config)) === NULL)
+ throw new Kohana_Database_Exception('database.undefined_group', $name);
+ }
+ }
+
+ // Merge the default config with the passed config
+ $this->config = array_merge($this->config, $config);
+
+ if (is_string($this->config['connection']))
+ {
+ // Make sure the connection is valid
+ if (strpos($this->config['connection'], '://') === FALSE)
+ throw new Kohana_Database_Exception('database.invalid_dsn', $this->config['connection']);
+
+ // Parse the DSN, creating an array to hold the connection parameters
+ $db = array
+ (
+ 'type' => FALSE,
+ 'user' => FALSE,
+ 'pass' => FALSE,
+ 'host' => FALSE,
+ 'port' => FALSE,
+ 'socket' => FALSE,
+ 'database' => FALSE
+ );
+
+ // Get the protocol and arguments
+ list ($db['type'], $connection) = explode('://', $this->config['connection'], 2);
+
+ if (strpos($connection, '@') !== FALSE)
+ {
+ // Get the username and password
+ list ($db['pass'], $connection) = explode('@', $connection, 2);
+ // Check if a password is supplied
+ $logindata = explode(':', $db['pass'], 2);
+ $db['pass'] = (count($logindata) > 1) ? $logindata[1] : '';
+ $db['user'] = $logindata[0];
+
+ // Prepare for finding the database
+ $connection = explode('/', $connection);
+
+ // Find the database name
+ $db['database'] = array_pop($connection);
+
+ // Reset connection string
+ $connection = implode('/', $connection);
+
+ // Find the socket
+ if (preg_match('/^unix\([^)]++\)/', $connection))
+ {
+ // This one is a little hairy: we explode based on the end of
+ // the socket, removing the 'unix(' from the connection string
+ list ($db['socket'], $connection) = explode(')', substr($connection, 5), 2);
+ }
+ elseif (strpos($connection, ':') !== FALSE)
+ {
+ // Fetch the host and port name
+ list ($db['host'], $db['port']) = explode(':', $connection, 2);
+ }
+ else
+ {
+ $db['host'] = $connection;
+ }
+ }
+ else
+ {
+ // File connection
+ $connection = explode('/', $connection);
+
+ // Find database file name
+ $db['database'] = array_pop($connection);
+
+ // Find database directory name
+ $db['socket'] = implode('/', $connection).'/';
+ }
+
+ // Reset the connection array to the database config
+ $this->config['connection'] = $db;
+ }
+ // Set driver name
+ $driver = 'Database_'.ucfirst($this->config['connection']['type']).'_Driver';
+
+ // Load the driver
+ if ( ! Kohana::auto_load($driver))
+ throw new Kohana_Database_Exception('core.driver_not_found', $this->config['connection']['type'], get_class($this));
+
+ // Initialize the driver
+ $this->driver = new $driver($this->config);
+
+ // Validate the driver
+ if ( ! ($this->driver instanceof Database_Driver))
+ throw new Kohana_Database_Exception('core.driver_implements', $this->config['connection']['type'], get_class($this), 'Database_Driver');
+
+ Kohana::log('debug', 'Database Library initialized');
+ }
+
+ /**
+ * Simple connect method to get the database queries up and running.
+ *
+ * @return void
+ */
+ public function connect()
+ {
+ // A link can be a resource or an object
+ if ( ! is_resource($this->link) AND ! is_object($this->link))
+ {
+ $this->link = $this->driver->connect();
+ if ( ! is_resource($this->link) AND ! is_object($this->link))
+ throw new Kohana_Database_Exception('database.connection', $this->driver->show_error());
+
+ // Clear password after successful connect
+ $this->config['connection']['pass'] = NULL;
+ }
+ }
+
+ /**
+ * Runs a query into the driver and returns the result.
+ *
+ * @param string SQL query to execute
+ * @return Database_Result
+ */
+ public function query($sql = '')
+ {
+ if ($sql == '') return FALSE;
+
+ // No link? Connect!
+ $this->link or $this->connect();
+
+ // Start the benchmark
+ $start = microtime(TRUE);
+
+ if (func_num_args() > 1) //if we have more than one argument ($sql)
+ {
+ $argv = func_get_args();
+ $binds = (is_array(next($argv))) ? current($argv) : array_slice($argv, 1);
+ }
+
+ // Compile binds if needed
+ if (isset($binds))
+ {
+ $sql = $this->compile_binds($sql, $binds);
+ }
+
+ // Fetch the result
+ $result = $this->driver->query($this->last_query = $sql);
+
+ // Stop the benchmark
+ $stop = microtime(TRUE);
+
+ if ($this->config['benchmark'] == TRUE)
+ {
+ // Benchmark the query
+ Database::$benchmarks[] = array('query' => $sql, 'time' => $stop - $start, 'rows' => count($result));
+ }
+
+ return $result;
+ }
+
+ /**
+ * Selects the column names for a database query.
+ *
+ * @param string string or array of column names to select
+ * @return Database_Core This Database object.
+ */
+ public function select($sql = '*')
+ {
+ if (func_num_args() > 1)
+ {
+ $sql = func_get_args();
+ }
+ elseif (is_string($sql))
+ {
+ $sql = explode(',', $sql);
+ }
+ else
+ {
+ $sql = (array) $sql;
+ }
+
+ foreach ($sql as $val)
+ {
+ if (($val = trim($val)) === '') continue;
+
+ if (strpos($val, '(') === FALSE AND $val !== '*')
+ {
+ if (preg_match('/^DISTINCT\s++(.+)$/i', $val, $matches))
+ {
+ // Only prepend with table prefix if table name is specified
+ $val = (strpos($matches[1], '.') !== FALSE) ? $this->config['table_prefix'].$matches[1] : $matches[1];
+
+ $this->distinct = TRUE;
+ }
+ else
+ {
+ $val = (strpos($val, '.') !== FALSE) ? $this->config['table_prefix'].$val : $val;
+ }
+
+ $val = $this->driver->escape_column($val);
+ }
+
+ $this->select[] = $val;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the from table(s) for a database query.
+ *
+ * @param string string or array of tables to select
+ * @return Database_Core This Database object.
+ */
+ public function from($sql)
+ {
+ if (func_num_args() > 1)
+ {
+ $sql = func_get_args();
+ }
+ elseif (is_string($sql))
+ {
+ $sql = explode(',', $sql);
+ }
+ else
+ {
+ $sql = array($sql);
+ }
+
+ foreach ($sql as $val)
+ {
+ if (is_string($val))
+ {
+ if (($val = trim($val)) === '') continue;
+
+ // TODO: Temporary solution, this should be moved to database driver (AS is checked for twice)
+ if (stripos($val, ' AS ') !== FALSE)
+ {
+ $val = str_ireplace(' AS ', ' AS ', $val);
+
+ list($table, $alias) = explode(' AS ', $val);
+
+ // Attach prefix to both sides of the AS
+ $val = $this->config['table_prefix'].$table.' AS '.$this->config['table_prefix'].$alias;
+ }
+ else
+ {
+ $val = $this->config['table_prefix'].$val;
+ }
+ }
+
+ $this->from[] = $val;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Generates the JOIN portion of the query.
+ *
+ * @param string table name
+ * @param string|array where key or array of key => value pairs
+ * @param string where value
+ * @param string type of join
+ * @return Database_Core This Database object.
+ */
+ public function join($table, $key, $value = NULL, $type = '')
+ {
+ $join = array();
+
+ if ( ! empty($type))
+ {
+ $type = strtoupper(trim($type));
+
+ if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE))
+ {
+ $type = '';
+ }
+ else
+ {
+ $type .= ' ';
+ }
+ }
+
+ $cond = array();
+ $keys = is_array($key) ? $key : array($key => $value);
+ foreach ($keys as $key => $value)
+ {
+ $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key;
+
+ if (is_string($value))
+ {
+ // Only escape if it's a string
+ $value = $this->driver->escape_column($this->config['table_prefix'].$value);
+ }
+
+ $cond[] = $this->driver->where($key, $value, 'AND ', count($cond), FALSE);
+ }
+
+ if ( ! is_array($this->join))
+ {
+ $this->join = array();
+ }
+
+ if ( ! is_array($table))
+ {
+ $table = array($table);
+ }
+
+ foreach ($table as $t)
+ {
+ if (is_string($t))
+ {
+ // TODO: Temporary solution, this should be moved to database driver (AS is checked for twice)
+ if (stripos($t, ' AS ') !== FALSE)
+ {
+ $t = str_ireplace(' AS ', ' AS ', $t);
+
+ list($table, $alias) = explode(' AS ', $t);
+
+ // Attach prefix to both sides of the AS
+ $t = $this->config['table_prefix'].$table.' AS '.$this->config['table_prefix'].$alias;
+ }
+ else
+ {
+ $t = $this->config['table_prefix'].$t;
+ }
+ }
+
+ $join['tables'][] = $this->driver->escape_column($t);
+ }
+
+ $join['conditions'] = '('.trim(implode(' ', $cond)).')';
+ $join['type'] = $type;
+
+ $this->join[] = $join;
+
+ return $this;
+ }
+
+
+ /**
+ * Selects the where(s) for a database query.
+ *
+ * @param string|array key name or array of key => value pairs
+ * @param string value to match with key
+ * @param boolean disable quoting of WHERE clause
+ * @return Database_Core This Database object.
+ */
+ public function where($key, $value = NULL, $quote = TRUE)
+ {
+ $quote = (func_num_args() < 2 AND ! is_array($key)) ? -1 : $quote;
+ if (is_object($key))
+ {
+ $keys = array((string) $key => '');
+ }
+ elseif ( ! is_array($key))
+ {
+ $keys = array($key => $value);
+ }
+ else
+ {
+ $keys = $key;
+ }
+
+ foreach ($keys as $key => $value)
+ {
+ $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key;
+ $this->where[] = $this->driver->where($key, $value, 'AND ', count($this->where), $quote);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the or where(s) for a database query.
+ *
+ * @param string|array key name or array of key => value pairs
+ * @param string value to match with key
+ * @param boolean disable quoting of WHERE clause
+ * @return Database_Core This Database object.
+ */
+ public function orwhere($key, $value = NULL, $quote = TRUE)
+ {
+ $quote = (func_num_args() < 2 AND ! is_array($key)) ? -1 : $quote;
+ if (is_object($key))
+ {
+ $keys = array((string) $key => '');
+ }
+ elseif ( ! is_array($key))
+ {
+ $keys = array($key => $value);
+ }
+ else
+ {
+ $keys = $key;
+ }
+
+ foreach ($keys as $key => $value)
+ {
+ $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key;
+ $this->where[] = $this->driver->where($key, $value, 'OR ', count($this->where), $quote);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the like(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string like value to match with field
+ * @param boolean automatically add starting and ending wildcards
+ * @return Database_Core This Database object.
+ */
+ public function like($field, $match = '', $auto = TRUE)
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->like($field, $match, $auto, 'AND ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the or like(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string like value to match with field
+ * @param boolean automatically add starting and ending wildcards
+ * @return Database_Core This Database object.
+ */
+ public function orlike($field, $match = '', $auto = TRUE)
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->like($field, $match, $auto, 'OR ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the not like(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string like value to match with field
+ * @param boolean automatically add starting and ending wildcards
+ * @return Database_Core This Database object.
+ */
+ public function notlike($field, $match = '', $auto = TRUE)
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->notlike($field, $match, $auto, 'AND ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the or not like(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string like value to match with field
+ * @return Database_Core This Database object.
+ */
+ public function ornotlike($field, $match = '', $auto = TRUE)
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->notlike($field, $match, $auto, 'OR ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the like(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string like value to match with field
+ * @return Database_Core This Database object.
+ */
+ public function regex($field, $match = '')
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->regex($field, $match, 'AND ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the or like(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string like value to match with field
+ * @return Database_Core This Database object.
+ */
+ public function orregex($field, $match = '')
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->regex($field, $match, 'OR ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the not regex(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string regex value to match with field
+ * @return Database_Core This Database object.
+ */
+ public function notregex($field, $match = '')
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->notregex($field, $match, 'AND ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the or not regex(s) for a database query.
+ *
+ * @param string|array field name or array of field => match pairs
+ * @param string regex value to match with field
+ * @return Database_Core This Database object.
+ */
+ public function ornotregex($field, $match = '')
+ {
+ $fields = is_array($field) ? $field : array($field => $match);
+
+ foreach ($fields as $field => $match)
+ {
+ $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field;
+ $this->where[] = $this->driver->notregex($field, $match, 'OR ', count($this->where));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Chooses the column to group by in a select query.
+ *
+ * @param string column name to group by
+ * @return Database_Core This Database object.
+ */
+ public function groupby($by)
+ {
+ if ( ! is_array($by))
+ {
+ $by = explode(',', (string) $by);
+ }
+
+ foreach ($by as $val)
+ {
+ $val = trim($val);
+
+ if ($val != '')
+ {
+ // Add the table prefix if we are using table.column names
+ if(strpos($val, '.'))
+ {
+ $val = $this->config['table_prefix'].$val;
+ }
+
+ $this->groupby[] = $this->driver->escape_column($val);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the having(s) for a database query.
+ *
+ * @param string|array key name or array of key => value pairs
+ * @param string value to match with key
+ * @param boolean disable quoting of WHERE clause
+ * @return Database_Core This Database object.
+ */
+ public function having($key, $value = '', $quote = TRUE)
+ {
+ $this->having[] = $this->driver->where($key, $value, 'AND', count($this->having), TRUE);
+ return $this;
+ }
+
+ /**
+ * Selects the or having(s) for a database query.
+ *
+ * @param string|array key name or array of key => value pairs
+ * @param string value to match with key
+ * @param boolean disable quoting of WHERE clause
+ * @return Database_Core This Database object.
+ */
+ public function orhaving($key, $value = '', $quote = TRUE)
+ {
+ $this->having[] = $this->driver->where($key, $value, 'OR', count($this->having), TRUE);
+ return $this;
+ }
+
+ /**
+ * Chooses which column(s) to order the select query by.
+ *
+ * @param string|array column(s) to order on, can be an array, single column, or comma seperated list of columns
+ * @param string direction of the order
+ * @return Database_Core This Database object.
+ */
+ public function orderby($orderby, $direction = NULL)
+ {
+ if ( ! is_array($orderby))
+ {
+ $orderby = array($orderby => $direction);
+ }
+
+ foreach ($orderby as $column => $direction)
+ {
+ $direction = strtoupper(trim($direction));
+
+ // Add a direction if the provided one isn't valid
+ if ( ! in_array($direction, array('ASC', 'DESC', 'RAND()', 'RANDOM()', 'NULL')))
+ {
+ $direction = 'ASC';
+ }
+
+ // Add the table prefix if a table.column was passed
+ if (strpos($column, '.'))
+ {
+ $column = $this->config['table_prefix'].$column;
+ }
+
+ $this->orderby[] = $this->driver->escape_column($column).' '.$direction;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Selects the limit section of a query.
+ *
+ * @param integer number of rows to limit result to
+ * @param integer offset in result to start returning rows from
+ * @return Database_Core This Database object.
+ */
+ public function limit($limit, $offset = NULL)
+ {
+ $this->limit = (int) $limit;
+
+ if ($offset !== NULL OR ! is_int($this->offset))
+ {
+ $this->offset($offset);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the offset portion of a query.
+ *
+ * @param integer offset value
+ * @return Database_Core This Database object.
+ */
+ public function offset($value)
+ {
+ $this->offset = (int) $value;
+
+ return $this;
+ }
+
+ /**
+ * Allows key/value pairs to be set for inserting or updating.
+ *
+ * @param string|array key name or array of key => value pairs
+ * @param string value to match with key
+ * @return Database_Core This Database object.
+ */
+ public function set($key, $value = '')
+ {
+ if ( ! is_array($key))
+ {
+ $key = array($key => $value);
+ }
+
+ foreach ($key as $k => $v)
+ {
+ // Add a table prefix if the column includes the table.
+ if (strpos($k, '.'))
+ $k = $this->config['table_prefix'].$k;
+
+ $this->set[$k] = $this->driver->escape($v);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Compiles the select statement based on the other functions called and runs the query.
+ *
+ * @param string table name
+ * @param string limit clause
+ * @param string offset clause
+ * @return Database_Result
+ */
+ public function get($table = '', $limit = NULL, $offset = NULL)
+ {
+ if ($table != '')
+ {
+ $this->from($table);
+ }
+
+ if ( ! is_null($limit))
+ {
+ $this->limit($limit, $offset);
+ }
+
+ $sql = $this->driver->compile_select(get_object_vars($this));
+
+ $this->reset_select();
+
+ $result = $this->query($sql);
+
+ $this->last_query = $sql;
+
+ return $result;
+ }
+
+ /**
+ * Compiles the select statement based on the other functions called and runs the query.
+ *
+ * @param string table name
+ * @param array where clause
+ * @param string limit clause
+ * @param string offset clause
+ * @return Database_Core This Database object.
+ */
+ public function getwhere($table = '', $where = NULL, $limit = NULL, $offset = NULL)
+ {
+ if ($table != '')
+ {
+ $this->from($table);
+ }
+
+ if ( ! is_null($where))
+ {
+ $this->where($where);
+ }
+
+ if ( ! is_null($limit))
+ {
+ $this->limit($limit, $offset);
+ }
+
+ $sql = $this->driver->compile_select(get_object_vars($this));
+
+ $this->reset_select();
+
+ $result = $this->query($sql);
+
+ return $result;
+ }
+
+ /**
+ * Compiles the select statement based on the other functions called and returns the query string.
+ *
+ * @param string table name
+ * @param string limit clause
+ * @param string offset clause
+ * @return string sql string
+ */
+ public function compile($table = '', $limit = NULL, $offset = NULL)
+ {
+ if ($table != '')
+ {
+ $this->from($table);
+ }
+
+ if ( ! is_null($limit))
+ {
+ $this->limit($limit, $offset);
+ }
+
+ $sql = $this->driver->compile_select(get_object_vars($this));
+
+ $this->reset_select();
+
+ return $sql;
+ }
+
+ /**
+ * Compiles an insert string and runs the query.
+ *
+ * @param string table name
+ * @param array array of key/value pairs to insert
+ * @return Database_Result Query result
+ */
+ public function insert($table = '', $set = NULL)
+ {
+ if ( ! is_null($set))
+ {
+ $this->set($set);
+ }
+
+ if ($this->set == NULL)
+ throw new Kohana_Database_Exception('database.must_use_set');
+
+ if ($table == '')
+ {
+ if ( ! isset($this->from[0]))
+ throw new Kohana_Database_Exception('database.must_use_table');
+
+ $table = $this->from[0];
+ }
+
+ // If caching is enabled, clear the cache before inserting
+ ($this->config['cache'] === TRUE) and $this->clear_cache();
+
+ $sql = $this->driver->insert($this->config['table_prefix'].$table, array_keys($this->set), array_values($this->set));
+
+ $this->reset_write();
+
+ return $this->query($sql);
+ }
+
+ /**
+ * Adds an "IN" condition to the where clause
+ *
+ * @param string Name of the column being examined
+ * @param mixed An array or string to match against
+ * @param bool Generate a NOT IN clause instead
+ * @return Database_Core This Database object.
+ */
+ public function in($field, $values, $not = FALSE)
+ {
+ if (is_array($values))
+ {
+ $escaped_values = array();
+ foreach ($values as $v)
+ {
+ if (is_numeric($v))
+ {
+ $escaped_values[] = $v;
+ }
+ else
+ {
+ $escaped_values[] = "'".$this->driver->escape_str($v)."'";
+ }
+ }
+ $values = implode(",", $escaped_values);
+ }
+
+ $where = $this->driver->escape_column(((strpos($field,'.') !== FALSE) ? $this->config['table_prefix'] : ''). $field).' '.($not === TRUE ? 'NOT ' : '').'IN ('.$values.')';
+ $this->where[] = $this->driver->where($where, '', 'AND ', count($this->where), -1);
+
+ return $this;
+ }
+
+ /**
+ * Adds a "NOT IN" condition to the where clause
+ *
+ * @param string Name of the column being examined
+ * @param mixed An array or string to match against
+ * @return Database_Core This Database object.
+ */
+ public function notin($field, $values)
+ {
+ return $this->in($field, $values, TRUE);
+ }
+
+ /**
+ * Compiles a merge string and runs the query.
+ *
+ * @param string table name
+ * @param array array of key/value pairs to merge
+ * @return Database_Result Query result
+ */
+ public function merge($table = '', $set = NULL)
+ {
+ if ( ! is_null($set))
+ {
+ $this->set($set);
+ }
+
+ if ($this->set == NULL)
+ throw new Kohana_Database_Exception('database.must_use_set');
+
+ if ($table == '')
+ {
+ if ( ! isset($this->from[0]))
+ throw new Kohana_Database_Exception('database.must_use_table');
+
+ $table = $this->from[0];
+ }
+
+ $sql = $this->driver->merge($this->config['table_prefix'].$table, array_keys($this->set), array_values($this->set));
+
+ $this->reset_write();
+ return $this->query($sql);
+ }
+
+ /**
+ * Compiles an update string and runs the query.
+ *
+ * @param string table name
+ * @param array associative array of update values
+ * @param array where clause
+ * @return Database_Result Query result
+ */
+ public function update($table = '', $set = NULL, $where = NULL)
+ {
+ if ( is_array($set))
+ {
+ $this->set($set);
+ }
+
+ if ( ! is_null($where))
+ {
+ $this->where($where);
+ }
+
+ if ($this->set == FALSE)
+ throw new Kohana_Database_Exception('database.must_use_set');
+
+ if ($table == '')
+ {
+ if ( ! isset($this->from[0]))
+ throw new Kohana_Database_Exception('database.must_use_table');
+
+ $table = $this->from[0];
+ }
+
+ $sql = $this->driver->update($this->config['table_prefix'].$table, $this->set, $this->where);
+
+ $this->reset_write();
+ return $this->query($sql);
+ }
+
+ /**
+ * Compiles a delete string and runs the query.
+ *
+ * @param string table name
+ * @param array where clause
+ * @return Database_Result Query result
+ */
+ public function delete($table = '', $where = NULL)
+ {
+ if ($table == '')
+ {
+ if ( ! isset($this->from[0]))
+ throw new Kohana_Database_Exception('database.must_use_table');
+
+ $table = $this->from[0];
+ }
+ else
+ {
+ $table = $this->config['table_prefix'].$table;
+ }
+
+ if (! is_null($where))
+ {
+ $this->where($where);
+ }
+
+ if (count($this->where) < 1)
+ throw new Kohana_Database_Exception('database.must_use_where');
+
+ $sql = $this->driver->delete($table, $this->where);
+
+ $this->reset_write();
+ return $this->query($sql);
+ }
+
+ /**
+ * Returns the last query run.
+ *
+ * @return string SQL
+ */
+ public function last_query()
+ {
+ return $this->last_query;
+ }
+
+ /**
+ * Count query records.
+ *
+ * @param string table name
+ * @param array where clause
+ * @return integer
+ */
+ public function count_records($table = FALSE, $where = NULL)
+ {
+ if (count($this->from) < 1)
+ {
+ if ($table == FALSE)
+ throw new Kohana_Database_Exception('database.must_use_table');
+
+ $this->from($table);
+ }
+
+ if ($where !== NULL)
+ {
+ $this->where($where);
+ }
+
+ $query = $this->select('COUNT(*) AS '.$this->escape_column('records_found'))->get()->result(TRUE);
+
+ return (int) $query->current()->records_found;
+ }
+
+ /**
+ * Resets all private select variables.
+ *
+ * @return void
+ */
+ protected function reset_select()
+ {
+ $this->select = array();
+ $this->from = array();
+ $this->join = array();
+ $this->where = array();
+ $this->orderby = array();
+ $this->groupby = array();
+ $this->having = array();
+ $this->distinct = FALSE;
+ $this->limit = FALSE;
+ $this->offset = FALSE;
+ }
+
+ /**
+ * Resets all private insert and update variables.
+ *
+ * @return void
+ */
+ protected function reset_write()
+ {
+ $this->set = array();
+ $this->from = array();
+ $this->where = array();
+ }
+
+ /**
+ * Lists all the tables in the current database.
+ *
+ * @return array
+ */
+ public function list_tables()
+ {
+ $this->link or $this->connect();
+
+ return $this->driver->list_tables();
+ }
+
+ /**
+ * See if a table exists in the database.
+ *
+ * @param string table name
+ * @param boolean True to attach table prefix
+ * @return boolean
+ */
+ public function table_exists($table_name, $prefix = TRUE)
+ {
+ if ($prefix)
+ return in_array($this->config['table_prefix'].$table_name, $this->list_tables());
+ else
+ return in_array($table_name, $this->list_tables());
+ }
+
+ /**
+ * Combine a SQL statement with the bind values. Used for safe queries.
+ *
+ * @param string query to bind to the values
+ * @param array array of values to bind to the query
+ * @return string
+ */
+ public function compile_binds($sql, $binds)
+ {
+ foreach ((array) $binds as $val)
+ {
+ // If the SQL contains no more bind marks ("?"), we're done.
+ if (($next_bind_pos = strpos($sql, '?')) === FALSE)
+ break;
+
+ // Properly escape the bind value.
+ $val = $this->driver->escape($val);
+
+ // Temporarily replace possible bind marks ("?"), in the bind value itself, with a placeholder.
+ $val = str_replace('?', '{%B%}', $val);
+
+ // Replace the first bind mark ("?") with its corresponding value.
+ $sql = substr($sql, 0, $next_bind_pos).$val.substr($sql, $next_bind_pos + 1);
+ }
+
+ // Restore placeholders.
+ return str_replace('{%B%}', '?', $sql);
+ }
+
+ /**
+ * Get the field data for a database table, along with the field's attributes.
+ *
+ * @param string table name
+ * @return array
+ */
+ public function field_data($table = '')
+ {
+ $this->link or $this->connect();
+
+ return $this->driver->field_data($this->config['table_prefix'].$table);
+ }
+
+ /**
+ * Get the field data for a database table, along with the field's attributes.
+ *
+ * @param string table name
+ * @return array
+ */
+ public function list_fields($table = '')
+ {
+ $this->link or $this->connect();
+
+ return $this->driver->list_fields($this->config['table_prefix'].$table);
+ }
+
+ /**
+ * Escapes a value for a query.
+ *
+ * @param mixed value to escape
+ * @return string
+ */
+ public function escape($value)
+ {
+ return $this->driver->escape($value);
+ }
+
+ /**
+ * Escapes a string for a query.
+ *
+ * @param string string to escape
+ * @return string
+ */
+ public function escape_str($str)
+ {
+ return $this->driver->escape_str($str);
+ }
+
+ /**
+ * Escapes a table name for a query.
+ *
+ * @param string string to escape
+ * @return string
+ */
+ public function escape_table($table)
+ {
+ return $this->driver->escape_table($table);
+ }
+
+ /**
+ * Escapes a column name for a query.
+ *
+ * @param string string to escape
+ * @return string
+ */
+ public function escape_column($table)
+ {
+ return $this->driver->escape_column($table);
+ }
+
+ /**
+ * Returns table prefix of current configuration.
+ *
+ * @return string
+ */
+ public function table_prefix()
+ {
+ return $this->config['table_prefix'];
+ }
+
+ /**
+ * Clears the query cache.
+ *
+ * @param string|TRUE clear cache by SQL statement or TRUE for last query
+ * @return Database_Core This Database object.
+ */
+ public function clear_cache($sql = NULL)
+ {
+ if ($sql === TRUE)
+ {
+ $this->driver->clear_cache($this->last_query);
+ }
+ elseif (is_string($sql))
+ {
+ $this->driver->clear_cache($sql);
+ }
+ else
+ {
+ $this->driver->clear_cache();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Pushes existing query space onto the query stack. Use push
+ * and pop to prevent queries from clashing before they are
+ * executed
+ *
+ * @return Database_Core This Databaes object
+ */
+ public function push()
+ {
+ array_push($this->query_history, array(
+ $this->select,
+ $this->from,
+ $this->join,
+ $this->where,
+ $this->orderby,
+ $this->order,
+ $this->groupby,
+ $this->having,
+ $this->distinct,
+ $this->limit,
+ $this->offset
+ ));
+
+ $this->reset_select();
+
+ return $this;
+ }
+
+ /**
+ * Pops from query stack into the current query space.
+ *
+ * @return Database_Core This Databaes object
+ */
+ public function pop()
+ {
+ if (count($this->query_history) == 0)
+ {
+ // No history
+ return $this;
+ }
+
+ list(
+ $this->select,
+ $this->from,
+ $this->join,
+ $this->where,
+ $this->orderby,
+ $this->order,
+ $this->groupby,
+ $this->having,
+ $this->distinct,
+ $this->limit,
+ $this->offset
+ ) = array_pop($this->query_history);
+
+ return $this;
+ }
+
+ /**
+ * Count the number of records in the last query, without LIMIT or OFFSET applied.
+ *
+ * @return integer
+ */
+ public function count_last_query()
+ {
+ if ($sql = $this->last_query())
+ {
+ if (stripos($sql, 'LIMIT') !== FALSE)
+ {
+ // Remove LIMIT from the SQL
+ $sql = preg_replace('/\sLIMIT\s+[^a-z]+/i', ' ', $sql);
+ }
+
+ if (stripos($sql, 'OFFSET') !== FALSE)
+ {
+ // Remove OFFSET from the SQL
+ $sql = preg_replace('/\sOFFSET\s+\d+/i', '', $sql);
+ }
+
+ // Get the total rows from the last query executed
+ $result = $this->query
+ (
+ 'SELECT COUNT(*) AS '.$this->escape_column('total_rows').' '.
+ 'FROM ('.trim($sql).') AS '.$this->escape_table('counted_results')
+ );
+
+ // Return the total number of rows from the query
+ return (int) $result->current()->total_rows;
+ }
+
+ return FALSE;
+ }
+
+} // End Database Class
+
+
+/**
+ * Sets the code for a Database exception.
+ */
+class Kohana_Database_Exception extends Kohana_Exception {
+
+ protected $code = E_DATABASE_ERROR;
+
+} // End Kohana Database Exception
diff --git a/Server/system/libraries/Database_Expression.php b/Server/system/libraries/Database_Expression.php
new file mode 100644
index 0000000..940a636
--- /dev/null
+++ b/Server/system/libraries/Database_Expression.php
@@ -0,0 +1,26 @@
+expression = $expression;
+ }
+
+ public function __toString()
+ {
+ return (string) $this->expression;
+ }
+
+} // End Database Expr Class
\ No newline at end of file
diff --git a/Server/system/libraries/Encrypt.php b/Server/system/libraries/Encrypt.php
new file mode 100644
index 0000000..3d564f9
--- /dev/null
+++ b/Server/system/libraries/Encrypt.php
@@ -0,0 +1,164 @@
+ $size)
+ {
+ // Shorten the key to the maximum size
+ $config['key'] = substr($config['key'], 0, $size);
+ }
+
+ // Find the initialization vector size
+ $config['iv_size'] = mcrypt_get_iv_size($config['cipher'], $config['mode']);
+
+ // Cache the config in the object
+ $this->config = $config;
+
+ Kohana::log('debug', 'Encrypt Library initialized');
+ }
+
+ /**
+ * Encrypts a string and returns an encrypted string that can be decoded.
+ *
+ * @param string data to be encrypted
+ * @return string encrypted data
+ */
+ public function encode($data)
+ {
+ // Set the rand type if it has not already been set
+ if (Encrypt::$rand === NULL)
+ {
+ if (KOHANA_IS_WIN)
+ {
+ // Windows only supports the system random number generator
+ Encrypt::$rand = MCRYPT_RAND;
+ }
+ else
+ {
+ if (defined('MCRYPT_DEV_URANDOM'))
+ {
+ // Use /dev/urandom
+ Encrypt::$rand = MCRYPT_DEV_URANDOM;
+ }
+ elseif (defined('MCRYPT_DEV_RANDOM'))
+ {
+ // Use /dev/random
+ Encrypt::$rand = MCRYPT_DEV_RANDOM;
+ }
+ else
+ {
+ // Use the system random number generator
+ Encrypt::$rand = MCRYPT_RAND;
+ }
+ }
+ }
+
+ if (Encrypt::$rand === MCRYPT_RAND)
+ {
+ // The system random number generator must always be seeded each
+ // time it is used, or it will not produce true random results
+ mt_srand();
+ }
+
+ // Create a random initialization vector of the proper size for the current cipher
+ $iv = mcrypt_create_iv($this->config['iv_size'], Encrypt::$rand);
+
+ // Encrypt the data using the configured options and generated iv
+ $data = mcrypt_encrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv);
+
+ // Use base64 encoding to convert to a string
+ return base64_encode($iv.$data);
+ }
+
+ /**
+ * Decrypts an encoded string back to its original value.
+ *
+ * @param string encoded string to be decrypted
+ * @return string decrypted data
+ */
+ public function decode($data)
+ {
+ // Convert the data back to binary
+ $data = base64_decode($data);
+
+ // Extract the initialization vector from the data
+ $iv = substr($data, 0, $this->config['iv_size']);
+
+ // Remove the iv from the data
+ $data = substr($data, $this->config['iv_size']);
+
+ // Return the decrypted data, trimming the \0 padding bytes from the end of the data
+ return rtrim(mcrypt_decrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv), "\0");
+ }
+
+} // End Encrypt
diff --git a/Server/system/libraries/Event_Observer.php b/Server/system/libraries/Event_Observer.php
new file mode 100644
index 0000000..086c8a2
--- /dev/null
+++ b/Server/system/libraries/Event_Observer.php
@@ -0,0 +1,70 @@
+update($caller);
+ }
+
+ /**
+ * Updates the observer subject with a new caller.
+ *
+ * @chainable
+ * @param object Event_Subject
+ * @return object
+ */
+ public function update(SplSubject $caller)
+ {
+ if ( ! ($caller instanceof Event_Subject))
+ throw new Kohana_Exception('event.invalid_subject', get_class($caller), get_class($this));
+
+ // Update the caller
+ $this->caller = $caller;
+
+ return $this;
+ }
+
+ /**
+ * Detaches this observer from the subject.
+ *
+ * @chainable
+ * @return object
+ */
+ public function remove()
+ {
+ // Detach this observer from the caller
+ $this->caller->detach($this);
+
+ return $this;
+ }
+
+ /**
+ * Notify the observer of a new message. This function must be defined in
+ * all observers and must take exactly one parameter of any type.
+ *
+ * @param mixed message string, object, or array
+ * @return void
+ */
+ abstract public function notify($message);
+
+} // End Event Observer
\ No newline at end of file
diff --git a/Server/system/libraries/Event_Subject.php b/Server/system/libraries/Event_Subject.php
new file mode 100644
index 0000000..d1ccc54
--- /dev/null
+++ b/Server/system/libraries/Event_Subject.php
@@ -0,0 +1,67 @@
+listeners[spl_object_hash($obj)] = $obj;
+
+ return $this;
+ }
+
+ /**
+ * Detach an observer from the object.
+ *
+ * @chainable
+ * @param object Event_Observer
+ * @return object
+ */
+ public function detach(SplObserver $obj)
+ {
+ // Remove the listener
+ unset($this->listeners[spl_object_hash($obj)]);
+
+ return $this;
+ }
+
+ /**
+ * Notify all attached observers of a new message.
+ *
+ * @chainable
+ * @param mixed message string, object, or array
+ * @return object
+ */
+ public function notify($message)
+ {
+ foreach ($this->listeners as $obj)
+ {
+ $obj->notify($message);
+ }
+
+ return $this;
+ }
+
+} // End Event Subject
\ No newline at end of file
diff --git a/Server/system/libraries/Image.php b/Server/system/libraries/Image.php
new file mode 100644
index 0000000..2de0658
--- /dev/null
+++ b/Server/system/libraries/Image.php
@@ -0,0 +1,431 @@
+ 'gif',
+ IMAGETYPE_JPEG => 'jpg',
+ IMAGETYPE_PNG => 'png',
+ IMAGETYPE_TIFF_II => 'tiff',
+ IMAGETYPE_TIFF_MM => 'tiff',
+ );
+
+ // Driver instance
+ protected $driver;
+
+ // Driver actions
+ protected $actions = array();
+
+ // Reference to the current image filename
+ protected $image = '';
+
+ /**
+ * Creates a new Image instance and returns it.
+ *
+ * @param string filename of image
+ * @param array non-default configurations
+ * @return object
+ */
+ public static function factory($image, $config = NULL)
+ {
+ return new Image($image, $config);
+ }
+
+ /**
+ * Creates a new image editor instance.
+ *
+ * @throws Kohana_Exception
+ * @param string filename of image
+ * @param array non-default configurations
+ * @return void
+ */
+ public function __construct($image, $config = NULL)
+ {
+ static $check;
+
+ // Make the check exactly once
+ ($check === NULL) and $check = function_exists('getimagesize');
+
+ if ($check === FALSE)
+ throw new Kohana_Exception('image.getimagesize_missing');
+
+ // Check to make sure the image exists
+ if ( ! is_file($image))
+ throw new Kohana_Exception('image.file_not_found', $image);
+
+ // Disable error reporting, to prevent PHP warnings
+ $ER = error_reporting(0);
+
+ // Fetch the image size and mime type
+ $image_info = getimagesize($image);
+
+ // Turn on error reporting again
+ error_reporting($ER);
+
+ // Make sure that the image is readable and valid
+ if ( ! is_array($image_info) OR count($image_info) < 3)
+ throw new Kohana_Exception('image.file_unreadable', $image);
+
+ // Check to make sure the image type is allowed
+ if ( ! isset(Image::$allowed_types[$image_info[2]]))
+ throw new Kohana_Exception('image.type_not_allowed', $image);
+
+ // Image has been validated, load it
+ $this->image = array
+ (
+ 'file' => str_replace('\\', '/', realpath($image)),
+ 'width' => $image_info[0],
+ 'height' => $image_info[1],
+ 'type' => $image_info[2],
+ 'ext' => Image::$allowed_types[$image_info[2]],
+ 'mime' => $image_info['mime']
+ );
+
+ // Load configuration
+ $this->config = (array) $config + Kohana::config('image');
+
+ // Set driver class name
+ $driver = 'Image_'.ucfirst($this->config['driver']).'_Driver';
+
+ // Load the driver
+ if ( ! Kohana::auto_load($driver))
+ throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this));
+
+ // Initialize the driver
+ $this->driver = new $driver($this->config['params']);
+
+ // Validate the driver
+ if ( ! ($this->driver instanceof Image_Driver))
+ throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Image_Driver');
+ }
+
+ /**
+ * Handles retrieval of pre-save image properties
+ *
+ * @param string property name
+ * @return mixed
+ */
+ public function __get($property)
+ {
+ if (isset($this->image[$property]))
+ {
+ return $this->image[$property];
+ }
+ else
+ {
+ throw new Kohana_Exception('core.invalid_property', $property, get_class($this));
+ }
+ }
+
+ /**
+ * Resize an image to a specific width and height. By default, Kohana will
+ * maintain the aspect ratio using the width as the master dimension. If you
+ * wish to use height as master dim, set $image->master_dim = Image::HEIGHT
+ * This method is chainable.
+ *
+ * @throws Kohana_Exception
+ * @param integer width
+ * @param integer height
+ * @param integer one of: Image::NONE, Image::AUTO, Image::WIDTH, Image::HEIGHT
+ * @return object
+ */
+ public function resize($width, $height, $master = NULL)
+ {
+ if ( ! $this->valid_size('width', $width))
+ throw new Kohana_Exception('image.invalid_width', $width);
+
+ if ( ! $this->valid_size('height', $height))
+ throw new Kohana_Exception('image.invalid_height', $height);
+
+ if (empty($width) AND empty($height))
+ throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__);
+
+ if ($master === NULL)
+ {
+ // Maintain the aspect ratio by default
+ $master = Image::AUTO;
+ }
+ elseif ( ! $this->valid_size('master', $master))
+ throw new Kohana_Exception('image.invalid_master');
+
+ $this->actions['resize'] = array
+ (
+ 'width' => $width,
+ 'height' => $height,
+ 'master' => $master,
+ );
+
+ return $this;
+ }
+
+ /**
+ * Crop an image to a specific width and height. You may also set the top
+ * and left offset.
+ * This method is chainable.
+ *
+ * @throws Kohana_Exception
+ * @param integer width
+ * @param integer height
+ * @param integer top offset, pixel value or one of: top, center, bottom
+ * @param integer left offset, pixel value or one of: left, center, right
+ * @return object
+ */
+ public function crop($width, $height, $top = 'center', $left = 'center')
+ {
+ if ( ! $this->valid_size('width', $width))
+ throw new Kohana_Exception('image.invalid_width', $width);
+
+ if ( ! $this->valid_size('height', $height))
+ throw new Kohana_Exception('image.invalid_height', $height);
+
+ if ( ! $this->valid_size('top', $top))
+ throw new Kohana_Exception('image.invalid_top', $top);
+
+ if ( ! $this->valid_size('left', $left))
+ throw new Kohana_Exception('image.invalid_left', $left);
+
+ if (empty($width) AND empty($height))
+ throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__);
+
+ $this->actions['crop'] = array
+ (
+ 'width' => $width,
+ 'height' => $height,
+ 'top' => $top,
+ 'left' => $left,
+ );
+
+ return $this;
+ }
+
+ /**
+ * Allows rotation of an image by 180 degrees clockwise or counter clockwise.
+ *
+ * @param integer degrees
+ * @return object
+ */
+ public function rotate($degrees)
+ {
+ $degrees = (int) $degrees;
+
+ if ($degrees > 180)
+ {
+ do
+ {
+ // Keep subtracting full circles until the degrees have normalized
+ $degrees -= 360;
+ }
+ while($degrees > 180);
+ }
+
+ if ($degrees < -180)
+ {
+ do
+ {
+ // Keep adding full circles until the degrees have normalized
+ $degrees += 360;
+ }
+ while($degrees < -180);
+ }
+
+ $this->actions['rotate'] = $degrees;
+
+ return $this;
+ }
+
+ /**
+ * Flip an image horizontally or vertically.
+ *
+ * @throws Kohana_Exception
+ * @param integer direction
+ * @return object
+ */
+ public function flip($direction)
+ {
+ if ($direction !== Image::HORIZONTAL AND $direction !== Image::VERTICAL)
+ throw new Kohana_Exception('image.invalid_flip');
+
+ $this->actions['flip'] = $direction;
+
+ return $this;
+ }
+
+ /**
+ * Change the quality of an image.
+ *
+ * @param integer quality as a percentage
+ * @return object
+ */
+ public function quality($amount)
+ {
+ $this->actions['quality'] = max(1, min($amount, 100));
+
+ return $this;
+ }
+
+ /**
+ * Sharpen an image.
+ *
+ * @param integer amount to sharpen, usually ~20 is ideal
+ * @return object
+ */
+ public function sharpen($amount)
+ {
+ $this->actions['sharpen'] = max(1, min($amount, 100));
+
+ return $this;
+ }
+
+ /**
+ * Save the image to a new image or overwrite this image.
+ *
+ * @throws Kohana_Exception
+ * @param string new image filename
+ * @param integer permissions for new image
+ * @param boolean keep or discard image process actions
+ * @return object
+ */
+ public function save($new_image = FALSE, $chmod = 0644, $keep_actions = FALSE)
+ {
+ // If no new image is defined, use the current image
+ empty($new_image) and $new_image = $this->image['file'];
+
+ // Separate the directory and filename
+ $dir = pathinfo($new_image, PATHINFO_DIRNAME);
+ $file = pathinfo($new_image, PATHINFO_BASENAME);
+
+ // Normalize the path
+ $dir = str_replace('\\', '/', realpath($dir)).'/';
+
+ if ( ! is_writable($dir))
+ throw new Kohana_Exception('image.directory_unwritable', $dir);
+
+ if ($status = $this->driver->process($this->image, $this->actions, $dir, $file))
+ {
+ if ($chmod !== FALSE)
+ {
+ // Set permissions
+ chmod($new_image, $chmod);
+ }
+ }
+
+ // Reset actions. Subsequent save() or render() will not apply previous actions.
+ if ($keep_actions === FALSE)
+ $this->actions = array();
+
+ return $status;
+ }
+
+ /**
+ * Output the image to the browser.
+ *
+ * @param boolean keep or discard image process actions
+ * @return object
+ */
+ public function render($keep_actions = FALSE)
+ {
+ $new_image = $this->image['file'];
+
+ // Separate the directory and filename
+ $dir = pathinfo($new_image, PATHINFO_DIRNAME);
+ $file = pathinfo($new_image, PATHINFO_BASENAME);
+
+ // Normalize the path
+ $dir = str_replace('\\', '/', realpath($dir)).'/';
+
+ // Process the image with the driver
+ $status = $this->driver->process($this->image, $this->actions, $dir, $file, $render = TRUE);
+
+ // Reset actions. Subsequent save() or render() will not apply previous actions.
+ if ($keep_actions === FALSE)
+ $this->actions = array();
+
+ return $status;
+ }
+
+ /**
+ * Sanitize a given value type.
+ *
+ * @param string type of property
+ * @param mixed property value
+ * @return boolean
+ */
+ protected function valid_size($type, & $value)
+ {
+ if (is_null($value))
+ return TRUE;
+
+ if ( ! is_scalar($value))
+ return FALSE;
+
+ switch ($type)
+ {
+ case 'width':
+ case 'height':
+ if (is_string($value) AND ! ctype_digit($value))
+ {
+ // Only numbers and percent signs
+ if ( ! preg_match('/^[0-9]++%$/D', $value))
+ return FALSE;
+ }
+ else
+ {
+ $value = (int) $value;
+ }
+ break;
+ case 'top':
+ if (is_string($value) AND ! ctype_digit($value))
+ {
+ if ( ! in_array($value, array('top', 'bottom', 'center')))
+ return FALSE;
+ }
+ else
+ {
+ $value = (int) $value;
+ }
+ break;
+ case 'left':
+ if (is_string($value) AND ! ctype_digit($value))
+ {
+ if ( ! in_array($value, array('left', 'right', 'center')))
+ return FALSE;
+ }
+ else
+ {
+ $value = (int) $value;
+ }
+ break;
+ case 'master':
+ if ($value !== Image::NONE AND
+ $value !== Image::AUTO AND
+ $value !== Image::WIDTH AND
+ $value !== Image::HEIGHT)
+ return FALSE;
+ break;
+ }
+
+ return TRUE;
+ }
+
+} // End Image
\ No newline at end of file
diff --git a/Server/system/libraries/Input.php b/Server/system/libraries/Input.php
new file mode 100644
index 0000000..0e23c80
--- /dev/null
+++ b/Server/system/libraries/Input.php
@@ -0,0 +1,452 @@
+use_xss_clean = (bool) Kohana::config('core.global_xss_filtering');
+
+ if (Input::$instance === NULL)
+ {
+ // magic_quotes_runtime is enabled
+ if (get_magic_quotes_runtime())
+ {
+ set_magic_quotes_runtime(0);
+ Kohana::log('debug', 'Disable magic_quotes_runtime! It is evil and deprecated: http://php.net/magic_quotes');
+ }
+
+ // magic_quotes_gpc is enabled
+ if (get_magic_quotes_gpc())
+ {
+ $this->magic_quotes_gpc = TRUE;
+ Kohana::log('debug', 'Disable magic_quotes_gpc! It is evil and deprecated: http://php.net/magic_quotes');
+ }
+
+ // register_globals is enabled
+ if (ini_get('register_globals'))
+ {
+ if (isset($_REQUEST['GLOBALS']))
+ {
+ // Prevent GLOBALS override attacks
+ exit('Global variable overload attack.');
+ }
+
+ // Destroy the REQUEST global
+ $_REQUEST = array();
+
+ // These globals are standard and should not be removed
+ $preserve = array('GLOBALS', '_REQUEST', '_GET', '_POST', '_FILES', '_COOKIE', '_SERVER', '_ENV', '_SESSION');
+
+ // This loop has the same effect as disabling register_globals
+ foreach (array_diff(array_keys($GLOBALS), $preserve) as $key)
+ {
+ global $$key;
+ $$key = NULL;
+
+ // Unset the global variable
+ unset($GLOBALS[$key], $$key);
+ }
+
+ // Warn the developer about register globals
+ Kohana::log('debug', 'Disable register_globals! It is evil and deprecated: http://php.net/register_globals');
+ }
+
+ if (is_array($_GET))
+ {
+ foreach ($_GET as $key => $val)
+ {
+ // Sanitize $_GET
+ $_GET[$this->clean_input_keys($key)] = $this->clean_input_data($val);
+ }
+ }
+ else
+ {
+ $_GET = array();
+ }
+
+ if (is_array($_POST))
+ {
+ foreach ($_POST as $key => $val)
+ {
+ // Sanitize $_POST
+ $_POST[$this->clean_input_keys($key)] = $this->clean_input_data($val);
+ }
+ }
+ else
+ {
+ $_POST = array();
+ }
+
+ if (is_array($_COOKIE))
+ {
+ foreach ($_COOKIE as $key => $val)
+ {
+ // Ignore special attributes in RFC2109 compliant cookies
+ if ($key == '$Version' OR $key == '$Path' OR $key == '$Domain')
+ continue;
+
+ // Sanitize $_COOKIE
+ $_COOKIE[$this->clean_input_keys($key)] = $this->clean_input_data($val);
+ }
+ }
+ else
+ {
+ $_COOKIE = array();
+ }
+
+ // Create a singleton
+ Input::$instance = $this;
+
+ Kohana::log('debug', 'Global GET, POST and COOKIE data sanitized');
+ }
+ }
+
+ /**
+ * Fetch an item from the $_GET array.
+ *
+ * @param string key to find
+ * @param mixed default value
+ * @param boolean XSS clean the value
+ * @return mixed
+ */
+ public function get($key = array(), $default = NULL, $xss_clean = FALSE)
+ {
+ return $this->search_array($_GET, $key, $default, $xss_clean);
+ }
+
+ /**
+ * Fetch an item from the $_POST array.
+ *
+ * @param string key to find
+ * @param mixed default value
+ * @param boolean XSS clean the value
+ * @return mixed
+ */
+ public function post($key = array(), $default = NULL, $xss_clean = FALSE)
+ {
+ return $this->search_array($_POST, $key, $default, $xss_clean);
+ }
+
+ /**
+ * Fetch an item from the $_COOKIE array.
+ *
+ * @param string key to find
+ * @param mixed default value
+ * @param boolean XSS clean the value
+ * @return mixed
+ */
+ public function cookie($key = array(), $default = NULL, $xss_clean = FALSE)
+ {
+ return $this->search_array($_COOKIE, $key, $default, $xss_clean);
+ }
+
+ /**
+ * Fetch an item from the $_SERVER array.
+ *
+ * @param string key to find
+ * @param mixed default value
+ * @param boolean XSS clean the value
+ * @return mixed
+ */
+ public function server($key = array(), $default = NULL, $xss_clean = FALSE)
+ {
+ return $this->search_array($_SERVER, $key, $default, $xss_clean);
+ }
+
+ /**
+ * Fetch an item from a global array.
+ *
+ * @param array array to search
+ * @param string key to find
+ * @param mixed default value
+ * @param boolean XSS clean the value
+ * @return mixed
+ */
+ protected function search_array($array, $key, $default = NULL, $xss_clean = FALSE)
+ {
+ if ($key === array())
+ return $array;
+
+ if ( ! isset($array[$key]))
+ return $default;
+
+ // Get the value
+ $value = $array[$key];
+
+ if ($this->use_xss_clean === FALSE AND $xss_clean === TRUE)
+ {
+ // XSS clean the value
+ $value = $this->xss_clean($value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Fetch the IP Address.
+ *
+ * @return string
+ */
+ public function ip_address()
+ {
+ if ($this->ip_address !== NULL)
+ return $this->ip_address;
+
+ // Server keys that could contain the client IP address
+ $keys = array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR');
+
+ foreach ($keys as $key)
+ {
+ if ($ip = $this->server($key))
+ {
+ $this->ip_address = $ip;
+
+ // An IP address has been found
+ break;
+ }
+ }
+
+ if ($comma = strrpos($this->ip_address, ',') !== FALSE)
+ {
+ $this->ip_address = substr($this->ip_address, $comma + 1);
+ }
+
+ if ( ! valid::ip($this->ip_address))
+ {
+ // Use an empty IP
+ $this->ip_address = '0.0.0.0';
+ }
+
+ return $this->ip_address;
+ }
+
+ /**
+ * Clean cross site scripting exploits from string.
+ * HTMLPurifier may be used if installed, otherwise defaults to built in method.
+ * Note - This function should only be used to deal with data upon submission.
+ * It's not something that should be used for general runtime processing
+ * since it requires a fair amount of processing overhead.
+ *
+ * @param string data to clean
+ * @param string xss_clean method to use ('htmlpurifier' or defaults to built-in method)
+ * @return string
+ */
+ public function xss_clean($data, $tool = NULL)
+ {
+ if ($tool === NULL)
+ {
+ // Use the default tool
+ $tool = Kohana::config('core.global_xss_filtering');
+ }
+
+ if (is_array($data))
+ {
+ foreach ($data as $key => $val)
+ {
+ $data[$key] = $this->xss_clean($val, $tool);
+ }
+
+ return $data;
+ }
+
+ // Do not clean empty strings
+ if (trim($data) === '')
+ return $data;
+
+ if ($tool === TRUE)
+ {
+ // NOTE: This is necessary because switch is NOT type-sensative!
+ $tool = 'default';
+ }
+
+ switch ($tool)
+ {
+ case 'htmlpurifier':
+ /**
+ * @todo License should go here, http://htmlpurifier.org/
+ */
+ if ( ! class_exists('HTMLPurifier_Config', FALSE))
+ {
+ // Load HTMLPurifier
+ require Kohana::find_file('vendor', 'htmlpurifier/HTMLPurifier.auto', TRUE);
+ require 'HTMLPurifier.func.php';
+ }
+
+ // Set configuration
+ $config = HTMLPurifier_Config::createDefault();
+ $config->set('HTML', 'TidyLevel', 'none'); // Only XSS cleaning now
+
+ // Run HTMLPurifier
+ $data = HTMLPurifier($data, $config);
+ break;
+ default:
+ // http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php
+ // +----------------------------------------------------------------------+
+ // | Copyright (c) 2001-2006 Bitflux GmbH |
+ // +----------------------------------------------------------------------+
+ // | Licensed under the Apache License, Version 2.0 (the "License"); |
+ // | you may not use this file except in compliance with the License. |
+ // | You may obtain a copy of the License at |
+ // | http://www.apache.org/licenses/LICENSE-2.0 |
+ // | Unless required by applicable law or agreed to in writing, software |
+ // | distributed under the License is distributed on an "AS IS" BASIS, |
+ // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
+ // | implied. See the License for the specific language governing |
+ // | permissions and limitations under the License. |
+ // +----------------------------------------------------------------------+
+ // | Author: Christian Stocker |
+ // +----------------------------------------------------------------------+
+ //
+ // Kohana Modifications:
+ // * Changed double quotes to single quotes, changed indenting and spacing
+ // * Removed magic_quotes stuff
+ // * Increased regex readability:
+ // * Used delimeters that aren't found in the pattern
+ // * Removed all unneeded escapes
+ // * Deleted U modifiers and swapped greediness where needed
+ // * Increased regex speed:
+ // * Made capturing parentheses non-capturing where possible
+ // * Removed parentheses where possible
+ // * Split up alternation alternatives
+ // * Made some quantifiers possessive
+
+ // Fix &entity\n;
+ $data = str_replace(array('&','<','>'), array('&','<','>'), $data);
+ $data = preg_replace('/(*\w+)[\x00-\x20]+;/u', '$1;', $data);
+ $data = preg_replace('/(*[0-9A-F]+);*/iu', '$1;', $data);
+ $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
+
+ // Remove any attribute starting with "on" or xmlns
+ $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
+
+ // Remove javascript: and vbscript: protocols
+ $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
+ $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
+ $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
+
+ // Only works in IE:
+ $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
+ $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
+ $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
+
+ // Remove namespaced elements (we do not need them)
+ $data = preg_replace('#*\w+:\w[^>]*+>#i', '', $data);
+
+ do
+ {
+ // Remove really unwanted tags
+ $old_data = $data;
+ $data = preg_replace('#*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
+ }
+ while ($old_data !== $data);
+ break;
+ }
+
+ return $data;
+ }
+
+ /**
+ * This is a helper method. It enforces W3C specifications for allowed
+ * key name strings, to prevent malicious exploitation.
+ *
+ * @param string string to clean
+ * @return string
+ */
+ public function clean_input_keys($str)
+ {
+ $chars = PCRE_UNICODE_PROPERTIES ? '\pL' : 'a-zA-Z';
+
+ if ( ! preg_match('#^['.$chars.'0-9:_.-]++$#uD', $str))
+ {
+ exit('Disallowed key characters in global data.');
+ }
+
+ return $str;
+ }
+
+ /**
+ * This is a helper method. It escapes data and forces all newline
+ * characters to "\n".
+ *
+ * @param unknown_type string to clean
+ * @return string
+ */
+ public function clean_input_data($str)
+ {
+ if (is_array($str))
+ {
+ $new_array = array();
+ foreach ($str as $key => $val)
+ {
+ // Recursion!
+ $new_array[$this->clean_input_keys($key)] = $this->clean_input_data($val);
+ }
+ return $new_array;
+ }
+
+ if ($this->magic_quotes_gpc === TRUE)
+ {
+ // Remove annoying magic quotes
+ $str = stripslashes($str);
+ }
+
+ if ($this->use_xss_clean === TRUE)
+ {
+ $str = $this->xss_clean($str);
+ }
+
+ if (strpos($str, "\r") !== FALSE)
+ {
+ // Standardize newlines
+ $str = str_replace(array("\r\n", "\r"), "\n", $str);
+ }
+
+ return $str;
+ }
+
+} // End Input Class
diff --git a/Server/system/libraries/Model.php b/Server/system/libraries/Model.php
new file mode 100644
index 0000000..0c9fd8d
--- /dev/null
+++ b/Server/system/libraries/Model.php
@@ -0,0 +1,31 @@
+db))
+ {
+ // Load the default database
+ $this->db = Database::instance($this->db);
+ }
+ }
+
+} // End Model
\ No newline at end of file
diff --git a/Server/system/libraries/ORM.php b/Server/system/libraries/ORM.php
new file mode 100644
index 0000000..c104860
--- /dev/null
+++ b/Server/system/libraries/ORM.php
@@ -0,0 +1,1431 @@
+object_name = strtolower(substr(get_class($this), 0, -6));
+ $this->object_plural = inflector::plural($this->object_name);
+
+ if (!isset($this->sorting))
+ {
+ // Default sorting
+ $this->sorting = array($this->primary_key => 'asc');
+ }
+
+ // Initialize database
+ $this->__initialize();
+
+ // Clear the object
+ $this->clear();
+
+ if (is_object($id))
+ {
+ // Load an object
+ $this->load_values((array) $id);
+ }
+ elseif (!empty($id))
+ {
+ // Find an object
+ $this->find($id);
+ }
+ }
+
+ /**
+ * Prepares the model database connection, determines the table name,
+ * and loads column information.
+ *
+ * @return void
+ */
+ public function __initialize()
+ {
+ if ( ! is_object($this->db))
+ {
+ // Get database instance
+ $this->db = Database::instance($this->db);
+ }
+
+ if (empty($this->table_name))
+ {
+ // Table name is the same as the object name
+ $this->table_name = $this->object_name;
+
+ if ($this->table_names_plural === TRUE)
+ {
+ // Make the table name plural
+ $this->table_name = inflector::plural($this->table_name);
+ }
+ }
+
+ if (is_array($this->ignored_columns))
+ {
+ // Make the ignored columns mirrored = mirrored
+ $this->ignored_columns = array_combine($this->ignored_columns, $this->ignored_columns);
+ }
+
+ // Load column information
+ $this->reload_columns();
+ }
+
+ /**
+ * Allows serialization of only the object data and state, to prevent
+ * "stale" objects being unserialized, which also requires less memory.
+ *
+ * @return array
+ */
+ public function __sleep()
+ {
+ // Store only information about the object
+ return array('object_name', 'object', 'changed', 'loaded', 'saved', 'sorting');
+ }
+
+ /**
+ * Prepares the database connection and reloads the object.
+ *
+ * @return void
+ */
+ public function __wakeup()
+ {
+ // Initialize database
+ $this->__initialize();
+
+ if ($this->reload_on_wakeup === TRUE)
+ {
+ // Reload the object
+ $this->reload();
+ }
+ }
+
+ /**
+ * Handles pass-through to database methods. Calls to query methods
+ * (query, get, insert, update) are not allowed. Query builder methods
+ * are chainable.
+ *
+ * @param string method name
+ * @param array method arguments
+ * @return mixed
+ */
+ public function __call($method, array $args)
+ {
+ if (method_exists($this->db, $method))
+ {
+ if (in_array($method, array('query', 'get', 'insert', 'update', 'delete')))
+ throw new Kohana_Exception('orm.query_methods_not_allowed');
+
+ // Method has been applied to the database
+ $this->db_applied[$method] = $method;
+
+ // Number of arguments passed
+ $num_args = count($args);
+
+ if ($method === 'select' AND $num_args > 3)
+ {
+ // Call select() manually to avoid call_user_func_array
+ $this->db->select($args);
+ }
+ else
+ {
+ // We use switch here to manually call the database methods. This is
+ // done for speed: call_user_func_array can take over 300% longer to
+ // make calls. Most database methods are 4 arguments or less, so this
+ // avoids almost any calls to call_user_func_array.
+
+ switch ($num_args)
+ {
+ case 0:
+ if (in_array($method, array('open_paren', 'close_paren', 'enable_cache', 'disable_cache')))
+ {
+ // Should return ORM, not Database
+ $this->db->$method();
+ }
+ else
+ {
+ // Support for things like reset_select, reset_write, list_tables
+ return $this->db->$method();
+ }
+ break;
+ case 1:
+ $this->db->$method($args[0]);
+ break;
+ case 2:
+ $this->db->$method($args[0], $args[1]);
+ break;
+ case 3:
+ $this->db->$method($args[0], $args[1], $args[2]);
+ break;
+ case 4:
+ $this->db->$method($args[0], $args[1], $args[2], $args[3]);
+ break;
+ default:
+ // Here comes the snail...
+ call_user_func_array(array($this->db, $method), $args);
+ break;
+ }
+ }
+
+ return $this;
+ }
+ else
+ {
+ throw new Kohana_Exception('core.invalid_method', $method, get_class($this));
+ }
+ }
+
+ /**
+ * Handles retrieval of all model values, relationships, and metadata.
+ *
+ * @param string column name
+ * @return mixed
+ */
+ public function __get($column)
+ {
+ if (array_key_exists($column, $this->object))
+ {
+ return $this->object[$column];
+ }
+ elseif (isset($this->related[$column]))
+ {
+ return $this->related[$column];
+ }
+ elseif ($column === 'primary_key_value')
+ {
+ return $this->object[$this->primary_key];
+ }
+ elseif ($model = $this->related_object($column))
+ {
+ // This handles the has_one and belongs_to relationships
+
+ if (in_array($model->object_name, $this->belongs_to) OR ! array_key_exists($this->foreign_key($column), $model->object))
+ {
+ // Foreign key lies in this table (this model belongs_to target model) OR an invalid has_one relationship
+ $where = array($model->table_name.'.'.$model->primary_key => $this->object[$this->foreign_key($column)]);
+ }
+ else
+ {
+ // Foreign key lies in the target table (this model has_one target model)
+ $where = array($this->foreign_key($column, $model->table_name) => $this->primary_key_value);
+ }
+
+ // one<>alias:one relationship
+ return $this->related[$column] = $model->find($where);
+ }
+ elseif (isset($this->has_many[$column]))
+ {
+ // Load the "middle" model
+ $through = ORM::factory(inflector::singular($this->has_many[$column]));
+
+ // Load the "end" model
+ $model = ORM::factory(inflector::singular($column));
+
+ // Join ON target model's primary key set to 'through' model's foreign key
+ // User-defined foreign keys must be defined in the 'through' model
+ $join_table = $through->table_name;
+ $join_col1 = $through->foreign_key($model->object_name, $join_table);
+ $join_col2 = $model->table_name.'.'.$model->primary_key;
+
+ // one<>alias:many relationship
+ return $this->related[$column] = $model
+ ->join($join_table, $join_col1, $join_col2)
+ ->where($through->foreign_key($this->object_name, $join_table), $this->object[$this->primary_key])
+ ->find_all();
+ }
+ elseif (in_array($column, $this->has_many))
+ {
+ // one<>many relationship
+ $model = ORM::factory(inflector::singular($column));
+ return $this->related[$column] = $model
+ ->where($this->foreign_key($column, $model->table_name), $this->object[$this->primary_key])
+ ->find_all();
+ }
+ elseif (in_array($column, $this->has_and_belongs_to_many))
+ {
+ // Load the remote model, always singular
+ $model = ORM::factory(inflector::singular($column));
+
+ if ($this->has($model, TRUE))
+ {
+ // many<>many relationship
+ return $this->related[$column] = $model
+ ->in($model->table_name.'.'.$model->primary_key, $this->changed_relations[$column])
+ ->find_all();
+ }
+ else
+ {
+ // empty many<>many relationship
+ return $this->related[$column] = $model
+ ->where($model->table_name.'.'.$model->primary_key, NULL)
+ ->find_all();
+ }
+ }
+ elseif (isset($this->ignored_columns[$column]))
+ {
+ return NULL;
+ }
+ elseif (in_array($column, array
+ (
+ 'object_name', 'object_plural', // Object
+ 'primary_key', 'primary_val', 'table_name', 'table_columns', // Table
+ 'loaded', 'saved', // Status
+ 'has_one', 'belongs_to', 'has_many', 'has_and_belongs_to_many', 'load_with' // Relationships
+ )))
+ {
+ // Model meta information
+ return $this->$column;
+ }
+ else
+ {
+ throw new Kohana_Exception('core.invalid_property', $column, get_class($this));
+ }
+ }
+
+ /**
+ * Handles setting of all model values, and tracks changes between values.
+ *
+ * @param string column name
+ * @param mixed column value
+ * @return void
+ */
+ public function __set($column, $value)
+ {
+ if (isset($this->ignored_columns[$column]))
+ {
+ return NULL;
+ }
+ elseif (isset($this->object[$column]) OR array_key_exists($column, $this->object))
+ {
+ if (isset($this->table_columns[$column]))
+ {
+ // Data has changed
+ $this->changed[$column] = $column;
+
+ // Object is no longer saved
+ $this->saved = FALSE;
+ }
+
+ $this->object[$column] = $this->load_type($column, $value);
+ }
+ elseif (in_array($column, $this->has_and_belongs_to_many) AND is_array($value))
+ {
+ // Load relations
+ $model = ORM::factory(inflector::singular($column));
+
+ if ( ! isset($this->object_relations[$column]))
+ {
+ // Load relations
+ $this->has($model);
+ }
+
+ // Change the relationships
+ $this->changed_relations[$column] = $value;
+
+ if (isset($this->related[$column]))
+ {
+ // Force a reload of the relationships
+ unset($this->related[$column]);
+ }
+ }
+ else
+ {
+ throw new Kohana_Exception('core.invalid_property', $column, get_class($this));
+ }
+ }
+
+ /**
+ * Checks if object data is set.
+ *
+ * @param string column name
+ * @return boolean
+ */
+ public function __isset($column)
+ {
+ return (isset($this->object[$column]) OR isset($this->related[$column]));
+ }
+
+ /**
+ * Unsets object data.
+ *
+ * @param string column name
+ * @return void
+ */
+ public function __unset($column)
+ {
+ unset($this->object[$column], $this->changed[$column], $this->related[$column]);
+ }
+
+ /**
+ * Displays the primary key of a model when it is converted to a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->object[$this->primary_key];
+ }
+
+ /**
+ * Returns the values of this object as an array.
+ *
+ * @return array
+ */
+ public function as_array()
+ {
+ $object = array();
+
+ foreach ($this->object as $key => $val)
+ {
+ // Reconstruct the array (calls __get)
+ $object[$key] = $this->$key;
+ }
+
+ return $object;
+ }
+
+ /**
+ * Binds another one-to-one object to this model. One-to-one objects
+ * can be nested using 'object1:object2' syntax
+ *
+ * @param string $target_path
+ * @return void
+ */
+ public function with($target_path)
+ {
+ if (isset($this->with_applied[$target_path]))
+ {
+ // Don't join anything already joined
+ return $this;
+ }
+
+ // Split object parts
+ $objects = explode(':', $target_path);
+ $target = $this;
+ foreach ($objects as $object)
+ {
+ // Go down the line of objects to find the given target
+ $parent = $target;
+ $target = $parent->related_object($object);
+
+ if ( ! $target)
+ {
+ // Can't find related object
+ return $this;
+ }
+ }
+
+ $target_name = $object;
+
+ // Pop-off top object to get the parent object (user:photo:tag becomes user:photo - the parent table prefix)
+ array_pop($objects);
+ $parent_path = implode(':', $objects);
+
+ if (empty($parent_path))
+ {
+ // Use this table name itself for the parent object
+ $parent_path = $this->table_name;
+ }
+ else
+ {
+ if( ! isset($this->with_applied[$parent_path]))
+ {
+ // If the parent object hasn't been joined yet, do it first (otherwise LEFT JOINs fail)
+ $this->with($parent_path);
+ }
+ }
+
+ // Add to with_applied to prevent duplicate joins
+ $this->with_applied[$target_path] = TRUE;
+
+ // Use the keys of the empty object to determine the columns
+ $select = array_keys($target->object);
+ foreach ($select as $i => $column)
+ {
+ // Add the prefix so that load_result can determine the relationship
+ $select[$i] = $target_path.'.'.$column.' AS '.$target_path.':'.$column;
+ }
+
+
+ // Select all of the prefixed keys in the object
+ $this->db->select($select);
+
+ if (in_array($target->object_name, $parent->belongs_to) OR ! isset($target->object[$parent->foreign_key($target_name)]))
+ {
+ // Parent belongs_to target, use target's primary key as join column
+ $join_col1 = $target->foreign_key(TRUE, $target_path);
+ $join_col2 = $parent->foreign_key($target_name, $parent_path);
+ }
+ else
+ {
+ // Parent has_one target, use parent's primary key as join column
+ $join_col2 = $parent->foreign_key(TRUE, $parent_path);
+ $join_col1 = $parent->foreign_key($target_name, $target_path);
+ }
+
+ // This allows for models to use different table prefixes (sharing the same database)
+ $join_table = new Database_Expression($target->db->table_prefix().$target->table_name.' AS '.$this->db->table_prefix().$target_path);
+
+ // Join the related object into the result
+ $this->db->join($join_table, $join_col1, $join_col2, 'LEFT');
+
+ return $this;
+ }
+
+ /**
+ * Finds and loads a single database row into the object.
+ *
+ * @chainable
+ * @param mixed primary key or an array of clauses
+ * @return ORM
+ */
+ public function find($id = NULL)
+ {
+ if ($id !== NULL)
+ {
+ if (is_array($id))
+ {
+ // Search for all clauses
+ $this->db->where($id);
+ }
+ else
+ {
+ // Search for a specific column
+ $this->db->where($this->table_name.'.'.$this->unique_key($id), $id);
+ }
+ }
+
+ return $this->load_result();
+ }
+
+ /**
+ * Finds multiple database rows and returns an iterator of the rows found.
+ *
+ * @chainable
+ * @param integer SQL limit
+ * @param integer SQL offset
+ * @return ORM_Iterator
+ */
+ public function find_all($limit = NULL, $offset = NULL)
+ {
+ if ($limit !== NULL AND ! isset($this->db_applied['limit']))
+ {
+ // Set limit
+ $this->limit($limit);
+ }
+
+ if ($offset !== NULL AND ! isset($this->db_applied['offset']))
+ {
+ // Set offset
+ $this->offset($offset);
+ }
+
+ return $this->load_result(TRUE);
+ }
+
+ /**
+ * Creates a key/value array from all of the objects available. Uses find_all
+ * to find the objects.
+ *
+ * @param string key column
+ * @param string value column
+ * @return array
+ */
+ public function select_list($key = NULL, $val = NULL)
+ {
+ if ($key === NULL)
+ {
+ $key = $this->primary_key;
+ }
+
+ if ($val === NULL)
+ {
+ $val = $this->primary_val;
+ }
+
+ // Return a select list from the results
+ return $this->select($key, $val)->find_all()->select_list($key, $val);
+ }
+
+ /**
+ * Validates the current object. This method should generally be called
+ * via the model, after the $_POST Validation object has been created.
+ *
+ * @param object Validation array
+ * @return boolean
+ */
+ public function validate(Validation $array, $save = FALSE)
+ {
+ $safe_array = $array->safe_array();
+
+ if ( ! $array->submitted())
+ {
+ foreach ($safe_array as $key => $value)
+ {
+ // Get the value from this object
+ $value = $this->$key;
+
+ if (is_object($value) AND $value instanceof ORM_Iterator)
+ {
+ // Convert the value to an array of primary keys
+ $value = $value->primary_key_array();
+ }
+
+ // Pre-fill data
+ $array[$key] = $value;
+ }
+ }
+
+ // Validate the array
+ if ($status = $array->validate())
+ {
+ // Grab only set fields (excludes missing data, unlike safe_array)
+ $fields = $array->as_array();
+
+ foreach ($fields as $key => $value)
+ {
+ if (isset($safe_array[$key]))
+ {
+ // Set new data, ignoring any missing fields or fields without rules
+ $this->$key = $value;
+ }
+ }
+
+ if ($save === TRUE OR is_string($save))
+ {
+ // Save this object
+ $this->save();
+
+ if (is_string($save))
+ {
+ // Redirect to the saved page
+ url::redirect($save);
+ }
+ }
+ }
+
+ // Return validation status
+ return $status;
+ }
+
+ /**
+ * Saves the current object.
+ *
+ * @chainable
+ * @return ORM
+ */
+ public function save()
+ {
+ if ( ! empty($this->changed))
+ {
+ $data = array();
+ foreach ($this->changed as $column)
+ {
+ // Compile changed data
+ $data[$column] = $this->object[$column];
+ }
+
+ if ($this->loaded === TRUE)
+ {
+ $query = $this->db
+ ->where($this->primary_key, $this->object[$this->primary_key])
+ ->update($this->table_name, $data);
+
+ // Object has been saved
+ $this->saved = TRUE;
+ }
+ else
+ {
+ $query = $this->db
+ ->insert($this->table_name, $data);
+
+ if ($query->count() > 0)
+ {
+ if (empty($this->object[$this->primary_key]))
+ {
+ // Load the insert id as the primary key
+ $this->object[$this->primary_key] = $query->insert_id();
+ }
+
+ // Object is now loaded and saved
+ $this->loaded = $this->saved = TRUE;
+ }
+ }
+
+ if ($this->saved === TRUE)
+ {
+ // All changes have been saved
+ $this->changed = array();
+ }
+ }
+
+ if ($this->saved === TRUE AND ! empty($this->changed_relations))
+ {
+ foreach ($this->changed_relations as $column => $values)
+ {
+ // All values that were added
+ $added = array_diff($values, $this->object_relations[$column]);
+
+ // All values that were saved
+ $removed = array_diff($this->object_relations[$column], $values);
+
+ if (empty($added) AND empty($removed))
+ {
+ // No need to bother
+ continue;
+ }
+
+ // Clear related columns
+ unset($this->related[$column]);
+
+ // Load the model
+ $model = ORM::factory(inflector::singular($column));
+
+ if (($join_table = array_search($column, $this->has_and_belongs_to_many)) === FALSE)
+ continue;
+
+ if (is_int($join_table))
+ {
+ // No "through" table, load the default JOIN table
+ $join_table = $model->join_table($this->table_name);
+ }
+
+ // Foreign keys for the join table
+ $object_fk = $this->foreign_key(NULL);
+ $related_fk = $model->foreign_key(NULL);
+
+ if ( ! empty($added))
+ {
+ foreach ($added as $id)
+ {
+ // Insert the new relationship
+ $this->db->insert($join_table, array
+ (
+ $object_fk => $this->object[$this->primary_key],
+ $related_fk => $id,
+ ));
+ }
+ }
+
+ if ( ! empty($removed))
+ {
+ $this->db
+ ->where($object_fk, $this->object[$this->primary_key])
+ ->in($related_fk, $removed)
+ ->delete($join_table);
+ }
+
+ // Clear all relations for this column
+ unset($this->object_relations[$column], $this->changed_relations[$column]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Deletes the current object from the database. This does NOT destroy
+ * relationships that have been created with other objects.
+ *
+ * @chainable
+ * @return ORM
+ */
+ public function delete($id = NULL)
+ {
+ if ($id === NULL AND $this->loaded)
+ {
+ // Use the the primary key value
+ $id = $this->object[$this->primary_key];
+ }
+
+ // Delete this object
+ $this->db->where($this->primary_key, $id)->delete($this->table_name);
+
+ return $this->clear();
+ }
+
+ /**
+ * Delete all objects in the associated table. This does NOT destroy
+ * relationships that have been created with other objects.
+ *
+ * @chainable
+ * @param array ids to delete
+ * @return ORM
+ */
+ public function delete_all($ids = NULL)
+ {
+ if (is_array($ids))
+ {
+ // Delete only given ids
+ $this->db->in($this->primary_key, $ids);
+ }
+ elseif (is_null($ids))
+ {
+ // Delete all records
+ $this->db->where('1=1');
+ }
+ else
+ {
+ // Do nothing - safeguard
+ return $this;
+ }
+
+ // Delete all objects
+ $this->db->delete($this->table_name);
+
+ return $this->clear();
+ }
+
+ /**
+ * Unloads the current object and clears the status.
+ *
+ * @chainable
+ * @return ORM
+ */
+ public function clear()
+ {
+ // Create an array with all the columns set to NULL
+ $columns = array_keys($this->table_columns);
+ $values = array_combine($columns, array_fill(0, count($columns), NULL));
+
+ // Replace the current object with an empty one
+ $this->load_values($values);
+
+ return $this;
+ }
+
+ /**
+ * Reloads the current object from the database.
+ *
+ * @chainable
+ * @return ORM
+ */
+ public function reload()
+ {
+ return $this->find($this->object[$this->primary_key]);
+ }
+
+ /**
+ * Reload column definitions.
+ *
+ * @chainable
+ * @param boolean force reloading
+ * @return ORM
+ */
+ public function reload_columns($force = FALSE)
+ {
+ if ($force === TRUE OR empty($this->table_columns))
+ {
+ if (isset(ORM::$column_cache[$this->object_name]))
+ {
+ // Use cached column information
+ $this->table_columns = ORM::$column_cache[$this->object_name];
+ }
+ else
+ {
+ // Load table columns
+ ORM::$column_cache[$this->object_name] = $this->table_columns = $this->list_fields();
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Tests if this object has a relationship to a different model.
+ *
+ * @param object related ORM model
+ * @param boolean check for any relations to given model
+ * @return boolean
+ */
+ public function has(ORM $model, $any = FALSE)
+ {
+ // Determine plural or singular relation name
+ $related = ($model->table_names_plural === TRUE) ? $model->object_plural : $model->object_name;
+
+ if (($join_table = array_search($related, $this->has_and_belongs_to_many)) === FALSE)
+ return FALSE;
+
+ if (is_int($join_table))
+ {
+ // No "through" table, load the default JOIN table
+ $join_table = $model->join_table($this->table_name);
+ }
+
+ if ( ! isset($this->object_relations[$related]))
+ {
+ // Load the object relationships
+ $this->changed_relations[$related] = $this->object_relations[$related] = $this->load_relations($join_table, $model);
+ }
+
+ if ( ! $model->empty_primary_key())
+ {
+ // Check if a specific object exists
+ return in_array($model->primary_key_value, $this->changed_relations[$related]);
+ }
+ elseif ($any)
+ {
+ // Check if any relations to given model exist
+ return ! empty($this->changed_relations[$related]);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ /**
+ * Adds a new relationship to between this model and another.
+ *
+ * @param object related ORM model
+ * @return boolean
+ */
+ public function add(ORM $model)
+ {
+ if ($this->has($model))
+ return TRUE;
+
+ // Get the faked column name
+ $column = $model->object_plural;
+
+ // Add the new relation to the update
+ $this->changed_relations[$column][] = $model->primary_key_value;
+
+ if (isset($this->related[$column]))
+ {
+ // Force a reload of the relationships
+ unset($this->related[$column]);
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Adds a new relationship to between this model and another.
+ *
+ * @param object related ORM model
+ * @return boolean
+ */
+ public function remove(ORM $model)
+ {
+ if ( ! $this->has($model))
+ return FALSE;
+
+ // Get the faked column name
+ $column = $model->object_plural;
+
+ if (($key = array_search($model->primary_key_value, $this->changed_relations[$column])) === FALSE)
+ return FALSE;
+
+ // Remove the relationship
+ unset($this->changed_relations[$column][$key]);
+
+ if (isset($this->related[$column]))
+ {
+ // Force a reload of the relationships
+ unset($this->related[$column]);
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Count the number of records in the table.
+ *
+ * @return integer
+ */
+ public function count_all()
+ {
+ // Return the total number of records in a table
+ return $this->db->count_records($this->table_name);
+ }
+
+ /**
+ * Proxy method to Database list_fields.
+ *
+ * @param string table name or NULL to use this table
+ * @return array
+ */
+ public function list_fields($table = NULL)
+ {
+ if ($table === NULL)
+ {
+ $table = $this->table_name;
+ }
+
+ // Proxy to database
+ return $this->db->list_fields($table);
+ }
+
+ /**
+ * Proxy method to Database field_data.
+ *
+ * @param string table name
+ * @return array
+ */
+ public function field_data($table)
+ {
+ // Proxy to database
+ return $this->db->field_data($table);
+ }
+
+ /**
+ * Proxy method to Database field_data.
+ *
+ * @chainable
+ * @param string SQL query to clear
+ * @return ORM
+ */
+ public function clear_cache($sql = NULL)
+ {
+ // Proxy to database
+ $this->db->clear_cache($sql);
+
+ ORM::$column_cache = array();
+
+ return $this;
+ }
+
+ /**
+ * Returns the unique key for a specific value. This method is expected
+ * to be overloaded in models if the model has other unique columns.
+ *
+ * @param mixed unique value
+ * @return string
+ */
+ public function unique_key($id)
+ {
+ return $this->primary_key;
+ }
+
+ /**
+ * Determines the name of a foreign key for a specific table.
+ *
+ * @param string related table name
+ * @param string prefix table name (used for JOINs)
+ * @return string
+ */
+ public function foreign_key($table = NULL, $prefix_table = NULL)
+ {
+ if ($table === TRUE)
+ {
+ if (is_string($prefix_table))
+ {
+ // Use prefix table name and this table's PK
+ return $prefix_table.'.'.$this->primary_key;
+ }
+ else
+ {
+ // Return the name of this table's PK
+ return $this->table_name.'.'.$this->primary_key;
+ }
+ }
+
+ if (is_string($prefix_table))
+ {
+ // Add a period for prefix_table.column support
+ $prefix_table .= '.';
+ }
+
+ if (isset($this->foreign_key[$table]))
+ {
+ // Use the defined foreign key name, no magic here!
+ $foreign_key = $this->foreign_key[$table];
+ }
+ else
+ {
+ if ( ! is_string($table) OR ! array_key_exists($table.'_'.$this->primary_key, $this->object))
+ {
+ // Use this table
+ $table = $this->table_name;
+
+ if (strpos($table, '.') !== FALSE)
+ {
+ // Hack around support for PostgreSQL schemas
+ list ($schema, $table) = explode('.', $table, 2);
+ }
+
+ if ($this->table_names_plural === TRUE)
+ {
+ // Make the key name singular
+ $table = inflector::singular($table);
+ }
+ }
+
+ $foreign_key = $table.'_'.$this->primary_key;
+ }
+
+ return $prefix_table.$foreign_key;
+ }
+
+ /**
+ * This uses alphabetical comparison to choose the name of the table.
+ *
+ * Example: The joining table of users and roles would be roles_users,
+ * because "r" comes before "u". Joining products and categories would
+ * result in categories_products, because "c" comes before "p".
+ *
+ * Example: zoo > zebra > robber > ocean > angel > aardvark
+ *
+ * @param string table name
+ * @return string
+ */
+ public function join_table($table)
+ {
+ if ($this->table_name > $table)
+ {
+ $table = $table.'_'.$this->table_name;
+ }
+ else
+ {
+ $table = $this->table_name.'_'.$table;
+ }
+
+ return $table;
+ }
+
+ /**
+ * Returns an ORM model for the given object name;
+ *
+ * @param string object name
+ * @return ORM
+ */
+ protected function related_object($object)
+ {
+ if (isset($this->has_one[$object]))
+ {
+ $object = ORM::factory($this->has_one[$object]);
+ }
+ elseif (isset($this->belongs_to[$object]))
+ {
+ $object = ORM::factory($this->belongs_to[$object]);
+ }
+ elseif (in_array($object, $this->has_one) OR in_array($object, $this->belongs_to))
+ {
+ $object = ORM::factory($object);
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return $object;
+ }
+
+ /**
+ * Loads an array of values into into the current object.
+ *
+ * @chainable
+ * @param array values to load
+ * @return ORM
+ */
+ public function load_values(array $values)
+ {
+ if (array_key_exists($this->primary_key, $values))
+ {
+ // Replace the object and reset the object status
+ $this->object = $this->changed = $this->related = array();
+
+ // Set the loaded and saved object status based on the primary key
+ $this->loaded = $this->saved = ($values[$this->primary_key] !== NULL);
+ }
+
+ // Related objects
+ $related = array();
+
+ foreach ($values as $column => $value)
+ {
+ if (strpos($column, ':') === FALSE)
+ {
+ if (isset($this->table_columns[$column]))
+ {
+ // The type of the value can be determined, convert the value
+ $value = $this->load_type($column, $value);
+ }
+
+ $this->object[$column] = $value;
+ }
+ else
+ {
+ list ($prefix, $column) = explode(':', $column, 2);
+
+ $related[$prefix][$column] = $value;
+ }
+ }
+
+ if ( ! empty($related))
+ {
+ foreach ($related as $object => $values)
+ {
+ // Load the related objects with the values in the result
+ $this->related[$object] = $this->related_object($object)->load_values($values);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Loads a value according to the types defined by the column metadata.
+ *
+ * @param string column name
+ * @param mixed value to load
+ * @return mixed
+ */
+ protected function load_type($column, $value)
+ {
+ $type = gettype($value);
+ if ($type == 'object' OR $type == 'array' OR ! isset($this->table_columns[$column]))
+ return $value;
+
+ // Load column data
+ $column = $this->table_columns[$column];
+
+ if ($value === NULL AND ! empty($column['null']))
+ return $value;
+
+ if ( ! empty($column['binary']) AND ! empty($column['exact']) AND (int) $column['length'] === 1)
+ {
+ // Use boolean for BINARY(1) fields
+ $column['type'] = 'boolean';
+ }
+
+ switch ($column['type'])
+ {
+ case 'int':
+ if ($value === '' AND ! empty($column['null']))
+ {
+ // Forms will only submit strings, so empty integer values must be null
+ $value = NULL;
+ }
+ elseif ((float) $value > PHP_INT_MAX)
+ {
+ // This number cannot be represented by a PHP integer, so we convert it to a string
+ $value = (string) $value;
+ }
+ else
+ {
+ $value = (int) $value;
+ }
+ break;
+ case 'float':
+ $value = (float) $value;
+ break;
+ case 'boolean':
+ $value = (bool) $value;
+ break;
+ case 'string':
+ $value = (string) $value;
+ break;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Loads a database result, either as a new object for this model, or as
+ * an iterator for multiple rows.
+ *
+ * @chainable
+ * @param boolean return an iterator or load a single row
+ * @return ORM for single rows
+ * @return ORM_Iterator for multiple rows
+ */
+ protected function load_result($array = FALSE)
+ {
+ if ($array === FALSE)
+ {
+ // Only fetch 1 record
+ $this->db->limit(1);
+ }
+
+ if ( ! isset($this->db_applied['select']))
+ {
+ // Select all columns by default
+ $this->db->select($this->table_name.'.*');
+ }
+
+ if ( ! empty($this->load_with))
+ {
+ foreach ($this->load_with as $alias => $object)
+ {
+ // Join each object into the results
+ if (is_string($alias))
+ {
+ // Use alias
+ $this->with($alias);
+ }
+ else
+ {
+ // Use object
+ $this->with($object);
+ }
+ }
+ }
+
+ if ( ! isset($this->db_applied['orderby']) AND ! empty($this->sorting))
+ {
+ $sorting = array();
+ foreach ($this->sorting as $column => $direction)
+ {
+ if (strpos($column, '.') === FALSE)
+ {
+ // Keeps sorting working properly when using JOINs on
+ // tables with columns of the same name
+ $column = $this->table_name.'.'.$column;
+ }
+
+ $sorting[$column] = $direction;
+ }
+
+ // Apply the user-defined sorting
+ $this->db->orderby($sorting);
+ }
+
+ // Load the result
+ $result = $this->db->get($this->table_name);
+
+ if ($array === TRUE)
+ {
+ // Return an iterated result
+ return new ORM_Iterator($this, $result);
+ }
+
+ if ($result->count() === 1)
+ {
+ // Load object values
+ $this->load_values($result->result(FALSE)->current());
+ }
+ else
+ {
+ // Clear the object, nothing was found
+ $this->clear();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return an array of all the primary keys of the related table.
+ *
+ * @param string table name
+ * @param object ORM model to find relations of
+ * @return array
+ */
+ protected function load_relations($table, ORM $model)
+ {
+ // Save the current query chain (otherwise the next call will clash)
+ $this->db->push();
+
+ $query = $this->db
+ ->select($model->foreign_key(NULL).' AS id')
+ ->from($table)
+ ->where($this->foreign_key(NULL, $table), $this->object[$this->primary_key])
+ ->get()
+ ->result(TRUE);
+
+ $this->db->pop();
+
+ $relations = array();
+ foreach ($query as $row)
+ {
+ $relations[] = $row->id;
+ }
+
+ return $relations;
+ }
+
+ /**
+ * Returns whether or not primary key is empty
+ *
+ * @return bool
+ */
+ protected function empty_primary_key()
+ {
+ return (empty($this->object[$this->primary_key]) AND $this->object[$this->primary_key] !== '0');
+ }
+
+} // End ORM
diff --git a/Server/system/libraries/ORM_Iterator.php b/Server/system/libraries/ORM_Iterator.php
new file mode 100644
index 0000000..41aa806
--- /dev/null
+++ b/Server/system/libraries/ORM_Iterator.php
@@ -0,0 +1,228 @@
+class_name = get_class($model);
+ $this->primary_key = $model->primary_key;
+ $this->primary_val = $model->primary_val;
+
+ // Database result
+ $this->result = $result->result(TRUE);
+ }
+
+ /**
+ * Returns an array of the results as ORM objects.
+ *
+ * @return array
+ */
+ public function as_array()
+ {
+ $array = array();
+
+ if ($results = $this->result->result_array())
+ {
+ // Import class name
+ $class = $this->class_name;
+
+ foreach ($results as $obj)
+ {
+ $array[] = new $class($obj);
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Return an array of all of the primary keys for this object.
+ *
+ * @return array
+ */
+ public function primary_key_array()
+ {
+ $ids = array();
+ foreach ($this->result as $row)
+ {
+ $ids[] = $row->{$this->primary_key};
+ }
+ return $ids;
+ }
+
+ /**
+ * Create a key/value array from the results.
+ *
+ * @param string key column
+ * @param string value column
+ * @return array
+ */
+ public function select_list($key = NULL, $val = NULL)
+ {
+ if ($key === NULL)
+ {
+ // Use the default key
+ $key = $this->primary_key;
+ }
+
+ if ($val === NULL)
+ {
+ // Use the default value
+ $val = $this->primary_val;
+ }
+
+ $array = array();
+ foreach ($this->result->result_array() as $row)
+ {
+ $array[$row->$key] = $row->$val;
+ }
+ return $array;
+ }
+
+ /**
+ * Return a range of offsets.
+ *
+ * @param integer start
+ * @param integer end
+ * @return array
+ */
+ public function range($start, $end)
+ {
+ // Array of objects
+ $array = array();
+
+ if ($this->result->offsetExists($start))
+ {
+ // Import the class name
+ $class = $this->class_name;
+
+ // Set the end offset
+ $end = $this->result->offsetExists($end) ? $end : $this->count();
+
+ for ($i = $start; $i < $end; $i++)
+ {
+ // Insert each object in the range
+ $array[] = new $class($this->result->offsetGet($i));
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Countable: count
+ */
+ public function count()
+ {
+ return $this->result->count();
+ }
+
+ /**
+ * Iterator: current
+ */
+ public function current()
+ {
+ if ($row = $this->result->current())
+ {
+ // Import class name
+ $class = $this->class_name;
+
+ $row = new $class($row);
+ }
+
+ return $row;
+ }
+
+ /**
+ * Iterator: key
+ */
+ public function key()
+ {
+ return $this->result->key();
+ }
+
+ /**
+ * Iterator: next
+ */
+ public function next()
+ {
+ return $this->result->next();
+ }
+
+ /**
+ * Iterator: rewind
+ */
+ public function rewind()
+ {
+ $this->result->rewind();
+ }
+
+ /**
+ * Iterator: valid
+ */
+ public function valid()
+ {
+ return $this->result->valid();
+ }
+
+ /**
+ * ArrayAccess: offsetExists
+ */
+ public function offsetExists($offset)
+ {
+ return $this->result->offsetExists($offset);
+ }
+
+ /**
+ * ArrayAccess: offsetGet
+ */
+ public function offsetGet($offset)
+ {
+ if ($this->result->offsetExists($offset))
+ {
+ // Import class name
+ $class = $this->class_name;
+
+ return new $class($this->result->offsetGet($offset));
+ }
+ }
+
+ /**
+ * ArrayAccess: offsetSet
+ *
+ * @throws Kohana_Database_Exception
+ */
+ public function offsetSet($offset, $value)
+ {
+ throw new Kohana_Database_Exception('database.result_read_only');
+ }
+
+ /**
+ * ArrayAccess: offsetUnset
+ *
+ * @throws Kohana_Database_Exception
+ */
+ public function offsetUnset($offset)
+ {
+ throw new Kohana_Database_Exception('database.result_read_only');
+ }
+
+} // End ORM Iterator
\ No newline at end of file
diff --git a/Server/system/libraries/ORM_Tree.php b/Server/system/libraries/ORM_Tree.php
new file mode 100644
index 0000000..cdb09fd
--- /dev/null
+++ b/Server/system/libraries/ORM_Tree.php
@@ -0,0 +1,76 @@
+related[$column]))
+ {
+ // Load child model
+ $model = ORM::factory(inflector::singular($this->ORM_Tree_children));
+
+ if (array_key_exists($this->ORM_Tree_parent_key, $this->object))
+ {
+ // Find children of this parent
+ $model->where($model->primary_key, $this->object[$this->ORM_Tree_parent_key])->find();
+ }
+
+ $this->related[$column] = $model;
+ }
+
+ return $this->related[$column];
+ }
+ elseif ($column === 'children')
+ {
+ if (empty($this->related[$column]))
+ {
+ $model = ORM::factory(inflector::singular($this->ORM_Tree_children));
+
+ if ($this->ORM_Tree_children === $this->table_name)
+ {
+ // Load children within this table
+ $this->related[$column] = $model
+ ->where($this->ORM_Tree_parent_key, $this->object[$this->primary_key])
+ ->find_all();
+ }
+ else
+ {
+ // Find first selection of children
+ $this->related[$column] = $model
+ ->where($this->foreign_key(), $this->object[$this->primary_key])
+ ->where($this->ORM_Tree_parent_key, NULL)
+ ->find_all();
+ }
+ }
+
+ return $this->related[$column];
+ }
+
+ return parent::__get($column);
+ }
+
+} // End ORM Tree
\ No newline at end of file
diff --git a/Server/system/libraries/ORM_Versioned.php b/Server/system/libraries/ORM_Versioned.php
new file mode 100644
index 0000000..7c3ee5d
--- /dev/null
+++ b/Server/system/libraries/ORM_Versioned.php
@@ -0,0 +1,143 @@
+last_version = 1 + ($this->last_version === NULL ? $this->object['version'] : $this->last_version);
+ $this->__set('version', $this->last_version);
+
+ parent::save();
+
+ if ($this->saved)
+ {
+ $data = array();
+ foreach ($this->object as $key => $value)
+ {
+ if ($key === 'id')
+ continue;
+
+ $data[$key] = $value;
+ }
+ $data[$this->foreign_key()] = $this->id;
+
+ $this->db->insert($this->table_name.'_versions', $data);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Loads previous version from current object
+ *
+ * @chainable
+ * @return ORM
+ */
+ public function previous()
+ {
+ if ( ! $this->loaded)
+ return $this;
+
+ $this->last_version = ($this->last_version === NULL) ? $this->object['version'] : $this->last_version;
+ $version = $this->last_version - 1;
+
+ $query = $this->db
+ ->where($this->foreign_key(), $this->object[$this->primary_key])
+ ->where('version', $version)
+ ->limit(1)
+ ->get($this->table_name.'_versions');
+
+ if ($query->count())
+ {
+ $this->load_values($query->result(FALSE)->current());
+ }
+
+ return $this;
+ }
+
+ /**
+ * Restores the object with data from stored version
+ *
+ * @param integer version number you want to restore
+ * @return ORM
+ */
+ public function restore($version)
+ {
+ if ( ! $this->loaded)
+ return $this;
+
+ $query = $this->db
+ ->where($this->foreign_key(), $this->object[$this->primary_key])
+ ->where('version', $version)
+ ->limit(1)
+ ->get($this->table_name.'_versions');
+
+ if ($query->count())
+ {
+ $row = $query->result(FALSE)->current();
+
+ foreach ($row as $key => $value)
+ {
+ if ($key === $this->primary_key OR $key === $this->foreign_key())
+ {
+ // Do not overwrite the primary key
+ continue;
+ }
+
+ if ($key === 'version')
+ {
+ // Always use the current version
+ $value = $this->version;
+ }
+
+ $this->__set($key, $value);
+ }
+
+ $this->save();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Overloads ORM::delete() to delete all versioned entries of current object
+ * and the object itself
+ *
+ * @param integer id of the object you want to delete
+ * @return ORM
+ */
+ public function delete($id = NULL)
+ {
+ if ($id === NULL)
+ {
+ // Use the current object id
+ $id = $this->object[$this->primary_key];
+ }
+
+ if ($status = parent::delete($id))
+ {
+ $this->db->where($this->foreign_key(), $id)->delete($this->table_name.'_versions');
+ }
+
+ return $status;
+ }
+
+}
\ No newline at end of file
diff --git a/Server/system/libraries/Pagination.php b/Server/system/libraries/Pagination.php
new file mode 100644
index 0000000..a8f7bb1
--- /dev/null
+++ b/Server/system/libraries/Pagination.php
@@ -0,0 +1,236 @@
+initialize($config);
+
+ Kohana::log('debug', 'Pagination Library initialized');
+ }
+
+ /**
+ * Sets config values.
+ *
+ * @throws Kohana_Exception
+ * @param array configuration settings
+ * @return void
+ */
+ public function initialize($config = array())
+ {
+ // Load config group
+ if (isset($config['group']))
+ {
+ // Load and validate config group
+ if ( ! is_array($group_config = Kohana::config('pagination.'.$config['group'])))
+ throw new Kohana_Exception('pagination.undefined_group', $config['group']);
+
+ // All pagination config groups inherit default config group
+ if ($config['group'] !== 'default')
+ {
+ // Load and validate default config group
+ if ( ! is_array($default_config = Kohana::config('pagination.default')))
+ throw new Kohana_Exception('pagination.undefined_group', 'default');
+
+ // Merge config group with default config group
+ $group_config += $default_config;
+ }
+
+ // Merge custom config items with config group
+ $config += $group_config;
+ }
+
+ // Assign config values to the object
+ foreach ($config as $key => $value)
+ {
+ if (property_exists($this, $key))
+ {
+ $this->$key = $value;
+ }
+ }
+
+ // Clean view directory
+ $this->directory = trim($this->directory, '/').'/';
+
+ // Build generic URL with page in query string
+ if ($this->query_string !== '')
+ {
+ // Extract current page
+ $this->current_page = isset($_GET[$this->query_string]) ? (int) $_GET[$this->query_string] : 1;
+
+ // Insert {page} placeholder
+ $_GET[$this->query_string] = '{page}';
+
+ // Create full URL
+ $base_url = ($this->base_url === '') ? Router::$current_uri : $this->base_url;
+ $this->url = url::site($base_url).'?'.str_replace('%7Bpage%7D', '{page}', http_build_query($_GET));
+
+ // Reset page number
+ $_GET[$this->query_string] = $this->current_page;
+ }
+
+ // Build generic URL with page as URI segment
+ else
+ {
+ // Use current URI if no base_url set
+ $this->url = ($this->base_url === '') ? Router::$segments : explode('/', trim($this->base_url, '/'));
+
+ // Convert uri 'label' to corresponding integer if needed
+ if (is_string($this->uri_segment))
+ {
+ if (($key = array_search($this->uri_segment, $this->url)) === FALSE)
+ {
+ // If uri 'label' is not found, auto add it to base_url
+ $this->url[] = $this->uri_segment;
+ $this->uri_segment = count($this->url) + 1;
+ }
+ else
+ {
+ $this->uri_segment = $key + 2;
+ }
+ }
+
+ // Insert {page} placeholder
+ $this->url[$this->uri_segment - 1] = '{page}';
+
+ // Create full URL
+ $this->url = url::site(implode('/', $this->url)).Router::$query_string;
+
+ // Extract current page
+ $this->current_page = URI::instance()->segment($this->uri_segment);
+ }
+
+ // Core pagination values
+ $this->total_items = (int) max(0, $this->total_items);
+ $this->items_per_page = (int) max(1, $this->items_per_page);
+ $this->total_pages = (int) ceil($this->total_items / $this->items_per_page);
+ $this->current_page = (int) min(max(1, $this->current_page), max(1, $this->total_pages));
+ $this->current_first_item = (int) min((($this->current_page - 1) * $this->items_per_page) + 1, $this->total_items);
+ $this->current_last_item = (int) min($this->current_first_item + $this->items_per_page - 1, $this->total_items);
+
+ // If there is no first/last/previous/next page, relative to the
+ // current page, value is set to FALSE. Valid page number otherwise.
+ $this->first_page = ($this->current_page === 1) ? FALSE : 1;
+ $this->last_page = ($this->current_page >= $this->total_pages) ? FALSE : $this->total_pages;
+ $this->previous_page = ($this->current_page > 1) ? $this->current_page - 1 : FALSE;
+ $this->next_page = ($this->current_page < $this->total_pages) ? $this->current_page + 1 : FALSE;
+
+ // SQL values
+ $this->sql_offset = (int) ($this->current_page - 1) * $this->items_per_page;
+ $this->sql_limit = sprintf(' LIMIT %d OFFSET %d ', $this->items_per_page, $this->sql_offset);
+ }
+
+ /**
+ * Generates the HTML for the chosen pagination style.
+ *
+ * @param string pagination style
+ * @return string pagination html
+ */
+ public function render($style = NULL)
+ {
+ // Hide single page pagination
+ if ($this->auto_hide === TRUE AND $this->total_pages <= 1)
+ return '';
+
+ if ($style === NULL)
+ {
+ // Use default style
+ $style = $this->style;
+ }
+
+ // Return rendered pagination view
+ return View::factory($this->directory.$style, get_object_vars($this))->render();
+ }
+
+ /**
+ * Magically converts Pagination object to string.
+ *
+ * @return string pagination html
+ */
+ public function __toString()
+ {
+ return $this->render();
+ }
+
+ /**
+ * Magically gets a pagination variable.
+ *
+ * @param string variable key
+ * @return mixed variable value if the key is found
+ * @return void if the key is not found
+ */
+ public function __get($key)
+ {
+ if (isset($this->$key))
+ return $this->$key;
+ }
+
+ /**
+ * Adds a secondary interface for accessing properties, e.g. $pagination->total_pages().
+ * Note that $pagination->total_pages is the recommended way to access properties.
+ *
+ * @param string function name
+ * @return string
+ */
+ public function __call($func, $args = NULL)
+ {
+ return $this->__get($func);
+ }
+
+} // End Pagination Class
\ No newline at end of file
diff --git a/Server/system/libraries/Profiler.php b/Server/system/libraries/Profiler.php
new file mode 100644
index 0000000..9da053f
--- /dev/null
+++ b/Server/system/libraries/Profiler.php
@@ -0,0 +1,271 @@
+show OR (is_array($this->show) AND ! in_array($args[0], $this->show)))
+ return FALSE;
+
+ // Class name
+ $class = 'Profiler_'.ucfirst($method);
+
+ $class = new $class();
+
+ $this->profiles[$args[0]] = $class;
+
+ return $class;
+ }
+
+ /**
+ * Disables the profiler for this page only.
+ * Best used when profiler is autoloaded.
+ *
+ * @return void
+ */
+ public function disable()
+ {
+ // Removes itself from the event queue
+ Event::clear('system.display', array($this, 'render'));
+ }
+
+ /**
+ * Render the profiler. Output is added to the bottom of the page by default.
+ *
+ * @param boolean return the output if TRUE
+ * @return void|string
+ */
+ public function render($return = FALSE)
+ {
+ $start = microtime(TRUE);
+
+ $get = isset($_GET['profiler']) ? explode(',', $_GET['profiler']) : array();
+ $this->show = empty($get) ? Kohana::config('profiler.show') : $get;
+
+ Event::run('profiler.run', $this);
+
+ $styles = '';
+ foreach ($this->profiles as $profile)
+ {
+ $styles .= $profile->styles();
+ }
+
+ // Don't display if there's no profiles
+ if (empty($this->profiles))
+ return;
+
+ // Load the profiler view
+ $data = array
+ (
+ 'profiles' => $this->profiles,
+ 'styles' => $styles,
+ 'execution_time' => microtime(TRUE) - $start
+ );
+ $view = new View('kohana_profiler', $data);
+
+ // Return rendered view if $return is TRUE
+ if ($return === TRUE)
+ return $view->render();
+
+ // Add profiler data to the output
+ if (stripos(Kohana::$output, '') !== FALSE)
+ {
+ // Closing body tag was found, insert the profiler data before it
+ Kohana::$output = str_ireplace('