├── README.md ├── app ├── .htaccess ├── api │ ├── .htaccess │ ├── Slim │ │ ├── Exception │ │ │ ├── Pass.php │ │ │ ├── RequestSlash.php │ │ │ └── Stop.php │ │ ├── Http │ │ │ ├── Cookie.php │ │ │ ├── CookieJar.php │ │ │ ├── Request.php │ │ │ ├── Response.php │ │ │ └── Uri.php │ │ ├── Log.php │ │ ├── Logger.php │ │ ├── Route.php │ │ ├── Router.php │ │ ├── Session │ │ │ ├── Flash.php │ │ │ ├── Handler.php │ │ │ └── Handler │ │ │ │ └── Cookies.php │ │ ├── Slim.php │ │ └── View.php │ └── index.php ├── css │ └── styles.css ├── index.html ├── js │ ├── app.js │ ├── controllers.js │ ├── directives.js │ ├── filters.js │ └── services.js ├── lib │ ├── bootstrap │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── docs.css │ │ │ │ └── pygments-manni.css │ │ │ ├── ico │ │ │ │ ├── apple-touch-icon-114-precomposed.png │ │ │ │ ├── apple-touch-icon-144-precomposed.png │ │ │ │ ├── apple-touch-icon-57-precomposed.png │ │ │ │ ├── apple-touch-icon-72-precomposed.png │ │ │ │ └── favicon.png │ │ │ └── js │ │ │ │ ├── application.js │ │ │ │ ├── customizer.js │ │ │ │ ├── filesaver.js │ │ │ │ ├── holder.js │ │ │ │ ├── html5shiv.js │ │ │ │ ├── jquery.js │ │ │ │ ├── jszip.js │ │ │ │ ├── less.js │ │ │ │ ├── raw-files.js │ │ │ │ ├── respond.min.js │ │ │ │ └── uglify.js │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap.css │ │ │ └── bootstrap.min.css │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ └── js │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.js │ └── jquery │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map └── views │ ├── index.html │ ├── user-create.html │ ├── user-edit.html │ ├── user-view.html │ └── users.html └── install └── angular-mysql.sql /README.md: -------------------------------------------------------------------------------- 1 | # Angular MySQL Application 2 | 3 | This application is a sample CRUD application built with Angular.js and MySQL and a RESTful API. Based on the sample application angular-cellar by Christophe Coenraets: 4 | 5 | https://github.com/ccoenraets/angular-cellar 6 | 7 | ### Uses 8 | 9 | 1. Angular.js v1.2.19 10 | 2. MySQL 11 | 3. PHP Slim framework (API) 12 | 4. Bootstrap 3 13 | 14 | ### Set Up 15 | 16 | 1. Create a MySQL database named **angular-mysql** 17 | 2. Run `install/angular-mysql.sql` to create and populate the **users** table (includes four dummy users) 18 | 3. Create Apache virtual root and point to `/app` folder 19 | 20 | ### Notes 21 | 22 | The `.htaccess` file in the `/app` folder redirects all requests to `index.html` (needed by Angular application) 23 | 24 | ### API Endpoints 25 | 26 | GET /api/users Gets all users 27 | POST /api/users Creates a new user 28 | GET /api/users/:id Gets a user by Id 29 | GET /api/users/search/:query Searches user by name 30 | PUT /api/users/:id Updates a user by Id 31 | DELETE /api/users/:id Deletes a user by Id -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | # Some hosts may require you to use the `RewriteBase` directive. 4 | # If you need to use the `RewriteBase` directive, it should be the 5 | # absolute physical path to the directory that contains this htaccess file. 6 | # 7 | # RewriteBase / 8 | 9 | RewriteCond %{REQUEST_FILENAME} !-f 10 | RewriteRule ^(.*)$ index.html [QSA,L] -------------------------------------------------------------------------------- /app/api/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | # Some hosts may require you to use the `RewriteBase` directive. 4 | # If you need to use the `RewriteBase` directive, it should be the 5 | # absolute physical path to the directory that contains this htaccess file. 6 | # 7 | # RewriteBase / 8 | 9 | RewriteCond %{REQUEST_FILENAME} !-f 10 | RewriteRule ^(.*)$ index.php [QSA,L] -------------------------------------------------------------------------------- /app/api/Slim/Exception/Pass.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Pass Exception 35 | * 36 | * This Exception will cause the Router::dispatch method 37 | * to skip the current matching route and continue to the next 38 | * matching route. If no subsequent routes are found, a 39 | * HTTP 404 Not Found response will be sent to the client. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Exception_Pass extends Exception {} -------------------------------------------------------------------------------- /app/api/Slim/Exception/RequestSlash.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Request Slash Exception 35 | * 36 | * This Exception is thrown when Slim detects a matching route 37 | * (defined with a trailing slash) and the HTTP request 38 | * matches the route but does not have a trailing slash. This 39 | * exception will be caught in `Slim::run` and trigger a 301 redirect 40 | * to the same resource URI with a trailing slash. 41 | * 42 | * @package Slim 43 | * @author Josh Lockhart 44 | * @since Version 1.0 45 | */ 46 | class Slim_Exception_RequestSlash extends Exception {} -------------------------------------------------------------------------------- /app/api/Slim/Exception/Stop.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Stop Exception 35 | * 36 | * This Exception is thrown when the Slim application needs to abort 37 | * processing and return control flow to the outer PHP script. 38 | * 39 | * @package Slim 40 | * @author Josh Lockhart 41 | * @since Version 1.0 42 | */ 43 | class Slim_Exception_Stop extends Exception {} -------------------------------------------------------------------------------- /app/api/Slim/Http/Cookie.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Cookie 35 | * 36 | * Object-oriented representation of a Cookie to be sent in an HTTP response 37 | * 38 | * @package Slim 39 | * @author Josh Lockhart 40 | * @since Version 1.0 41 | */ 42 | class Slim_Http_Cookie { 43 | 44 | /** 45 | * @var string 46 | */ 47 | protected $name; 48 | 49 | /** 50 | * @var string 51 | */ 52 | protected $value; 53 | 54 | /** 55 | * @var int UNIX timestamp 56 | */ 57 | protected $expires; 58 | 59 | /** 60 | * @var string 61 | */ 62 | protected $path; 63 | 64 | /** 65 | * @var string 66 | */ 67 | protected $domain; 68 | 69 | /** 70 | * @var bool 71 | */ 72 | protected $secure; 73 | 74 | /** 75 | * @var bool 76 | */ 77 | protected $httponly; 78 | 79 | /** 80 | * Constructor 81 | * @param string $name The cookie name 82 | * @param string $value The cookie value 83 | * @param mixed $time The duration of the cookie; 84 | * If integer, should be a UNIX timestamp; 85 | * If string, converted to UNIX timestamp with `strtotime`; 86 | * @param string $path The path on the server in which the cookie will be available on 87 | * @param string $domain The domain that the cookie is available to 88 | * @param bool $secure Indicates that the cookie should only be transmitted over a secure 89 | * HTTPS connection from the client 90 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 91 | * @return void 92 | */ 93 | public function __construct( $name, $value = null, $expires = 0, $path = null, $domain = null, $secure = false, $httponly = false ) { 94 | $this->setName($name); 95 | $this->setValue($value); 96 | $this->setExpires($expires); 97 | $this->setPath($path); 98 | $this->setDomain($domain); 99 | $this->setSecure($secure); 100 | $this->setHttpOnly($httponly); 101 | } 102 | 103 | /** 104 | * Get cookie name 105 | * @return string 106 | */ 107 | public function getName() { 108 | return $this->name; 109 | } 110 | 111 | /** 112 | * Set cookie name 113 | * @param string $name 114 | * @return void 115 | */ 116 | public function setName( $name ) { 117 | $this->name = (string)$name; 118 | } 119 | 120 | /** 121 | * Get cookie value 122 | * @return string 123 | */ 124 | public function getValue() { 125 | return $this->value; 126 | } 127 | 128 | /** 129 | * Set cookie value 130 | * @param string $value 131 | * @return void 132 | */ 133 | public function setValue( $value ) { 134 | $this->value = (string)$value; 135 | } 136 | 137 | /** 138 | * Get cookie expiration time 139 | * @return int UNIX timestamp 140 | */ 141 | public function getExpires() { 142 | return $this->expires; 143 | } 144 | 145 | /** 146 | * Set cookie expiration time 147 | * @param string|int Cookie expiration time 148 | * @return void 149 | */ 150 | public function setExpires( $time ) { 151 | $this->expires = is_string($time) ? strtotime($time) : (int)$time; 152 | } 153 | 154 | /** 155 | * Get cookie path 156 | * @return string 157 | */ 158 | public function getPath() { 159 | return $this->path; 160 | } 161 | 162 | /** 163 | * Set cookie path 164 | * @param string $path 165 | * @return void 166 | */ 167 | public function setPath( $path ) { 168 | $this->path = (string)$path; 169 | } 170 | 171 | /** 172 | * Get cookie domain 173 | * @return string 174 | */ 175 | public function getDomain() { 176 | return $this->domain; 177 | } 178 | 179 | /** 180 | * Set cookie domain 181 | * @param string $domain 182 | * @return void 183 | */ 184 | public function setDomain( $domain ) { 185 | $this->domain = (string)$domain; 186 | } 187 | 188 | /** 189 | * Is cookie sent only if SSL/HTTPS is used? 190 | * @return bool 191 | */ 192 | public function getSecure() { 193 | return $this->secure; 194 | } 195 | 196 | /** 197 | * Set whether cookie is sent only if SSL/HTTPS is used 198 | * @param bool $secure 199 | * @return void 200 | */ 201 | public function setSecure( $secure ) { 202 | $this->secure = (bool)$secure; 203 | } 204 | 205 | /** 206 | * Is cookie sent with HTTP protocol only? 207 | * @return bool 208 | */ 209 | public function getHttpOnly() { 210 | return $this->httponly; 211 | } 212 | 213 | /** 214 | * Set whether cookie is sent with HTTP protocol only 215 | * @param bool $httponly 216 | * @return void 217 | */ 218 | public function setHttpOnly( $httponly ) { 219 | $this->httponly = (bool)$httponly; 220 | } 221 | 222 | } -------------------------------------------------------------------------------- /app/api/Slim/Http/CookieJar.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Cooke Jar 35 | * 36 | * Used to manage signed, encrypted Cookies. Provides: 37 | * 38 | * - Cookie integrity and authenticity with HMAC 39 | * - Confidentiality with symmetric encryption 40 | * - Protection from replay attack if using SSL or TLS 41 | * - Protection from interception if using SSL or TLS 42 | * 43 | * This code was originally called "BigOrNot_CookieManager" and written by 44 | * Matthieu Huguet released under "CopyLeft" license. I have cleaned up the 45 | * code formatting to conform with Slim Framework contributor guidelines and 46 | * added additional code where necessary to play nice with Slim Cookie objects. 47 | * 48 | * Requirements: 49 | * 50 | * - libmcrypt > 2.4.x 51 | * 52 | * @author Matthies Huguet 53 | */ 54 | class Slim_Http_CookieJar { 55 | 56 | /** 57 | * @var string Server secret key 58 | */ 59 | protected $_secret = ''; 60 | 61 | /** 62 | * @var int Cryptographic algorithm used to encrypt cookies data 63 | */ 64 | protected $_algorithm = MCRYPT_RIJNDAEL_256; 65 | 66 | /** 67 | * @var int Cryptographic mode (CBC, CFB ...) 68 | */ 69 | protected $_mode = MCRYPT_MODE_CBC; 70 | 71 | /** 72 | * @var resource mcrypt module resource 73 | */ 74 | protected $_cryptModule = null; 75 | 76 | /** 77 | * @var bool Enable high confidentiality for cookie value (symmetric encryption) 78 | */ 79 | protected $_highConfidentiality = true; 80 | 81 | /** 82 | * @var bool Enable SSL support 83 | */ 84 | protected $_ssl = false; 85 | 86 | /** 87 | * @var array[Cookie] Cookie objects 88 | */ 89 | protected $_cookies = array(); 90 | 91 | /** 92 | * Constructor 93 | * 94 | * Initialize cookie manager and mcrypt module. 95 | * 96 | * @param string $secret Server's secret key 97 | * @param array $config 98 | * @throws Exception If secret key is empty 99 | * @throws Exception If unable to open mcypt module 100 | */ 101 | public function __construct( $secret, $config = null ) { 102 | if ( empty($secret) ) { 103 | throw new Exception('You must provide a secret key'); 104 | } 105 | $this->_secret = $secret; 106 | if ( $config !== null && !is_array($config) ) { 107 | throw new Exception('Config must be an array'); 108 | } 109 | if ( is_array($config) ) { 110 | if ( isset($config['high_confidentiality']) ) { 111 | $this->_highConfidentiality = $config['high_confidentiality']; 112 | } 113 | if ( isset($config['mcrypt_algorithm']) ) { 114 | $this->_algorithm = $config['mcrypt_algorithm']; 115 | } 116 | if ( isset($config['mcrypt_mode']) ) { 117 | $this->_mode = $config['mcrypt_mode']; 118 | } 119 | if ( isset($config['enable_ssl']) ) { 120 | $this->_ssl = $config['enable_ssl']; 121 | } 122 | } 123 | if ( extension_loaded('mcrypt') ) { 124 | $this->_cryptModule = mcrypt_module_open($this->_algorithm, '', $this->_mode, ''); 125 | if ( $this->_cryptModule === false ) { 126 | throw new Exception('Error while loading mcrypt module'); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * Get the high confidentiality mode 133 | * 134 | * @return bool TRUE if cookie data encryption is enabled, or FALSE if it isn't 135 | */ 136 | public function getHighConfidentiality() { 137 | return $this->_highConfidentiality; 138 | } 139 | 140 | /** 141 | * Enable or disable cookie data encryption 142 | * 143 | * @param bool $enable TRUE to enable, FALSE to disable 144 | * @return CookieJar 145 | */ 146 | public function setHighConfidentiality( $enable ) { 147 | $this->_highConfidentiality = (bool)$enable; 148 | return $this; 149 | } 150 | 151 | /** 152 | * Get the SSL status (enabled or disabled?) 153 | * 154 | * @return bool TRUE if SSL support is enabled, or FALSE if it isn't 155 | */ 156 | public function getSSL() { 157 | return $this->_ssl; 158 | } 159 | 160 | /** 161 | * Enable SSL support (not enabled by default) 162 | * 163 | * Pro: Protect against replay attack 164 | * Con: Cookie's lifetime is limited to SSL session's lifetime 165 | * 166 | * @param bool $enable TRUE to enable, FALSE to disable 167 | * @return CookieJar 168 | */ 169 | public function setSSL( $enable ) { 170 | $this->_ssl = (bool)$enable; 171 | return $this; 172 | } 173 | 174 | /** 175 | * Get Cookies for Response 176 | * 177 | * @author Josh Lockhart 178 | * @return array[Cookie] 179 | */ 180 | public function getResponseCookies() { 181 | return $this->_cookies; 182 | } 183 | 184 | /** 185 | * Get Cookie with name for Response 186 | * 187 | * @author Josh Lockhart 188 | * @param string $cookiename The name of the Cookie 189 | * @return Cookie|null Cookie, or NULL if Cookie with name not found 190 | */ 191 | public function getResponseCookie( $cookiename ) { 192 | return isset($this->_cookies[$cookiename]) ? $this->_cookies[$cookiename] : null; 193 | } 194 | 195 | /** 196 | * Set a secure cookie 197 | * 198 | * @param string $name Cookie name 199 | * @param string $value Cookie value 200 | * @param string $username User identifier 201 | * @param integer $expire Expiration time 202 | * @param string $path Cookie path 203 | * @param string $domain Cookie domain 204 | * @param bool $secure When TRUE, send the cookie only on a secure connection 205 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 206 | */ 207 | public function setCookie( $cookiename, $value, $username, $expire = 0, $path = '/', $domain = '', $secure = false, $httponly = null ) { 208 | $secureValue = extension_loaded('mcrypt') ? $this->_secureCookieValue($value, $username, $expire) : $value; 209 | $this->setClassicCookie($cookiename, $secureValue, $expire, $path, $domain, $secure, $httponly); 210 | } 211 | 212 | /** 213 | * Delete a cookie 214 | * 215 | * @param string $name Cookie name 216 | * @param string $path Cookie path 217 | * @param string $domain Cookie domain 218 | * @param bool $secure When TRUE, send the cookie only on a secure connection 219 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 220 | */ 221 | public function deleteCookie( $name, $path = '/', $domain = '', $secure = false, $httponly = null ) { 222 | $expire = 315554400; /* 1980-01-01 */ 223 | $this->_cookies[$name] = new Slim_Http_Cookie($name, '', $expire, $path, $domain, $secure, $httponly); 224 | //setcookie($name, '', $expire, $path, $domain, $secure, $httponly); 225 | } 226 | 227 | /** 228 | * Get a secure cookie value 229 | * 230 | * Verify the integrity of cookie data and decrypt it. If the cookie 231 | * is invalid, it can be automatically destroyed (default behaviour) 232 | * 233 | * @param string $cookiename Cookie name 234 | * @param bool $delete Destroy the cookie if invalid? 235 | * @return string|false The Cookie value, or FALSE if Cookie invalid 236 | */ 237 | public function getCookieValue( $cookiename, $deleteIfInvalid = true ) { 238 | if ( $this->cookieExists($cookiename) ) { 239 | if ( extension_loaded('mcrypt') ) { 240 | $cookieValues = explode('|', $_COOKIE[$cookiename]); 241 | if ( (count($cookieValues) === 4) && ($cookieValues[1] == 0 || $cookieValues[1] >= time()) ) { 242 | $key = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1], $this->_secret); 243 | $cookieData = base64_decode($cookieValues[2]); 244 | if ( $cookieData !== '' && $this->getHighConfidentiality() ) { 245 | $data = $this->_decrypt($cookieData, $key, md5($cookieValues[1])); 246 | } else { 247 | $data = $cookieData; 248 | } 249 | if ( $this->_ssl && isset($_SERVER['SSL_SESSION_ID']) ) { 250 | $verifKey = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1] . $data . $_SERVER['SSL_SESSION_ID'], $key); 251 | } else { 252 | $verifKey = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1] . $data, $key); 253 | } 254 | if ( $verifKey == $cookieValues[3] ) { 255 | return $data; 256 | } 257 | } 258 | } else { 259 | return $_COOKIE[$cookiename]; 260 | } 261 | } 262 | if ( $deleteIfInvalid ) { 263 | $this->deleteCookie($cookiename); 264 | } 265 | return false; 266 | } 267 | 268 | /** 269 | * Send a classic (unsecure) cookie 270 | * 271 | * @param string $name Cookie name 272 | * @param string $value Cookie value 273 | * @param integer $expire Expiration time 274 | * @param string $path Cookie path 275 | * @param string $domain Cookie domain 276 | * @param bool $secure When TRUE, send the cookie only on a secure connection 277 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 278 | */ 279 | public function setClassicCookie( $cookiename, $value, $expire = 0, $path = '/', $domain = '', $secure = false, $httponly = null ) { 280 | /* httponly option is only available for PHP version >= 5.2 */ 281 | if ( $httponly === null ) { 282 | $this->_cookies[$cookiename] = new Slim_Http_Cookie($cookiename, $value, $expire, $path, $domain, $secure); 283 | //setcookie($cookiename, $value, $expire, $path, $domain, $secure); 284 | } else { 285 | $this->_cookies[$cookiename] = new Slim_Http_Cookie($cookiename, $value, $expire, $path, $domain, $secure, $httponly); 286 | //setcookie($cookiename, $value, $expire, $path, $domain, $secure, $httponly); 287 | } 288 | } 289 | 290 | /** 291 | * Verify if a cookie exists 292 | * 293 | * @param string $cookiename 294 | * @return bool TRUE if cookie exist, or FALSE if not 295 | */ 296 | public function cookieExists($cookiename) { 297 | return isset($_COOKIE[$cookiename]); 298 | } 299 | 300 | /** 301 | * Secure a cookie value 302 | * 303 | * The initial value is transformed with this protocol: 304 | * 305 | * secureValue = username|expire|base64((value)k,expire)|HMAC(user|expire|value,k) 306 | * where k = HMAC(user|expire, sk) 307 | * and sk is server's secret key 308 | * (value)k,md5(expire) is the result an cryptographic function (ex: AES256) on "value" with key k and initialisation vector = md5(expire) 309 | * 310 | * @param string $value Unsecure value 311 | * @param string $username User identifier 312 | * @param integer $expire Expiration time 313 | * @return string Secured value 314 | */ 315 | protected function _secureCookieValue( $value, $username, $expire ) { 316 | if ( is_string($expire) ) { 317 | $expire = strtotime($expire); 318 | } 319 | $key = hash_hmac('sha1', $username . $expire, $this->_secret); 320 | if ( $value !== '' && $this->getHighConfidentiality() ) { 321 | $encryptedValue = base64_encode($this->_encrypt($value, $key, md5($expire))); 322 | } else { 323 | $encryptedValue = base64_encode($value); 324 | } 325 | if ( $this->_ssl && isset($_SERVER['SSL_SESSION_ID']) ) { 326 | $verifKey = hash_hmac('sha1', $username . $expire . $value . $_SERVER['SSL_SESSION_ID'], $key); 327 | } else { 328 | $verifKey = hash_hmac('sha1', $username . $expire . $value, $key); 329 | } 330 | $result = array($username, $expire, $encryptedValue, $verifKey); 331 | return implode('|', $result); 332 | } 333 | 334 | /** 335 | * Encrypt a given data with a given key and a given initialisation vector 336 | * 337 | * @param string $data Data to crypt 338 | * @param string $key Secret key 339 | * @param string $iv Initialisation vector 340 | * @return string Encrypted data 341 | */ 342 | protected function _encrypt( $data, $key, $iv ) { 343 | $iv = $this->_validateIv($iv); 344 | $key = $this->_validateKey($key); 345 | mcrypt_generic_init($this->_cryptModule, $key, $iv); 346 | $res = @mcrypt_generic($this->_cryptModule, $data); 347 | mcrypt_generic_deinit($this->_cryptModule); 348 | return $res; 349 | } 350 | 351 | /** 352 | * Decrypt a given data with a given key and a given initialisation vector 353 | * 354 | * @param string $data Data to crypt 355 | * @param string $key Secret key 356 | * @param string $iv Initialisation vector 357 | * @return string Encrypted data 358 | */ 359 | protected function _decrypt( $data, $key, $iv ) { 360 | $iv = $this->_validateIv($iv); 361 | $key = $this->_validateKey($key); 362 | mcrypt_generic_init($this->_cryptModule, $key, $iv); 363 | $decryptedData = mdecrypt_generic($this->_cryptModule, $data); 364 | $res = str_replace("\x0", '', $decryptedData); 365 | mcrypt_generic_deinit($this->_cryptModule); 366 | return $res; 367 | } 368 | 369 | /** 370 | * Validate Initialization vector 371 | * 372 | * If given IV is too long for the selected mcrypt algorithm, it will be truncated 373 | * 374 | * @param string $iv Initialization vector 375 | * @return string 376 | */ 377 | protected function _validateIv($iv) { 378 | $ivSize = mcrypt_enc_get_iv_size($this->_cryptModule); 379 | if ( strlen($iv) > $ivSize ) { 380 | $iv = substr($iv, 0, $ivSize); 381 | } 382 | return $iv; 383 | } 384 | 385 | /** 386 | * Validate key 387 | * 388 | * If given key is too long for the selected mcrypt algorithm, it will be truncated 389 | * 390 | * @param string $key key 391 | * @param string 392 | */ 393 | protected function _validateKey($key) { 394 | $keySize = mcrypt_enc_get_key_size($this->_cryptModule); 395 | if ( strlen($key) > $keySize ) { 396 | $key = substr($key, 0, $keySize); 397 | } 398 | return $key; 399 | } 400 | 401 | } 402 | -------------------------------------------------------------------------------- /app/api/Slim/Http/Request.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Request 35 | * 36 | * Object-oriented representation of an HTTP request. This class 37 | * is responsible for parsing the raw HTTP request into a format 38 | * usable by the Slim application. 39 | * 40 | * This class will automatically remove slashes from GET, POST, PUT, 41 | * and Cookie data if magic quotes are enabled. 42 | * 43 | * @package Slim 44 | * @author Josh Lockhart 45 | * @author Kris Jordan 46 | * @since Version 1.0 47 | */ 48 | class Slim_Http_Request { 49 | 50 | const METHOD_HEAD = 'HEAD'; 51 | const METHOD_GET = 'GET'; 52 | const METHOD_POST = 'POST'; 53 | const METHOD_PUT = 'PUT'; 54 | const METHOD_DELETE = 'DELETE'; 55 | const METHOD_OPTIONS = 'OPTIONS'; 56 | const METHOD_OVERRIDE = '_METHOD'; 57 | 58 | /** 59 | * @var string Request method (ie. "GET", "POST", "PUT", "DELETE", "HEAD") 60 | */ 61 | protected $method; 62 | 63 | /** 64 | * @var array Key-value array of HTTP request headers 65 | */ 66 | protected $headers; 67 | 68 | /** 69 | * @var array Names of additional headers to parse from the current 70 | * HTTP request that are not prefixed with "HTTP_" 71 | */ 72 | protected $additionalHeaders = array('content-type', 'content-length', 'php-auth-user', 'php-auth-pw', 'auth-type', 'x-requested-with'); 73 | 74 | /** 75 | * @var array Key-value array of cookies sent with the 76 | * current HTTP request 77 | */ 78 | protected $cookies; 79 | 80 | /** 81 | * @var array Key-value array of HTTP GET parameters 82 | */ 83 | protected $get; 84 | 85 | /** 86 | * @var array Key-value array of HTTP POST parameters 87 | */ 88 | protected $post; 89 | 90 | /** 91 | * @var array Key-value array of HTTP PUT parameters 92 | */ 93 | protected $put; 94 | 95 | /** 96 | * @var string Raw body of HTTP request 97 | */ 98 | protected $body; 99 | 100 | /** 101 | * @var string Content type of HTTP request 102 | */ 103 | protected $contentType; 104 | 105 | /** 106 | * @var string Resource URI (ie. "/person/1") 107 | */ 108 | protected $resource; 109 | 110 | /** 111 | * @var string The root URI of the Slim application without trailing slash. 112 | * This will be "" if the app is installed at the web 113 | * document root. If the app is installed in a 114 | * sub-directory "/foo", this will be "/foo". 115 | */ 116 | protected $root; 117 | 118 | /** 119 | * Constructor 120 | */ 121 | public function __construct() { 122 | $this->method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : false; 123 | $this->headers = $this->loadHttpHeaders(); 124 | $this->body = @file_get_contents('php://input'); 125 | $this->get = self::stripSlashesIfMagicQuotes($_GET); 126 | $this->post = self::stripSlashesIfMagicQuotes($_POST); 127 | $this->put = self::stripSlashesIfMagicQuotes($this->loadPutParameters()); 128 | $this->cookies = self::stripSlashesIfMagicQuotes($_COOKIE); 129 | $this->root = Slim_Http_Uri::getBaseUri(true); 130 | $this->resource = Slim_Http_Uri::getUri(true); 131 | $this->checkForHttpMethodOverride(); 132 | } 133 | 134 | /** 135 | * Is this a GET request? 136 | * @return bool 137 | */ 138 | public function isGet() { 139 | return $this->method === self::METHOD_GET; 140 | } 141 | 142 | /** 143 | * Is this a POST request? 144 | * @return bool 145 | */ 146 | public function isPost() { 147 | return $this->method === self::METHOD_POST; 148 | } 149 | 150 | /** 151 | * Is this a PUT request? 152 | * @return bool 153 | */ 154 | public function isPut() { 155 | return $this->method === self::METHOD_PUT; 156 | } 157 | 158 | /** 159 | * Is this a DELETE request? 160 | * @return bool 161 | */ 162 | public function isDelete() { 163 | return $this->method === self::METHOD_DELETE; 164 | } 165 | 166 | /** 167 | * Is this a HEAD request? 168 | * @return bool 169 | */ 170 | public function isHead() { 171 | return $this->method === self::METHOD_HEAD; 172 | } 173 | 174 | /** 175 | * Is this a OPTIONS request? 176 | * @return bool 177 | */ 178 | public function isOptions() { 179 | return $this->method === self::METHOD_OPTIONS; 180 | } 181 | 182 | /** 183 | * Is this a XHR request? 184 | * @return bool 185 | */ 186 | public function isAjax() { 187 | return ( $this->params('isajax') || $this->headers('X_REQUESTED_WITH') === 'XMLHttpRequest' ); 188 | } 189 | 190 | /** 191 | * Fetch a PUT|POST|GET parameter value 192 | * 193 | * The preferred method to fetch the value of a 194 | * PUT, POST, or GET parameter (searched in that order). 195 | * 196 | * @param string $key The paramter name 197 | * @return string|null The value of parameter, or NULL if parameter not found 198 | */ 199 | public function params( $key ) { 200 | foreach ( array('put', 'post', 'get') as $dataSource ) { 201 | $source = $this->$dataSource; 202 | if ( isset($source[(string)$key]) ) { 203 | return $source[(string)$key]; 204 | } 205 | } 206 | return null; 207 | } 208 | 209 | /** 210 | * Fetch GET parameter(s) 211 | * @param string $key Name of parameter 212 | * @return array|string|null All parameters, parameter value if $key 213 | * and parameter exists, or NULL if $key 214 | * and parameter does not exist. 215 | */ 216 | public function get( $key = null ) { 217 | return $this->arrayOrArrayValue($this->get, $key); 218 | } 219 | 220 | /** 221 | * Fetch POST parameter(s) 222 | * @param string $key Name of parameter 223 | * @return array|string|null All parameters, parameter value if $key 224 | * and parameter exists, or NULL if $key 225 | * and parameter does not exist. 226 | */ 227 | public function post( $key = null ) { 228 | return $this->arrayOrArrayValue($this->post, $key); 229 | } 230 | 231 | /** 232 | * Fetch PUT parameter(s) 233 | * @param string $key Name of parameter 234 | * @return array|string|null All parameters, parameter value if $key 235 | * and parameter exists, or NULL if $key 236 | * and parameter does not exist. 237 | */ 238 | public function put( $key = null ) { 239 | return $this->arrayOrArrayValue($this->put, $key); 240 | } 241 | 242 | /** 243 | * Fetch COOKIE value(s) 244 | * @param string $key The cookie name 245 | * @return array|string|null All parameters, parameter value if $key 246 | * and parameter exists, or NULL if $key 247 | * and parameter does not exist. 248 | */ 249 | public function cookies( $key = null ) { 250 | return $this->arrayOrArrayValue($this->cookies, $key); 251 | } 252 | 253 | /** 254 | * Get HTTP request header 255 | * @param string $key The header name 256 | * @return array|string|null All parameters, parameter value if $key 257 | * and parameter exists, or NULL if $key 258 | * and parameter does not exist. 259 | */ 260 | public function headers( $key = null ) { 261 | return is_null($key) ? $this->headers : $this->arrayOrArrayValue($this->headers, $this->convertHttpHeaderName($key)); 262 | } 263 | 264 | /** 265 | * Get HTTP request body 266 | * @return string|false String, or FALSE if body could not be read 267 | */ 268 | public function getBody() { 269 | return $this->body; 270 | } 271 | 272 | /** 273 | * Get HTTP method 274 | * @return string 275 | */ 276 | public function getMethod() { 277 | return $this->method; 278 | } 279 | 280 | /** 281 | * Get HTTP request content type 282 | * @return string 283 | */ 284 | public function getContentType() { 285 | if ( !isset($this->contentType) ) { 286 | $contentType = 'application/x-www-form-urlencoded'; 287 | $header = $this->headers('CONTENT_TYPE'); 288 | if ( !is_null($header) ) { 289 | $headerParts = preg_split('/\s*;\s*/', $header); 290 | $contentType = $headerParts[0]; 291 | } 292 | $this->contentType = $contentType; 293 | } 294 | return $this->contentType; 295 | } 296 | 297 | /** 298 | * Get HTTP request resource URI 299 | * @return string 300 | */ 301 | public function getResourceUri() { 302 | return $this->resource; 303 | } 304 | 305 | /** 306 | * Get HTTP request root URI 307 | * @return string 308 | */ 309 | public function getRootUri() { 310 | return $this->root; 311 | } 312 | 313 | /** 314 | * Fetch array or array value 315 | * @param array $array 316 | * @param string $key 317 | * @return array|mixed Array if key is null, else array value 318 | */ 319 | protected function arrayOrArrayValue( array &$array, $key = null ) { 320 | return is_null($key) ? $array : $this->arrayValueForKey($array, $key); 321 | } 322 | 323 | /** 324 | * Fetch value from array 325 | * @return mixed|null 326 | */ 327 | protected function arrayValueForKey( array &$array, $key ) { 328 | return isset($array[(string)$key]) ? $array[(string)$key] : null; 329 | } 330 | 331 | /** 332 | * Strip slashes from string or array of strings 333 | * @param array|string $rawData 334 | * @return array|string 335 | */ 336 | public static function stripSlashesIfMagicQuotes( $rawData ) { 337 | if ( get_magic_quotes_gpc() ) { 338 | return is_array($rawData) ? array_map(array('self', 'stripSlashesIfMagicQuotes'), $rawData) : stripslashes($rawData); 339 | } else { 340 | return $rawData; 341 | } 342 | } 343 | 344 | /** 345 | * Get PUT parameters 346 | * @return array Key-value array of HTTP request PUT parameters 347 | */ 348 | protected function loadPutParameters() { 349 | if ( $this->getContentType() === 'application/x-www-form-urlencoded' ) { 350 | $input = is_string($this->body) ? $this->body : ''; 351 | if ( function_exists('mb_parse_str') ) { 352 | mb_parse_str($input, $output); 353 | } else { 354 | parse_str($input, $output); 355 | } 356 | return $output; 357 | } else { 358 | return array(); 359 | } 360 | } 361 | 362 | /** 363 | * Get HTTP request headers 364 | * @return array Key-value array of HTTP request headers 365 | */ 366 | protected function loadHttpHeaders() { 367 | $headers = array(); 368 | foreach ( $_SERVER as $key => $value ) { 369 | $key = $this->convertHttpHeaderName($key); 370 | if ( strpos($key, 'http-') === 0 || in_array($key, $this->additionalHeaders) ) { 371 | $name = str_replace('http-', '', $key); 372 | $headers[$name] = $value; 373 | } 374 | } 375 | return $headers; 376 | } 377 | 378 | /** 379 | * Convert HTTP header name 380 | * @return string 381 | */ 382 | protected function convertHttpHeaderName( $name ) { 383 | return str_replace('_', '-', strtolower($name)); 384 | } 385 | 386 | /** 387 | * Check for HTTP request method override 388 | * 389 | * Because traditional web browsers do not support PUT and DELETE 390 | * HTTP methods, we use a hidden form input field to 391 | * mimic PUT and DELETE requests. We check for this override here. 392 | * 393 | * @return void 394 | */ 395 | protected function checkForHttpMethodOverride() { 396 | if ( isset($this->post[self::METHOD_OVERRIDE]) ) { 397 | $this->method = $this->post[self::METHOD_OVERRIDE]; 398 | unset($this->post[self::METHOD_OVERRIDE]); 399 | if ( $this->isPut() ) { 400 | $this->put = $this->post; 401 | } 402 | } 403 | } 404 | 405 | } -------------------------------------------------------------------------------- /app/api/Slim/Http/Response.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Response 35 | * 36 | * Object-oriented representation of an HTTP response that is 37 | * returned to the client. This class is responsible for: 38 | * 39 | * - HTTP response status 40 | * - HTTP response body 41 | * - HTTP response headers 42 | * - HTTP response cookies 43 | * 44 | * @package Slim 45 | * @author Josh Lockhart 46 | * @author Kris Jordan 47 | * @since Version 1.0 48 | */ 49 | class Slim_Http_Response { 50 | 51 | /** 52 | * @var Slim_Http_Request 53 | */ 54 | protected $request; 55 | 56 | /** 57 | * @var string 58 | */ 59 | protected $httpVersion = '1.1'; 60 | 61 | /** 62 | * @var int HTTP status code 63 | */ 64 | protected $status = 200; 65 | 66 | /** 67 | * @var array Key-value array of HTTP response headers 68 | */ 69 | protected $headers = array(); 70 | 71 | /** 72 | * @var string HTTP response body 73 | */ 74 | protected $body = ''; 75 | 76 | /** 77 | * @var int Length of HTTP response body 78 | */ 79 | protected $length = 0; 80 | 81 | /** 82 | * @var array HTTP response codes and messages 83 | */ 84 | protected static $messages = array( 85 | //Informational 1xx 86 | 100 => '100 Continue', 87 | 101 => '101 Switching Protocols', 88 | //Successful 2xx 89 | 200 => '200 OK', 90 | 201 => '201 Created', 91 | 202 => '202 Accepted', 92 | 203 => '203 Non-Authoritative Information', 93 | 204 => '204 No Content', 94 | 205 => '205 Reset Content', 95 | 206 => '206 Partial Content', 96 | //Redirection 3xx 97 | 300 => '300 Multiple Choices', 98 | 301 => '301 Moved Permanently', 99 | 302 => '302 Found', 100 | 303 => '303 See Other', 101 | 304 => '304 Not Modified', 102 | 305 => '305 Use Proxy', 103 | 306 => '306 (Unused)', 104 | 307 => '307 Temporary Redirect', 105 | //Client Error 4xx 106 | 400 => '400 Bad Request', 107 | 401 => '401 Unauthorized', 108 | 402 => '402 Payment Required', 109 | 403 => '403 Forbidden', 110 | 404 => '404 Not Found', 111 | 405 => '405 Method Not Allowed', 112 | 406 => '406 Not Acceptable', 113 | 407 => '407 Proxy Authentication Required', 114 | 408 => '408 Request Timeout', 115 | 409 => '409 Conflict', 116 | 410 => '410 Gone', 117 | 411 => '411 Length Required', 118 | 412 => '412 Precondition Failed', 119 | 413 => '413 Request Entity Too Large', 120 | 414 => '414 Request-URI Too Long', 121 | 415 => '415 Unsupported Media Type', 122 | 416 => '416 Requested Range Not Satisfiable', 123 | 417 => '417 Expectation Failed', 124 | 422 => '422 Unprocessable Entity', 125 | 423 => '423 Locked', 126 | //Server Error 5xx 127 | 500 => '500 Internal Server Error', 128 | 501 => '501 Not Implemented', 129 | 502 => '502 Bad Gateway', 130 | 503 => '503 Service Unavailable', 131 | 504 => '504 Gateway Timeout', 132 | 505 => '505 HTTP Version Not Supported' 133 | ); 134 | 135 | /** 136 | * @var CookieJar Manages Cookies to be sent with this Response 137 | */ 138 | protected $cookieJar; 139 | 140 | /** 141 | * Constructor 142 | */ 143 | public function __construct( Slim_Http_Request $req ) { 144 | $this->request = $req; 145 | $this->header('Content-Type', 'text/html'); 146 | } 147 | 148 | /** 149 | * Set and/or get the HTTP response version 150 | * @param string $version 151 | * @return void 152 | * @throws InvalidArgumentException If argument is not a valid HTTP version 153 | */ 154 | public function httpVersion( $version = null ) { 155 | if ( $version ) { 156 | $version = (string)$version; 157 | if ( $version === '1.0' || $version === '1.1' ) { 158 | $this->httpVersion = $version; 159 | } else { 160 | throw new InvalidArgumentException('Invalid HTTP version in Response object'); 161 | } 162 | } 163 | return $this->httpVersion; 164 | } 165 | 166 | /** 167 | * Set and/or get the HTTP response status code 168 | * @param int $status 169 | * @return int 170 | * @throws InvalidArgumentException If argument is not a valid HTTP status code 171 | */ 172 | public function status( $status = null ) { 173 | if ( !is_null($status) ) { 174 | if ( !in_array(intval($status), array_keys(self::$messages)) ) { 175 | throw new InvalidArgumentException('Cannot set Response status. Provided status code "' . $status . '" is not a valid HTTP response code.'); 176 | } 177 | $this->status = intval($status); 178 | } 179 | return $this->status; 180 | } 181 | 182 | /** 183 | * Get HTTP response headers 184 | * @return array 185 | */ 186 | public function headers() { 187 | return $this->headers; 188 | } 189 | 190 | /** 191 | * Get and/or set an HTTP response header 192 | * @param string $key The header name 193 | * @param string $value The header value 194 | * @return string|null The header value, or NULL if header not set 195 | */ 196 | public function header( $key, $value = null ) { 197 | if ( !is_null($value) ) { 198 | $this->headers[$key] = $value; 199 | } 200 | return isset($this->headers[$key]) ? $this->headers[$key] : null; 201 | } 202 | 203 | /** 204 | * Set the HTTP response body 205 | * @param string $body The new HTTP response body 206 | * @return string The new HTTP response body 207 | */ 208 | public function body( $body = null ) { 209 | if ( !is_null($body) ) { 210 | $this->body = ''; 211 | $this->length = 0; 212 | $this->write($body); 213 | } 214 | return $this->body; 215 | } 216 | 217 | /** 218 | * Append the HTTP response body 219 | * @param string $body Content to append to the current HTTP response body 220 | * @return string The updated HTTP response body 221 | */ 222 | public function write( $body ) { 223 | $body = (string)$body; 224 | $this->length += strlen($body); 225 | $this->body .= $body; 226 | $this->header('Content-Length', $this->length); 227 | return $body; 228 | } 229 | 230 | /** 231 | * Set cookie jar 232 | * @param Slim_Http_CookieJar $cookieJar 233 | * @return void 234 | */ 235 | public function setCookieJar( Slim_Http_CookieJar $cookieJar ) { 236 | $this->cookieJar = $cookieJar; 237 | } 238 | 239 | /** 240 | * Get cookie jar 241 | * @return Slim_Http_CookieJar 242 | */ 243 | public function getCookieJar() { 244 | return $this->cookieJar; 245 | } 246 | 247 | /** 248 | * Finalize response headers before response is sent 249 | * @return void 250 | */ 251 | public function finalize() { 252 | if ( in_array($this->status, array(204, 304)) ) { 253 | $this->body(''); 254 | unset($this->headers['Content-Type']); 255 | } 256 | } 257 | 258 | /** 259 | * Get message for HTTP status code 260 | * @return string|null 261 | */ 262 | public static function getMessageForCode( $status ) { 263 | return isset(self::$messages[$status]) ? self::$messages[$status] : null; 264 | } 265 | 266 | /** 267 | * Can this HTTP response have a body? 268 | * @return bool 269 | */ 270 | public function canHaveBody() { 271 | return ( $this->status < 100 || $this->status >= 200 ) && $this->status != 204 && $this->status != 304; 272 | } 273 | 274 | /** 275 | * Send headers for HTTP response 276 | * @return void 277 | */ 278 | protected function sendHeaders() { 279 | //Finalize response 280 | $this->finalize(); 281 | 282 | if ( substr(PHP_SAPI, 0, 3) === 'cgi') { 283 | //Send Status header if running with fastcgi 284 | header('Status: ' . self::getMessageForCode($this->status())); 285 | } else { 286 | //Else send HTTP message 287 | header(sprintf('HTTP/%s %s', $this->httpVersion, self::getMessageForCode($this->status()))); 288 | } 289 | 290 | //Send headers 291 | foreach ( $this->headers() as $name => $value ) { 292 | header("$name: $value"); 293 | } 294 | 295 | //Send cookies 296 | foreach ( $this->getCookieJar()->getResponseCookies() as $name => $cookie ) { 297 | setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpires(), $cookie->getPath(), $cookie->getDomain(), $cookie->getSecure(), $cookie->getHttpOnly()); 298 | } 299 | 300 | //Flush all output to client 301 | flush(); 302 | } 303 | 304 | /** 305 | * Send HTTP response 306 | * 307 | * This method will set Response headers, set Response cookies, 308 | * and `echo` the Response body to the current output buffer. 309 | * 310 | * @return void 311 | */ 312 | public function send() { 313 | if ( !headers_sent() ) { 314 | $this->sendHeaders(); 315 | } 316 | if ( $this->canHaveBody() && $this->request->isHead() === false ) { 317 | echo $this->body; 318 | } 319 | } 320 | 321 | } -------------------------------------------------------------------------------- /app/api/Slim/Http/Uri.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Uri 35 | * 36 | * Parses base uri and application uri from Request. 37 | * 38 | * @package Slim 39 | * @author Josh Lockhart 40 | * @since Version 1.0 41 | */ 42 | class Slim_Http_Uri { 43 | 44 | /** 45 | * @var string "https" or "http" 46 | */ 47 | protected static $scheme; 48 | 49 | /** 50 | * @var string 51 | */ 52 | protected static $baseUri; 53 | 54 | /** 55 | * @var string 56 | */ 57 | protected static $uri; 58 | 59 | /** 60 | * @var string The URI query string, excluding leading "?" 61 | */ 62 | protected static $queryString; 63 | 64 | /** 65 | * Get Base URI without trailing slash 66 | * @param bool $reload Force reparse the base URI? 67 | * @return string 68 | */ 69 | public static function getBaseUri( $reload = false ) { 70 | if ( $reload || is_null(self::$baseUri) ) { 71 | $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF']; //Full Request URI 72 | $scriptName = $_SERVER['SCRIPT_NAME']; //Script path from docroot 73 | $baseUri = strpos($requestUri, $scriptName) === 0 ? $scriptName : str_replace('\\', '/', dirname($scriptName)); 74 | self::$baseUri = rtrim($baseUri, '/'); 75 | } 76 | return self::$baseUri; 77 | } 78 | 79 | /** 80 | * Get URI with leading slash 81 | * @param bool $reload Force reparse the URI? 82 | * @return string 83 | * @throws RuntimeException If unable if unable to determine URI 84 | */ 85 | public static function getUri( $reload = false ) { 86 | if ( $reload || is_null(self::$uri) ) { 87 | $uri = ''; 88 | if ( !empty($_SERVER['PATH_INFO']) ) { 89 | $uri = $_SERVER['PATH_INFO']; 90 | } else { 91 | if ( isset($_SERVER['REQUEST_URI']) ) { 92 | $uri = parse_url(self::getScheme() . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); 93 | } else if ( isset($_SERVER['PHP_SELF']) ) { 94 | $uri = $_SERVER['PHP_SELF']; 95 | } else { 96 | throw new RuntimeException('Unable to detect request URI'); 97 | } 98 | } 99 | if ( self::getBaseUri() !== '' && strpos($uri, self::getBaseUri()) === 0 ) { 100 | $uri = substr($uri, strlen(self::getBaseUri())); 101 | } 102 | self::$uri = '/' . ltrim($uri, '/'); 103 | } 104 | return self::$uri; 105 | } 106 | 107 | /** 108 | * Get URI Scheme 109 | * @param bool $reload For reparse the URL scheme? 110 | * @return string "https" or "http" 111 | */ 112 | public static function getScheme( $reload = false ) { 113 | if ( $reload || is_null(self::$scheme) ) { 114 | self::$scheme = ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ) ? 'http' : 'https'; 115 | } 116 | return self::$scheme; 117 | } 118 | 119 | /** 120 | * Get URI Query String 121 | * @param bool $reload For reparse the URL query string? 122 | * @return string 123 | */ 124 | public static function getQueryString( $reload = false ) { 125 | if ( $reload || is_null(self::$queryString) ) { 126 | self::$queryString = $_SERVER['QUERY_STRING']; 127 | } 128 | return self::$queryString; 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /app/api/Slim/Log.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Log Adapter 35 | * 36 | * This is an adapter for your own custom Logger. This adapter assumes 37 | * your custom Logger provides the following public instance methods: 38 | * 39 | * debug( mixed $object ) 40 | * info( mixed $object ) 41 | * warn( mixed $object ) 42 | * error( mixed $object ) 43 | * fatal( mixed $object ) 44 | * 45 | * This class assumes nothing else about your custom Logger, so you are free 46 | * to use Apache's Log4PHP logger or any other log class that, at the 47 | * very least, implements the five public instance methods shown above. 48 | * 49 | * @package Slim 50 | * @author Josh Lockhart 51 | * @since Version 1.0 52 | */ 53 | class Slim_Log { 54 | 55 | /** 56 | * @var mixed An object that implements expected Logger interface 57 | */ 58 | protected $logger; 59 | 60 | /** 61 | * @var bool Enable logging? 62 | */ 63 | protected $enabled; 64 | 65 | /** 66 | * Constructor 67 | */ 68 | public function __construct() { 69 | $this->enabled = true; 70 | } 71 | 72 | /** 73 | * Enable or disable logging 74 | * @param bool $enabled 75 | * @return void 76 | */ 77 | public function setEnabled( $enabled ) { 78 | if ( $enabled ) { 79 | $this->enabled = true; 80 | } else { 81 | $this->enabled = false; 82 | } 83 | } 84 | 85 | /** 86 | * Is logging enabled? 87 | * @return bool 88 | */ 89 | public function isEnabled() { 90 | return $this->enabled; 91 | } 92 | 93 | /** 94 | * Log debug message 95 | * @param mixed $object 96 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 97 | */ 98 | public function debug( $object ) { 99 | return isset($this->logger) && $this->isEnabled() ? $this->logger->debug($object) : false; 100 | } 101 | 102 | /** 103 | * Log info message 104 | * @param mixed $object 105 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 106 | */ 107 | public function info( $object ) { 108 | return isset($this->logger) && $this->isEnabled() ? $this->logger->info($object) : false; 109 | } 110 | 111 | /** 112 | * Log warn message 113 | * @param mixed $object 114 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 115 | */ 116 | public function warn( $object ) { 117 | return isset($this->logger) && $this->isEnabled() ? $this->logger->warn($object) : false; 118 | } 119 | 120 | /** 121 | * Log error message 122 | * @param mixed $object 123 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 124 | */ 125 | public function error( $object ) { 126 | return isset($this->logger) && $this->isEnabled() ? $this->logger->error($object) : false; 127 | } 128 | 129 | /** 130 | * Log fatal message 131 | * @param mixed $object 132 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 133 | */ 134 | public function fatal( $object ) { 135 | return isset($this->logger) && $this->isEnabled() ? $this->logger->fatal($object) : false; 136 | } 137 | 138 | /** 139 | * Set Logger 140 | * @param mixed $logger 141 | * @return void 142 | */ 143 | public function setLogger( $logger ) { 144 | $this->logger = $logger; 145 | } 146 | 147 | /** 148 | * Get Logger 149 | * @return mixed 150 | */ 151 | public function getLogger() { 152 | return $this->logger; 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /app/api/Slim/Logger.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Logger 35 | * 36 | * A simple Logger that writes to a daily-unique log file in 37 | * a user-specified directory. By default, this class will write log 38 | * messages for all log levels; the log level may be changed to filter 39 | * unwanted log messages from the log file. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Logger { 46 | 47 | /** 48 | * @var array Log levels 49 | */ 50 | protected $levels = array( 51 | 0 => 'FATAL', 52 | 1 => 'ERROR', 53 | 2 => 'WARN', 54 | 3 => 'INFO', 55 | 4 => 'DEBUG' 56 | ); 57 | 58 | /** 59 | * @var string Absolute path to log directory with trailing slash 60 | */ 61 | protected $directory; 62 | 63 | /** 64 | * Constructor 65 | * @param string $directory Absolute or relative path to log directory 66 | * @param int $level The maximum log level reported by this Logger 67 | */ 68 | public function __construct( $directory, $level = 4 ) { 69 | $this->setDirectory($directory); 70 | $this->setLevel($level); 71 | } 72 | 73 | /** 74 | * Set log directory 75 | * @param string $directory Absolute or relative path to log directory 76 | * @return void 77 | */ 78 | public function setDirectory( $directory ) { 79 | $realPath = realpath($directory); 80 | if ( $realPath ) { 81 | $this->directory = rtrim($realPath, '/') . '/'; 82 | } else { 83 | $this->directory = false; 84 | } 85 | } 86 | 87 | /** 88 | * Get log directory 89 | * @return string|false Absolute path to log directory with trailing slash 90 | */ 91 | public function getDirectory() { 92 | return $this->directory; 93 | } 94 | 95 | /** 96 | * Set log level 97 | * @param int The maximum log level reported by this Logger 98 | * @return void 99 | * @throws InvalidArgumentException If level specified is not 0, 1, 2, 3, 4 100 | */ 101 | public function setLevel( $level ) { 102 | $theLevel = (int)$level; 103 | if ( $theLevel >= 0 && $theLevel <= 4 ) { 104 | $this->level = $theLevel; 105 | } else { 106 | throw new InvalidArgumentException('Invalid Log Level. Must be one of: 0, 1, 2, 3, 4.'); 107 | } 108 | } 109 | 110 | /** 111 | * Get log level 112 | * @return int 113 | */ 114 | public function getLevel() { 115 | return $this->level; 116 | } 117 | 118 | /** 119 | * Log debug data 120 | * @param mixed $data 121 | * @return void 122 | */ 123 | public function debug( $data ) { 124 | $this->log($data, 4); 125 | } 126 | 127 | /** 128 | * Log info data 129 | * @param mixed $data 130 | * @return void 131 | */ 132 | public function info( $data ) { 133 | $this->log($data, 3); 134 | } 135 | 136 | /** 137 | * Log warn data 138 | * @param mixed $data 139 | * @return void 140 | */ 141 | public function warn( $data ) { 142 | $this->log($data, 2); 143 | } 144 | 145 | /** 146 | * Log error data 147 | * @param mixed $data 148 | * @return void 149 | */ 150 | public function error( $data ) { 151 | $this->log($data, 1); 152 | } 153 | 154 | /** 155 | * Log fatal data 156 | * @param mixed $data 157 | * @return void 158 | */ 159 | public function fatal( $data ) { 160 | $this->log($data, 0); 161 | } 162 | 163 | /** 164 | * Get absolute path to current daily log file 165 | * @return string 166 | */ 167 | public function getFile() { 168 | return $this->getDirectory() . strftime('%Y-%m-%d') . '.log'; 169 | } 170 | 171 | /** 172 | * Log data to file 173 | * @param mixed $data 174 | * @param int $level 175 | * @return void 176 | * @throws RuntimeException If log directory not found or not writable 177 | */ 178 | protected function log( $data, $level ) { 179 | $dir = $this->getDirectory(); 180 | if ( $dir == false || !is_dir($dir) ) { 181 | throw new RuntimeException("Log directory '$dir' invalid."); 182 | } 183 | if ( !is_writable($dir) ) { 184 | throw new RuntimeException("Log directory '$dir' not writable."); 185 | } 186 | if ( $level <= $this->getLevel() ) { 187 | $this->write(sprintf("[%s] %s - %s\r\n", $this->levels[$level], date('c'), (string)$data)); 188 | } 189 | } 190 | 191 | /** 192 | * Persist data to log 193 | * @param string Log message 194 | * @return void 195 | */ 196 | protected function write( $data ) { 197 | @file_put_contents($this->getFile(), $data, FILE_APPEND | LOCK_EX); 198 | } 199 | 200 | } -------------------------------------------------------------------------------- /app/api/Slim/Route.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Route 35 | * 36 | * @package Slim 37 | * @author Josh Lockhart 38 | * @since Version 1.0 39 | */ 40 | class Slim_Route { 41 | 42 | /** 43 | * @var string The route pattern (ie. "/books/:id") 44 | */ 45 | protected $pattern; 46 | 47 | /** 48 | * @var mixed The callable associated with this route 49 | */ 50 | protected $callable; 51 | 52 | /** 53 | * @var array Conditions for this route's URL parameters 54 | */ 55 | protected $conditions = array(); 56 | 57 | /** 58 | * @var array Default conditions applied to all Route instances 59 | */ 60 | protected static $defaultConditions = array(); 61 | 62 | /** 63 | * @var string The name of this route (optional) 64 | */ 65 | protected $name; 66 | 67 | /** 68 | * @var array Key-value array of URL parameters 69 | */ 70 | protected $params = array(); 71 | 72 | /** 73 | * @var array HTTP methods supported by this Route 74 | */ 75 | protected $methods = array(); 76 | 77 | /** 78 | * @var Slim_Router The Router to which this Route belongs 79 | */ 80 | protected $router; 81 | 82 | /** 83 | * @var array[Callable] Middleware 84 | */ 85 | protected $middleware = array(); 86 | 87 | /** 88 | * Constructor 89 | * @param string $pattern The URL pattern (ie. "/books/:id") 90 | * @param mixed $callable Anything that returns TRUE for is_callable() 91 | */ 92 | public function __construct( $pattern, $callable ) { 93 | $this->setPattern($pattern); 94 | $this->setCallable($callable); 95 | $this->setConditions(self::getDefaultConditions()); 96 | } 97 | 98 | /** 99 | * Set default route conditions for all instances 100 | * @param array $defaultConditions 101 | * @return void 102 | */ 103 | public static function setDefaultConditions( array $defaultConditions ) { 104 | self::$defaultConditions = $defaultConditions; 105 | } 106 | 107 | /** 108 | * Get default route conditions for all instances 109 | * @return array 110 | */ 111 | public static function getDefaultConditions() { 112 | return self::$defaultConditions; 113 | } 114 | 115 | /** 116 | * Get route pattern 117 | * @return string 118 | */ 119 | public function getPattern() { 120 | return $this->pattern; 121 | } 122 | 123 | /** 124 | * Set route pattern 125 | * @param string $pattern 126 | * @return void 127 | */ 128 | public function setPattern( $pattern ) { 129 | $this->pattern = str_replace(')', ')?', (string)$pattern); 130 | } 131 | 132 | /** 133 | * Get route callable 134 | * @return mixed 135 | */ 136 | public function getCallable() { 137 | return $this->callable; 138 | } 139 | 140 | /** 141 | * Set route callable 142 | * @param mixed $callable 143 | * @return void 144 | */ 145 | public function setCallable($callable) { 146 | $this->callable = $callable; 147 | } 148 | 149 | /** 150 | * Get route conditions 151 | * @return array 152 | */ 153 | public function getConditions() { 154 | return $this->conditions; 155 | } 156 | 157 | /** 158 | * Set route conditions 159 | * @param array $conditions 160 | * @return void 161 | */ 162 | public function setConditions( array $conditions ) { 163 | $this->conditions = $conditions; 164 | } 165 | 166 | /** 167 | * Get route name 168 | * @return string|null 169 | */ 170 | public function getName() { 171 | return $this->name; 172 | } 173 | 174 | /** 175 | * Set route name 176 | * @param string $name 177 | * @return void 178 | */ 179 | public function setName( $name ) { 180 | $this->name = (string)$name; 181 | $this->router->cacheNamedRoute($this->name, $this); 182 | } 183 | 184 | /** 185 | * Get route parameters 186 | * @return array 187 | */ 188 | public function getParams() { 189 | return $this->params; 190 | } 191 | 192 | /** 193 | * Add supported HTTP method(s) 194 | * @return void 195 | */ 196 | public function setHttpMethods() { 197 | $args = func_get_args(); 198 | $this->methods = $args; 199 | } 200 | 201 | /** 202 | * Get supported HTTP methods 203 | * @return array 204 | */ 205 | public function getHttpMethods() { 206 | return $this->methods; 207 | } 208 | 209 | /** 210 | * Append supported HTTP methods 211 | * @return void 212 | */ 213 | public function appendHttpMethods() { 214 | $args = func_get_args(); 215 | $this->methods = array_merge($this->methods, $args); 216 | } 217 | 218 | /** 219 | * Append supported HTTP methods (alias for Route::appendHttpMethods) 220 | * @return Slim_Route 221 | */ 222 | public function via() { 223 | $args = func_get_args(); 224 | $this->methods = array_merge($this->methods, $args); 225 | return $this; 226 | } 227 | 228 | /** 229 | * Detect support for an HTTP method 230 | * @return bool 231 | */ 232 | public function supportsHttpMethod( $method ) { 233 | return in_array($method, $this->methods); 234 | } 235 | 236 | /** 237 | * Get router 238 | * @return Slim_Router 239 | */ 240 | public function getRouter() { 241 | return $this->router; 242 | } 243 | 244 | /** 245 | * Set router 246 | * @param Slim_Router $router 247 | * @return void 248 | */ 249 | public function setRouter( Slim_Router $router ) { 250 | $this->router = $router; 251 | } 252 | 253 | /** 254 | * Get middleware 255 | * @return array[Callable] 256 | */ 257 | public function getMiddleware() { 258 | return $this->middleware; 259 | } 260 | 261 | /** 262 | * Set middleware 263 | * 264 | * This method allows middleware to be assigned to a specific Route. 265 | * If the method argument `is_callable` (including callable arrays!), 266 | * we directly append the argument to `$this->middleware`. Else, we 267 | * assume the argument is an array of callables and merge the array 268 | * with `$this->middleware`. Even if non-callables are included in the 269 | * argument array, we still merge them; we lazily check each item 270 | * against `is_callable` during Route::dispatch(). 271 | * 272 | * @param Callable|array[Callable] 273 | * @return Slim_Route 274 | * @throws InvalidArgumentException If argument is not callable or not an array 275 | */ 276 | public function setMiddleware( $middleware ) { 277 | if ( is_callable($middleware) ) { 278 | $this->middleware[] = $middleware; 279 | } else if ( is_array($middleware) ) { 280 | $this->middleware = array_merge($this->middleware, $middleware); 281 | } else { 282 | throw new InvalidArgumentException('Route middleware must be callable or an array of callables'); 283 | } 284 | return $this; 285 | } 286 | 287 | /** 288 | * Matches URI? 289 | * 290 | * Parse this route's pattern, and then compare it to an HTTP resource URI 291 | * This method was modeled after the techniques demonstrated by Dan Sosedoff at: 292 | * 293 | * http://blog.sosedoff.com/2009/09/20/rails-like-php-url-router/ 294 | * 295 | * @param string $resourceUri A Request URI 296 | * @return bool 297 | */ 298 | public function matches( $resourceUri ) { 299 | //Extract URL params 300 | preg_match_all('@:([\w]+)@', $this->pattern, $paramNames, PREG_PATTERN_ORDER); 301 | $paramNames = $paramNames[0]; 302 | 303 | //Convert URL params into regex patterns, construct a regex for this route 304 | $patternAsRegex = preg_replace_callback('@:[\w]+@', array($this, 'convertPatternToRegex'), $this->pattern); 305 | if ( substr($this->pattern, -1) === '/' ) { 306 | $patternAsRegex = $patternAsRegex . '?'; 307 | } 308 | $patternAsRegex = '@^' . $patternAsRegex . '$@'; 309 | 310 | //Cache URL params' names and values if this route matches the current HTTP request 311 | if ( preg_match($patternAsRegex, $resourceUri, $paramValues) ) { 312 | array_shift($paramValues); 313 | foreach ( $paramNames as $index => $value ) { 314 | $val = substr($value, 1); 315 | if ( isset($paramValues[$val]) ) { 316 | $this->params[$val] = urldecode($paramValues[$val]); 317 | } 318 | } 319 | return true; 320 | } else { 321 | return false; 322 | } 323 | } 324 | 325 | /** 326 | * Convert a URL parameter (ie. ":id") into a regular expression 327 | * @param array URL parameters 328 | * @return string Regular expression for URL parameter 329 | */ 330 | protected function convertPatternToRegex( $matches ) { 331 | $key = str_replace(':', '', $matches[0]); 332 | if ( array_key_exists($key, $this->conditions) ) { 333 | return '(?P<' . $key . '>' . $this->conditions[$key] . ')'; 334 | } else { 335 | return '(?P<' . $key . '>[a-zA-Z0-9_\-\.\!\~\*\\\'\(\)\:\@\&\=\$\+,%]+)'; 336 | } 337 | } 338 | 339 | /** 340 | * Set route name 341 | * @param string $name The name of the route 342 | * @return Slim_Route 343 | */ 344 | public function name( $name ) { 345 | $this->setName($name); 346 | return $this; 347 | } 348 | 349 | /** 350 | * Merge route conditions 351 | * @param array $conditions Key-value array of URL parameter conditions 352 | * @return Slim_Route 353 | */ 354 | public function conditions( array $conditions ) { 355 | $this->conditions = array_merge($this->conditions, $conditions); 356 | return $this; 357 | } 358 | 359 | /** 360 | * Dispatch route 361 | * 362 | * This method invokes this route's callable. If middleware is 363 | * registered for this route, each callable middleware is invoked in 364 | * the order specified. 365 | * 366 | * This method is smart about trailing slashes on the route pattern. 367 | * If this route's pattern is defined with a trailing slash, and if the 368 | * current request URI does not have a trailing slash but otherwise 369 | * matches this route's pattern, a Slim_Exception_RequestSlash 370 | * will be thrown triggering an HTTP 301 Permanent Redirect to the same 371 | * URI _with_ a trailing slash. This Exception is caught in the 372 | * `Slim::run` loop. If this route's pattern is defined without a 373 | * trailing slash, and if the current request URI does have a trailing 374 | * slash, this route will not be matched and a 404 Not Found 375 | * response will be sent if no subsequent matching routes are found. 376 | * 377 | * @return bool Was route callable invoked successfully? 378 | * @throws Slim_Exception_RequestSlash 379 | */ 380 | public function dispatch() { 381 | if ( substr($this->pattern, -1) === '/' && substr($this->router->getRequest()->getResourceUri(), -1) !== '/' ) { 382 | throw new Slim_Exception_RequestSlash(); 383 | } 384 | //Invoke middleware 385 | foreach ( $this->middleware as $mw ) { 386 | if ( is_callable($mw) ) { 387 | call_user_func($mw); 388 | } 389 | } 390 | //Invoke callable 391 | if ( is_callable($this->getCallable()) ) { 392 | call_user_func_array($this->callable, array_values($this->params)); 393 | return true; 394 | } 395 | return false; 396 | } 397 | 398 | } -------------------------------------------------------------------------------- /app/api/Slim/Router.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Router 35 | * 36 | * Responsible for registering route paths with associated callables. 37 | * When a Slim application is run, the Router finds a matching Route for 38 | * the current HTTP request, and if a matching route is found, executes 39 | * the Route's associated callable passing it parameters from the Request URI. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Router implements IteratorAggregate { 46 | 47 | /** 48 | * @var Slim_Http_Request 49 | */ 50 | protected $request; 51 | 52 | /** 53 | * @var array Lookup hash of routes, keyed by Request method 54 | */ 55 | protected $routes; 56 | 57 | /** 58 | * @var array Lookup hash of named routes, keyed by route name 59 | */ 60 | protected $namedRoutes; 61 | 62 | /** 63 | * @var array Array of routes that match the Request method and URL 64 | */ 65 | protected $matchedRoutes; 66 | 67 | /** 68 | * @var mixed Callable to be invoked if no matching routes are found 69 | */ 70 | protected $notFound; 71 | 72 | /** 73 | * @var mixed Callable to be invoked if application error 74 | */ 75 | protected $error; 76 | 77 | /** 78 | * Constructor 79 | * @param Slim_Http_Request $request The HTTP request object 80 | */ 81 | public function __construct( Slim_Http_Request $request ) { 82 | $this->request = $request; 83 | $this->routes = array(); 84 | } 85 | 86 | /** 87 | * Get Iterator 88 | * @return ArrayIterator 89 | */ 90 | public function getIterator() { 91 | return new ArrayIterator($this->getMatchedRoutes()); 92 | } 93 | 94 | /** 95 | * Get Request 96 | * @return Slim_Http_Request 97 | */ 98 | public function getRequest() { 99 | return $this->request; 100 | } 101 | 102 | /** 103 | * Set Request 104 | * @param Slim_Http_Request $req 105 | * @return void 106 | */ 107 | public function setRequest( Slim_Http_Request $req ) { 108 | $this->request = $req; 109 | } 110 | 111 | /** 112 | * Return routes that match the current request 113 | * @return array[Slim_Route] 114 | */ 115 | public function getMatchedRoutes( $reload = false ) { 116 | if ( $reload || is_null($this->matchedRoutes) ) { 117 | $this->matchedRoutes = array(); 118 | foreach ( $this->routes as $route ) { 119 | if ( $route->matches($this->request->getResourceUri()) ) { 120 | $this->matchedRoutes[] = $route; 121 | } 122 | } 123 | } 124 | return $this->matchedRoutes; 125 | } 126 | 127 | /** 128 | * Map a route to a callback function 129 | * @param string $pattern The URL pattern (ie. "/books/:id") 130 | * @param mixed $callable Anything that returns TRUE for is_callable() 131 | * @return Slim_Route 132 | */ 133 | public function map( $pattern, $callable ) { 134 | $route = new Slim_Route($pattern, $callable); 135 | $route->setRouter($this); 136 | $this->routes[] = $route; 137 | return $route; 138 | } 139 | 140 | /** 141 | * Cache named route 142 | * @param string $name The route name 143 | * @param Slim_Route $route The route object 144 | * @throws RuntimeException If a named route already exists with the same name 145 | * @return void 146 | */ 147 | public function cacheNamedRoute( $name, Slim_Route $route ) { 148 | if ( isset($this->namedRoutes[(string)$name]) ) { 149 | throw new RuntimeException('Named route already exists with name: ' . $name); 150 | } 151 | $this->namedRoutes[$name] = $route; 152 | } 153 | 154 | /** 155 | * Get URL for named route 156 | * @param string $name The name of the route 157 | * @param array Associative array of URL parameter names and values 158 | * @throws RuntimeException If named route not found 159 | * @return string The URL for the given route populated with the given parameters 160 | */ 161 | public function urlFor( $name, $params = array() ) { 162 | if ( !isset($this->namedRoutes[(string)$name]) ) { 163 | throw new RuntimeException('Named route not found for name: ' . $name); 164 | } 165 | $pattern = $this->namedRoutes[(string)$name]->getPattern(); 166 | $search = $replace = array(); 167 | foreach ( $params as $key => $value ) { 168 | $search[] = ':' . $key; 169 | $replace[] = $value; 170 | } 171 | $pattern = str_replace($search, $replace, $pattern); 172 | //Remove remnants of unpopulated, trailing optional pattern segments 173 | return preg_replace(array( 174 | '@\(\/?:.+\/??\)\??@', 175 | '@\?|\(|\)@' 176 | ), '', $this->request->getRootUri() . $pattern); 177 | } 178 | 179 | /** 180 | * Register a 404 Not Found callback 181 | * @param mixed $callable Anything that returns TRUE for is_callable() 182 | * @return mixed 183 | */ 184 | public function notFound( $callable = null ) { 185 | if ( is_callable($callable) ) { 186 | $this->notFound = $callable; 187 | } 188 | return $this->notFound; 189 | } 190 | 191 | /** 192 | * Register a 500 Error callback 193 | * @param mixed $callable Anything that returns TRUE for is_callable() 194 | * @return mixed 195 | */ 196 | public function error( $callable = null ) { 197 | if ( is_callable($callable) ) { 198 | $this->error = $callable; 199 | } 200 | return $this->error; 201 | } 202 | 203 | } -------------------------------------------------------------------------------- /app/api/Slim/Session/Flash.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Flash Messaging 35 | * 36 | * This class enables Flash messaging. Messages are persisted in $_SESSION 37 | * with a user-defined key. 38 | * 39 | * USAGE: 40 | * 41 | * 1. Set Flash message to be shown on the next request 42 | * 43 | * Slim::flash('error', 'The object could not be saved'); 44 | * 45 | * 2. Set Flash message to be shown on the current request 46 | * 47 | * Slim::flashNow('error', 'The object could not be saved'); 48 | * 49 | * 3. Keep old Flash messages for the next request 50 | * 51 | * Slim::flashKeep(); 52 | * 53 | * @package Slim 54 | * @author Josh Lockhart 55 | * @since Version 1.3 56 | */ 57 | class Slim_Session_Flash implements ArrayAccess { 58 | 59 | /** 60 | * @var string Key used to identify flash information in $_SESSION array 61 | */ 62 | protected $sessionKey = 'flash'; 63 | 64 | /** 65 | * @var array[array] Storage for flash messages 66 | */ 67 | protected $messages = array( 68 | 'prev' => array(), //flash messages from prev request 69 | 'next' => array(), //flash messages for next request 70 | 'now' => array() //flash messages for current request 71 | ); 72 | 73 | /** 74 | * Constructor 75 | * 76 | * Establishes Flash session key and loads existing 77 | * Flash messages from the $_SESSION. 78 | * 79 | * @param string $sessionKey 80 | * @return void 81 | */ 82 | public function __construct( $sessionKey = null ) { 83 | if ( !is_null($sessionKey) ) { 84 | $this->setSessionKey($sessionKey); 85 | } 86 | $this->load(); 87 | } 88 | 89 | /** 90 | * Set the $_SESSION key used to access Flash messages 91 | * @param string $key 92 | * @throws RuntimeException If session key is null 93 | * @return Slim_Session_Flash 94 | */ 95 | public function setSessionKey( $key ) { 96 | if ( is_null($key) ) { 97 | throw new RuntimeException('Session key cannot be null'); 98 | } 99 | $this->sessionKey = (string)$key; 100 | return $this; 101 | } 102 | 103 | /** 104 | * Get the $_SESSION key used to access Flash messages 105 | * @return string 106 | */ 107 | public function getSessionKey() { 108 | return $this->sessionKey; 109 | } 110 | 111 | /** 112 | * Set a Flash message for the current request 113 | * @param string $key 114 | * @param string $value 115 | * @return Slim_Session_Flash 116 | */ 117 | public function now( $key, $value ) { 118 | $this->messages['now'][(string)$key] = $value; 119 | return $this->save(); 120 | } 121 | 122 | /** 123 | * Set a Flash message for the next request 124 | * @param string $key 125 | * @param string $value 126 | * @return Slim_Session_Flash 127 | */ 128 | public function set( $key, $value ) { 129 | $this->messages['next'][(string)$key] = $value; 130 | return $this->save(); 131 | } 132 | 133 | /** 134 | * Get Flash messages intended for the current request's View 135 | * @return array[String] 136 | */ 137 | public function getMessages() { 138 | return array_merge($this->messages['prev'], $this->messages['now']); 139 | } 140 | 141 | /** 142 | * Load Flash messages from $_SESSION 143 | * @return Slim_Session_Flash 144 | */ 145 | public function load() { 146 | $this->messages['prev'] = isset($_SESSION[$this->sessionKey]) ? $_SESSION[$this->sessionKey] : array(); 147 | return $this; 148 | } 149 | 150 | /** 151 | * Transfer Flash messages from the previous request 152 | * so they are available to the next request. 153 | * @return Slim_Session_Flash 154 | */ 155 | public function keep() { 156 | foreach ( $this->messages['prev'] as $key => $val ) { 157 | $this->messages['next'][$key] = $val; 158 | } 159 | return $this->save(); 160 | } 161 | 162 | /** 163 | * Save Flash messages to $_SESSION 164 | * @return Slim_Session_Flash 165 | */ 166 | public function save() { 167 | $_SESSION[$this->sessionKey] = $this->messages['next']; 168 | return $this; 169 | } 170 | 171 | /***** ARRAY ACCESS INTERFACE *****/ 172 | 173 | public function offsetExists( $offset ) { 174 | $messages = $this->getMessages(); 175 | return isset($messages[$offset]); 176 | } 177 | 178 | public function offsetGet( $offset ) { 179 | $messages = $this->getMessages(); 180 | return isset($messages[$offset]) ? $messages[$offset] : null; 181 | } 182 | 183 | public function offsetSet( $offset, $value ) { 184 | $this->now($offset, $value); 185 | } 186 | 187 | public function offsetUnset( $offset ) { 188 | unset($this->messages['prev'][$offset]); 189 | unset($this->messages['now'][$offset]); 190 | } 191 | 192 | } -------------------------------------------------------------------------------- /app/api/Slim/Session/Handler.php: -------------------------------------------------------------------------------- 1 | app = $app; 58 | return session_set_save_handler( 59 | array($this, 'open'), 60 | array($this, 'close'), 61 | array($this, 'read'), 62 | array($this, 'write'), 63 | array($this, 'destroy'), 64 | array($this, 'gc') 65 | ); 66 | } 67 | 68 | /** 69 | * Open session 70 | * 71 | * @param string $savePath 72 | * @param string $sessionName 73 | * @return mixed 74 | */ 75 | abstract public function open( $savePath, $sessionName ); 76 | 77 | /** 78 | * Close session 79 | * 80 | * @return mixed 81 | */ 82 | abstract public function close(); 83 | 84 | /** 85 | * Read session data with ID 86 | * 87 | * @param string $id The session identifier 88 | * @return string 89 | */ 90 | abstract public function read( $id ); 91 | 92 | /** 93 | * Write session data with ID 94 | * 95 | * The "write" handler is not executed until after the output stream is 96 | * closed. Thus, output from debugging statements in the "write" handler 97 | * will never be seen in the browser. If debugging output is necessary, it 98 | * is suggested that the debug output be written to a file instead. 99 | * 100 | * @param string $id The session identifier 101 | * @param mixed $sessionData The session data 102 | * @return mixed 103 | */ 104 | abstract public function write( $id, $sessionData ); 105 | 106 | /** 107 | * Destroy session with ID 108 | * 109 | * @param string $id The session identifier 110 | * @return mixed 111 | */ 112 | abstract public function destroy( $id ); 113 | 114 | /** 115 | * Session garbage collection 116 | * 117 | * Executed when the PHP session garbage collector is invoked; should 118 | * remove all session data older than the `$maxLifetime`. 119 | * 120 | * @param int $maxLifetime 121 | * @return mixed 122 | */ 123 | abstract public function gc( $maxLifetime ); 124 | 125 | } -------------------------------------------------------------------------------- /app/api/Slim/Session/Handler/Cookies.php: -------------------------------------------------------------------------------- 1 | app->getEncryptedCookie($id); 57 | } 58 | 59 | public function write( $id, $sessionData ) { 60 | $this->app->setEncryptedCookie($id, $sessionData, 0); 61 | } 62 | 63 | public function destroy( $id ) { 64 | $this->app->deleteCookie($id); 65 | } 66 | 67 | public function gc( $maxLifetime ) { 68 | return true; //Not used 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /app/api/Slim/View.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Slim View 35 | * 36 | * The View is responsible for rendering and/or displaying a template. 37 | * It is recommended that you subclass View and re-implement the 38 | * `View::render` method to use a custom templating engine such as 39 | * Smarty, Twig, Mustache, etc. It is important that `View::render` 40 | * `return` the final template output. Do not `echo` the output. 41 | * 42 | * @package Slim 43 | * @author Josh Lockhart 44 | * @since Version 1.0 45 | */ 46 | class Slim_View { 47 | 48 | /** 49 | * @var array Key-value array of data available to the template 50 | */ 51 | protected $data = array(); 52 | 53 | /** 54 | * @var string Absolute or relative path to the templates directory 55 | */ 56 | protected $templatesDirectory; 57 | 58 | /** 59 | * Constructor 60 | * 61 | * This is empty but may be overridden in a subclass 62 | */ 63 | public function __construct() {} 64 | 65 | /** 66 | * Get data 67 | * @param string $key 68 | * @return array|mixed|null All View data if no $key, value of datum 69 | * if $key, or NULL if $key but datum 70 | * does not exist. 71 | */ 72 | public function getData( $key = null ) { 73 | if ( !is_null($key) ) { 74 | return isset($this->data[$key]) ? $this->data[$key] : null; 75 | } else { 76 | return $this->data; 77 | } 78 | } 79 | 80 | /** 81 | * Set data 82 | * 83 | * This method is overloaded to accept two different method signatures. 84 | * You may use this to set a specific key with a specfic value, 85 | * or you may use this to set all data to a specific array. 86 | * 87 | * USAGE: 88 | * 89 | * View::setData('color', 'red'); 90 | * View::setData(array('color' => 'red', 'number' => 1)); 91 | * 92 | * @param string|array 93 | * @param mixed Optional. Only use if first argument is a string. 94 | * @return void 95 | * @throws InvalidArgumentException If incorrect method signature 96 | */ 97 | public function setData() { 98 | $args = func_get_args(); 99 | if ( count($args) === 1 && is_array($args[0]) ) { 100 | $this->data = $args[0]; 101 | } else if ( count($args) === 2 ) { 102 | $this->data[(string)$args[0]] = $args[1]; 103 | } else { 104 | throw new InvalidArgumentException('Cannot set View data with provided arguments. Usage: `View::setData( $key, $value );` or `View::setData([ key => value, ... ]);`'); 105 | } 106 | } 107 | 108 | /** 109 | * Append data to existing View data 110 | * @param array $data 111 | * @return void 112 | */ 113 | public function appendData( array $data ) { 114 | $this->data = array_merge($this->data, $data); 115 | } 116 | 117 | /** 118 | * Get templates directory 119 | * @return string|null Path to templates directory without trailing slash 120 | */ 121 | public function getTemplatesDirectory() { 122 | return $this->templatesDirectory; 123 | } 124 | 125 | /** 126 | * Set templates directory 127 | * @param string $dir 128 | * @return void 129 | * @throws RuntimeException If directory is not a directory or does not exist 130 | */ 131 | public function setTemplatesDirectory( $dir ) { 132 | if ( !is_dir($dir) ) { 133 | throw new RuntimeException('Cannot set View templates directory to: ' . $dir . '. Directory does not exist.'); 134 | } 135 | $this->templatesDirectory = rtrim($dir, '/'); 136 | } 137 | 138 | /** 139 | * Display template 140 | * 141 | * This method echoes the rendered template to the current output buffer 142 | * 143 | * @param string $template Path to template file relative to templates directoy 144 | * @return void 145 | */ 146 | public function display( $template ) { 147 | echo $this->render($template); 148 | } 149 | 150 | /** 151 | * Render template 152 | * @param string $template Path to template file relative to templates directory 153 | * @return string Rendered template 154 | * @throws RuntimeException If template does not exist 155 | */ 156 | public function render( $template ) { 157 | extract($this->data); 158 | $templatePath = $this->getTemplatesDirectory() . '/' . ltrim($template, '/'); 159 | if ( !file_exists($templatePath) ) { 160 | throw new RuntimeException('View cannot render template `' . $templatePath . '`. Template does not exist.'); 161 | } 162 | ob_start(); 163 | require $templatePath; 164 | return ob_get_clean(); 165 | } 166 | 167 | } -------------------------------------------------------------------------------- /app/api/index.php: -------------------------------------------------------------------------------- 1 | get('/users', 'getUsers'); 8 | $app->post('/users', 'addUser'); 9 | 10 | $app->get('/users/:id', 'getUser'); 11 | $app->get('/users/search/:query', 'findByName'); 12 | $app->put('/users/:id', 'updateUser'); 13 | $app->delete('/users/:id', 'deleteUser'); 14 | 15 | $app->run(); 16 | 17 | function getConnection() { 18 | $dbhost="127.0.0.1"; 19 | $dbuser="root"; 20 | $dbpass="root"; 21 | $dbname="angular-mysql"; 22 | $dbh = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass); 23 | $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 24 | return $dbh; 25 | } 26 | 27 | function getUsers() { 28 | $sql = "select * FROM users ORDER BY id"; 29 | try { 30 | $db = getConnection(); 31 | $stmt = $db->query($sql); 32 | $users = $stmt->fetchAll(PDO::FETCH_OBJ); 33 | $db = null; 34 | echo json_encode($users); 35 | } catch(PDOException $e) { 36 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 37 | } 38 | } 39 | 40 | function getUser($id) { 41 | $sql = "SELECT * FROM users WHERE id=:id"; 42 | try { 43 | $db = getConnection(); 44 | $stmt = $db->prepare($sql); 45 | $stmt->bindParam("id", $id); 46 | $stmt->execute(); 47 | $user = $stmt->fetchObject(); 48 | $db = null; 49 | echo json_encode($user); 50 | } catch(PDOException $e) { 51 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 52 | } 53 | } 54 | 55 | function addUser() { 56 | error_log('addUser\n', 3, '/var/tmp/php.log'); 57 | $request = Slim::getInstance()->request(); 58 | $user = json_decode($request->getBody()); 59 | $sql = "INSERT INTO users (firstname, lastname, email, company) VALUES (:firstname, :lastname, :email, :company)"; 60 | try { 61 | $db = getConnection(); 62 | $stmt = $db->prepare($sql); 63 | $stmt->bindParam("firstname", $user->firstname); 64 | $stmt->bindParam("lastname", $user->lastname); 65 | $stmt->bindParam("email", $user->email); 66 | $stmt->bindParam("company", $user->company); 67 | $stmt->execute(); 68 | $user->id = $db->lastInsertId(); 69 | $db = null; 70 | echo json_encode($user); 71 | } catch(PDOException $e) { 72 | error_log($e->getMessage(), 3, '/var/tmp/php.log'); 73 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 74 | } 75 | } 76 | 77 | function updateUser($id) { 78 | $request = Slim::getInstance()->request(); 79 | $body = $request->getBody(); 80 | $user = json_decode($body); 81 | $sql = "UPDATE users SET firstname=:firstname, lastname=:lastname, email=:email, company=:company WHERE id=:id"; 82 | try { 83 | $db = getConnection(); 84 | $stmt = $db->prepare($sql); 85 | $stmt->bindParam("firstname", $user->firstname); 86 | $stmt->bindParam("lastname", $user->lastname); 87 | $stmt->bindParam("email", $user->email); 88 | $stmt->bindParam("company", $user->company); 89 | $stmt->bindParam("id", $id); 90 | $stmt->execute(); 91 | $db = null; 92 | echo json_encode($user); 93 | } catch(PDOException $e) { 94 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 95 | } 96 | } 97 | 98 | function deleteUser($id) { 99 | $sql = "DELETE FROM users WHERE id=:id"; 100 | try { 101 | $db = getConnection(); 102 | $stmt = $db->prepare($sql); 103 | $stmt->bindParam("id", $id); 104 | $stmt->execute(); 105 | $db = null; 106 | } catch(PDOException $e) { 107 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 108 | } 109 | } 110 | 111 | function findByName($query) { 112 | $sql = "SELECT * FROM users WHERE UPPER(firstname) LIKE :query ORDER BY firstname"; 113 | try { 114 | $db = getConnection(); 115 | $stmt = $db->prepare($sql); 116 | $query = "%".$query."%"; 117 | $stmt->bindParam("query", $query); 118 | $stmt->execute(); 119 | $users = $stmt->fetchAll(PDO::FETCH_OBJ); 120 | $db = null; 121 | echo '{"user": ' . json_encode($users) . '}'; 122 | } catch(PDOException $e) { 123 | echo '{"error":{"text":'. $e->getMessage() .'}}'; 124 | } 125 | } 126 | 127 | ?> -------------------------------------------------------------------------------- /app/css/styles.css: -------------------------------------------------------------------------------- 1 | /* The html and body elements cannot have any padding or margin */ 2 | html, 3 | body { 4 | height: 100%; /* for sticky footer */ 5 | } 6 | 7 | /* Wrapper for page content to push down footer */ 8 | #wrap { 9 | min-height: 100%; 10 | height: auto !important; 11 | height: 100%; 12 | margin: 0 auto -80px; /* must equal footer height */ 13 | } 14 | 15 | #push, 16 | #footer { 17 | height: 80px; 18 | } 19 | 20 | #footer { 21 | padding: 0 20px; 22 | } 23 | 24 | .margin-left-10 { margin-left: 10px; } 25 | .margin-right-10{ margin-right: 10px; } 26 | 27 | .red, a.red { color: #d2322d } 28 | .blue, a.blue { color: #428bca } 29 | .green, a.green { color: #47a447 } 30 | 31 | a.glyphicon { 32 | cursor: pointer; 33 | } 34 | 35 | .pull-up { margin: -45px 0 15px 0 } -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 44 | 45 |
46 | 47 |
48 | 49 |
50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /app/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Main app config and initialisation 4 | var app = angular.module('myApp', [ 5 | 'ng', 6 | 'ngResource', 7 | 'ngRoute', 8 | 'myApp.controllers', 9 | 'myApp.directives', 10 | 'myApp.filters', 11 | 'myApp.services' 12 | ]); 13 | 14 | 15 | // Configuration and routes 16 | app.config(function($routeProvider, $locationProvider) { 17 | 18 | $routeProvider 19 | .when('/', { templateUrl: 'views/index.html', controller: 'IndexController', title: 'Home' }) 20 | .when('/users', { templateUrl: 'views/users.html', controller: 'UsersController', title: 'Users' }) 21 | .when('/user/create', { templateUrl: 'views/user-create.html', controller: 'UserController', title: 'Create User' }) 22 | .when('/user/edit/:id', { templateUrl: 'views/user-edit.html', controller: 'UserController', title: 'Edit User' }) 23 | .when('/user/view/:id', { templateUrl: 'views/user-view.html', controller: 'UserController', title: 'View User' }) 24 | .otherwise({ redirectTo:'/' }); 25 | 26 | // Removes the # in urls 27 | $locationProvider.html5Mode(true); 28 | 29 | }); 30 | 31 | 32 | // Run 33 | app.run(function($rootScope, $route, applicationName) { 34 | 35 | $rootScope.applicationName = applicationName; 36 | 37 | $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { 38 | $rootScope.title = $route.current.title; 39 | }); 40 | 41 | }); -------------------------------------------------------------------------------- /app/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Controllers 4 | 5 | var app = angular.module('myApp.controllers', [ 6 | 'ng', 7 | 'ngResource', 8 | 'myApp.services' 9 | ]); 10 | 11 | 12 | // Controller for root page 13 | app.controller('IndexController', function($http, $scope, applicationName) { 14 | 15 | $scope.applicationName = applicationName; 16 | 17 | // For some reason $resource won't work here, so went for $http.get() 18 | $http.get('/api/users') 19 | .success( 20 | function(data, status, headers, config) { 21 | 22 | $scope.users = data; 23 | }) 24 | .error( 25 | function(data, status, headers, config) { 26 | 27 | $scope.status = status; 28 | }); 29 | }); 30 | 31 | // Controller for Users page 32 | app.controller('UsersController', function($resource, $scope, $location, $route) { 33 | 34 | var Users = $resource('/api/users'); 35 | 36 | // Get Users from API 37 | $scope.users = Users.query(); 38 | 39 | // Delete a User then relaod view 40 | $scope.deleteUser = function(userId) { 41 | 42 | var User = $resource('/api/users/:id', { id: userId }); 43 | 44 | User.delete( 45 | function() { 46 | // success 47 | $route.reload(); 48 | }, 49 | function(error) { 50 | // error 51 | console.log(error); 52 | } 53 | ); 54 | } 55 | 56 | }); 57 | 58 | // Controller for User pages 59 | app.controller('UserController', function($routeParams, $resource, $scope, $location, $window) { 60 | 61 | var userId = $routeParams.id; 62 | 63 | if (userId) { 64 | var User = $resource('/api/users/:id', { id: userId }); 65 | 66 | // Get User from API 67 | $scope.user = User.get(); 68 | } 69 | 70 | // Create a User 71 | $scope.createUser = function() { 72 | 73 | var Users = $resource('/api/users'); 74 | 75 | if ($scope.userForm.$valid) { 76 | Users.save($scope.user, 77 | function() { 78 | // success 79 | $location.path('/users'); 80 | }, 81 | function(error) { 82 | // error 83 | console.log(error); 84 | } 85 | ); 86 | } 87 | } 88 | 89 | // Update User details 90 | $scope.updateUser = function() { 91 | 92 | var User = $resource('/api/users/:id', { id: userId }, { update: { method:'PUT' } }); 93 | 94 | if ($scope.userForm.$valid) { 95 | User.update($scope.user, 96 | function() { 97 | // success 98 | $location.path('/users'); 99 | }, 100 | function(error) { 101 | // error 102 | console.log(error); 103 | } 104 | ); 105 | } 106 | } 107 | 108 | // Delete a User 109 | $scope.deleteUser = function(userId) { 110 | 111 | var User = $resource('/api/users/:id', { id: userId }); 112 | 113 | User.delete( 114 | function() { 115 | // success 116 | $location.path('/users'); 117 | }, 118 | function(error) { 119 | // error 120 | console.log(error); 121 | } 122 | ); 123 | } 124 | 125 | // Go back in history 126 | $scope.goBack = function() { 127 | 128 | $window.history.back(); 129 | }; 130 | 131 | }); -------------------------------------------------------------------------------- /app/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Directives 4 | 5 | var app = angular.module('myApp.directives', []); 6 | 7 | 8 | /* 9 | Adds copyright year, e.g. 10 |

Company

11 | */ 12 | app.directive('copyright', function() { 13 | 14 | return function(scope, elm, attrs) { 15 | 16 | var year = new Date().getFullYear(); 17 | elm.prepend('© ' + year + ' '); 18 | }; 19 | }); -------------------------------------------------------------------------------- /app/js/filters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Filters 4 | 5 | var app = angular.module('myApp.filters', []); 6 | 7 | // Format the page title using values defined in services 8 | app.filter('title', [ 9 | 'applicationName', 10 | 'separator', 11 | function(applicationName, separator) { 12 | 13 | return function(text) { 14 | 15 | return applicationName + separator + text; 16 | } 17 | } 18 | ]); -------------------------------------------------------------------------------- /app/js/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Services 4 | 5 | var app = angular.module('myApp.services', []); 6 | 7 | // Set values for use in page title 8 | 9 | app.value('applicationName', 'Angular MySQL'); 10 | app.value('separator', ' - '); 11 | -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/css/pygments-manni.css: -------------------------------------------------------------------------------- 1 | .hll { background-color: #ffffcc } 2 | /*{ background: #f0f3f3; }*/ 3 | .c { color: #999; } /* Comment */ 4 | .err { color: #AA0000; background-color: #FFAAAA } /* Error */ 5 | .k { color: #006699; } /* Keyword */ 6 | .o { color: #555555 } /* Operator */ 7 | .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ 8 | .cp { color: #009999 } /* Comment.Preproc */ 9 | .c1 { color: #999; } /* Comment.Single */ 10 | .cs { color: #999; } /* Comment.Special */ 11 | .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ 12 | .ge { font-style: italic } /* Generic.Emph */ 13 | .gr { color: #FF0000 } /* Generic.Error */ 14 | .gh { color: #003300; } /* Generic.Heading */ 15 | .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ 16 | .go { color: #AAAAAA } /* Generic.Output */ 17 | .gp { color: #000099; } /* Generic.Prompt */ 18 | .gs { } /* Generic.Strong */ 19 | .gu { color: #003300; } /* Generic.Subheading */ 20 | .gt { color: #99CC66 } /* Generic.Traceback */ 21 | .kc { color: #006699; } /* Keyword.Constant */ 22 | .kd { color: #006699; } /* Keyword.Declaration */ 23 | .kn { color: #006699; } /* Keyword.Namespace */ 24 | .kp { color: #006699 } /* Keyword.Pseudo */ 25 | .kr { color: #006699; } /* Keyword.Reserved */ 26 | .kt { color: #007788; } /* Keyword.Type */ 27 | .m { color: #FF6600 } /* Literal.Number */ 28 | .s { color: #d44950 } /* Literal.String */ 29 | .na { color: #4f9fcf } /* Name.Attribute */ 30 | .nb { color: #336666 } /* Name.Builtin */ 31 | .nc { color: #00AA88; } /* Name.Class */ 32 | .no { color: #336600 } /* Name.Constant */ 33 | .nd { color: #9999FF } /* Name.Decorator */ 34 | .ni { color: #999999; } /* Name.Entity */ 35 | .ne { color: #CC0000; } /* Name.Exception */ 36 | .nf { color: #CC00FF } /* Name.Function */ 37 | .nl { color: #9999FF } /* Name.Label */ 38 | .nn { color: #00CCFF; } /* Name.Namespace */ 39 | .nt { color: #2f6f9f; } /* Name.Tag */ 40 | .nv { color: #003333 } /* Name.Variable */ 41 | .ow { color: #000000; } /* Operator.Word */ 42 | .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .mf { color: #FF6600 } /* Literal.Number.Float */ 44 | .mh { color: #FF6600 } /* Literal.Number.Hex */ 45 | .mi { color: #FF6600 } /* Literal.Number.Integer */ 46 | .mo { color: #FF6600 } /* Literal.Number.Oct */ 47 | .sb { color: #CC3300 } /* Literal.String.Backtick */ 48 | .sc { color: #CC3300 } /* Literal.String.Char */ 49 | .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ 50 | .s2 { color: #CC3300 } /* Literal.String.Double */ 51 | .se { color: #CC3300; } /* Literal.String.Escape */ 52 | .sh { color: #CC3300 } /* Literal.String.Heredoc */ 53 | .si { color: #AA0000 } /* Literal.String.Interpol */ 54 | .sx { color: #CC3300 } /* Literal.String.Other */ 55 | .sr { color: #33AAAA } /* Literal.String.Regex */ 56 | .s1 { color: #CC3300 } /* Literal.String.Single */ 57 | .ss { color: #FFCC33 } /* Literal.String.Symbol */ 58 | .bp { color: #336666 } /* Name.Builtin.Pseudo */ 59 | .vc { color: #003333 } /* Name.Variable.Class */ 60 | .vg { color: #003333 } /* Name.Variable.Global */ 61 | .vi { color: #003333 } /* Name.Variable.Instance */ 62 | .il { color: #FF6600 } /* Literal.Number.Integer.Long */ 63 | 64 | .css .o, 65 | .css .o + .nt, 66 | .css .nt + .nt { color: #999; } 67 | -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/ico/apple-touch-icon-114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/assets/ico/apple-touch-icon-114-precomposed.png -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/ico/apple-touch-icon-144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/assets/ico/apple-touch-icon-144-precomposed.png -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/ico/apple-touch-icon-57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/assets/ico/apple-touch-icon-57-precomposed.png -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/ico/apple-touch-icon-72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/assets/ico/apple-touch-icon-72-precomposed.png -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/ico/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/assets/ico/favicon.png -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/js/application.js: -------------------------------------------------------------------------------- 1 | // NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT 2 | // IT'S ALL JUST JUNK FOR OUR DOCS! 3 | // ++++++++++++++++++++++++++++++++++++++++++ 4 | 5 | !function ($) { 6 | 7 | $(function(){ 8 | 9 | var $window = $(window) 10 | var $body = $(document.body) 11 | 12 | var navHeight = $('.navbar').outerHeight(true) + 10 13 | 14 | $body.scrollspy({ 15 | target: '.bs-sidebar', 16 | offset: navHeight 17 | }) 18 | 19 | $window.on('load', function () { 20 | $body.scrollspy('refresh') 21 | }) 22 | 23 | $('.bs-docs-container [href=#]').click(function (e) { 24 | e.preventDefault() 25 | }) 26 | 27 | // back to top 28 | setTimeout(function () { 29 | var $sideBar = $('.bs-sidebar') 30 | 31 | $sideBar.affix({ 32 | offset: { 33 | top: function () { 34 | var offsetTop = $sideBar.offset().top 35 | var sideBarMargin = parseInt($sideBar.children(0).css('margin-top'), 10) 36 | var navOuterHeight = $('.bs-docs-nav').height() 37 | 38 | return (this.top = offsetTop - navOuterHeight - sideBarMargin) 39 | } 40 | , bottom: function () { 41 | return (this.bottom = $('.bs-footer').outerHeight(true)) 42 | } 43 | } 44 | }) 45 | }, 100) 46 | 47 | setTimeout(function () { 48 | $('.bs-top').affix() 49 | }, 100) 50 | 51 | // tooltip demo 52 | $('.tooltip-demo').tooltip({ 53 | selector: "[data-toggle=tooltip]", 54 | container: "body" 55 | }) 56 | 57 | $('.tooltip-test').tooltip() 58 | $('.popover-test').popover() 59 | 60 | $('.bs-docs-navbar').tooltip({ 61 | selector: "a[data-toggle=tooltip]", 62 | container: ".bs-docs-navbar .nav" 63 | }) 64 | 65 | // popover demo 66 | $("[data-toggle=popover]") 67 | .popover() 68 | 69 | // button state demo 70 | $('#fat-btn') 71 | .click(function () { 72 | var btn = $(this) 73 | btn.button('loading') 74 | setTimeout(function () { 75 | btn.button('reset') 76 | }, 3000) 77 | }) 78 | 79 | // carousel demo 80 | $('.bs-docs-carousel-example').carousel() 81 | }) 82 | 83 | }(window.jQuery) 84 | -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/js/customizer.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { // wait for load in a dumb way because B-0 2 | var cw = '/*!\n * Bootstrap v3.0.0\n *\n * Copyright 2013 Twitter, Inc\n * Licensed under the Apache License v2.0\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Designed and built with all the love in the world @twitter by @mdo and @fat.\n */\n\n' 3 | 4 | function showError(msg, err) { 5 | $('
\ 6 |
\ 7 | ×\ 8 |

' + msg + '

' + 9 | (err.extract ? '
' + err.extract.join('\n') + '
' : '') + '\ 10 |
\ 11 |
').appendTo('body').alert() 12 | throw err 13 | } 14 | 15 | function showCallout(msg, showUpTop) { 16 | var callout = $('
\ 17 |

Attention!

\ 18 |

' + msg + '

\ 19 |
') 20 | 21 | if (showUpTop) { 22 | callout.appendTo('.bs-docs-container') 23 | } else { 24 | callout.insertAfter('.bs-customize-download') 25 | } 26 | } 27 | 28 | function getQueryParam(key) { 29 | key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); // escape RegEx meta chars 30 | var match = location.search.match(new RegExp("[?&]"+key+"=([^&]+)(&|$)")); 31 | return match && decodeURIComponent(match[1].replace(/\+/g, " ")); 32 | } 33 | 34 | function createGist(configData) { 35 | var data = { 36 | "description": "Bootstrap Customizer Config", 37 | "public": true, 38 | "files": { 39 | "config.json": { 40 | "content": JSON.stringify(configData, null, 2) 41 | } 42 | } 43 | } 44 | $.ajax({ 45 | url: 'https://api.github.com/gists', 46 | type: 'POST', 47 | dataType: 'json', 48 | data: JSON.stringify(data) 49 | }) 50 | .success(function(result) { 51 | history.replaceState(false, document.title, window.location.origin + window.location.pathname + '?id=' + result.id) 52 | }) 53 | .error(function(err) { 54 | showError('Ruh roh! Could not save gist file, configuration not saved.', err) 55 | }) 56 | } 57 | 58 | function getCustomizerData() { 59 | var vars = {} 60 | 61 | $('#less-variables-section input') 62 | .each(function () { 63 | $(this).val() && (vars[ $(this).prev().text() ] = $(this).val()) 64 | }) 65 | 66 | var data = { 67 | vars: vars, 68 | css: $('#less-section input:checked') .map(function () { return this.value }).toArray(), 69 | js: $('#plugin-section input:checked').map(function () { return this.value }).toArray() 70 | } 71 | 72 | if ($.isEmptyObject(data.vars) && !data.css.length && !data.js.length) return 73 | 74 | return data 75 | } 76 | 77 | function parseUrl() { 78 | var id = getQueryParam('id') 79 | 80 | if (!id) return 81 | 82 | $.ajax({ 83 | url: 'https://api.github.com/gists/' + id, 84 | type: 'GET', 85 | dataType: 'json' 86 | }) 87 | .success(function(result) { 88 | var data = JSON.parse(result.files['config.json'].content) 89 | if (data.js) { 90 | $('#plugin-section input').each(function () { 91 | $(this).prop('checked', ~$.inArray(this.value, data.js)) 92 | }) 93 | } 94 | if (data.css) { 95 | $('#less-section input').each(function () { 96 | $(this).prop('checked', ~$.inArray(this.value, data.css)) 97 | }) 98 | } 99 | if (data.vars) { 100 | for (var i in data.vars) { 101 | $('input[data-var="' + i + '"]').val(data.vars[i]) 102 | } 103 | } 104 | }) 105 | .error(function(err) { 106 | showError('Error fetching bootstrap config file', err) 107 | }) 108 | } 109 | 110 | function generateZip(css, js, fonts, complete) { 111 | if (!css && !js) return showError('Ruh roh! No Bootstrap files selected.', new Error('no Bootstrap')) 112 | 113 | var zip = new JSZip() 114 | 115 | if (css) { 116 | var cssFolder = zip.folder('css') 117 | for (var fileName in css) { 118 | cssFolder.file(fileName, css[fileName]) 119 | } 120 | } 121 | 122 | if (js) { 123 | var jsFolder = zip.folder('js') 124 | for (var fileName in js) { 125 | jsFolder.file(fileName, js[fileName]) 126 | } 127 | } 128 | 129 | if (fonts) { 130 | var fontsFolder = zip.folder('fonts') 131 | for (var fileName in fonts) { 132 | fontsFolder.file(fileName, fonts[fileName]) 133 | } 134 | } 135 | 136 | var content = zip.generate({type:"blob"}) 137 | 138 | complete(content) 139 | } 140 | 141 | function generateCustomCSS(vars) { 142 | var result = '' 143 | 144 | for (var key in vars) { 145 | result += key + ': ' + vars[key] + ';\n' 146 | } 147 | 148 | return result + '\n\n' 149 | } 150 | 151 | function generateFonts() { 152 | var glyphicons = $('#less-section [value="glyphicons.less"]:checked') 153 | if (glyphicons.length) { 154 | return __fonts 155 | } 156 | } 157 | 158 | function generateCSS() { 159 | var $checked = $('#less-section input:checked') 160 | 161 | if (!$checked.length) return false 162 | 163 | var result = {} 164 | var vars = {} 165 | var css = '' 166 | 167 | $('#less-variables-section input') 168 | .each(function () { 169 | $(this).val() && (vars[ $(this).prev().text() ] = $(this).val()) 170 | }) 171 | 172 | css += __less['variables.less'] 173 | if (vars) css += generateCustomCSS(vars) 174 | css += __less['mixins.less'] 175 | css += __less['normalize.less'] 176 | css += __less['scaffolding.less'] 177 | css += $checked 178 | .map(function () { return __less[this.value] }) 179 | .toArray() 180 | .join('\n') 181 | 182 | css = css.replace(/@import[^\n]*/gi, '') //strip any imports 183 | 184 | try { 185 | var parser = new less.Parser({ 186 | paths: ['variables.less', 'mixins.less'] 187 | , optimization: 0 188 | , filename: 'bootstrap.css' 189 | }).parse(css, function (err, tree) { 190 | if (err) { 191 | return showError('Ruh roh! Could not parse less files.', err) 192 | } 193 | result = { 194 | 'bootstrap.css' : cw + tree.toCSS(), 195 | 'bootstrap.min.css' : cw + tree.toCSS({ compress: true }) 196 | } 197 | }) 198 | } catch (err) { 199 | return showError('Ruh roh! Could not parse less files.', err) 200 | } 201 | 202 | return result 203 | } 204 | 205 | function generateJavascript() { 206 | var $checked = $('#plugin-section input:checked') 207 | if (!$checked.length) return false 208 | 209 | var js = $checked 210 | .map(function () { return __js[this.value] }) 211 | .toArray() 212 | .join('\n') 213 | 214 | return { 215 | 'bootstrap.js': js, 216 | 'bootstrap.min.js': cw + uglify(js) 217 | } 218 | } 219 | 220 | var inputsComponent = $('#less-section input') 221 | var inputsPlugin = $('#plugin-section input') 222 | var inputsVariables = $('#less-variables-section input') 223 | 224 | $('#less-section .toggle').on('click', function (e) { 225 | e.preventDefault() 226 | inputsComponent.prop('checked', !inputsComponent.is(':checked')) 227 | }) 228 | 229 | $('#plugin-section .toggle').on('click', function (e) { 230 | e.preventDefault() 231 | inputsPlugin.prop('checked', !inputsPlugin.is(':checked')) 232 | }) 233 | 234 | $('#less-variables-section .toggle').on('click', function (e) { 235 | e.preventDefault() 236 | inputsVariables.val('') 237 | }) 238 | 239 | $('[data-dependencies]').on('click', function () { 240 | if (!$(this).is(':checked')) return 241 | var dependencies = this.getAttribute('data-dependencies') 242 | if (!dependencies) return 243 | dependencies = dependencies.split(',') 244 | for (var i = 0; i < dependencies.length; i++) { 245 | var dependency = $('[value="' + dependencies[i] + '"]') 246 | dependency && dependency.prop('checked', true) 247 | } 248 | }) 249 | 250 | $('[data-dependents]').on('click', function () { 251 | if ($(this).is(':checked')) return 252 | var dependents = this.getAttribute('data-dependents') 253 | if (!dependents) return 254 | dependents = dependents.split(',') 255 | for (var i = 0; i < dependents.length; i++) { 256 | var dependent = $('[value="' + dependents[i] + '"]') 257 | dependent && dependent.prop('checked', false) 258 | } 259 | }) 260 | 261 | var $compileBtn = $('#btn-compile') 262 | var $downloadBtn = $('#btn-download') 263 | 264 | $compileBtn.on('click', function (e) { 265 | e.preventDefault() 266 | 267 | $compileBtn.attr('disabled', 'disabled') 268 | 269 | generateZip(generateCSS(), generateJavascript(), generateFonts(), function (blob) { 270 | $compileBtn.removeAttr('disabled') 271 | saveAs(blob, "bootstrap.zip") 272 | createGist(getCustomizerData()) 273 | }) 274 | }) 275 | 276 | // browser support alerts 277 | if (!window.URL && navigator.userAgent.toLowerCase().indexOf('safari') != -1) { 278 | showCallout("Looks like you're using safari, which sadly doesn't have the best support\ 279 | for HTML5 blobs. Because of this your file will be downloaded with the name \"untitled\".\ 280 | However, if you check your downloads folder, just rename this \"untitled\" file\ 281 | to \"bootstrap.zip\" and you should be good to go!") 282 | } else if (!window.URL && !window.webkitURL) { 283 | $('.bs-docs-section, .bs-sidebar').css('display', 'none') 284 | 285 | showCallout("Looks like your current browser doesn't support the Bootstrap Customizer. Please take a second\ 286 | to upgrade to a more modern browser.", true) 287 | } 288 | 289 | parseUrl() 290 | } 291 | -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/js/filesaver.js: -------------------------------------------------------------------------------- 1 | /* Blob.js 2 | * A Blob implementation. 3 | * 2013-06-20 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * By Devin Samarin, https://github.com/eboyjr 7 | * License: X11/MIT 8 | * See LICENSE.md 9 | */ 10 | 11 | /*global self, unescape */ 12 | /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 13 | plusplus: true */ 14 | 15 | /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ 16 | 17 | if (typeof Blob !== "function" || typeof URL === "undefined") 18 | if (typeof Blob === "function" && typeof webkitURL !== "undefined") self.URL = webkitURL; 19 | else var Blob = (function (view) { 20 | "use strict"; 21 | 22 | var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || view.MSBlobBuilder || (function(view) { 23 | var 24 | get_class = function(object) { 25 | return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; 26 | } 27 | , FakeBlobBuilder = function BlobBuilder() { 28 | this.data = []; 29 | } 30 | , FakeBlob = function Blob(data, type, encoding) { 31 | this.data = data; 32 | this.size = data.length; 33 | this.type = type; 34 | this.encoding = encoding; 35 | } 36 | , FBB_proto = FakeBlobBuilder.prototype 37 | , FB_proto = FakeBlob.prototype 38 | , FileReaderSync = view.FileReaderSync 39 | , FileException = function(type) { 40 | this.code = this[this.name = type]; 41 | } 42 | , file_ex_codes = ( 43 | "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " 44 | + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" 45 | ).split(" ") 46 | , file_ex_code = file_ex_codes.length 47 | , real_URL = view.URL || view.webkitURL || view 48 | , real_create_object_URL = real_URL.createObjectURL 49 | , real_revoke_object_URL = real_URL.revokeObjectURL 50 | , URL = real_URL 51 | , btoa = view.btoa 52 | , atob = view.atob 53 | 54 | , ArrayBuffer = view.ArrayBuffer 55 | , Uint8Array = view.Uint8Array 56 | ; 57 | FakeBlob.fake = FB_proto.fake = true; 58 | while (file_ex_code--) { 59 | FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; 60 | } 61 | if (!real_URL.createObjectURL) { 62 | URL = view.URL = {}; 63 | } 64 | URL.createObjectURL = function(blob) { 65 | var 66 | type = blob.type 67 | , data_URI_header 68 | ; 69 | if (type === null) { 70 | type = "application/octet-stream"; 71 | } 72 | if (blob instanceof FakeBlob) { 73 | data_URI_header = "data:" + type; 74 | if (blob.encoding === "base64") { 75 | return data_URI_header + ";base64," + blob.data; 76 | } else if (blob.encoding === "URI") { 77 | return data_URI_header + "," + decodeURIComponent(blob.data); 78 | } if (btoa) { 79 | return data_URI_header + ";base64," + btoa(blob.data); 80 | } else { 81 | return data_URI_header + "," + encodeURIComponent(blob.data); 82 | } 83 | } else if (real_create_object_URL) { 84 | return real_create_object_URL.call(real_URL, blob); 85 | } 86 | }; 87 | URL.revokeObjectURL = function(object_URL) { 88 | if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { 89 | real_revoke_object_URL.call(real_URL, object_URL); 90 | } 91 | }; 92 | FBB_proto.append = function(data/*, endings*/) { 93 | var bb = this.data; 94 | // decode data to a binary string 95 | if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { 96 | var 97 | str = "" 98 | , buf = new Uint8Array(data) 99 | , i = 0 100 | , buf_len = buf.length 101 | ; 102 | for (; i < buf_len; i++) { 103 | str += String.fromCharCode(buf[i]); 104 | } 105 | bb.push(str); 106 | } else if (get_class(data) === "Blob" || get_class(data) === "File") { 107 | if (FileReaderSync) { 108 | var fr = new FileReaderSync; 109 | bb.push(fr.readAsBinaryString(data)); 110 | } else { 111 | // async FileReader won't work as BlobBuilder is sync 112 | throw new FileException("NOT_READABLE_ERR"); 113 | } 114 | } else if (data instanceof FakeBlob) { 115 | if (data.encoding === "base64" && atob) { 116 | bb.push(atob(data.data)); 117 | } else if (data.encoding === "URI") { 118 | bb.push(decodeURIComponent(data.data)); 119 | } else if (data.encoding === "raw") { 120 | bb.push(data.data); 121 | } 122 | } else { 123 | if (typeof data !== "string") { 124 | data += ""; // convert unsupported types to strings 125 | } 126 | // decode UTF-16 to binary string 127 | bb.push(unescape(encodeURIComponent(data))); 128 | } 129 | }; 130 | FBB_proto.getBlob = function(type) { 131 | if (!arguments.length) { 132 | type = null; 133 | } 134 | return new FakeBlob(this.data.join(""), type, "raw"); 135 | }; 136 | FBB_proto.toString = function() { 137 | return "[object BlobBuilder]"; 138 | }; 139 | FB_proto.slice = function(start, end, type) { 140 | var args = arguments.length; 141 | if (args < 3) { 142 | type = null; 143 | } 144 | return new FakeBlob( 145 | this.data.slice(start, args > 1 ? end : this.data.length) 146 | , type 147 | , this.encoding 148 | ); 149 | }; 150 | FB_proto.toString = function() { 151 | return "[object Blob]"; 152 | }; 153 | return FakeBlobBuilder; 154 | }(view)); 155 | 156 | return function Blob(blobParts, options) { 157 | var type = options ? (options.type || "") : ""; 158 | var builder = new BlobBuilder(); 159 | if (blobParts) { 160 | for (var i = 0, len = blobParts.length; i < len; i++) { 161 | builder.append(blobParts[i]); 162 | } 163 | } 164 | return builder.getBlob(type); 165 | }; 166 | }(self)); 167 | 168 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 169 | var saveAs=saveAs||(navigator.msSaveOrOpenBlob&&navigator.msSaveOrOpenBlob.bind(navigator))||(function(h){"use strict";var r=h.document,l=function(){return h.URL||h.webkitURL||h},e=h.URL||h.webkitURL||h,n=r.createElementNS("http://www.w3.org/1999/xhtml","a"),g=!h.externalHost&&"download" in n,j=function(t){var s=r.createEvent("MouseEvents");s.initMouseEvent("click",true,false,h,0,0,0,0,0,false,false,false,false,0,null);t.dispatchEvent(s)},o=h.webkitRequestFileSystem,p=h.requestFileSystem||o||h.mozRequestFileSystem,m=function(s){(h.setImmediate||h.setTimeout)(function(){throw s},0)},c="application/octet-stream",k=0,b=[],i=function(){var t=b.length;while(t--){var s=b[t];if(typeof s==="string"){e.revokeObjectURL(s)}else{s.remove()}}b.length=0},q=function(t,s,w){s=[].concat(s);var v=s.length;while(v--){var x=t["on"+s[v]];if(typeof x==="function"){try{x.call(t,w||t)}catch(u){m(u)}}}},f=function(t,u){var v=this,B=t.type,E=false,x,w,s=function(){var F=l().createObjectURL(t);b.push(F);return F},A=function(){q(v,"writestart progress write writeend".split(" "))},D=function(){if(E||!x){x=s(t)}if(w){w.location.href=x}else{window.open(x,"_blank")}v.readyState=v.DONE;A()},z=function(F){return function(){if(v.readyState!==v.DONE){return F.apply(this,arguments)}}},y={create:true,exclusive:false},C;v.readyState=v.INIT;if(!u){u="download"}if(g){x=s(t);n.href=x;n.download=u;j(n);v.readyState=v.DONE;A();return}if(h.chrome&&B&&B!==c){C=t.slice||t.webkitSlice;t=C.call(t,0,t.size,c);E=true}if(o&&u!=="download"){u+=".download"}if(B===c||o){w=h}if(!p){D();return}k+=t.size;p(h.TEMPORARY,k,z(function(F){F.root.getDirectory("saved",y,z(function(G){var H=function(){G.getFile(u,y,z(function(I){I.createWriter(z(function(J){J.onwriteend=function(K){w.location.href=I.toURL();b.push(I);v.readyState=v.DONE;q(v,"writeend",K)};J.onerror=function(){var K=J.error;if(K.code!==K.ABORT_ERR){D()}};"writestart progress write abort".split(" ").forEach(function(K){J["on"+K]=v["on"+K]});J.write(t);v.abort=function(){J.abort();v.readyState=v.DONE};v.readyState=v.WRITING}),D)}),D)};G.getFile(u,{create:false},z(function(I){I.remove();H()}),z(function(I){if(I.code===I.NOT_FOUND_ERR){H()}else{D()}}))}),D)}),D)},d=f.prototype,a=function(s,t){return new f(s,t)};d.abort=function(){var s=this;s.readyState=s.DONE;q(s,"abort")};d.readyState=d.INIT=0;d.WRITING=1;d.DONE=2;d.error=d.onwritestart=d.onprogress=d.onwrite=d.onabort=d.onerror=d.onwriteend=null;h.addEventListener("unload",i,false);return a}(self)); -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/js/holder.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Holder - 2.0 - client side image placeholders 4 | (c) 2012-2013 Ivan Malopinsky / http://imsky.co 5 | 6 | Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 7 | Commercial use requires attribution. 8 | 9 | */ 10 | 11 | var Holder = Holder || {}; 12 | (function (app, win) { 13 | 14 | var preempted = false, 15 | fallback = false, 16 | canvas = document.createElement('canvas'); 17 | 18 | //getElementsByClassName polyfill 19 | document.getElementsByClassName||(document.getElementsByClassName=function(e){var t=document,n,r,i,s=[];if(t.querySelectorAll)return t.querySelectorAll("."+e);if(t.evaluate){r=".//*[contains(concat(' ', @class, ' '), ' "+e+" ')]",n=t.evaluate(r,t,null,0,null);while(i=n.iterateNext())s.push(i)}else{n=t.getElementsByTagName("*"),r=new RegExp("(^|\\s)"+e+"(\\s|$)");for(i=0;i= 0.75) { 72 | text_height = Math.floor(text_height * 0.75 * (width/text_width)); 73 | } 74 | //Resetting font size if necessary 75 | ctx.font = "bold " + (text_height * ratio) + "px " + font; 76 | ctx.fillText(text, (width / 2), (height / 2), width); 77 | return canvas.toDataURL("image/png"); 78 | } 79 | 80 | function render(mode, el, holder, src) { 81 | var dimensions = holder.dimensions, 82 | theme = holder.theme, 83 | text = holder.text ? decodeURIComponent(holder.text) : holder.text; 84 | var dimensions_caption = dimensions.width + "x" + dimensions.height; 85 | theme = (text ? extend(theme, { 86 | text: text 87 | }) : theme); 88 | theme = (holder.font ? extend(theme, { 89 | font: holder.font 90 | }) : theme); 91 | if (mode == "image") { 92 | el.setAttribute("data-src", src); 93 | el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); 94 | if (fallback || !holder.auto) { 95 | el.style.width = dimensions.width + "px"; 96 | el.style.height = dimensions.height + "px"; 97 | } 98 | if (fallback) { 99 | el.style.backgroundColor = theme.background; 100 | } else { 101 | el.setAttribute("src", draw(ctx, dimensions, theme, ratio)); 102 | } 103 | } else if (mode == "background") { 104 | if (!fallback) { 105 | el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")"; 106 | el.style.backgroundSize = dimensions.width + "px " + dimensions.height + "px"; 107 | } 108 | } else if (mode == "fluid") { 109 | el.setAttribute("data-src", src); 110 | el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); 111 | if (dimensions.height.substr(-1) == "%") { 112 | el.style.height = dimensions.height 113 | } else { 114 | el.style.height = dimensions.height + "px" 115 | } 116 | if (dimensions.width.substr(-1) == "%") { 117 | el.style.width = dimensions.width 118 | } else { 119 | el.style.width = dimensions.width + "px" 120 | } 121 | if (el.style.display == "inline" || el.style.display == "") { 122 | el.style.display = "block"; 123 | } 124 | if (fallback) { 125 | el.style.backgroundColor = theme.background; 126 | } else { 127 | el.holderData = holder; 128 | fluid_images.push(el); 129 | fluid_update(el); 130 | } 131 | } 132 | }; 133 | 134 | function fluid_update(element) { 135 | var images; 136 | if (element.nodeType == null) { 137 | images = fluid_images; 138 | } else { 139 | images = [element] 140 | } 141 | for (i in images) { 142 | var el = images[i] 143 | if (el.holderData) { 144 | var holder = el.holderData; 145 | el.setAttribute("src", draw(ctx, { 146 | height: el.clientHeight, 147 | width: el.clientWidth 148 | }, holder.theme, ratio)); 149 | } 150 | } 151 | } 152 | 153 | function parse_flags(flags, options) { 154 | 155 | var ret = { 156 | theme: settings.themes.gray 157 | }, render = false; 158 | 159 | for (sl = flags.length, j = 0; j < sl; j++) { 160 | var flag = flags[j]; 161 | if (app.flags.dimensions.match(flag)) { 162 | render = true; 163 | ret.dimensions = app.flags.dimensions.output(flag); 164 | } else if (app.flags.fluid.match(flag)) { 165 | render = true; 166 | ret.dimensions = app.flags.fluid.output(flag); 167 | ret.fluid = true; 168 | } else if (app.flags.colors.match(flag)) { 169 | ret.theme = app.flags.colors.output(flag); 170 | } else if (options.themes[flag]) { 171 | //If a theme is specified, it will override custom colors 172 | ret.theme = options.themes[flag]; 173 | } else if (app.flags.text.match(flag)) { 174 | ret.text = app.flags.text.output(flag); 175 | } else if (app.flags.font.match(flag)) { 176 | ret.font = app.flags.font.output(flag); 177 | } else if (app.flags.auto.match(flag)) { 178 | ret.auto = true; 179 | } 180 | } 181 | 182 | return render ? ret : false; 183 | 184 | }; 185 | 186 | 187 | 188 | if (!canvas.getContext) { 189 | fallback = true; 190 | } else { 191 | if (canvas.toDataURL("image/png") 192 | .indexOf("data:image/png") < 0) { 193 | //Android doesn't support data URI 194 | fallback = true; 195 | } else { 196 | var ctx = canvas.getContext("2d"); 197 | } 198 | } 199 | 200 | var dpr = 1, bsr = 1; 201 | 202 | if(!fallback){ 203 | dpr = window.devicePixelRatio || 1, 204 | bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; 205 | } 206 | 207 | var ratio = dpr / bsr; 208 | 209 | var fluid_images = []; 210 | 211 | var settings = { 212 | domain: "holder.js", 213 | images: "img", 214 | bgnodes: ".holderjs", 215 | themes: { 216 | "gray": { 217 | background: "#eee", 218 | foreground: "#aaa", 219 | size: 12 220 | }, 221 | "social": { 222 | background: "#3a5a97", 223 | foreground: "#fff", 224 | size: 12 225 | }, 226 | "industrial": { 227 | background: "#434A52", 228 | foreground: "#C2F200", 229 | size: 12 230 | } 231 | }, 232 | stylesheet: ".holderjs-fluid {font-size:16px;font-weight:bold;text-align:center;font-family:sans-serif;margin:0}" 233 | }; 234 | 235 | 236 | app.flags = { 237 | dimensions: { 238 | regex: /^(\d+)x(\d+)$/, 239 | output: function (val) { 240 | var exec = this.regex.exec(val); 241 | return { 242 | width: +exec[1], 243 | height: +exec[2] 244 | } 245 | } 246 | }, 247 | fluid: { 248 | regex: /^([0-9%]+)x([0-9%]+)$/, 249 | output: function (val) { 250 | var exec = this.regex.exec(val); 251 | return { 252 | width: exec[1], 253 | height: exec[2] 254 | } 255 | } 256 | }, 257 | colors: { 258 | regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i, 259 | output: function (val) { 260 | var exec = this.regex.exec(val); 261 | return { 262 | size: settings.themes.gray.size, 263 | foreground: "#" + exec[2], 264 | background: "#" + exec[1] 265 | } 266 | } 267 | }, 268 | text: { 269 | regex: /text\:(.*)/, 270 | output: function (val) { 271 | return this.regex.exec(val)[1]; 272 | } 273 | }, 274 | font: { 275 | regex: /font\:(.*)/, 276 | output: function (val) { 277 | return this.regex.exec(val)[1]; 278 | } 279 | }, 280 | auto: { 281 | regex: /^auto$/ 282 | } 283 | } 284 | 285 | for (var flag in app.flags) { 286 | if (!app.flags.hasOwnProperty(flag)) continue; 287 | app.flags[flag].match = function (val) { 288 | return val.match(this.regex) 289 | } 290 | } 291 | 292 | app.add_theme = function (name, theme) { 293 | name != null && theme != null && (settings.themes[name] = theme); 294 | return app; 295 | }; 296 | 297 | app.add_image = function (src, el) { 298 | var node = selector(el); 299 | if (node.length) { 300 | for (var i = 0, l = node.length; i < l; i++) { 301 | var img = document.createElement("img") 302 | img.setAttribute("data-src", src); 303 | node[i].appendChild(img); 304 | } 305 | } 306 | return app; 307 | }; 308 | 309 | app.run = function (o) { 310 | var options = extend(settings, o), 311 | images = [], imageNodes = [], bgnodes = []; 312 | 313 | if(typeof(options.images) == "string"){ 314 | imageNodes = selector(options.images); 315 | } 316 | else if (window.NodeList && options.images instanceof window.NodeList) { 317 | imageNodes = options.images; 318 | } else if (window.Node && options.images instanceof window.Node) { 319 | imageNodes = [options.images]; 320 | } 321 | 322 | if(typeof(options.bgnodes) == "string"){ 323 | bgnodes = selector(options.bgnodes); 324 | } else if (window.NodeList && options.elements instanceof window.NodeList) { 325 | bgnodes = options.bgnodes; 326 | } else if (window.Node && options.bgnodes instanceof window.Node) { 327 | bgnodes = [options.bgnodes]; 328 | } 329 | 330 | preempted = true; 331 | 332 | for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]); 333 | 334 | var holdercss = document.getElementById("holderjs-style"); 335 | if (!holdercss) { 336 | holdercss = document.createElement("style"); 337 | holdercss.setAttribute("id", "holderjs-style"); 338 | holdercss.type = "text/css"; 339 | document.getElementsByTagName("head")[0].appendChild(holdercss); 340 | } 341 | 342 | if (!options.nocss) { 343 | if (holdercss.styleSheet) { 344 | holdercss.styleSheet.cssText += options.stylesheet; 345 | } else { 346 | holdercss.appendChild(document.createTextNode(options.stylesheet)); 347 | } 348 | } 349 | 350 | var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)"); 351 | 352 | for (var l = bgnodes.length, i = 0; i < l; i++) { 353 | var src = window.getComputedStyle(bgnodes[i], null) 354 | .getPropertyValue("background-image"); 355 | var flags = src.match(cssregex); 356 | var bgsrc = bgnodes[i].getAttribute("data-background-src"); 357 | 358 | if (flags) { 359 | var holder = parse_flags(flags[1].split("/"), options); 360 | if (holder) { 361 | render("background", bgnodes[i], holder, src); 362 | } 363 | } 364 | else if(bgsrc != null){ 365 | var holder = parse_flags(bgsrc.substr(bgsrc.lastIndexOf(options.domain) + options.domain.length + 1) 366 | .split("/"), options); 367 | if(holder){ 368 | render("background", bgnodes[i], holder, src); 369 | } 370 | } 371 | } 372 | 373 | for (l = images.length, i = 0; i < l; i++) { 374 | 375 | var attr_src = attr_data_src = src = null; 376 | 377 | try{ 378 | attr_src = images[i].getAttribute("src"); 379 | attr_datasrc = images[i].getAttribute("data-src"); 380 | }catch(e){} 381 | 382 | if (attr_datasrc == null && !! attr_src && attr_src.indexOf(options.domain) >= 0) { 383 | src = attr_src; 384 | } else if ( !! attr_datasrc && attr_datasrc.indexOf(options.domain) >= 0) { 385 | src = attr_datasrc; 386 | } 387 | 388 | if (src) { 389 | var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1) 390 | .split("/"), options); 391 | if (holder) { 392 | if (holder.fluid) { 393 | render("fluid", images[i], holder, src) 394 | } else { 395 | render("image", images[i], holder, src); 396 | } 397 | } 398 | } 399 | } 400 | return app; 401 | }; 402 | 403 | contentLoaded(win, function () { 404 | if (window.addEventListener) { 405 | window.addEventListener("resize", fluid_update, false); 406 | window.addEventListener("orientationchange", fluid_update, false); 407 | } else { 408 | window.attachEvent("onresize", fluid_update) 409 | } 410 | preempted || app.run(); 411 | }); 412 | 413 | if (typeof define === "function" && define.amd) { 414 | define("Holder", [], function () { 415 | return app; 416 | }); 417 | } 418 | 419 | })(Holder, window); 420 | -------------------------------------------------------------------------------- /app/lib/bootstrap/assets/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d #mq-test-1 { width: 42px; }',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); 4 | 5 | /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 6 | (function(a){"use strict";function x(){u(!0)}var b={};a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,b.mediaQueriesSupported;var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var a=m.shift();v(a.href,function(b){p(b,a.href,a.media),h[a.href]=!0,setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(a){var b="clientWidth",h=d[b],k="CSS1Compat"===c.compatMode&&h||c.body[b]||h,m={},n=l[l.length-1],o=(new Date).getTime();if(a&&q&&i>o-q)return clearTimeout(r),r=setTimeout(u,i),void 0;q=o;for(var p in e)if(e.hasOwnProperty(p)){var v=e[p],w=v.minw,x=v.maxw,y=null===w,z=null===x,A="em";w&&(w=parseFloat(w)*(w.indexOf(A)>-1?t||s():1)),x&&(x=parseFloat(x)*(x.indexOf(A)>-1?t||s():1)),v.hasquery&&(y&&z||!(y||k>=w)||!(z||x>=k))||(m[v.media]||(m[v.media]=[]),m[v.media].push(f[v.rules]))}for(var B in g)g.hasOwnProperty(B)&&g[B]&&g[B].parentNode===j&&j.removeChild(g[B]);for(var C in m)if(m.hasOwnProperty(C)){var D=c.createElement("style"),E=m[C].join("\n");D.type="text/css",D.media=C,j.insertBefore(D,n.nextSibling),D.styleSheet?D.styleSheet.cssText=E:D.appendChild(c.createTextNode(E)),g.push(D)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)})(this); 7 | -------------------------------------------------------------------------------- /app/lib/bootstrap/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | .btn-default, 2 | .btn-primary, 3 | .btn-success, 4 | .btn-info, 5 | .btn-warning, 6 | .btn-danger { 7 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); 8 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 9 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 10 | } 11 | 12 | .btn-default:active, 13 | .btn-primary:active, 14 | .btn-success:active, 15 | .btn-info:active, 16 | .btn-warning:active, 17 | .btn-danger:active, 18 | .btn-default.active, 19 | .btn-primary.active, 20 | .btn-success.active, 21 | .btn-info.active, 22 | .btn-warning.active, 23 | .btn-danger.active { 24 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 25 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 26 | } 27 | 28 | .btn:active, 29 | .btn.active { 30 | background-image: none; 31 | } 32 | 33 | .btn-default { 34 | text-shadow: 0 1px 0 #fff; 35 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6)); 36 | background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%); 37 | background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%); 38 | background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%); 39 | background-repeat: repeat-x; 40 | border-color: #e0e0e0; 41 | border-color: #ccc; 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); 43 | } 44 | 45 | .btn-default:active, 46 | .btn-default.active { 47 | background-color: #e6e6e6; 48 | border-color: #e0e0e0; 49 | } 50 | 51 | .btn-primary { 52 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); 53 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); 54 | background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); 55 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 56 | background-repeat: repeat-x; 57 | border-color: #2d6ca2; 58 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 59 | } 60 | 61 | .btn-primary:active, 62 | .btn-primary.active { 63 | background-color: #3071a9; 64 | border-color: #2d6ca2; 65 | } 66 | 67 | .btn-success { 68 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); 69 | background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); 70 | background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); 71 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 72 | background-repeat: repeat-x; 73 | border-color: #419641; 74 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 75 | } 76 | 77 | .btn-success:active, 78 | .btn-success.active { 79 | background-color: #449d44; 80 | border-color: #419641; 81 | } 82 | 83 | .btn-warning { 84 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); 85 | background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); 86 | background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 87 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 88 | background-repeat: repeat-x; 89 | border-color: #eb9316; 90 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 91 | } 92 | 93 | .btn-warning:active, 94 | .btn-warning.active { 95 | background-color: #ec971f; 96 | border-color: #eb9316; 97 | } 98 | 99 | .btn-danger { 100 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); 101 | background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); 102 | background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); 103 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 104 | background-repeat: repeat-x; 105 | border-color: #c12e2a; 106 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 107 | } 108 | 109 | .btn-danger:active, 110 | .btn-danger.active { 111 | background-color: #c9302c; 112 | border-color: #c12e2a; 113 | } 114 | 115 | .btn-info { 116 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); 117 | background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); 118 | background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 119 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 120 | background-repeat: repeat-x; 121 | border-color: #2aabd2; 122 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 123 | } 124 | 125 | .btn-info:active, 126 | .btn-info.active { 127 | background-color: #31b0d5; 128 | border-color: #2aabd2; 129 | } 130 | 131 | .thumbnail, 132 | .img-thumbnail { 133 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 134 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 135 | } 136 | 137 | .dropdown-menu > li > a:hover, 138 | .dropdown-menu > li > a:focus, 139 | .dropdown-menu > .active > a, 140 | .dropdown-menu > .active > a:hover, 141 | .dropdown-menu > .active > a:focus { 142 | background-color: #357ebd; 143 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); 144 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); 145 | background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); 146 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 147 | background-repeat: repeat-x; 148 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 149 | } 150 | 151 | .navbar { 152 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8)); 153 | background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%); 154 | background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); 155 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); 156 | background-repeat: repeat-x; 157 | border-radius: 4px; 158 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 159 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 160 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 161 | } 162 | 163 | .navbar .navbar-nav > .active > a { 164 | background-color: #f8f8f8; 165 | } 166 | 167 | .navbar-brand, 168 | .navbar-nav > li > a { 169 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 170 | } 171 | 172 | .navbar-inverse { 173 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222)); 174 | background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%); 175 | background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%); 176 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); 177 | background-repeat: repeat-x; 178 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 179 | } 180 | 181 | .navbar-inverse .navbar-nav > .active > a { 182 | background-color: #222222; 183 | } 184 | 185 | .navbar-inverse .navbar-brand, 186 | .navbar-inverse .navbar-nav > li > a { 187 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 188 | } 189 | 190 | .navbar-static-top, 191 | .navbar-fixed-top, 192 | .navbar-fixed-bottom { 193 | border-radius: 0; 194 | } 195 | 196 | .alert { 197 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); 198 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 199 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 200 | } 201 | 202 | .alert-success { 203 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc)); 204 | background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%); 205 | background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 206 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 207 | background-repeat: repeat-x; 208 | border-color: #b2dba1; 209 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 210 | } 211 | 212 | .alert-info { 213 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0)); 214 | background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%); 215 | background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 216 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 217 | background-repeat: repeat-x; 218 | border-color: #9acfea; 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 220 | } 221 | 222 | .alert-warning { 223 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0)); 224 | background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%); 225 | background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 226 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 227 | background-repeat: repeat-x; 228 | border-color: #f5e79e; 229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 230 | } 231 | 232 | .alert-danger { 233 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3)); 234 | background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%); 235 | background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 236 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 237 | background-repeat: repeat-x; 238 | border-color: #dca7a7; 239 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 240 | } 241 | 242 | .progress { 243 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5)); 244 | background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%); 245 | background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 246 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 247 | background-repeat: repeat-x; 248 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 249 | } 250 | 251 | .progress-bar { 252 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); 253 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); 254 | background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); 255 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 256 | background-repeat: repeat-x; 257 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 258 | } 259 | 260 | .progress-bar-success { 261 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); 262 | background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); 263 | background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); 264 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 265 | background-repeat: repeat-x; 266 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 267 | } 268 | 269 | .progress-bar-info { 270 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); 271 | background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); 272 | background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 273 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 274 | background-repeat: repeat-x; 275 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 276 | } 277 | 278 | .progress-bar-warning { 279 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); 280 | background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); 281 | background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 282 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 283 | background-repeat: repeat-x; 284 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 285 | } 286 | 287 | .progress-bar-danger { 288 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); 289 | background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); 290 | background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); 291 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 292 | background-repeat: repeat-x; 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 294 | } 295 | 296 | .list-group { 297 | border-radius: 4px; 298 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 299 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 300 | } 301 | 302 | .list-group-item.active, 303 | .list-group-item.active:hover, 304 | .list-group-item.active:focus { 305 | text-shadow: 0 -1px 0 #3071a9; 306 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3)); 307 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%); 308 | background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%); 309 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 310 | background-repeat: repeat-x; 311 | border-color: #3278b3; 312 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 313 | } 314 | 315 | .panel { 316 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 317 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 318 | } 319 | 320 | .panel-default > .panel-heading { 321 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8)); 322 | background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%); 323 | background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 324 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 325 | background-repeat: repeat-x; 326 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 327 | } 328 | 329 | .panel-primary > .panel-heading { 330 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); 331 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); 332 | background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); 333 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 334 | background-repeat: repeat-x; 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 336 | } 337 | 338 | .panel-success > .panel-heading { 339 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6)); 340 | background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%); 341 | background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 342 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 343 | background-repeat: repeat-x; 344 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 345 | } 346 | 347 | .panel-info > .panel-heading { 348 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3)); 349 | background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%); 350 | background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 351 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 352 | background-repeat: repeat-x; 353 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 354 | } 355 | 356 | .panel-warning > .panel-heading { 357 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc)); 358 | background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%); 359 | background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 360 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 361 | background-repeat: repeat-x; 362 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 363 | } 364 | 365 | .panel-danger > .panel-heading { 366 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc)); 367 | background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%); 368 | background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 369 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 370 | background-repeat: repeat-x; 371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 372 | } 373 | 374 | .well { 375 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5)); 376 | background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%); 377 | background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 378 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 379 | background-repeat: repeat-x; 380 | border-color: #dcdcdc; 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 382 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 383 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 384 | } -------------------------------------------------------------------------------- /app/lib/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,0%,#e6e6e6,100%);background-image:-moz-linear-gradient(top,#fff 0,#e6e6e6 100%);background-image:linear-gradient(to bottom,#fff 0,#e6e6e6 100%);background-repeat:repeat-x;border-color:#e0e0e0;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0)}.btn-default:active,.btn-default.active{background-color:#e6e6e6;border-color:#e0e0e0}.btn-primary{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;border-color:#2d6ca2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.btn-primary:active,.btn-primary.active{background-color:#3071a9;border-color:#2d6ca2}.btn-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;border-color:#419641;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.btn-success:active,.btn-success.active{background-color:#449d44;border-color:#419641}.btn-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;border-color:#eb9316;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.btn-warning:active,.btn-warning.active{background-color:#ec971f;border-color:#eb9316}.btn-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;border-color:#c12e2a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.btn-danger:active,.btn-danger.active{background-color:#c9302c;border-color:#c12e2a}.btn-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;border-color:#2aabd2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.btn-info:active,.btn-info.active{background-color:#31b0d5;border-color:#2aabd2}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#f8f8f8));background-image:-webkit-linear-gradient(top,#fff,0%,#f8f8f8,100%);background-image:-moz-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar .navbar-nav>.active>a{background-color:#f8f8f8}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-gradient(linear,left 0,left 100%,from(#3c3c3c),to(#222));background-image:-webkit-linear-gradient(top,#3c3c3c,0%,#222,100%);background-image:-moz-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0)}.navbar-inverse .navbar-nav>.active>a{background-color:#222}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#c8e5bc));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#c8e5bc,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#b9def0,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#f8efc0));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#f8efc0,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#e7c3c3));background-image:-webkit-linear-gradient(top,#f2dede,0%,#e7c3c3,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#ebebeb,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3278b3));background-image:-webkit-linear-gradient(top,#428bca,0%,#3278b3,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5,0%,#e8e8e8,100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#d0e9c6));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#d0e9c6,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#c4e3f3));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#c4e3f3,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#faf2cc));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#faf2cc,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#ebcccc));background-image:-webkit-linear-gradient(top,#f2dede,0%,#ebcccc,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-gradient(linear,left 0,left 100%,from(#e8e8e8),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#e8e8e8,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /app/lib/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/lib/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotaweb/angular-mysql/a57fa9b08d1778f5f40d40df26ac089bbbf5417f/app/lib/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/views/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Welcome to {{applicationName}}

4 | 5 |

This is an example CRUD application using Angular.js, MySQL and RESTful services.

6 | 7 |
8 | 9 |
10 | 11 |

Users:

12 | 13 | 18 | 19 |
-------------------------------------------------------------------------------- /app/views/user-create.html: -------------------------------------------------------------------------------- 1 |

Create User

2 | 3 |
4 | 5 |
6 | 7 | 8 | Required 9 |
10 | 11 |
12 | 13 | 14 | Required 15 |
16 | 17 |
18 | 19 | 20 | Required 21 | Invalid email 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 | 32 | 35 | 36 |
-------------------------------------------------------------------------------- /app/views/user-edit.html: -------------------------------------------------------------------------------- 1 |

Edit User

2 | 3 |
4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 | Required 14 |
15 | 16 |
17 | 18 | 19 | Required 20 |
21 | 22 |
23 | 24 | 25 | Required 26 | Invalid email 27 |
28 | 29 |
30 | 31 | 32 |
33 | 34 |
35 | 36 | 37 | 40 | 41 | 42 |
43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/views/user-view.html: -------------------------------------------------------------------------------- 1 |

{{user.firstname}} {{user.lastname}}

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Id{{user.id}}
Firstname{{user.firstname}}
Lastname{{user.lastname}}
Email{{user.email}}
Company{{user.company}}
29 | 30 | 31 | Edit 32 | 33 | 34 | 37 | 38 | -------------------------------------------------------------------------------- /app/views/users.html: -------------------------------------------------------------------------------- 1 |

Users

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 30 | 33 | 34 | 35 |
{{header}}IdFirstnameLastnameEmailCompanyViewEditDelete
{{user.id}}{{user.firstname}}{{user.lastname}}{{user.email}}{{user.company}} 25 | 26 | 28 | 29 | 31 | 32 |
36 | 37 | 38 | Create 39 | 40 | -------------------------------------------------------------------------------- /install/angular-mysql.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : localhost 5 | Source Server Version : 50525 6 | Source Host : localhost 7 | Source Database : angular-mysql 8 | 9 | Target Server Version : 50525 10 | File Encoding : utf-8 11 | 12 | Date: 09/15/2013 16:44:29 PM 13 | */ 14 | 15 | SET NAMES utf8; 16 | SET FOREIGN_KEY_CHECKS = 0; 17 | 18 | -- ---------------------------- 19 | -- Table structure for `users` 20 | -- ---------------------------- 21 | DROP TABLE IF EXISTS `users`; 22 | CREATE TABLE `users` ( 23 | `id` int(11) NOT NULL AUTO_INCREMENT, 24 | `firstname` varchar(50) DEFAULT NULL, 25 | `lastname` varchar(50) DEFAULT NULL, 26 | `email` varchar(100) DEFAULT NULL, 27 | `company` varchar(50) DEFAULT NULL, 28 | PRIMARY KEY (`id`) 29 | ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=latin1; 30 | 31 | -- ---------------------------- 32 | -- Records of `users` 33 | -- ---------------------------- 34 | BEGIN; 35 | INSERT INTO `users` VALUES ('1', 'John', 'Henry', 'john.henry@acme.com', 'Acme Industries'), ('2', 'Mary', 'Smith', 'mary.smith@ibm.com', 'IBM'), ('3', 'George', 'Reynolds', 'george@microsoft.com', 'Microsoft'), ('4', 'Joanne', 'Klum', 'jo.klum@bauhaus.de', 'Bauhaus'); 36 | COMMIT; 37 | 38 | SET FOREIGN_KEY_CHECKS = 1; 39 | --------------------------------------------------------------------------------