diff --git a/.dockerignore b/.dockerignore index 42c3e152..10a06d66 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,8 +5,9 @@ !Dockerfile # Include application specific files -!system/ -!application/ +!app/ +!composer.json +!composer.lock # Include Node package files !package.json diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..214cc819 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +CI_ENVIRONMENT = production +app.baseURL = 'http://localhost:8080/' +app.defaultLocale = en + +database.default.hostname = lisa-mariadb +database.default.database = lisa +database.default.username = lisa +database.default.password = lisa +database.default.DBDriver = MySQLi +database.default.port = 3306 + +email.fromEmail = 'lisa@example.com' +email.fromName = 'Lisa' +# email.protocol = smtp +# email.SMTPHost = smtp.example.com +# email.SMTPUser = '' +# email.SMTPPass = '' +# email.SMTPPort = 587 +# email.SMTPCrypto = tls \ No newline at end of file diff --git a/.github/workflows/continuous-integration.yaml b/.github/workflows/continuous-integration.yaml index dc60efa7..fee56fb9 100644 --- a/.github/workflows/continuous-integration.yaml +++ b/.github/workflows/continuous-integration.yaml @@ -28,6 +28,9 @@ jobs: - name: Wait until test environment is up run: npx wait-on http-get://localhost:8080/Install + - name: PHP 8.5 migration regression checks + run: docker compose exec -T php-fpm composer test + - name: Cypress run uses: cypress-io/github-action@v6.7.8 with: diff --git a/.gitignore b/.gitignore index 4c8efa1c..a4fd375b 100755 --- a/.gitignore +++ b/.gitignore @@ -15,11 +15,20 @@ user_guide_src/cilexer/build/* user_guide_src/cilexer/dist/* user_guide_src/cilexer/pycilexer.egg-info/* /vendor/ +/.env +/writable/* +!/writable/.htaccess +!/writable/index.html +!/writable/*/ +/writable/*/* +!/writable/*/index.html node_modules/ # Ignore Cypress' video recordings *.mp4 +/cypress/screenshots/ +/cypress/videos/ # IDE Files #------------------------- diff --git a/README.md b/README.md index 27ef3a56..d14fbf27 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # LISA +LISA runs on CodeIgniter 4.7.5 or newer and PHP 8.5. + [![Continuous Integration](https://github.com/Addono/LISA/actions/workflows/continuous-integration.yaml/badge.svg)](https://github.com/Addono/LISA/actions/workflows/continuous-integration.yaml) [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg)](#contributors) [![Project Status: Inactive – The project has reached a stable, usable state but is no longer being actively developed; support/maintenance will be provided as time allows.](https://www.repostatus.org/badges/latest/inactive.svg)](https://www.repostatus.org/#inactive) @@ -15,27 +17,19 @@ Many use-cases can be imagined, in general LISA gives each user an integer sized ```bash # Clone the repository -git clone https://github.com/Addono/lisa && cd lisa +git clone https://github.com/omit01/LISA.git && cd LISA # Install dependencies -npm install - -# (Optional) Configure the database, the defaults are for a docker compose instance -vim application/config/database.php +npm ci -# (Optional) Configure general settings, setting the hostname is mandatory -# by default it is set to http://localhost:8080, if your Docker instance -# is not at localhost, then you need to set $config['base_url'] manually -# in application/config/config.php -vim application/config/config.php - -# (Optional) Although you will get nasty errors in your logs whenever it -# tries to send email. -vim application/config/email.php +# Configure the URL, database, locale, and optional SMTP settings +cp .env.example .env # Configuration is finished, let's deploy the application and all required services -docker compose up -d +docker compose up --build -d ``` +The PHP container installs the locked Composer dependencies and prepares `writable/` before starting PHP-FPM. The default database settings match Docker Compose. Change `app.baseURL` in `.env` when using another hostname. Never commit `.env` or expose it through your web server. + One small last thing we need to do is to run all database migrations, either point your browser at [http://localhost:8080/install](http://localhost:8080/install) or run`curl http://localhost:8080/install`. :rocket: And we are live at [http://localhost:8080](http://localhost:8080) :rocket: @@ -46,18 +40,35 @@ _Note: Depending on how you installed Docker it might be that Docker is not acce ### Manual Requirements: - * PHP 7* with the mysqli extension enabled. - * MySQL database + * PHP 8.5 with `intl`, `mbstring`, and `mysqli` extensions. + * Composer 2, Node.js, and npm. + * MySQL or MariaDB. + +1. Clone the repository, run `composer install` and `npm ci`. +2. Copy `.env.example` to `.env` and configure your URL, database, locale, and SMTP server. +3. Configure the web server's document root as `public/`, not the repository root. Forward dynamic requests to `public/index.php` and serve `/node_modules/` from the installed frontend dependencies, as shown in the Nginx configuration. +4. Make `writable/` writable by the PHP process. Application code and `vendor/` must not be publicly accessible. +5. Open `/Install` to initialize the database, then change the default administrator password. + +### Migrating an Existing Installation + +Back up the database and the old configuration before deployment. This is a CI3-to-CI4 application conversion, following the [official migration guide](https://codeigniter4.github.io/userguide/installation/upgrade_4xx.html). - 1. Download a copy/make a clone/make a fork of the project and move it to a folder in your web server. - 1. Rename or copy `config.php-setup`, `database.php-setup` and `email.php-config` to `config.php`, `database.php` and `email.php` respectively, these files can be found in `application/config/`. - 1. Edit these config files to match your system. - * Add your hostname in `config.php`. - * Enter the settings of your database in `database.php`. - * (Optional, but recommended) Configure an SMTP server in `email.php`. - 1. Navigate to __YOUR_HOSTNAME__/index.php/Install to initialize the database. +Move your database, URL, SMTP, and language settings from the old `application/config/` files into `.env` using the names in `.env.example`. Existing tables, password hashes, model-version keys, and transaction data are retained. Preserve any configured database table prefix with `database.default.DBPrefix`. Do not import a fresh database or recreate existing tables. Running `/Install` again applies only outstanding schema versions. Existing sessions are replaced, so users must log in again. + +The source lives in `app/`; the framework is installed under `vendor/`. The old `application/`, bundled `system/`, and root entry point are no longer used. Keep the `/Install` endpoint restricted to operators in production, and configure SMTP to deliver low-credit notifications. + +### Verification + +```bash +# Isolated regression checks; requires the sqlite3 extension +docker compose exec -T php-fpm composer test + +# Browser checks against the running MariaDB-backed application +npx cypress run --record false +``` -*The Docker Compose deploy method and the CI are using 7.4. The version used there is leading and the only one supported. +The regression checks use an in-memory database and do not modify application data. Cypress uses the running application and creates test users; run it against a development database. ## Development Environment @@ -88,7 +99,7 @@ Afterwards, use the admin user to create new users, which can be done in the bac ## Customisation ### Language -Currently, the language used by the application is hard-coded into `Handler.php`, for future releases it is planned to move configurations like these into the database. For now, one should edit hard-coded language or substitute all language files in the `nederlands` language folder with your language of preference. During development I tried to add translations for both English and Dutch, so the English translation files should be complete. +Set `app.defaultLocale = en` or `app.defaultLocale = nl` in `.env`. Frontend translations are in `app/Language//Lisa.php`; administrator translations are in `Admin.php`. The existing default, application, and custom dictionaries have been combined into these files. ## Contributors✨ diff --git a/system/.htaccess b/app/.htaccess old mode 100755 new mode 100644 similarity index 89% rename from system/.htaccess rename to app/.htaccess index 97c65d2d..3462048a --- a/system/.htaccess +++ b/app/.htaccess @@ -3,4 +3,4 @@ Deny from all - \ No newline at end of file + diff --git a/app/Common.php b/app/Common.php new file mode 100644 index 00000000..95f55442 --- /dev/null +++ b/app/Common.php @@ -0,0 +1,15 @@ + + */ + public array $allowedHostnames = []; + + /** + * -------------------------------------------------------------------------- + * Index File + * -------------------------------------------------------------------------- + * + * Typically, this will be your `index.php` file, unless you've renamed it to + * something else. If you have configured your web server to remove this file + * from your site URIs, set this variable to an empty string. + */ + public string $indexPage = ''; + + /** + * -------------------------------------------------------------------------- + * URI PROTOCOL + * -------------------------------------------------------------------------- + * + * This item determines which server global should be used to retrieve the + * URI string. The default setting of 'REQUEST_URI' works for most servers. + * If your links do not seem to work, try one of the other delicious flavors: + * + * 'REQUEST_URI': Uses $_SERVER['REQUEST_URI'] + * 'QUERY_STRING': Uses $_SERVER['QUERY_STRING'] + * 'PATH_INFO': Uses $_SERVER['PATH_INFO'] + * + * WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! + */ + public string $uriProtocol = 'REQUEST_URI'; + + /* + |-------------------------------------------------------------------------- + | Allowed URL Characters + |-------------------------------------------------------------------------- + | + | This lets you specify which characters are permitted within your URLs. + | When someone tries to submit a URL with disallowed characters they will + | get a warning message. + | + | As a security measure you are STRONGLY encouraged to restrict URLs to + | as few characters as possible. + | + | By default, only these are allowed: `a-z 0-9~%.:_-` + | + | Set an empty string to allow all characters -- but only if you are insane. + | + | The configured value is actually a regular expression character group + | and it will be used as: '/\A[]+\z/iu' + | + | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! + | + */ + public string $permittedURIChars = 'a-z 0-9~%.:_\-'; + + /** + * -------------------------------------------------------------------------- + * Default Locale + * -------------------------------------------------------------------------- + * + * The Locale roughly represents the language and location that your visitor + * is viewing the site from. It affects the language strings and other + * strings (like currency markers, numbers, etc), that your program + * should run under for this request. + */ + public string $defaultLocale = 'en'; + + /** + * -------------------------------------------------------------------------- + * Negotiate Locale + * -------------------------------------------------------------------------- + * + * If true, the current Request object will automatically determine the + * language to use based on the value of the Accept-Language header. + * + * If false, no automatic detection will be performed. + */ + public bool $negotiateLocale = false; + + /** + * -------------------------------------------------------------------------- + * Supported Locales + * -------------------------------------------------------------------------- + * + * If $negotiateLocale is true, this array lists the locales supported + * by the application in descending order of priority. If no match is + * found, the first locale will be used. + * + * IncomingRequest::setLocale() also uses this list. + * + * @var list + */ + public array $supportedLocales = ['en', 'nl']; + + /** + * -------------------------------------------------------------------------- + * Application Timezone + * -------------------------------------------------------------------------- + * + * The default timezone that will be used in your application to display + * dates with the date helper, and can be retrieved through app_timezone() + * + * @see https://www.php.net/manual/en/timezones.php for list of timezones + * supported by PHP. + */ + public string $appTimezone = 'UTC'; + + /** + * -------------------------------------------------------------------------- + * Default Character Set + * -------------------------------------------------------------------------- + * + * This determines which character set is used by default in various methods + * that require a character set to be provided. + * + * @see http://php.net/htmlspecialchars for a list of supported charsets. + */ + public string $charset = 'UTF-8'; + + /** + * -------------------------------------------------------------------------- + * Force Global Secure Requests + * -------------------------------------------------------------------------- + * + * If true, this will force every request made to this application to be + * made via a secure connection (HTTPS). If the incoming request is not + * secure, the user will be redirected to a secure version of the page + * and the HTTP Strict Transport Security (HSTS) header will be set. + */ + public bool $forceGlobalSecureRequests = false; + + /** + * -------------------------------------------------------------------------- + * Reverse Proxy IPs + * -------------------------------------------------------------------------- + * + * If your server is behind a reverse proxy, you must whitelist the proxy + * IP addresses from which CodeIgniter should trust headers such as + * X-Forwarded-For or Client-IP in order to properly identify + * the visitor's IP address. + * + * You need to set a proxy IP address or IP address with subnets and + * the HTTP header for the client IP address. + * + * Here are some examples: + * [ + * '10.0.1.200' => 'X-Forwarded-For', + * '192.168.5.0/24' => 'X-Real-IP', + * ] + * + * @var array + */ + public array $proxyIPs = []; + + /** + * -------------------------------------------------------------------------- + * Content Security Policy + * -------------------------------------------------------------------------- + * + * Enables the Response's Content Secure Policy to restrict the sources that + * can be used for images, scripts, CSS files, audio, video, etc. If enabled, + * the Response object will populate default values for the policy from the + * `ContentSecurityPolicy.php` file. Controllers can always add to those + * restrictions at run time. + * + * For a better understanding of CSP, see these documents: + * + * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + * @see http://www.w3.org/TR/CSP/ + */ + public bool $CSPEnabled = false; +} diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php new file mode 100644 index 00000000..6a6c3cc5 --- /dev/null +++ b/app/Config/Autoload.php @@ -0,0 +1,92 @@ +|string> + */ + public $psr4 = [ + APP_NAMESPACE => APPPATH, + ]; + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * Prototype: + * $classmap = [ + * 'MyClass' => '/path/to/class/file.php' + * ]; + * + * @var array + */ + public $classmap = []; + + /** + * ------------------------------------------------------------------- + * Files + * ------------------------------------------------------------------- + * The files array provides a list of paths to __non-class__ files + * that will be autoloaded. This can be useful for bootstrap operations + * or for loading functions. + * + * Prototype: + * $files = [ + * '/path/to/my/file.php', + * ]; + * + * @var list + */ + public $files = []; + + /** + * ------------------------------------------------------------------- + * Helpers + * ------------------------------------------------------------------- + * Prototype: + * $helpers = [ + * 'form', + * ]; + * + * @var list + */ + public $helpers = ['tables', 'mdbt_model']; +} diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php new file mode 100644 index 00000000..a868447a --- /dev/null +++ b/app/Config/Boot/development.php @@ -0,0 +1,34 @@ + + * + * @see https://www.php.net/manual/en/curl.constants.php#constant.curl-lock-data-connect + */ + public array $shareConnectionOptions = [ + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_DNS, + ]; + + /** + * -------------------------------------------------------------------------- + * CURLRequest Share Options + * -------------------------------------------------------------------------- + * + * Whether share options between requests or not. + * + * If true, all the options won't be reset between requests. + * It may cause an error request with unnecessary headers. + */ + public bool $shareOptions = false; +} diff --git a/app/Config/Cache.php b/app/Config/Cache.php new file mode 100644 index 00000000..38ac5419 --- /dev/null +++ b/app/Config/Cache.php @@ -0,0 +1,198 @@ + WRITEPATH . 'cache/', + 'mode' => 0640, + ]; + + /** + * ------------------------------------------------------------------------- + * Memcached settings + * ------------------------------------------------------------------------- + * + * Your Memcached servers can be specified below, if you are using + * the Memcached drivers. + * + * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached + * + * @var array{host?: string, port?: int, weight?: int, raw?: bool} + */ + public array $memcached = [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1, + 'raw' => false, + ]; + + /** + * ------------------------------------------------------------------------- + * Redis settings + * ------------------------------------------------------------------------- + * + * Your Redis server can be specified below, if you are using + * the Redis or Predis drivers. + * + * @var array{ + * host?: string, + * password?: string|null, + * port?: int, + * timeout?: int, + * async?: bool, + * persistent?: bool, + * database?: int + * } + */ + public array $redis = [ + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + 'async' => false, // specific to Predis and ignored by the native Redis extension + 'persistent' => false, + 'database' => 0, + ]; + + /** + * -------------------------------------------------------------------------- + * Available Cache Handlers + * -------------------------------------------------------------------------- + * + * This is an array of cache engine alias' and class names. Only engines + * that are listed here are allowed to be used. + * + * @var array> + */ + public array $validHandlers = [ + 'apcu' => ApcuHandler::class, + 'dummy' => DummyHandler::class, + 'file' => FileHandler::class, + 'memcached' => MemcachedHandler::class, + 'predis' => PredisHandler::class, + 'redis' => RedisHandler::class, + 'wincache' => WincacheHandler::class, + ]; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Include Query String + * -------------------------------------------------------------------------- + * + * Whether to take the URL query string into consideration when generating + * output cache files. Valid options are: + * + * false = Disabled + * true = Enabled, take all query parameters into account. + * Please be aware that this may result in numerous cache + * files generated for the same page over and over again. + * ['q'] = Enabled, but only take into account the specified list + * of query parameters. + * + * @var bool|list + */ + public $cacheQueryString = false; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Status Codes + * -------------------------------------------------------------------------- + * + * HTTP status codes that are allowed to be cached. Only responses with + * these status codes will be cached by the PageCache filter. + * + * Default: [] - Cache all status codes (backward compatible) + * + * Recommended: [200] - Only cache successful responses + * + * You can also use status codes like: + * [200, 404, 410] - Cache successful responses and specific error codes + * [200, 201, 202, 203, 204] - All 2xx successful responses + * + * WARNING: Using [] may cache temporary error pages (404, 500, etc). + * Consider restricting to [200] for production applications to avoid + * caching errors that should be temporary. + * + * @var list + */ + public array $cacheStatusCodes = []; +} diff --git a/app/Config/Constants.php b/app/Config/Constants.php new file mode 100644 index 00000000..fb56bb1c --- /dev/null +++ b/app/Config/Constants.php @@ -0,0 +1,79 @@ +|string|null + */ + public $defaultSrc; + + /** + * Lists allowed scripts' URLs. + * + * @var list|string + */ + public $scriptSrc = 'self'; + + /** + * Specifies valid sources for JavaScript @@ -47,7 +62,7 @@ public function getJavascript($group): string url: "", data: { action: 'updateData', - ci->security->get_csrf_token_name()?>: "ci->security->get_csrf_hash()?>" + : "" }, type: "POST", dataType: "json", @@ -79,7 +94,7 @@ public function getJavascript($group): string data: { id: $button.data('id'), action: 'buy', - ci->security->get_csrf_token_name()?>: "ci->security->get_csrf_hash()?>" + : "" }, type: "POST", dataType: "json" @@ -90,7 +105,7 @@ public function getJavascript($group): string // On success case '': status = 'notice'; - message = '' + message = '' .replace('[name]', json.name) .replace('[newAmount]', json.new_amount) .replace('[amount]', json.amount); @@ -99,7 +114,7 @@ public function getJavascript($group): string // Show a party popper for each 50th consumption. let partyInterval = 50; if (json.consume_count % partyInterval === 0) { - message += ' ' + + message += ' ' + .replace('[count]', json.consume_count) .replace('[next_amount]', partyInterval); } @@ -111,26 +126,26 @@ public function getJavascript($group): string status = 'error'; switch (json.) { case '': - message = ''; + message = ''; break; case '': case '': - message = ''; + message = ''; break; case '': - message = ''; + message = ''; break; case '': - message = ''; + message = ''; break; default: - message = ''; + message = ''; break; } break; default: status = 'error'; - message = ''; + message = ''; break; } @@ -143,7 +158,7 @@ public function getJavascript($group): string snackbarContainer.MaterialSnackbar.showSnackbar(data); // Show the snackbar }).fail(function (xhr, status, errorMessage) { if (xhr.status === 403) { - alert(""); + alert(""); location.reload(); return; } @@ -171,10 +186,7 @@ public function getJavascript($group): string }); @@ -23,8 +25,7 @@ protected function dependencies() } private function add($loginId) { - return $this->db - ->insert(self::name(), [ + return $this->db->table(self::name())->insert([ Login::FIELD_LOGIN_ID => $loginId, ]); } @@ -36,10 +37,10 @@ private function add($loginId) { * @return int */ public function get($loginId) { - $result = $this->db + $result = $this->db->table(self::name()) ->where([Login::FIELD_LOGIN_ID => $loginId]) - ->get(self::name()) - ->row_array()[self::FIELD_AMOUNT]; + ->get() + ->getRowArray()[self::FIELD_AMOUNT] ?? null; // Check if the user was present. if ($result === null) { @@ -52,14 +53,14 @@ public function get($loginId) { } public function getAll() { - return $this->db - ->get(self::name()) - ->result_array() + return $this->db->table(self::name()) + ->get() + ->getResultArray() ; } public function change($loginId, $authorId, $delta, $type) { - $this->db->trans_start(); + $this->db->transStart(); $oldAmount = $this->get($loginId); // Check if the tuple even exists. @@ -69,10 +70,10 @@ public function change($loginId, $authorId, $delta, $type) { $newAmount = $oldAmount + $delta; } - $success = $this->set($loginId, $authorId, $newAmount, $delta, $type); - $this->db->trans_complete(); + $success = $this->setBalance($loginId, $authorId, $newAmount, $delta, $type); + $this->db->transComplete(); - return $this->db->trans_status() && $success; + return $this->db->transStatus() && $success; } /** @@ -84,20 +85,17 @@ public function change($loginId, $authorId, $delta, $type) { * @param $delta * @return bool */ - private function set($loginId, $authorId, $amount, $delta, $type) { - $this->db->trans_start(); - $result = $this->db - ->replace( - self::name(), - [ + private function setBalance($loginId, $authorId, $amount, $delta, $type) { + $this->db->transStart(); + $result = $this->db->table(self::name())->replace([ self::FIELD_AMOUNT => $amount, Login::FIELD_LOGIN_ID => $loginId ] ); // Log this action as a new transaction. - $this->Transaction->add($loginId, $authorId, $amount, $delta, $type); - $this->db->trans_complete(); + model(Transaction::class)->add($loginId, $authorId, $amount, $delta, $type); + $this->db->transComplete(); return $result; } diff --git a/application/models/Login.php b/app/Models/Login.php old mode 100755 new mode 100644 similarity index 79% rename from application/models/Login.php rename to app/Models/Login.php index 4604a31c..1e186690 --- a/application/models/Login.php +++ b/app/Models/Login.php @@ -1,4 +1,6 @@ db->insert( - $this->name(), - [ + return $this->db->table($this->name())->insert([ self::FIELD_USERNAME => $username, self::FIELD_PASSWORD => password_hash($password, PASSWORD_DEFAULT), ] @@ -37,8 +37,8 @@ public function add($username, $password) { * @param $data * @return bool */ - public function update($loginId, $data) { - return $this->db->update(self::name(), $data, [self::FIELD_LOGIN_ID => $loginId]); + public function updateDetails($loginId, $data) { + return $this->db->table(self::name())->update($data, [self::FIELD_LOGIN_ID => $loginId]); } /** @@ -53,7 +53,7 @@ public function updatePassword($loginId, $password) { self::FIELD_PASSWORD => password_hash($password, PASSWORD_DEFAULT), ]; - return $this->update($loginId, $data); + return $this->updateDetails($loginId, $data); } /** @@ -75,11 +75,11 @@ public function checkUsernamePasswordCredentials($username, $password) { * @return bool */ public function checkUserIdPasswordCredentials($userId, $password) { - $result = $this->db + $result = $this->db->table($this->name()) ->where([self::FIELD_LOGIN_ID => $userId]) - ->get($this->name()) - ->row_array(); - if (key_exists(self::FIELD_PASSWORD, $result)) { + ->get() + ->getRowArray(); + if (isset($result[self::FIELD_PASSWORD])) { return password_verify($password, $result[self::FIELD_PASSWORD]); } else { return false; @@ -87,9 +87,9 @@ public function checkUserIdPasswordCredentials($userId, $password) { } public function usernameExists($username) { - $result = $this->db + $result = $this->db->table($this->name()) ->where([self::FIELD_USERNAME => $username]) - ->count_all_results($this->name()); + ->countAllResults(); return $result > 0; } @@ -98,27 +98,27 @@ public function usernameNotExists($username) { } public function getUsernames() { - return $this->db + return $this->db->table($this->name()) ->select([self::FIELD_USERNAME]) - ->get($this->name()) - ->result(); + ->get() + ->getResult(); } public function getUsername($loginId) { - return $this->db + return $this->db->table($this->name()) ->where([self::FIELD_LOGIN_ID => $loginId]) - ->get($this->name()) - ->row_array()[self::FIELD_USERNAME]; + ->get() + ->getRowArray()[self::FIELD_USERNAME]; } public function getLoginIdFromUsername($username) { - $row = $this->db + $row = $this->db->table($this->name()) ->where([self::FIELD_USERNAME => $username]) - ->get($this->name()) - ->row_array(); + ->get() + ->getRowArray(); if ($row === null) { - throw new Exception('Username "'.$username.'" was not found in the login table.'); + throw new \Exception('Username "'.$username.'" was not found in the login table.'); } return $row[self::FIELD_LOGIN_ID]; diff --git a/application/models/LoginReset.php b/app/Models/LoginReset.php old mode 100755 new mode 100644 similarity index 81% rename from application/models/LoginReset.php rename to app/Models/LoginReset.php index 30419c78..2ab556dd --- a/application/models/LoginReset.php +++ b/app/Models/LoginReset.php @@ -1,4 +1,6 @@ @@ -25,14 +27,12 @@ protected function dependencies() * @param $loginId * @return string The key when successful, null when failed. */ - public function add($loginId): string { + public function add($loginId): ?string { do { $key = sha1(random_bytes(30)); } while ($this->exists($key)); - $res = $this->db->replace( - self::name(), - [ + $res = $this->db->table(self::name())->replace([ self::FIELD_RESET_KEY => $key, Login::FIELD_LOGIN_ID => $loginId, ] @@ -46,10 +46,10 @@ public function add($loginId): string { } public function exists($key) { - $res = $this->db + $res = $this->db->table(self::name()) ->where([self::FIELD_RESET_KEY => $key]) - ->get(self::name()) - ->row_array(); + ->get() + ->getRowArray(); if ($res===null) { return false; @@ -59,15 +59,14 @@ public function exists($key) { } public function get($key) { - return $this->db + return $this->db->table(self::name()) ->where([self::FIELD_RESET_KEY => $key]) - ->get(self::name()) - ->row_array(); + ->get() + ->getRowArray(); } public function remove($key) { - return $this->db - ->delete(self::name(), [self::FIELD_RESET_KEY => $key]); + return $this->db->table(self::name())->delete([self::FIELD_RESET_KEY => $key]); } public function v1() { diff --git a/application/models/LoginReset_User.php b/app/Models/LoginReset_User.php old mode 100755 new mode 100644 similarity index 65% rename from application/models/LoginReset_User.php rename to app/Models/LoginReset_User.php index f6a7812a..ec74f467 --- a/application/models/LoginReset_User.php +++ b/app/Models/LoginReset_User.php @@ -1,4 +1,6 @@ * @date 1-3-2017 @@ -22,25 +24,25 @@ protected function dependencies() } public function updatePassword($key, $password) { - if (!$this->LoginReset->exists($key)) { + if (!model(LoginReset::class)->exists($key)) { return false; } - $this->db->trans_start(); + $this->db->transStart(); // Get the all data accompanied by this key. - $res = $this->LoginReset->get($key); + $res = model(LoginReset::class)->get($key); // Change the password of the user. - $this->Login->updatePassword($res[Login::FIELD_LOGIN_ID], $password); + model(Login::class)->updatePassword($res[Login::FIELD_LOGIN_ID], $password); // Remove the key since it now is used. - $success = $this->LoginReset->remove($key); + $success = model(LoginReset::class)->remove($key); if (!$success) { - $this->db->trans_rollback(); + $this->db->transRollback(); return false; } - $this->db->trans_complete(); + $this->db->transComplete(); - return $this->db->trans_status(); + return $this->db->transStatus(); } } \ No newline at end of file diff --git a/application/models/LoginRole.php b/app/Models/LoginRole.php old mode 100755 new mode 100644 similarity index 81% rename from application/models/LoginRole.php rename to app/Models/LoginRole.php index 7acb8fb9..df267c8a --- a/application/models/LoginRole.php +++ b/app/Models/LoginRole.php @@ -1,4 +1,6 @@ @@ -30,9 +32,7 @@ protected function dependencies() public function add($loginId, $roleId) { $exists = $this->exists($loginId, $roleId); if (!$exists) { - return $this->db->insert( - self::name(), - [ + return $this->db->table(self::name())->insert([ Login::FIELD_LOGIN_ID => $loginId, Role::FIELD_ROLE_ID => $roleId, ] @@ -50,12 +50,12 @@ public function add($loginId, $roleId) { * @return bool True if it exists, else false. */ public function exists($loginId, $roleId) { - $result = $this->db + $result = $this->db->table(self::name()) ->where([ Login::FIELD_LOGIN_ID => $loginId, Role::FIELD_ROLE_ID => $roleId, ]) - ->count_all_results(self::name()); + ->countAllResults(); return $result > 0; } @@ -67,10 +67,10 @@ public function exists($loginId, $roleId) { * @return array */ public function getFromLoginId($loginId) { - return $this->db + return $this->db->table(self::name()) ->where([Login::FIELD_LOGIN_ID => $loginId]) - ->get(self::name()) - ->result_array(); + ->get() + ->getResultArray(); } /** @@ -80,10 +80,10 @@ public function getFromLoginId($loginId) { * @return array */ public function getFromRoleId($roleId) { - return $this->db + return $this->db->table(self::name()) ->where([Role::FIELD_ROLE_ID => $roleId]) - ->get(self::name()) - ->result_array(); + ->get() + ->getResultArray(); } /** @@ -94,15 +94,15 @@ public function getFromRoleId($roleId) { * @return bool True on success, else false. */ public function setRolesForLoginId($loginId, array $roleIds) { - $this->db->trans_start(); + $this->db->transStart(); $this->deleteRolesForLoginId($loginId); foreach ($roleIds as $roleId) { $this->add($loginId, $roleId); } - $this->db->trans_complete(); + $this->db->transComplete(); - return $this->db->trans_status(); + return $this->db->transStatus(); } /** @@ -111,7 +111,7 @@ public function setRolesForLoginId($loginId, array $roleIds) { * @param int $loginId */ private function deleteRolesForLoginId($loginId) { - $this->db->delete(self::name(), [Login::FIELD_LOGIN_ID => $loginId]); + $this->db->table(self::name())->delete([Login::FIELD_LOGIN_ID => $loginId]); } //====================================== @@ -157,8 +157,8 @@ public function v2() { } public function v3() { - $loginId = $this->Login->getLoginIdFromUsername(Login::INITIAL_LOGIN_USERNAME); - $roleIdAdmin = $this->Role->getRoleIdFromRoleName(Role::ROLE_ADMIN); + $loginId = model(Login::class)->getLoginIdFromUsername(Login::INITIAL_LOGIN_USERNAME); + $roleIdAdmin = model(Role::class)->getRoleIdFromRoleName(Role::ROLE_ADMIN); $this->add($loginId, $roleIdAdmin); } diff --git a/application/models/Login_User.php b/app/Models/Login_User.php old mode 100755 new mode 100644 similarity index 87% rename from application/models/Login_User.php rename to app/Models/Login_User.php index 6726bfd4..e3a79387 --- a/application/models/Login_User.php +++ b/app/Models/Login_User.php @@ -1,4 +1,6 @@ * @date 1-3-2017 @@ -26,8 +28,8 @@ protected function dependencies() public function exists($loginId) { return $this->joinUser() ->where([field(Login::FIELD_LOGIN_ID, Login::name()) => $loginId]) - ->get(Login::name()) - ->num_rows() > 0; + ->get() + ->getNumRows() > 0; } /** @@ -36,7 +38,7 @@ public function exists($loginId) { * @return CI_DB_query_builder */ public function joinUser() { - return $this->db->join(User::name(), + return $this->db->table(Login::name())->join(User::name(), eq( field(Login::FIELD_LOGIN_ID, Login::name()), field(Login::FIELD_LOGIN_ID, User::name()) diff --git a/application/models/Login_User_LoginRole.php b/app/Models/Login_User_LoginRole.php old mode 100755 new mode 100644 similarity index 65% rename from application/models/Login_User_LoginRole.php rename to app/Models/Login_User_LoginRole.php index 822ae04b..ab72a9f5 --- a/application/models/Login_User_LoginRole.php +++ b/app/Models/Login_User_LoginRole.php @@ -1,4 +1,6 @@ @@ -25,20 +27,20 @@ protected function dependencies() public function add($username, $password, $firstName, $lastName, $email, array $roles) { - $this->db->trans_start(); + $this->db->transStart(); // Create a new login entry. - $this->Login->add($username, $password); - $loginId = $this->Login->getLoginIdFromUsername($username); + model(Login::class)->add($username, $password); + $loginId = model(Login::class)->getLoginIdFromUsername($username); // Create a new user profile. - $this->User->add($loginId, $firstName, $lastName, $email); + model(User::class)->add($loginId, $firstName, $lastName, $email); // Add all roles to the new account. foreach ($roles as $role) { - $this->LoginRole->add($loginId, $role); + model(LoginRole::class)->add($loginId, $role); } - $this->db->trans_complete(); + $this->db->transComplete(); - return $this->db->trans_status(); + return $this->db->transStatus(); } } \ No newline at end of file diff --git a/application/models/Login_User_LoginRole_Role.php b/app/Models/Login_User_LoginRole_Role.php old mode 100755 new mode 100644 similarity index 78% rename from application/models/Login_User_LoginRole_Role.php rename to app/Models/Login_User_LoginRole_Role.php index 46ef04d5..34060eed --- a/application/models/Login_User_LoginRole_Role.php +++ b/app/Models/Login_User_LoginRole_Role.php @@ -1,4 +1,6 @@ @@ -38,28 +40,28 @@ protected function dependencies() } public function getAllUserData() { - $users = $this->Login_User->joinUser() + $users = model(Login_User::class)->joinUser() ->select($this->selectNonCritical()) // Select on the login id, username, first name, last name, and email. - ->get([Login::name()]) - ->result_array(); + ->get() + ->getResultArray(); // Add the roles to each user. foreach($users as $key => $user) { $loginId = $user[Login::FIELD_LOGIN_ID]; - $users[$key]['roles'] = $this->Role_LoginRole->getRolesFromLoginId($loginId); + $users[$key]['roles'] = model(Role_LoginRole::class)->getRolesFromLoginId($loginId); } return $users; } public function getUserData($loginId) { - $user = $this->Login_User->joinUser() + $user = model(Login_User::class)->joinUser() ->select($this->selectNonCritical()) ->where([field(Login::FIELD_LOGIN_ID, Login::name()) => $loginId]) - ->get([Login::name()]) - ->row_array(); + ->get() + ->getRowArray(); - $user['roles'] = $this->Role_LoginRole->getRolesFromLoginId($loginId); + $user['roles'] = model(Role_LoginRole::class)->getRolesFromLoginId($loginId); return $user; } diff --git a/app/Models/ModelFrame.php b/app/Models/ModelFrame.php new file mode 100644 index 00000000..e8e94fce --- /dev/null +++ b/app/Models/ModelFrame.php @@ -0,0 +1,15 @@ +getShortName(); + + return db_connect()->getPrefix() . strtolower(preg_replace('/(?getRoleNameExists($roleName); if (!$roleExists) { - return $this->db->insert($this->name(), [self::FIELD_ROLE_NAME => $roleName]); + return $this->db->table($this->name())->insert([self::FIELD_ROLE_NAME => $roleName]); } else { return false; } } public function get($roleId) { - $res = $this->db + $res = $this->db->table($this->name()) ->where([self::FIELD_ROLE_ID => $roleId]) - ->get($this->name()) - ->row_array() + ->get() + ->getRowArray() ; return $res; } public function getByName($roleName) { - $res = $this->db + $res = $this->db->table($this->name()) ->where([self::FIELD_ROLE_NAME=> $roleName]) - ->get($this->name()) - ->row_array() + ->get() + ->getRowArray() ; return $res; @@ -55,9 +57,9 @@ public function getByName($roleName) { * @return bool */ public function getRoleNameExists($roleName) { - $result = $this->db + $result = $this->db->table($this->name()) ->where([self::FIELD_ROLE_NAME => $roleName]) - ->count_all_results($this->name()); + ->countAllResults(); return $result > 0; } @@ -73,10 +75,10 @@ public function getRoleIdFromRoleName($roleName) { return false; } - return $this->db + return $this->db->table($this->name()) ->where([self::FIELD_ROLE_NAME => $roleName]) - ->get($this->name()) - ->row_array()[self::FIELD_ROLE_ID]; + ->get() + ->getRowArray()[self::FIELD_ROLE_ID]; } /** @@ -85,9 +87,9 @@ public function getRoleIdFromRoleName($roleName) { * @return array */ public function getRoles() { - return $this->db - ->get($this->name()) - ->result_array(); + return $this->db->table($this->name()) + ->get() + ->getResultArray(); } //====================================== diff --git a/application/models/Role_LoginRole.php b/app/Models/Role_LoginRole.php old mode 100755 new mode 100644 similarity index 85% rename from application/models/Role_LoginRole.php rename to app/Models/Role_LoginRole.php index aff29171..f97c8bcc --- a/application/models/Role_LoginRole.php +++ b/app/Models/Role_LoginRole.php @@ -1,4 +1,6 @@ @@ -27,13 +29,13 @@ protected function dependencies() * @return bool True if the user has this role, else false. */ public function userHasRole($loginId, $roleName) { - $count = $this->db + $count = $this->db->table([LoginRole::name(), Role::name()]) ->where([ Role::name().'.'.Role::FIELD_ROLE_NAME => $roleName, LoginRole::name().'.'.Login::FIELD_LOGIN_ID => $loginId, ]) ->where(Role::name().'.'.Role::FIELD_ROLE_ID.' = '.LoginRole::name().'.'.Role::FIELD_ROLE_ID) - ->count_all_results([LoginRole::name(), Role::name()]); + ->countAllResults(); return $count > 0; } @@ -45,7 +47,7 @@ public function userHasRole($loginId, $roleName) { * @return array */ public function getRolesFromLoginId($loginId) { - return $this->db + return $this->db->table(LoginRole::name()) ->where([Login::FIELD_LOGIN_ID => $loginId]) ->join(Role::name(), eq( @@ -53,7 +55,7 @@ public function getRolesFromLoginId($loginId) { field(Role::FIELD_ROLE_ID, LoginRole::name()) ) ) - ->get(LoginRole::name()) - ->result_array(); + ->get() + ->getResultArray(); } } \ No newline at end of file diff --git a/application/models/Transaction.php b/app/Models/Transaction.php old mode 100755 new mode 100644 similarity index 69% rename from application/models/Transaction.php rename to app/Models/Transaction.php index fc7482bf..8cab211c --- a/application/models/Transaction.php +++ b/app/Models/Transaction.php @@ -1,4 +1,6 @@ @@ -36,9 +38,7 @@ protected function dependencies() public function add($subjectId, $authorId, $amount, $delta, $type = self::TYPE_CONSUME) { - $this->db->insert( - self::name(), - [ + $this->db->table(self::name())->insert([ self::FIELD_SUBJECT_ID => $subjectId, self::FIELD_AUTHOR_ID => $authorId, Consumption::FIELD_AMOUNT => $amount, @@ -50,37 +50,37 @@ public function add($subjectId, $authorId, $amount, $delta, $type = self::TYPE_C public function getAllFromSubject($subjectId) { - return $this->db + return $this->db->table(self::name()) ->where([self::FIELD_SUBJECT_ID => $subjectId]) - ->get(self::name()) - ->result_array(); + ->get() + ->getResultArray(); } public function getAllFromAuthor($authorId) { - return $this->db + return $this->db->table(self::name()) ->where([self::FIELD_AUTHOR_ID => $authorId]) - ->get(self::name()) - ->result_array(); + ->get() + ->getResultArray(); } public function getAll() { - return $this->db - ->get(self::name()) - ->result_array(); + return $this->db->table(self::name()) + ->get() + ->getResultArray(); } public function getConsumeCountForSubject(int $subjectId) { - $amount = $this->db - ->select_sum(self::FIELD_DELTA, 'sum') + $amount = $this->db->table(self::name()) + ->selectSum(self::FIELD_DELTA, 'sum') ->where([ self::FIELD_SUBJECT_ID => $subjectId, self::FIELD_TYPE => self::TYPE_CONSUME, ]) - ->get(self::name()) - ->first_row() + ->get() + ->getFirstRow() ->sum; return abs($amount); @@ -92,16 +92,16 @@ public function getConsumeCountForSubject(int $subjectId) * @param int $limit The amount of users retrieved to compute the top score over. * @return string SQL for this query. */ - public function getSumQuerySql(bool $positive, int $limit) { - $sumQuery = $this->db + public function getSumQuerySql(bool $positive, ?int $limit) { + $sumQuery = $this->db->table(self::name()) ->select(self::FIELD_SUBJECT_ID) - ->select_sum(self::FIELD_DELTA, 'sum') + ->selectSum(self::FIELD_DELTA, 'sum') ->where(self::FIELD_DELTA . ($positive?'>':'<') . ' 0') - ->group_by(self::FIELD_SUBJECT_ID) - ->limit($limit) - ->order_by('sum ' . ($positive?'DESC':'ASC')); + ->groupBy(self::FIELD_SUBJECT_ID) + ->limit($limit ?? 0) + ->orderBy('sum ' . ($positive?'DESC':'ASC')); - return $sumQuery->get_compiled_select(self::name()); + return $sumQuery->getCompiledSelect(); } /** @@ -114,13 +114,20 @@ public function getSumQuerySql(bool $positive, int $limit) { public function getSumDeltaSubjectIdWithinTop(int $subjectId, int $position): bool { // Check if the specified subject is within the earlier retrieved leaderboard. - $result = $this->db - ->from('(' . $this->getSumQuerySql(false, $position) . ') `' . $this->db->dbprefix('t') . '`') // Insert SQL from $sumQuery and name it t for later use - ->where('t.' . self::FIELD_SUBJECT_ID . '=' . $subjectId) // Check if the subject is in the sumQuery + $alias = $this->db->prefixTable('t'); + $result = $this->db->newQuery() + ->fromSubquery($this->db->table(self::name()) + ->select(self::FIELD_SUBJECT_ID) + ->selectSum(self::FIELD_DELTA, 'sum') + ->where(self::FIELD_DELTA . ' <', 0) + ->groupBy(self::FIELD_SUBJECT_ID) + ->orderBy('sum', 'ASC') + ->limit($position), $alias) + ->where($alias . '.' . self::FIELD_SUBJECT_ID, $subjectId) ->get(); // If a row is returned, then the user is on the leaderboard. - return $result->num_rows() > 0; + return $result->getNumRows() > 0; } /** @@ -131,15 +138,15 @@ public function getSumDeltaSubjectIdWithinTop(int $subjectId, int $position): bo */ public function getSumDeltaSubjectIdByWeek(int $subjectId): array { - return $this->db + return $this->db->table(self::name()) ->select('YEAR(' . self::FIELD_TIME . ') as year, WEEKOFYEAR(' . self::FIELD_TIME . ') as week') - ->select_sum(self::FIELD_DELTA, 'sum') + ->selectSum(self::FIELD_DELTA, 'sum') ->where(self::FIELD_SUBJECT_ID, $subjectId) ->where([self::FIELD_TYPE => self::TYPE_CONSUME]) - ->group_by('YEAR(' . self::FIELD_TIME . '), WEEKOFYEAR(' . self::FIELD_TIME . ')') - ->order_by(self::FIELD_TIME, 'asc') - ->get(self::name()) - ->result_array(); + ->groupBy('YEAR(' . self::FIELD_TIME . '), WEEKOFYEAR(' . self::FIELD_TIME . ')') + ->orderBy(self::FIELD_TIME, 'asc') + ->get() + ->getResultArray(); } /** @@ -149,44 +156,42 @@ public function getSumDeltaSubjectIdByWeek(int $subjectId): array */ public function getSumDeltaByWeek(): array { - return $this->db + return $this->db->table(self::name()) ->select('YEAR(' . self::FIELD_TIME . ') as year, WEEKOFYEAR(' . self::FIELD_TIME . ') as week') - ->select_sum(self::FIELD_DELTA, 'sum') + ->selectSum(self::FIELD_DELTA, 'sum') ->where([self::FIELD_TYPE => self::TYPE_CONSUME]) - ->group_by('YEAR(' . self::FIELD_TIME . '), WEEKOFYEAR(' . self::FIELD_TIME . ')') - ->order_by(self::FIELD_TIME, 'asc') - ->get(self::name()) - ->result_array(); + ->groupBy('YEAR(' . self::FIELD_TIME . '), WEEKOFYEAR(' . self::FIELD_TIME . ')') + ->orderBy(self::FIELD_TIME, 'asc') + ->get() + ->getResultArray(); } public function getConsumedWeeksForLeaderboard(int $position): array { - $sumQuery = $this->db + $sumQuery = $this->db->table(self::name()) ->select(self::FIELD_SUBJECT_ID) - ->select_sum(self::FIELD_DELTA, 'sum') + ->selectSum(self::FIELD_DELTA, 'sum') ->where([self::FIELD_TYPE => self::TYPE_CONSUME]) - ->group_by(self::FIELD_SUBJECT_ID) + ->groupBy(self::FIELD_SUBJECT_ID) ->limit($position) - ->order_by('sum ' . 'ASC') - ->get_compiled_select(self::name()) + ->orderBy('sum ' . 'ASC') + ->getCompiledSelect() ; - $result = $this->db + $result = $this->db->table(self::name()) ->select([ self::FIELD_SUBJECT_ID, 'YEAR('.self::FIELD_TIME.') * 52 + WEEKOFYEAR('.self::FIELD_TIME.') as weekyear', ]) - ->from(self::name()) ->where(self::FIELD_TYPE, self::TYPE_CONSUME) - ->where_in(self::FIELD_SUBJECT_ID, 'SELECT `subject_id` FROM (' - . $sumQuery . ') `' . $this->db->dbprefix('t') . '`', - false - ) - ->group_by(['weekyear', self::FIELD_SUBJECT_ID]) - ->order_by('time', 'ASC') + ->whereIn(self::FIELD_SUBJECT_ID, $this->db->newQuery() + ->select(self::FIELD_SUBJECT_ID) + ->from('(' . $sumQuery . ') ' . $this->db->prefixTable('t'))) + ->groupBy(['weekyear', self::FIELD_SUBJECT_ID]) + ->orderBy('time', 'ASC') ->get() - ->result_array() + ->getResultArray() ; return $result; diff --git a/application/models/User.php b/app/Models/User.php old mode 100755 new mode 100644 similarity index 84% rename from application/models/User.php rename to app/Models/User.php index bf67ec6f..1c5baa67 --- a/application/models/User.php +++ b/app/Models/User.php @@ -1,4 +1,6 @@ db + return $this->db->table(self::name()) ->where([Login::FIELD_LOGIN_ID => $loginId]) ->limit(1) - ->get(self::name()) - ->row_array() + ->get() + ->getRowArray() ; } @@ -43,9 +45,7 @@ public function get(int $loginId): array * @return bool True on success, else false. */ public function add($loginId, $firstName, $lastName, $email) { - return $this->db->insert( - $this->name(), - [ + return $this->db->table($this->name())->insert([ Login::FIELD_LOGIN_ID => $loginId, self::FIELD_FIRST_NAME => $firstName, self::FIELD_LAST_NAME => $lastName, @@ -60,10 +60,10 @@ public function add($loginId, $firstName, $lastName, $email) { * @return array */ public function getLoginIdToName() { - $data = $this->db + $data = $this->db->table(self::name()) ->select([Login::FIELD_LOGIN_ID, self::FIELD_FIRST_NAME, self::FIELD_LAST_NAME]) - ->get(self::name()) - ->result_array(); + ->get() + ->getResultArray(); // Combine first and last name. foreach ($data as $key => $row) { @@ -81,9 +81,9 @@ public function getLoginIdToName() { * @return bool True if the user exists */ public function exists($loginId) { - $result = $this->db + $result = $this->db->table($this->name()) ->where([Login::FIELD_LOGIN_ID => $loginId]) - ->count_all_results($this->name()); + ->countAllResults(); return $result > 0; } @@ -95,10 +95,10 @@ public function exists($loginId) { * @return string */ public function getName($loginId) { - $row = $this->db + $row = $this->db->table(self::name()) ->where([Login::FIELD_LOGIN_ID => $loginId]) - ->get(self::name()) - ->row_array(); + ->get() + ->getRowArray(); $firstName = $row[self::FIELD_FIRST_NAME]; $lastName = $row[self::FIELD_LAST_NAME]; @@ -113,8 +113,8 @@ public function getName($loginId) { * @param $data * @return bool */ - public function update($loginId, $data) { - return $this->db->update(self::name(), $data, [Login::FIELD_LOGIN_ID => $loginId]); + public function updateDetails($loginId, $data) { + return $this->db->table(self::name())->update($data, [Login::FIELD_LOGIN_ID => $loginId]); } /** @@ -131,7 +131,7 @@ public function updateName($loginId, $firstName, $lastName) { self::FIELD_LAST_NAME => $lastName, ]; - return $this->update($loginId, $data); + return $this->updateDetails($loginId, $data); } /** @@ -146,7 +146,7 @@ public function updateEmail($loginId, $email) { self::FIELD_EMAIL => $email, ]; - return $this->update($loginId, $data); + return $this->updateDetails($loginId, $data); } //====================================== @@ -187,7 +187,7 @@ public function v2() { } public function v3() { - $loginId = $this->Login->getLoginIdFromUsername(Login::INITIAL_LOGIN_USERNAME); + $loginId = model(Login::class)->getLoginIdFromUsername(Login::INITIAL_LOGIN_USERNAME); $this->add($loginId, self::INITIAL_USER_FIRST_NAME, self::INITIAL_USER_LAST_NAME, self::INITIAL_USER_EMAIL); } diff --git a/application/models/User_Consumption.php b/app/Models/User_Consumption.php old mode 100755 new mode 100644 similarity index 89% rename from application/models/User_Consumption.php rename to app/Models/User_Consumption.php index fc29c6f7..971cbb31 --- a/application/models/User_Consumption.php +++ b/app/Models/User_Consumption.php @@ -1,4 +1,6 @@ @@ -21,7 +23,7 @@ protected function dependencies() } public function join($type = '') { - return $this->db + return $this->db->table(Consumption::name()) ->join( User::name(), eq( diff --git a/application/models/User_Consumption_LoginRole.php b/app/Models/User_Consumption_LoginRole.php old mode 100755 new mode 100644 similarity index 70% rename from application/models/User_Consumption_LoginRole.php rename to app/Models/User_Consumption_LoginRole.php index 3239bc59..7e9fbdf1 --- a/application/models/User_Consumption_LoginRole.php +++ b/app/Models/User_Consumption_LoginRole.php @@ -1,4 +1,6 @@ @@ -29,11 +31,11 @@ protected function dependencies() } public function get($roleId) { - $users = $this->User_LoginRole->getUsersWithRole($roleId); + $users = model(User_LoginRole::class)->getUsersWithRole($roleId); foreach ($users as $key => $user) { - $users[$key][Consumption::FIELD_AMOUNT] = $this->Consumption->get($user[Login::FIELD_LOGIN_ID]); - $users[$key][Transaction::FIELD_TIME] = $this->User_Transaction->getLatestForSubjectId($user[Login::FIELD_LOGIN_ID]); + $users[$key][Consumption::FIELD_AMOUNT] = model(Consumption::class)->get($user[Login::FIELD_LOGIN_ID]); + $users[$key][Transaction::FIELD_TIME] = model(User_Transaction::class)->getLatestForSubjectId($user[Login::FIELD_LOGIN_ID]); } return $users; diff --git a/application/models/User_LoginRole.php b/app/Models/User_LoginRole.php old mode 100755 new mode 100644 similarity index 78% rename from application/models/User_LoginRole.php rename to app/Models/User_LoginRole.php index 9d80683d..df6b9ec2 --- a/application/models/User_LoginRole.php +++ b/app/Models/User_LoginRole.php @@ -1,4 +1,6 @@ @@ -22,15 +24,15 @@ protected function dependencies() public function getUsersWithRole($roleId) { return $this->join() - ->order_by(User::FIELD_FIRST_NAME) - ->order_by(User::FIELD_LAST_NAME) + ->orderBy(User::FIELD_FIRST_NAME) + ->orderBy(User::FIELD_LAST_NAME) ->where([field(Role::FIELD_ROLE_ID, LoginRole::name()) => $roleId]) - ->get(LoginRole::name()) - ->result_array(); + ->get() + ->getResultArray(); } public function join() { - return $this->db + return $this->db->table(LoginRole::name()) ->join( User::name(), eq( diff --git a/application/models/User_Transaction.php b/app/Models/User_Transaction.php old mode 100755 new mode 100644 similarity index 68% rename from application/models/User_Transaction.php rename to app/Models/User_Transaction.php index b89aad30..f44ab63e --- a/application/models/User_Transaction.php +++ b/app/Models/User_Transaction.php @@ -1,4 +1,6 @@ @@ -25,15 +27,15 @@ protected function dependencies() public function getAll($where = [], $limit = null) { // Retrieve all transactions - $transactions = $this->db + $transactions = $this->db->table(Transaction::name()) ->select('*, UNIX_TIMESTAMP(' . Transaction::FIELD_TIME . ') as ' . Transaction::FIELD_TIME . '_unix') - ->order_by(Transaction::FIELD_TIME, 'DESC') + ->orderBy(Transaction::FIELD_TIME, 'DESC') ->where($where) - ->limit($limit) - ->get(Transaction::name()) - ->result_array(); + ->limit($limit ?? 0) + ->get() + ->getResultArray(); - $users = $this->User->getLoginIdToName(); + $users = model(User::class)->getLoginIdToName(); foreach ($transactions as $key => $t) { $transactions[$key]['author_name'] = $users[$t[Transaction::FIELD_AUTHOR_ID]]; @@ -46,11 +48,12 @@ public function getAll($where = [], $limit = null) public function getSumDeltaForAllSubjectId(bool $positive, $limit = null) { // Join the leaderboard on the user database. - return $this->db - ->from('(' . $this->ci->Transaction->getSumQuerySql($positive, $limit) . ') `' . $this->db->dbprefix('t') . '`') - ->where('t.' . Transaction::FIELD_SUBJECT_ID . '=' . User::name() . '.' . Login::FIELD_LOGIN_ID) - ->get(User::name()) - ->result_array(); + $alias = $this->db->prefixTable('t'); + return $this->db->table(User::name()) + ->join('(' . model(Transaction::class)->getSumQuerySql($positive, $limit) . ') ' . $alias, + $alias . '.' . Transaction::FIELD_SUBJECT_ID . '=' . User::name() . '.' . Login::FIELD_LOGIN_ID) + ->get() + ->getResultArray(); } public function getAllForSubjectId($subjectId, $limit = null) @@ -65,12 +68,12 @@ public function getAllForAuthorId($authorId, $limit = null) public function getLatestForSubjectId($subjectId, $limit = 1) { - $row = $this->db + $row = $this->db->table(Transaction::name()) ->where(Transaction::FIELD_SUBJECT_ID . '=' . $subjectId) - ->order_by(Transaction::FIELD_TIME, 'DESC') + ->orderBy(Transaction::FIELD_TIME, 'DESC') ->limit($limit) - ->get(Transaction::name()) - ->row_array(); + ->get() + ->getRowArray(); // Handle the case when no data was returned iin case the user has no transactions. if (!isset($row[self::FIELD_TIME])) { diff --git a/application/pages/admin/ApiPage.php b/app/Pages/Admin/ApiPage.php old mode 100755 new mode 100644 similarity index 55% rename from application/pages/admin/ApiPage.php rename to app/Pages/Admin/ApiPage.php index c16c9180..4d349909 --- a/application/pages/admin/ApiPage.php +++ b/app/Pages/Admin/ApiPage.php @@ -1,4 +1,24 @@ diff --git a/application/pages/admin/ApiResetPage.php b/app/Pages/Admin/ApiResetPage.php old mode 100755 new mode 100644 similarity index 67% rename from application/pages/admin/ApiResetPage.php rename to app/Pages/Admin/ApiResetPage.php index e965ff27..81a31a1a --- a/application/pages/admin/ApiResetPage.php +++ b/app/Pages/Admin/ApiResetPage.php @@ -1,4 +1,24 @@ @@ -28,13 +48,13 @@ function call() } // Check that an valid id is passed. - if (!$this->ci->Role_LoginRole->userHasRole($id, Role::ROLE_USER)) { + if (!model(Role_LoginRole::class)->userHasRole($id, Role::ROLE_USER)) { // error invalid user specified. $this->setError(self::USER_NOT_FOUND); return; } - $result = $this->ci->LoginReset->add($id); + $result = model(LoginReset::class)->add($id); if ($result) { $this->setStatus(ApiFrame::STATUS_SUCCESS); @@ -47,7 +67,7 @@ function call() function hasAccess(): bool { // Ensure that the request originates from a admin. - return isLoggedInAndHasRole($this->ci, [Role::ROLE_ADMIN]); + return isLoggedInAndHasRole([Role::ROLE_ADMIN]); } /** diff --git a/application/pages/admin/DefaultPage.php b/app/Pages/Admin/DefaultPage.php old mode 100755 new mode 100644 similarity index 71% rename from application/pages/admin/DefaultPage.php rename to app/Pages/Admin/DefaultPage.php index 7be9238c..8d4ac8f6 --- a/application/pages/admin/DefaultPage.php +++ b/app/Pages/Admin/DefaultPage.php @@ -1,5 +1,25 @@ @@ -18,7 +38,7 @@ public function getViews(): array public function hasAccess(): bool { - return isLoggedInAndHasRole($this->ci, [Role::ROLE_ADMIN]); + return isLoggedInAndHasRole([Role::ROLE_ADMIN]); } protected function getFormValidationRules() @@ -32,9 +52,9 @@ protected function getFormValidationRules() public function beforeView() { /** @var User_Consumption_LoginRole $user_Consumption_LoginRole */ - $user_Consumption_LoginRole = $this->ci->User_Consumption_LoginRole; + $user_Consumption_LoginRole = model(User_Consumption_LoginRole::class); /** @var Role $role */ - $role = $this->ci->Role; + $role = model(Role::class); $roleId = $role->getByName(Role::ROLE_USER)[Role::FIELD_ROLE_ID]; $result = $user_Consumption_LoginRole->get($roleId); diff --git a/application/pages/admin/GiveUserPage.php b/app/Pages/Admin/GiveUserPage.php old mode 100755 new mode 100644 similarity index 55% rename from application/pages/admin/GiveUserPage.php rename to app/Pages/Admin/GiveUserPage.php index 7e932bf1..eb7e342c --- a/application/pages/admin/GiveUserPage.php +++ b/app/Pages/Admin/GiveUserPage.php @@ -1,5 +1,25 @@ @@ -22,12 +42,12 @@ public function getViews(): array public function hasAccess(): bool { // Check if the user is logged in and has the required rights. - $hasRights = isLoggedInAndHasRole($this->ci, [Role::ROLE_ADMIN]); + $hasRights = isLoggedInAndHasRole([Role::ROLE_ADMIN]); // Check if the given login id is valid and has a user account connected to it. $loginId = $this->getDataKey('subpage'); - $validLoginId = $loginId !== null && $this->ci->Login_User->exists($loginId); + $validLoginId = $loginId !== null && model(Login_User::class)->exists($loginId); return $hasRights && $validLoginId; } @@ -35,15 +55,15 @@ public function hasAccess(): bool public function onFormSuccess() { $loginId = $this->getDataKey('subpage'); - $authorId = getLoggedInLoginId($this->ci->session); - $name = $this->ci->User->getName($loginId); + $authorId = getLoggedInLoginId(session()); + $name = model(User::class)->getName($loginId); $amount = set_value('amount'); - $success = $this->ci->Consumption->change($loginId, $authorId, $amount, Transaction::TYPE_UPGRADE); + $success = model(Consumption::class)->change($loginId, $authorId, $amount, Transaction::TYPE_UPGRADE); if ($success) { - $this->addSuccessMessage(sprintf(lang('application_give_user_success'), '' . $name . '', $amount)); + $this->addSuccessMessage(sprintf(lang('Admin.application_give_user_success'), '' . $name . '', $amount)); } else { - $this->addDangerMessage(lang('application_server_error')); + $this->addDangerMessage(lang('Admin.application_server_error')); } } @@ -52,16 +72,16 @@ protected function getFormValidationRules() return [ [ 'field' => 'amount', - 'label' => lang('application_give_user_amount'), + 'label' => lang('Admin.application_give_user_amount'), 'rules' => [ 'required', 'integer', 'greater_than[0]', ], 'errors' => [ - 'required' => lang('application_give_user_error_amount_required'), - 'integer' => lang('application_give_user_error_not_integer'), - 'greater_than[0]' => lang('application_give_user_error_amount_not_positive'), + 'required' => lang('Admin.application_give_user_error_amount_required'), + 'integer' => lang('Admin.application_give_user_error_not_integer'), + 'greater_than[0]' => lang('Admin.application_give_user_error_amount_not_positive'), ] ], ]; @@ -75,11 +95,11 @@ public function beforeView() $loginId = $this->getDataKey('subpage'); // Get the users name. - $name = $this->ci->User->getName($loginId); + $name = model(User::class)->getName($loginId); $this->setData('name', $name); // Get the current amount of consumptions - $amount = $this->ci->Consumption->get($loginId); + $amount = model(Consumption::class)->get($loginId); $this->setData('amount', $amount); } diff --git a/application/pages/admin/LoginPage.php b/app/Pages/Admin/LoginPage.php old mode 100755 new mode 100644 similarity index 64% rename from application/pages/admin/LoginPage.php rename to app/Pages/Admin/LoginPage.php index 8e196711..6bef2fae --- a/application/pages/admin/LoginPage.php +++ b/app/Pages/Admin/LoginPage.php @@ -1,5 +1,25 @@ @@ -32,7 +52,7 @@ protected function getFormValidationRules() */ public function beforeView() { - redirect('login'); + return redirect()->to(site_url('login')); } /** diff --git a/application/pages/admin/LogoutPage.php b/app/Pages/Admin/LogoutPage.php old mode 100755 new mode 100644 similarity index 64% rename from application/pages/admin/LogoutPage.php rename to app/Pages/Admin/LogoutPage.php index ee4d4691..ba80dedf --- a/application/pages/admin/LogoutPage.php +++ b/app/Pages/Admin/LogoutPage.php @@ -1,5 +1,25 @@ @@ -32,7 +52,7 @@ protected function getFormValidationRules() */ public function beforeView() { - redirect('logout'); + return redirect()->to(site_url('logout')); } /** diff --git a/application/pages/admin/Menu.php b/app/Pages/Admin/Menu.php old mode 100755 new mode 100644 similarity index 51% rename from application/pages/admin/Menu.php rename to app/Pages/Admin/Menu.php index e12894a5..c6522283 --- a/application/pages/admin/Menu.php +++ b/app/Pages/Admin/Menu.php @@ -1,4 +1,24 @@ @@ -10,14 +30,14 @@ class Menu extends MenuFrame protected function menuElements() { return [ - new MenuPage(lang('application_menu_dashboard'), DefaultPage::class, 'fa fa-dashboard fa-fw'), - new MenuPage(lang('application_menu_transactions'), TransactionsPage::class, 'fa fa-exchange fa-fw'), + new MenuPage(lang('Admin.application_menu_dashboard'), DefaultPage::class, 'fa fa-dashboard fa-fw'), + new MenuPage(lang('Admin.application_menu_transactions'), TransactionsPage::class, 'fa fa-exchange fa-fw'), [ - new MenuItem(lang('application_menu_manage_users'), 'fa fa-bar-chart-o'), - new MenuPage(lang('application_menu_overview'), UserOverviewPage::class, 'fa fa-th-list fa-fw'), - new MenuPage(lang('application_menu_new_user'), NewUserPage::class, 'fa fa-plus-circle fa-fw'), + new MenuItem(lang('Admin.application_menu_manage_users'), 'fa fa-bar-chart-o'), + new MenuPage(lang('Admin.application_menu_overview'), UserOverviewPage::class, 'fa fa-th-list fa-fw'), + new MenuPage(lang('Admin.application_menu_new_user'), NewUserPage::class, 'fa fa-plus-circle fa-fw'), ], - new MenuLink(lang('application_menu_main_environment'), '../', 'fa fa-home fa-fw'), + new MenuLink(lang('Admin.application_menu_main_environment'), '../', 'fa fa-home fa-fw'), ]; } diff --git a/application/pages/admin/NewUserPage.php b/app/Pages/Admin/NewUserPage.php old mode 100755 new mode 100644 similarity index 55% rename from application/pages/admin/NewUserPage.php rename to app/Pages/Admin/NewUserPage.php index 7a6208e2..eb154a18 --- a/application/pages/admin/NewUserPage.php +++ b/app/Pages/Admin/NewUserPage.php @@ -1,4 +1,24 @@ @@ -26,7 +46,7 @@ public function beforeView() { $this->setData('roleNameKey', Role::FIELD_ROLE_NAME); $this->setData('roleIdKey', Role::FIELD_ROLE_ID); - $this->setData('roles', $this->ci->Role->getRoles()); + $this->setData('roles', model(Role::class)->getRoles()); } /** @@ -36,7 +56,7 @@ public function beforeView() */ public function hasAccess(): bool { - return isLoggedInAndHasRole($this->ci, [Role::ROLE_ADMIN]); + return isLoggedInAndHasRole([Role::ROLE_ADMIN]); } public function onFormSuccess() @@ -54,12 +74,12 @@ public function onFormSuccess() $userRoles = []; } - $success = $this->ci->Login_User_LoginRole->add($username, $password, $firstName, $lastName, $email, $userRoles); + $success = model(Login_User_LoginRole::class)->add($username, $password, $firstName, $lastName, $email, $userRoles); if ($success) { - $this->addSuccessMessage(sprintf(lang('application_new_user_success'), $username)); + $this->addSuccessMessage(sprintf(lang('Admin.application_new_user_success'), $username)); } else { - $this->addWarningMessage(lang('application_new_user_error_unknown')); + $this->addWarningMessage(lang('Admin.application_new_user_error_unknown')); } } @@ -74,68 +94,68 @@ protected function getFormValidationRules() // All fields are required. [ 'field' => 'username', - 'label' => lang('application_new_user_username'), + 'label' => lang('Admin.application_new_user_username'), 'rules' => [ 'required', - ['usernameNotExists', [$this->ci->Login, 'usernameNotExists']], + ['usernameNotExists', [model(Login::class), 'usernameNotExists']], 'min_length[1]' ], 'errors' => [ - 'required' => lang('application_new_user_error_required'), - 'usernameNotExists' => lang('application_new_user_error_username_exists'), - 'min_length[1]' => lang('application_new_user_error_short_username'), + 'required' => lang('Admin.application_new_user_error_required'), + 'usernameNotExists' => lang('Admin.application_new_user_error_username_exists'), + 'min_length[1]' => lang('Admin.application_new_user_error_short_username'), ], ], [ 'field' => 'password', - 'label' => lang('application_new_user_password'), + 'label' => lang('Admin.application_new_user_password'), 'rules' => [ 'required', 'min_length[6]', 'matches[confirm-password]', ], 'errors' => [ - 'required' => lang('application_new_user_error_required'), - 'min_length[6]' => lang('application_new_user_error_password_not_strong_enough'), - 'matches[confirm-password]' => lang('application_new_user_error_password_not_equal'), + 'required' => lang('Admin.application_new_user_error_required'), + 'min_length[6]' => lang('Admin.application_new_user_error_password_not_strong_enough'), + 'matches[confirm-password]' => lang('Admin.application_new_user_error_password_not_equal'), ], ], [ 'field' => 'confirm-password', - 'label' => lang('application_new_user_confirm_password'), + 'label' => lang('Admin.application_new_user_confirm_password'), 'rules' => [ 'required', ], 'errors' => [ - 'required' => lang('application_new_user_error_required'), + 'required' => lang('Admin.application_new_user_error_required'), ], ], [ 'field' => 'email', - 'label' => lang('application_new_user_email'), + 'label' => lang('Admin.application_new_user_email'), 'rules' => [ 'required', 'valid_email', ], 'errors' => [ - 'required' => lang('application_new_user_error_required'), - 'valid_email' => lang('application_new_user_error_valid_email'), + 'required' => lang('Admin.application_new_user_error_required'), + 'valid_email' => lang('Admin.application_new_user_error_valid_email'), ], ], [ 'field' => 'first-name', - 'label' => lang('application_new_user_first_name'), + 'label' => lang('Admin.application_new_user_first_name'), 'rules' => 'required', 'errors' => [ - 'required' => lang('application_new_user_error_required'), + 'required' => lang('Admin.application_new_user_error_required'), ], ], [ 'field' => 'last-name', - 'label' => lang('application_new_user_last_name'), + 'label' => lang('Admin.application_new_user_last_name'), 'rules' => 'required', 'errors' => [ - 'required' => lang('application_new_user_error_required'), + 'required' => lang('Admin.application_new_user_error_required'), ], ], ]; diff --git a/application/pages/admin/PageNotFoundPage.php b/app/Pages/Admin/PageNotFoundPage.php old mode 100755 new mode 100644 similarity index 59% rename from application/pages/admin/PageNotFoundPage.php rename to app/Pages/Admin/PageNotFoundPage.php index ed6932dd..8895e8ca --- a/application/pages/admin/PageNotFoundPage.php +++ b/app/Pages/Admin/PageNotFoundPage.php @@ -1,5 +1,25 @@ @@ -30,7 +50,7 @@ protected function getFormValidationRules() */ public function beforeView() { - redirect('PageNotFound'); // todo give it its own PNF page. + return redirect()->to(site_url('PageNotFound')); // todo give it its own PNF page. } /** diff --git a/application/pages/admin/TransactionsPage.php b/app/Pages/Admin/TransactionsPage.php old mode 100755 new mode 100644 similarity index 72% rename from application/pages/admin/TransactionsPage.php rename to app/Pages/Admin/TransactionsPage.php index d0040c42..05b99e1e --- a/application/pages/admin/TransactionsPage.php +++ b/app/Pages/Admin/TransactionsPage.php @@ -1,5 +1,25 @@ @@ -18,7 +38,7 @@ public function getViews(): array public function hasAccess(): bool { - return isLoggedInAndHasRole($this->ci, [Role::ROLE_ADMIN]); + return isLoggedInAndHasRole([Role::ROLE_ADMIN]); } protected function getFormValidationRules() @@ -31,7 +51,7 @@ protected function getFormValidationRules() */ public function beforeView() { - $transactions = $this->ci->User_Transaction->getAll(); + $transactions = model(User_Transaction::class)->getAll(); $this->setData('transactions', $transactions); $fields = [ diff --git a/application/pages/admin/UserOverviewPage.php b/app/Pages/Admin/UserOverviewPage.php old mode 100755 new mode 100644 similarity index 79% rename from application/pages/admin/UserOverviewPage.php rename to app/Pages/Admin/UserOverviewPage.php index 2dc7a1c5..bc3cf9d7 --- a/application/pages/admin/UserOverviewPage.php +++ b/app/Pages/Admin/UserOverviewPage.php @@ -1,4 +1,24 @@ @@ -34,7 +54,7 @@ public function beforeView() 'roles' => 'roles', 'role_name' => Role::FIELD_ROLE_NAME, ]; - $userData = $this->ci->Login_User_LoginRole_Role->getAllUserData(); + $userData = model(Login_User_LoginRole_Role::class)->getAllUserData(); $this->setData('userData', $userData); $this->setdata('userDataFields', $userDataFields); @@ -56,7 +76,7 @@ public function beforeView() }) ' ); - require_once APPPATH . 'pages/admin/ApiResetPage.php'; + $this->addScript( ''); @@ -125,7 +145,7 @@ protected function getHelpers(): array private function queryStreakLength(): array { /** @var Transaction $transactionModel */ - $transactionModel = $this->ci->Transaction; + $transactionModel = model(Transaction::class); // Retrieve all week user pairs for which there has been consumptions by leader board members. $consumedWeeks = $transactionModel->getConsumedWeeksForLeaderboard(self::LEADERBOARD_SIZE); @@ -140,7 +160,7 @@ private function queryStreakLength(): array } // Query the database for the current week, the DB is used instead of PHP since their interpretation of time might differ. - $currentWeek = (int)$this->ci->db->query('SELECT YEAR(NOW()) * 52 + WEEKOFYEAR(NOW()) as `currentWeek`')->row()->currentWeek; + $currentWeek = (int) db_connect()->query('SELECT YEAR(NOW()) * 52 + WEEKOFYEAR(NOW()) as `currentWeek`')->getRow()->currentWeek; // Count the amount of consecutive weeks since this week $streakLength = []; diff --git a/application/pages/default/LoginPage.php b/app/Pages/Frontend/LoginPage.php old mode 100755 new mode 100644 similarity index 56% rename from application/pages/default/LoginPage.php rename to app/Pages/Frontend/LoginPage.php index 829203f6..ea0d11b4 --- a/application/pages/default/LoginPage.php +++ b/app/Pages/Frontend/LoginPage.php @@ -1,4 +1,24 @@ - @@ -26,25 +46,25 @@ protected function getFormValidationRules() return [ [ 'field' => 'username', - 'label' => lang('login_username'), + 'label' => lang('Lisa.login_username'), 'rules' => [ 'required', - ['usernameExists', [$this->ci->Login, 'usernameExists']], + ['usernameExists', [model(Login::class), 'usernameExists']], ], 'errors' => [ - 'required' => lang('login_error_field_required'), - 'usernameExists' => lang('login_error_username_does_not_exist'), + 'required' => lang('Lisa.login_error_field_required'), + 'usernameExists' => lang('Lisa.login_error_username_does_not_exist'), ], ], [ 'field' => 'password', - 'label' => lang('login_password'), + 'label' => lang('Lisa.login_password'), 'rules' => [ 'trim', 'required', ], 'errors' => [ - 'required' => lang('login_error_field_required'), + 'required' => lang('Lisa.login_error_field_required'), ], ], ]; @@ -52,7 +72,7 @@ protected function getFormValidationRules() public function hasAccess(): bool { - return !isLoggedIn($this->ci->session); + return !isLoggedIn(session()); } /** @@ -63,13 +83,13 @@ public function beforeView() if ($this->formSuccess) { $username = set_value('username'); $password = set_value('password'); - $validCredentials = $this->ci->Login->checkUsernamePasswordCredentials($username, $password); + $validCredentials = model(Login::class)->checkUsernamePasswordCredentials($username, $password); if ($validCredentials) { - $loginId = $this->ci->Login->getLoginIdFromUsername($username); - setLoggedIn($this->ci->session, $loginId); - redirect(); + $loginId = model(Login::class)->getLoginIdFromUsername($username); + setLoggedIn(session(), $loginId); + return redirect()->to(site_url('')); } else { - $this->addWarningMessage(lang('login_error_invalid_credentials')); + $this->addWarningMessage(lang('Lisa.login_error_invalid_credentials')); } } } diff --git a/application/pages/default/LogoutPage.php b/app/Pages/Frontend/LogoutPage.php old mode 100755 new mode 100644 similarity index 59% rename from application/pages/default/LogoutPage.php rename to app/Pages/Frontend/LogoutPage.php index f35f8f36..5a5e7689 --- a/application/pages/default/LogoutPage.php +++ b/app/Pages/Frontend/LogoutPage.php @@ -1,5 +1,25 @@ @@ -27,7 +47,7 @@ protected function getFormValidationRules() public function hasAccess(): bool { - return isLoggedIn($this->ci->session); + return isLoggedIn(session()); } /** @@ -35,8 +55,8 @@ public function hasAccess(): bool */ public function beforeView() { - setLoggedOut($this->ci->session); - redirect(); + setLoggedOut(session()); + return redirect()->to(site_url('')); } /** diff --git a/application/pages/default/Menu.php b/app/Pages/Frontend/Menu.php old mode 100755 new mode 100644 similarity index 52% rename from application/pages/default/Menu.php rename to app/Pages/Frontend/Menu.php index 3bde4f26..2f8dcd12 --- a/application/pages/default/Menu.php +++ b/app/Pages/Frontend/Menu.php @@ -1,4 +1,24 @@ @@ -9,14 +29,14 @@ class Menu extends MenuFrame protected function menuElements() { return [ - new MenuPage(lang('menu_consume'), ConsumePage::class, 'fa fa-glass fa-fw'), - new MenuPage(lang('menu_transactions'), TransactionsPage::class, 'fa fa-history fa-fw'), - new MenuPage(lang('menu_leaderboard'), LeaderboardPage::class, 'fa fa-trophy fa-fw'), - new MenuPage(lang('menu_admin'), AdminPage::class, 'fa fa-dashboard fa-fw'), - new MenuPage(lang('menu_reset_password'), ResetPasswordPage::class, 'fa fa-cogs fa-fw'), + new MenuPage(lang('Lisa.menu_consume'), ConsumePage::class, 'fa fa-glass fa-fw'), + new MenuPage(lang('Lisa.menu_transactions'), TransactionsPage::class, 'fa fa-history fa-fw'), + new MenuPage(lang('Lisa.menu_leaderboard'), LeaderboardPage::class, 'fa fa-trophy fa-fw'), + new MenuPage(lang('Lisa.menu_admin'), AdminPage::class, 'fa fa-dashboard fa-fw'), + new MenuPage(lang('Lisa.menu_reset_password'), ResetPasswordPage::class, 'fa fa-cogs fa-fw'), // Since only the pages which the user has access rights for will be shown, only one of these will be displayed. - new MenuPage(lang('login_login'), LoginPage::class, 'fa fa-sign-in fa-fw'), - new MenuPage(lang('logout_logout'), LogoutPage::class, 'fa fa-sign-out fa-fw'), + new MenuPage(lang('Lisa.login_login'), LoginPage::class, 'fa fa-sign-in fa-fw'), + new MenuPage(lang('Lisa.logout_logout'), LogoutPage::class, 'fa fa-sign-out fa-fw'), ]; } diff --git a/application/pages/default/PageNotFoundPage.php b/app/Pages/Frontend/PageNotFoundPage.php old mode 100755 new mode 100644 similarity index 62% rename from application/pages/default/PageNotFoundPage.php rename to app/Pages/Frontend/PageNotFoundPage.php index 27d16178..0ea43ae5 --- a/application/pages/default/PageNotFoundPage.php +++ b/app/Pages/Frontend/PageNotFoundPage.php @@ -1,5 +1,25 @@ diff --git a/application/pages/default/ResetPage.php b/app/Pages/Frontend/ResetPage.php old mode 100755 new mode 100644 similarity index 65% rename from application/pages/default/ResetPage.php rename to app/Pages/Frontend/ResetPage.php index ba38488f..5061b9d8 --- a/application/pages/default/ResetPage.php +++ b/app/Pages/Frontend/ResetPage.php @@ -1,5 +1,25 @@ @@ -26,12 +46,12 @@ public function getViews(): array { switch ($this->show) { case self::SHOW_SUCCESS: - $this->addSuccessMessage(lang('reset_success')); + $this->addSuccessMessage(lang('Lisa.reset_success')); return [ 'reset-success-header', ]; case self::SHOW_KEY_NOT_FOUND: - $this->addWarningMessage(lang('reset_error_key_not_found')); + $this->addWarningMessage(lang('Lisa.reset_error_key_not_found')); return [ '404-header', ]; @@ -52,26 +72,26 @@ protected function getFormValidationRules() return [ [ 'field' => 'password', - 'label' => lang('form_field_password'), + 'label' => lang('Lisa.form_field_password'), 'rules' => [ 'required', 'min_length[6]', 'matches[confirm-password]', ], 'errors' => [ - 'required' => lang('form_error_required'), - 'min_length[6]' => lang('form_error_password_not_strong_enough'), - 'matches[confirm-password]' => lang('form_error_password_not_equal'), + 'required' => lang('Lisa.form_error_required'), + 'min_length[6]' => lang('Lisa.form_error_password_not_strong_enough'), + 'matches[confirm-password]' => lang('Lisa.form_error_password_not_equal'), ], ], [ 'field' => 'confirm-password', - 'label' => lang('form_field_confirm_password'), + 'label' => lang('Lisa.form_field_confirm_password'), 'rules' => [ 'required', ], 'errors' => [ - 'required' => lang('form_error_required'), + 'required' => lang('Lisa.form_error_required'), ], ], ]; @@ -82,9 +102,9 @@ public function onFormSuccess() $key = $this->getDataKey('subpage'); $password = set_value('password'); - $this->success = $this->ci->LoginReset_User->updatePassword($key, $password); + $this->success = model(LoginReset_User::class)->updatePassword($key, $password); if (!$this->success) { - $this->addDangerMessage(lang('reset_error_update_password_failed')); + $this->addDangerMessage(lang('Lisa.reset_error_update_password_failed')); } } @@ -98,7 +118,7 @@ public function beforeView() $this->show = self::SHOW_SUCCESS; } else { $key = $this->getDataKey('subpage'); - $exists = $this->ci->LoginReset->exists($key); + $exists = model(LoginReset::class)->exists($key); if ($exists) { $this->show = self::SHOW_FORM; } else { diff --git a/application/pages/default/ResetPasswordPage.php b/app/Pages/Frontend/ResetPasswordPage.php old mode 100755 new mode 100644 similarity index 55% rename from application/pages/default/ResetPasswordPage.php rename to app/Pages/Frontend/ResetPasswordPage.php index e5a034cd..d7018cfc --- a/application/pages/default/ResetPasswordPage.php +++ b/app/Pages/Frontend/ResetPasswordPage.php @@ -1,5 +1,25 @@ @@ -25,7 +45,7 @@ protected function getFormValidationRules() public function hasAccess(): bool { - return isLoggedIn($this->ci->session); + return isLoggedIn(session()); } /** @@ -33,14 +53,14 @@ public function hasAccess(): bool */ public function beforeView() { - $id = getLoggedInLoginId($this->ci->session); + $id = getLoggedInLoginId(session()); - $key = $this->ci->LoginReset->add($id); + $key = model(LoginReset::class)->add($id); if ($key) { - redirect('Reset/' . $key); + return redirect()->to(site_url('Reset/' . $key)); } else { - redirect('PageNotFound'); // @todo redirect to 500 page instead of 404. + return redirect()->to(site_url('PageNotFound')); // @todo redirect to 500 page instead of 404. } } diff --git a/application/pages/default/TransactionApiPage.php b/app/Pages/Frontend/TransactionApiPage.php similarity index 81% rename from application/pages/default/TransactionApiPage.php rename to app/Pages/Frontend/TransactionApiPage.php index 71d79bd0..bc796b1b 100644 --- a/application/pages/default/TransactionApiPage.php +++ b/app/Pages/Frontend/TransactionApiPage.php @@ -1,4 +1,24 @@ @@ -34,7 +54,7 @@ function call() function hasAccess(): bool { // Ensure that the request originates from a user. - return isLoggedInAndHasRole($this->ci, [Role::ROLE_USER]); + return isLoggedInAndHasRole([Role::ROLE_USER]); } /** @@ -78,7 +98,7 @@ protected function getHelpers(): array private function actionUpdateData(): void { - $result = $this->ci->Consumption->getAll(); + $result = model(Consumption::class)->getAll(); if ($result !== null) { $this->setStatus(ApiFrame::STATUS_SUCCESS); @@ -100,25 +120,25 @@ private function actionBuy(): void } // Check that a valid id is passed. - if (!$this->ci->Role_LoginRole->userHasRole($id, Role::ROLE_USER)) { + if (!model(Role_LoginRole::class)->userHasRole($id, Role::ROLE_USER)) { // error invalid user specified. $this->setError(self::USER_NOT_FOUND); return; } - $authorId = getLoggedInLoginId($this->ci->session); + $authorId = getLoggedInLoginId(session()); $amount = 1; - $result = $this->ci->Consumption->change($id, $authorId, -$amount, Transaction::TYPE_CONSUME); + $result = model(Consumption::class)->change($id, $authorId, -$amount, Transaction::TYPE_CONSUME); if ($result) { - $newAmount = $this->ci->Consumption->get($id); + $newAmount = model(Consumption::class)->get($id); $this->setStatus(ApiFrame::STATUS_SUCCESS); - $this->setResult('name', $this->ci->User->getName($id)); + $this->setResult('name', model(User::class)->getName($id)); $this->setResult('new_amount', $newAmount); $this->setResult('amount', $amount); - $this->setResult('updated_data', $this->ci->Consumption->getAll()); - $this->setResult('consume_count', $this->ci->Transaction->getConsumeCountForSubject($id)); + $this->setResult('updated_data', model(Consumption::class)->getAll()); + $this->setResult('consume_count', model(Transaction::class)->getConsumeCountForSubject($id)); if ($newAmount < 0) { $this->sendEmail($id, $newAmount); @@ -130,33 +150,31 @@ private function actionBuy(): void private function sendEmail(int $id, int $credits) { - $this->ci->config->load('email'); - /** @var User $userModel */ - $userModel = $this->ci->User; + $userModel = model(User::class); $userData = $userModel->get($id); - $this->ci->load->library('email'); - /** @var CI_Email $emailLibrary */ - $emailLibrary =& $this->ci->email; + + $emailLibrary = service('email'); + $emailLibrary->clear(); $emailSuccess = false; try { $emailLibrary - ->from($this->ci->config->item('from_email'), $this->ci->config->item('from_name')) - ->to($userData[User::FIELD_EMAIL]) - ->set_mailtype('html') - ->subject(lang('email_low_credits_subject')) - ->message($this->getEmailBody($userData[User::FIELD_FIRST_NAME], $credits)) - ->send() - ; - $emailSuccess = true; - } catch (Exception $e) { } + ->setFrom(config('Email')->fromEmail, config('Email')->fromName) + ->setTo($userData[User::FIELD_EMAIL]) + ->setMailType('html') + ->setSubject(lang('Lisa.email_low_credits_subject')) + ->setMessage($this->getEmailBody($userData[User::FIELD_FIRST_NAME], $credits)); + $emailSuccess = $emailLibrary->send(false); + } catch (\Throwable $exception) { + log_message('error', 'Email notification failed: {message}', ['message' => $exception->getMessage()]); + } if ($emailSuccess) { log_message('info', 'Email send to '.$userData[User::FIELD_EMAIL].' to notify about a lack of credits '.$credits.'.'); } else { log_message('error', 'Failed sending email message for '.$id.'.'); - log_message('debug', 'Email debug info:'.PHP_EOL.$emailLibrary->print_debugger()); + log_message('debug', 'Email debug info:'.PHP_EOL.$emailLibrary->printDebugger()); } } @@ -258,7 +276,7 @@ private function getEmailBody(string $name, int $credits)
- + @@ -267,10 +285,10 @@ private function getEmailBody(string $name, int $credits)
-

-

-

- +

+

+

+
diff --git a/application/pages/default/TransactionsPage.php b/app/Pages/Frontend/TransactionsPage.php old mode 100755 new mode 100644 similarity index 69% rename from application/pages/default/TransactionsPage.php rename to app/Pages/Frontend/TransactionsPage.php index e91bd0b3..4d5010c0 --- a/application/pages/default/TransactionsPage.php +++ b/app/Pages/Frontend/TransactionsPage.php @@ -1,5 +1,25 @@ @@ -18,7 +38,7 @@ public function getViews(): array public function hasAccess(): bool { - return isLoggedInAndHasRole($this->ci, Role::ROLE_USER); + return isLoggedInAndHasRole(Role::ROLE_USER); } protected function getFormValidationRules() @@ -31,7 +51,7 @@ protected function getFormValidationRules() */ public function beforeView() { - $loginId = getLoggedInLoginId($this->ci->session); + $loginId = getLoggedInLoginId(session()); $limit = $this->getDataKey('subpage') ?? 100; if (!(int) $limit || $limit <= 0) { @@ -39,8 +59,8 @@ public function beforeView() } $this->setData('limit', $limit); - $transactions['subject'] = $this->ci->User_Transaction->getAllForSubjectId($loginId, $limit); - $transactions['author'] = $this->ci->User_Transaction->getAllForAuthorId($loginId, $limit); + $transactions['subject'] = model(User_Transaction::class)->getAllForSubjectId($loginId, $limit); + $transactions['author'] = model(User_Transaction::class)->getAllForAuthorId($loginId, $limit); $this->setData('transactions', $transactions); $fields = [ @@ -53,11 +73,11 @@ public function beforeView() ]; $this->setData('fields', $fields); - $sumDeltaByWeek = $this->ci->Transaction->getSumDeltaSubjectIdByWeek($loginId); + $sumDeltaByWeek = model(Transaction::class)->getSumDeltaSubjectIdByWeek($loginId); + - $this->ci->load->library('Graph'); /** @var Graph $graphLibrary */ - $graphLibrary = $this->ci->graph; + $graphLibrary = (new \App\Libraries\Graph()); $this->addScript($graphLibrary->includeJsLibrary()); $this->addScript($graphLibrary->getGraphForTransactions($sumDeltaByWeek)); @@ -68,7 +88,7 @@ public function beforeView() $(this).parents('ul').children('.active').removeClass('active'); $(this).parents('li').addClass('active'); }); - + $(\".pagination.horizontal-radio\").click(function() { $(this).find('input[type=radio]').attr('checked', true); }); diff --git a/application/pages/MenuFrame.php b/app/Pages/MenuFrame.php old mode 100755 new mode 100644 similarity index 84% rename from application/pages/MenuFrame.php rename to app/Pages/MenuFrame.php index 803a2405..1e836f27 --- a/application/pages/MenuFrame.php +++ b/app/Pages/MenuFrame.php @@ -1,15 +1,33 @@ * @date 3-3-2017 */ -abstract class MenuFrame extends CI_Controller +abstract class MenuFrame { - private $ci; + private $group; public function __construct($group) { - $this->ci = get_instance(); $this->group = $group; } @@ -51,7 +69,7 @@ private function buildListItem($menuItem) { /** @var MenuPage $menuItem*/ $pageClass = $menuItem->getPage(); - require_once(APPPATH.'pages/'.$this->group.'/'.$pageClass.'.php'); + /** @var PageFrame $page */ $page = new $pageClass(['group' => $this->group], false); @@ -80,7 +98,7 @@ private function getLink($menuItem) { case MenuPage::class: /** @var MenuPage $menuItem */ $page = $menuItem->getPage(); - $pageName = substr($page, 0, -strlen('Page')); + $pageName = substr((new \ReflectionClass($page))->getShortName(), 0, -strlen('Page')); $pageParams = $menuItem->getParams(); if (empty($pageParams)) { @@ -93,7 +111,7 @@ private function getLink($menuItem) { case MenuItem::class: return null; default: - throw new Exception('Invalid menu item parsed.'); + throw new \Exception('Invalid menu item parsed.'); } } diff --git a/application/pages/PageFrame.php b/app/Pages/PageFrame.php old mode 100755 new mode 100644 similarity index 76% rename from application/pages/PageFrame.php rename to app/Pages/PageFrame.php index 717b4ef4..e0ebae63 --- a/application/pages/PageFrame.php +++ b/app/Pages/PageFrame.php @@ -1,4 +1,23 @@ - * @date 6-2-2017 @@ -13,17 +32,13 @@ abstract class PageFrame extends RequestFrame protected $formSuccess = false; protected $data = []; - protected $ci; protected $accessibleBy; + private $validateForm; public function __construct(array $data, $validateForm = true) { parent::__construct($data); - if ($validateForm) { - $this->formSuccess = $this->formValidate(); - } else { - $this->formSuccess = true; - } + $this->validateForm = $validateForm; } public function show() { @@ -31,31 +46,34 @@ public function show() { // Check if the user has access. if (!$this->hasAccess()) { - if (isLoggedIn($this->session)) { - redirect('InsufficientRights'); - } else { - redirect($group .'/login'); - } - exit; + return redirect()->to(site_url(isLoggedIn(session()) ? 'InsufficientRights' : $group . '/login')); } + $this->formSuccess = $this->validateForm && $this->formValidate(); + // Call the form success if a valid form was submitted. if ($this->getFormSuccess()) { - $this->onFormSuccess(); + $response = $this->onFormSuccess(); + if ($response instanceof \CodeIgniter\HTTP\ResponseInterface) { + return $response; + } } - $this->beforeView(); + $response = $this->beforeView(); + if ($response instanceof \CodeIgniter\HTTP\ResponseInterface) { + return $response; + } $views = $this->getViews(); if ($views === null) { - exit; + return ''; } else { - $this->ci->load->view('templates/'.$group.'/header', $this->data); + $html = view('templates/'.$group.'/header', $this->data); foreach ($views as $v) { - $this->ci->load->view('page/'.$group.'/' . $v, $this->data); + $html .= view('page/'.$group.'/' . $v, $this->data); } - $this->ci->load->view('templates/'.$group.'/footer', $this->data); + return $html . view('templates/'.$group.'/footer', $this->data); } } /** @@ -199,28 +217,27 @@ private function formValidate() { * @return bool */ private function evaluateRequirementAsRule($requirement, array $formInput) { - // Reset the rules and insert the data. - $this->ci->form_validation->reset_validation()->set_data($formInput); - - $rule = []; - - // Check if the requirement is of the type matches, if so it is required to also validate the form matches refers to. $requirementName = $this->getRequirementName($requirement['rules']); - if (stripos($requirementName, 'matches') === 0) { - preg_match('/matches\[([^\]]*?)\]/', $requirementName, $matches); - - $rule[] = [ - 'field' => $matches[1], - 'rules' => 'returnTrue', // Give it some dummy rule. - ]; + if (in_array($requirementName, ['trim', 'returnTrue'], true)) { + return true; } - $rule[] = $requirement; - - // Initialise the rule to be evaluated. - $this->ci->form_validation->set_rules($rule); - - // Evaluate the rule. - $success = $this->ci->form_validation->run(); + $rule = $requirement['rules']; + if (is_array($rule)) { + $rule = $rule[0]; + if (is_array($rule)) { + $callback = $rule[1]; + $rule = static fn ($value) => $callback($value); + } + } + $validation = service('validation'); + $validation->reset(); + $validation->setRules([ + $requirement['field'] => [ + 'label' => $requirement['label'] ?? $requirement['field'], + 'rules' => is_string($rule) ? $rule : [$rule], + ], + ]); + $success = $validation->run($formInput); if (!$success) { // Generate the error message. @@ -270,7 +287,7 @@ private function convertRequirementToRule($requirement, $rule) 'label' => $rule['label'], 'rules' => [$requirement], ]; - if (array_key_exists($requirementName, $rule['errors'])) { + if (array_key_exists($requirementName, $rule['errors'] ?? [])) { $requirementAsRule['errors'] = [ $requirementName => $rule['errors'][$requirementName], ]; diff --git a/app/Pages/RequestFrame.php b/app/Pages/RequestFrame.php new file mode 100644 index 00000000..dbc078fc --- /dev/null +++ b/app/Pages/RequestFrame.php @@ -0,0 +1,22 @@ +data = $data; + helper(array_merge(['tables', 'mdbt_model', 'mdbt_form', 'login_state', 'form'], $this->getHelpers())); + } + + abstract public function hasAccess(): bool; + + abstract protected function getModels(): array; + + abstract protected function getLibraries(): array; + + abstract protected function getHelpers(): array; +} \ No newline at end of file diff --git a/app/Pages/RequestInterface.php b/app/Pages/RequestInterface.php new file mode 100644 index 00000000..f870afcd --- /dev/null +++ b/app/Pages/RequestInterface.php @@ -0,0 +1,37 @@ + + * @date 5-4-2017 + */ +interface RequestInterface +{ + + public function __construct(array $data); + + /** + * Show the page. + * + * @return null + */ + public function show(); +} \ No newline at end of file diff --git a/app/ThirdParty/.gitkeep b/app/ThirdParty/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/Views/error/insufficientRights.php b/app/Views/error/insufficientRights.php new file mode 100644 index 00000000..1e3f388c --- /dev/null +++ b/app/Views/error/insufficientRights.php @@ -0,0 +1,10 @@ + + * @date 02-03-2017 + */ +?> +
+

+

+ \ No newline at end of file diff --git a/app/Views/errors/cli/error_404.php b/app/Views/errors/cli/error_404.php new file mode 100644 index 00000000..456ea3e8 --- /dev/null +++ b/app/Views/errors/cli/error_404.php @@ -0,0 +1,7 @@ +getFile()) . ':' . $exception->getLine(), 'green')); +CLI::newLine(); + +$last = $exception; + +while ($prevException = $last->getPrevious()) { + $last = $prevException; + + CLI::write(' Caused by:'); + CLI::write(' [' . $prevException::class . ']', 'red'); + CLI::write(' ' . $prevException->getMessage()); + CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green')); + CLI::newLine(); +} + +// The backtrace +if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) { + $backtraces = $last->getTrace(); + + if ($backtraces) { + CLI::write('Backtrace:', 'green'); + } + + foreach ($backtraces as $i => $error) { + $padFile = ' '; // 4 spaces + $padClass = ' '; // 7 spaces + $c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT); + + if (isset($error['file'])) { + $filepath = clean_path($error['file']) . ':' . $error['line']; + + CLI::write($c . $padFile . CLI::color($filepath, 'yellow')); + } else { + CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow')); + } + + $function = ''; + + if (isset($error['class'])) { + $type = ($error['type'] === '->') ? '()' . $error['type'] : $error['type']; + $function .= $padClass . $error['class'] . $type . $error['function']; + } elseif (! isset($error['class']) && isset($error['function'])) { + $function .= $padClass . $error['function']; + } + + $args = implode(', ', array_map(static fn ($value): string => match (true) { + is_object($value) => 'Object(' . $value::class . ')', + is_array($value) => $value !== [] ? '[...]' : '[]', + $value === null => 'null', // return the lowercased version + default => var_export($value, true), + }, array_values($error['args'] ?? []))); + + $function .= '(' . $args . ')'; + + CLI::write($function); + CLI::newLine(); + } +} diff --git a/application/views/errors/cli/error_general.php b/app/Views/errors/cli/error_general.php old mode 100755 new mode 100644 similarity index 50% rename from application/views/errors/cli/error_general.php rename to app/Views/errors/cli/error_general.php index 6984b61e..31883269 --- a/application/views/errors/cli/error_general.php +++ b/app/Views/errors/cli/error_general.php @@ -1,5 +1,5 @@ + A PHP Error was encountered diff --git a/application/cache/index.html b/app/Views/errors/cli/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/cache/index.html rename to app/Views/errors/cli/index.html diff --git a/app/Views/errors/cli/production.php b/app/Views/errors/cli/production.php new file mode 100644 index 00000000..7db744ec --- /dev/null +++ b/app/Views/errors/cli/production.php @@ -0,0 +1,5 @@ + + + + + <?= lang('Errors.badRequest') ?> + + + + +
+

400

+ +

+ + + + + +

+
+ + diff --git a/app/Views/errors/html/error_404.php b/app/Views/errors/html/error_404.php new file mode 100644 index 00000000..e506f083 --- /dev/null +++ b/app/Views/errors/html/error_404.php @@ -0,0 +1,84 @@ + + + + + <?= lang('Errors.pageNotFound') ?> + + + + +
+

404

+ +

+ + + + + +

+
+ + diff --git a/application/views/errors/html/error_db.php b/app/Views/errors/html/error_db.php old mode 100755 new mode 100644 similarity index 94% rename from application/views/errors/html/error_db.php rename to app/Views/errors/html/error_db.php index f5a43f63..20c16108 --- a/application/views/errors/html/error_db.php +++ b/app/Views/errors/html/error_db.php @@ -1,5 +1,5 @@ diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php new file mode 100644 index 00000000..2c4e0091 --- /dev/null +++ b/app/Views/errors/html/error_exception.php @@ -0,0 +1,429 @@ + + + + + + + + <?= esc($title) ?> + + + + + + + +
+
+ Displayed at — + PHP: — + CodeIgniter: -- + Environment: +
+
+

getCode() ? ' #' . $exception->getCode() : '') ?>

+

+ getMessage())) ?> + getMessage())) ?>" + rel="noreferrer" target="_blank">search → +

+
+
+ + +
+

at line

+ + +
+ +
+ +
+ +
+ getPrevious()) { + $last = $prevException; + ?> + +
+    Caused by:
+    getCode() ? ' #' . $prevException->getCode() : '') ?>
+
+    getMessage())) ?>
+    getMessage())) ?>"
+       rel="noreferrer" target="_blank">search →
+    getFile()) . ':' . $prevException->getLine()) ?>
+    
+ + +
+ + +
+ + + +
+ + +
+ +
    + $row) : ?> + +
  1. +

    + + + + + {PHP internal code} + + + + +   —   + + + ( arguments ) +

    + + + getParameters(); + } + + foreach ($row['args'] as $key => $value) : ?> + + + + + + +
    name : "#{$key}") ?>
    +
    + + () + + + + +   —   () + +

    + + + +
    + +
    + +
  2. + + +
+ +
+ + +
+ + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathgetUri()) ?>
HTTP MethodgetMethod()) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
+ + + + + + + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + headers(); ?> + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
+ getValueLine(), 'html'); + } else { + foreach ($value as $i => $header) { + echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html'); + } + } + ?> +
+ + +
+ + + setStatusCode(http_response_code()); + ?> +
+ + + + + +
Response StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
+ + headers(); ?> + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
+ getHeaderLine($name), 'html'); + } else { + foreach ($value as $i => $header) { + echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html'); + } + } + ?> +
+ + +
+ + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ +
+ +
+ + + + diff --git a/application/views/errors/html/error_general.php b/app/Views/errors/html/error_general.php old mode 100755 new mode 100644 similarity index 94% rename from application/views/errors/html/error_general.php rename to app/Views/errors/html/error_general.php index fc3b2eba..025aefa2 --- a/application/views/errors/html/error_general.php +++ b/app/Views/errors/html/error_general.php @@ -1,5 +1,5 @@ diff --git a/application/views/errors/html/error_php.php b/app/Views/errors/html/error_php.php old mode 100755 new mode 100644 similarity index 92% rename from application/views/errors/html/error_php.php rename to app/Views/errors/html/error_php.php index b146f9c5..252142b2 --- a/application/views/errors/html/error_php.php +++ b/app/Views/errors/html/error_php.php @@ -1,5 +1,5 @@
diff --git a/application/config/index.html b/app/Views/errors/html/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/config/index.html rename to app/Views/errors/html/index.html diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php new file mode 100644 index 00000000..2f59a8de --- /dev/null +++ b/app/Views/errors/html/production.php @@ -0,0 +1,25 @@ + + + + + + + <?= lang('Errors.whoops') ?> + + + + + +
+ +

+ +

+ +
+ + + + diff --git a/application/controllers/index.html b/app/Views/errors/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/controllers/index.html rename to app/Views/errors/index.html diff --git a/application/core/index.html b/app/Views/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/core/index.html rename to app/Views/index.html diff --git a/application/views/page/admin/dashboard.php b/app/Views/page/admin/dashboard.php old mode 100755 new mode 100644 similarity index 56% rename from application/views/page/admin/dashboard.php rename to app/Views/page/admin/dashboard.php index 93c7ef8e..2eb209fe --- a/application/views/page/admin/dashboard.php +++ b/app/Views/page/admin/dashboard.php @@ -1,4 +1,4 @@ - * @date 26-02-2017 @@ -7,17 +7,17 @@
-

+

-

+

-

+

', $negativeUsers)?>

\ No newline at end of file diff --git a/application/views/page/admin/give-user.php b/app/Views/page/admin/give-user.php old mode 100755 new mode 100644 similarity index 56% rename from application/views/page/admin/give-user.php rename to app/Views/page/admin/give-user.php index c970b6de..96bb6a4e --- a/application/views/page/admin/give-user.php +++ b/app/Views/page/admin/give-user.php @@ -7,7 +7,7 @@
-

'.$name.'')?>

+

'.$name.'')?>

@@ -15,17 +15,17 @@
- '.$name.'')?> + '.$name.'')?>
- - -

+ + +

- +
diff --git a/application/views/page/admin/new-user.php b/app/Views/page/admin/new-user.php old mode 100755 new mode 100644 similarity index 69% rename from application/views/page/admin/new-user.php rename to app/Views/page/admin/new-user.php index 7c9ef19f..706edbcf --- a/application/views/page/admin/new-user.php +++ b/app/Views/page/admin/new-user.php @@ -1,4 +1,4 @@ - * @date 26-02-2017 @@ -7,7 +7,7 @@
-

+

@@ -15,65 +15,65 @@
- +
- + -

+

- +
@
-

+

- + -

+

- + -

+

- + -

+

- + -

+

- +
- +
diff --git a/application/views/page/admin/transactions.php b/app/Views/page/admin/transactions.php old mode 100755 new mode 100644 similarity index 67% rename from application/views/page/admin/transactions.php rename to app/Views/page/admin/transactions.php index 3c54d9b9..b456f1f1 --- a/application/views/page/admin/transactions.php +++ b/app/Views/page/admin/transactions.php @@ -1,4 +1,4 @@ - * @date 26-02-2017 @@ -7,7 +7,7 @@
-

+

@@ -15,18 +15,18 @@
- +
- - - - - + + + + + diff --git a/application/views/page/admin/user-overview.php b/app/Views/page/admin/user-overview.php old mode 100755 new mode 100644 similarity index 74% rename from application/views/page/admin/user-overview.php rename to app/Views/page/admin/user-overview.php index 5c393eab..eceac4e0 --- a/application/views/page/admin/user-overview.php +++ b/app/Views/page/admin/user-overview.php @@ -1,4 +1,4 @@ - * @date 26-02-2017 @@ -7,7 +7,7 @@
-

+

@@ -15,19 +15,19 @@
- +
- - - - - - + + + + + + @@ -45,17 +45,17 @@
diff --git a/application/views/page/admin/user.php b/app/Views/page/admin/user.php old mode 100755 new mode 100644 similarity index 67% rename from application/views/page/admin/user.php rename to app/Views/page/admin/user.php index 00a0ec31..0983a01e --- a/application/views/page/admin/user.php +++ b/app/Views/page/admin/user.php @@ -7,7 +7,7 @@
-

'.$username.'')?>

+

'.$username.'')?>

@@ -15,22 +15,22 @@
- +
- + -

+

- + -

+

- +
@@ -39,22 +39,22 @@
- +
- + -

+

- + -

+

- +
@@ -63,20 +63,20 @@
- +
- +
@
-

+

- +
@@ -85,13 +85,13 @@
- +
- + $role) { ?>
- +
diff --git a/app/Views/page/default/404-header.php b/app/Views/page/default/404-header.php new file mode 100644 index 00000000..f62d92d3 --- /dev/null +++ b/app/Views/page/default/404-header.php @@ -0,0 +1,11 @@ + + * @date 29-1-2017 + */ +?> +
+

+

+ +
\ No newline at end of file diff --git a/application/views/page/default/consume-header.php b/app/Views/page/default/consume-header.php old mode 100755 new mode 100644 similarity index 86% rename from application/views/page/default/consume-header.php rename to app/Views/page/default/consume-header.php index e6b557af..136e4a55 --- a/application/views/page/default/consume-header.php +++ b/app/Views/page/default/consume-header.php @@ -11,13 +11,13 @@
-

+

-

+

- + -1
@@ -52,8 +52,8 @@ class="buy btn btn-white">-1 - - + + @@ -68,7 +68,7 @@ class="buy btn btn-white">-1 @@ -81,5 +81,5 @@ class="buy btn btn-white">-1 - transactions->getSnackbarFooterHtml();?> + getSnackbarFooterHtml();?> diff --git a/app/Views/page/default/default-header.php b/app/Views/page/default/default-header.php new file mode 100644 index 00000000..230958a1 --- /dev/null +++ b/app/Views/page/default/default-header.php @@ -0,0 +1,11 @@ + + * @date 29-1-2017 + */ +?> +
+

+

+ +
\ No newline at end of file diff --git a/application/views/page/default/leaderboard-header.php b/app/Views/page/default/leaderboard-header.php similarity index 78% rename from application/views/page/default/leaderboard-header.php rename to app/Views/page/default/leaderboard-header.php index 6a795472..d63f80be 100644 --- a/application/views/page/default/leaderboard-header.php +++ b/app/Views/page/default/leaderboard-header.php @@ -12,8 +12,8 @@
-

-

+

+

@@ -24,9 +24,9 @@
- - - + + + @@ -43,7 +43,7 @@ - + @@ -53,5 +53,5 @@ - transactions->getSnackbarFooterHtml();?> + getSnackbarFooterHtml();?> diff --git a/application/views/page/default/login-header.php b/app/Views/page/default/login-header.php old mode 100755 new mode 100644 similarity index 90% rename from application/views/page/default/login-header.php rename to app/Views/page/default/login-header.php index 4070a984..c5d03016 --- a/application/views/page/default/login-header.php +++ b/app/Views/page/default/login-header.php @@ -12,7 +12,7 @@ diff --git a/application/views/page/default/logout-header.php b/app/Views/page/default/logout-header.php old mode 100755 new mode 100644 similarity index 100% rename from application/views/page/default/logout-header.php rename to app/Views/page/default/logout-header.php diff --git a/application/views/page/default/not-user-header.php b/app/Views/page/default/not-user-header.php old mode 100755 new mode 100644 similarity index 52% rename from application/views/page/default/not-user-header.php rename to app/Views/page/default/not-user-header.php index 5375dce4..ba1425d9 --- a/application/views/page/default/not-user-header.php +++ b/app/Views/page/default/not-user-header.php @@ -5,6 +5,6 @@ */ ?>
-

-

+

+

\ No newline at end of file diff --git a/application/views/page/default/reset-header.php b/app/Views/page/default/reset-header.php old mode 100755 new mode 100644 similarity index 85% rename from application/views/page/default/reset-header.php rename to app/Views/page/default/reset-header.php index 0dff0601..5db71522 --- a/application/views/page/default/reset-header.php +++ b/app/Views/page/default/reset-header.php @@ -11,7 +11,7 @@ diff --git a/application/views/page/default/404-header.php b/app/Views/page/default/reset-success-header.php old mode 100755 new mode 100644 similarity index 52% rename from application/views/page/default/404-header.php rename to app/Views/page/default/reset-success-header.php index ab3aae36..74e229c4 --- a/application/views/page/default/404-header.php +++ b/app/Views/page/default/reset-success-header.php @@ -5,7 +5,7 @@ */ ?>
-

-

- +

+

+
\ No newline at end of file diff --git a/application/views/page/default/transactions-header.php b/app/Views/page/default/transactions-header.php old mode 100755 new mode 100644 similarity index 73% rename from application/views/page/default/transactions-header.php rename to app/Views/page/default/transactions-header.php index 580bf2b7..4da5bb21 --- a/application/views/page/default/transactions-header.php +++ b/app/Views/page/default/transactions-header.php @@ -10,17 +10,17 @@
-

+

- transactions->getBuyButtonHtml($u[$fields['login_id']])?> + getBuyButtonHtml($u[$fields['login_id']])?>
#
transactions->getBuyButtonHtml($entry[$fields['login_id']])?>getBuyButtonHtml($entry[$fields['login_id']])?>
- - - - - + + + + + @@ -65,19 +65,19 @@

- + +100 - +

- - - - - + + + + + diff --git a/app/Views/templates/admin/footer.php b/app/Views/templates/admin/footer.php new file mode 100644 index 00000000..b2b33e71 --- /dev/null +++ b/app/Views/templates/admin/footer.php @@ -0,0 +1,31 @@ + + * @date 26-2-2017 + */ +?> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/views/templates/admin/header.php b/app/Views/templates/admin/header.php old mode 100755 new mode 100644 similarity index 77% rename from application/views/templates/admin/header.php rename to app/Views/templates/admin/header.php index cfd255a8..a2ea6d02 --- a/application/views/templates/admin/header.php +++ b/app/Views/templates/admin/header.php @@ -8,23 +8,23 @@ - <?=lang('application_title')?> + <?=lang('Admin.application_title')?> - + - + - - + + - + - + @@ -47,7 +47,7 @@ - - + -
', - 'heading_row_start' => '', - 'heading_previous_cell' => '', - 'heading_title_cell' => '', - 'heading_next_cell' => '', - 'heading_row_end' => '', - 'week_row_start' => '', - 'week_day_cell' => '', - 'week_row_end' => '', - 'cal_row_start' => '', - 'cal_cell_start' => '', - 'cal_cell_end_today' => '', - 'cal_cell_end_other' => '', - 'cal_row_end' => '', - 'table_close' => '
<<{heading}>>
{week_day}
', - 'cal_cell_start_today' => '', - 'cal_cell_start_other' => '', - 'cal_cell_content' => '{day}', - 'cal_cell_content_today' => '{day}', - 'cal_cell_no_content' => '{day}', - 'cal_cell_no_content_today' => '{day}', - 'cal_cell_blank' => ' ', - 'cal_cell_other' => '{day}', - 'cal_cell_end' => '
' - ); - } - - // -------------------------------------------------------------------- - - /** - * Parse Template - * - * Harvests the data within the template {pseudo-variables} - * used to display the calendar - * - * @return CI_Calendar - */ - public function parse_template() - { - $this->replacements = $this->default_template(); - - if (empty($this->template)) - { - return $this; - } - - if (is_string($this->template)) - { - $today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today'); - - foreach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today', 'cal_cell_start_other', 'cal_cell_other', 'cal_cell_end_other') as $val) - { - if (preg_match('/\{'.$val.'\}(.*?)\{\/'.$val.'\}/si', $this->template, $match)) - { - $this->replacements[$val] = $match[1]; - } - elseif (in_array($val, $today, TRUE)) - { - $this->replacements[$val] = $this->replacements[substr($val, 0, -6)]; - } - } - } - elseif (is_array($this->template)) - { - $this->replacements = array_merge($this->replacements, $this->template); - } - - return $this; - } - -} diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php deleted file mode 100755 index 4c51e7aa..00000000 --- a/system/libraries/Cart.php +++ /dev/null @@ -1,567 +0,0 @@ -CI =& get_instance(); - - // Are any config settings being passed manually? If so, set them - $config = is_array($params) ? $params : array(); - - // Load the Sessions class - $this->CI->load->driver('session', $config); - - // Grab the shopping cart array from the session table - $this->_cart_contents = $this->CI->session->userdata('cart_contents'); - if ($this->_cart_contents === NULL) - { - // No cart exists so we'll set some base values - $this->_cart_contents = array('cart_total' => 0, 'total_items' => 0); - } - - log_message('info', 'Cart Class Initialized'); - } - - // -------------------------------------------------------------------- - - /** - * Insert items into the cart and save it to the session table - * - * @param array - * @return bool - */ - public function insert($items = array()) - { - // Was any cart data passed? No? Bah... - if ( ! is_array($items) OR count($items) === 0) - { - log_message('error', 'The insert method must be passed an array containing data.'); - return FALSE; - } - - // You can either insert a single product using a one-dimensional array, - // or multiple products using a multi-dimensional one. The way we - // determine the array type is by looking for a required array key named "id" - // at the top level. If it's not found, we will assume it's a multi-dimensional array. - - $save_cart = FALSE; - if (isset($items['id'])) - { - if (($rowid = $this->_insert($items))) - { - $save_cart = TRUE; - } - } - else - { - foreach ($items as $val) - { - if (is_array($val) && isset($val['id'])) - { - if ($this->_insert($val)) - { - $save_cart = TRUE; - } - } - } - } - - // Save the cart data if the insert was successful - if ($save_cart === TRUE) - { - $this->_save_cart(); - return isset($rowid) ? $rowid : TRUE; - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Insert - * - * @param array - * @return bool - */ - protected function _insert($items = array()) - { - // Was any cart data passed? No? Bah... - if ( ! is_array($items) OR count($items) === 0) - { - log_message('error', 'The insert method must be passed an array containing data.'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Does the $items array contain an id, quantity, price, and name? These are required - if ( ! isset($items['id'], $items['qty'], $items['price'], $items['name'])) - { - log_message('error', 'The cart array must contain a product ID, quantity, price, and name.'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Prep the quantity. It can only be a number. Duh... also trim any leading zeros - $items['qty'] = (float) $items['qty']; - - // If the quantity is zero or blank there's nothing for us to do - if ($items['qty'] == 0) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods - // Not totally sure we should impose this rule, but it seems prudent to standardize IDs. - // Note: These can be user-specified by setting the $this->product_id_rules variable. - if ( ! preg_match('/^['.$this->product_id_rules.']+$/i', $items['id'])) - { - log_message('error', 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes, and underscores'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods. - // Note: These can be user-specified by setting the $this->product_name_rules variable. - if ($this->product_name_safe && ! preg_match('/^['.$this->product_name_rules.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $items['name'])) - { - log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Prep the price. Remove leading zeros and anything that isn't a number or decimal point. - $items['price'] = (float) $items['price']; - - // We now need to create a unique identifier for the item being inserted into the cart. - // Every time something is added to the cart it is stored in the master cart array. - // Each row in the cart array, however, must have a unique index that identifies not only - // a particular product, but makes it possible to store identical products with different options. - // For example, what if someone buys two identical t-shirts (same product ID), but in - // different sizes? The product ID (and other attributes, like the name) will be identical for - // both sizes because it's the same shirt. The only difference will be the size. - // Internally, we need to treat identical submissions, but with different options, as a unique product. - // Our solution is to convert the options array to a string and MD5 it along with the product ID. - // This becomes the unique "row ID" - if (isset($items['options']) && count($items['options']) > 0) - { - $rowid = md5($items['id'].serialize($items['options'])); - } - else - { - // No options were submitted so we simply MD5 the product ID. - // Technically, we don't need to MD5 the ID in this case, but it makes - // sense to standardize the format of array indexes for both conditions - $rowid = md5($items['id']); - } - - // -------------------------------------------------------------------- - - // Now that we have our unique "row ID", we'll add our cart items to the master array - // grab quantity if it's already there and add it on - $old_quantity = isset($this->_cart_contents[$rowid]['qty']) ? (int) $this->_cart_contents[$rowid]['qty'] : 0; - - // Re-create the entry, just to make sure our index contains only the data from this submission - $items['rowid'] = $rowid; - $items['qty'] += $old_quantity; - $this->_cart_contents[$rowid] = $items; - - return $rowid; - } - - // -------------------------------------------------------------------- - - /** - * Update the cart - * - * This function permits the quantity of a given item to be changed. - * Typically it is called from the "view cart" page if a user makes - * changes to the quantity before checkout. That array must contain the - * product ID and quantity for each item. - * - * @param array - * @return bool - */ - public function update($items = array()) - { - // Was any cart data passed? - if ( ! is_array($items) OR count($items) === 0) - { - return FALSE; - } - - // You can either update a single product using a one-dimensional array, - // or multiple products using a multi-dimensional one. The way we - // determine the array type is by looking for a required array key named "rowid". - // If it's not found we assume it's a multi-dimensional array - $save_cart = FALSE; - if (isset($items['rowid'])) - { - if ($this->_update($items) === TRUE) - { - $save_cart = TRUE; - } - } - else - { - foreach ($items as $val) - { - if (is_array($val) && isset($val['rowid'])) - { - if ($this->_update($val) === TRUE) - { - $save_cart = TRUE; - } - } - } - } - - // Save the cart data if the insert was successful - if ($save_cart === TRUE) - { - $this->_save_cart(); - return TRUE; - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Update the cart - * - * This function permits changing item properties. - * Typically it is called from the "view cart" page if a user makes - * changes to the quantity before checkout. That array must contain the - * rowid and quantity for each item. - * - * @param array - * @return bool - */ - protected function _update($items = array()) - { - // Without these array indexes there is nothing we can do - if ( ! isset($items['rowid'], $this->_cart_contents[$items['rowid']])) - { - return FALSE; - } - - // Prep the quantity - if (isset($items['qty'])) - { - $items['qty'] = (float) $items['qty']; - // Is the quantity zero? If so we will remove the item from the cart. - // If the quantity is greater than zero we are updating - if ($items['qty'] == 0) - { - unset($this->_cart_contents[$items['rowid']]); - return TRUE; - } - } - - // find updatable keys - $keys = array_intersect(array_keys($this->_cart_contents[$items['rowid']]), array_keys($items)); - // if a price was passed, make sure it contains valid data - if (isset($items['price'])) - { - $items['price'] = (float) $items['price']; - } - - // product id & name shouldn't be changed - foreach (array_diff($keys, array('id', 'name')) as $key) - { - $this->_cart_contents[$items['rowid']][$key] = $items[$key]; - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Save the cart array to the session DB - * - * @return bool - */ - protected function _save_cart() - { - // Let's add up the individual prices and set the cart sub-total - $this->_cart_contents['total_items'] = $this->_cart_contents['cart_total'] = 0; - foreach ($this->_cart_contents as $key => $val) - { - // We make sure the array contains the proper indexes - if ( ! is_array($val) OR ! isset($val['price'], $val['qty'])) - { - continue; - } - - $this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']); - $this->_cart_contents['total_items'] += $val['qty']; - $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']); - } - - // Is our cart empty? If so we delete it from the session - if (count($this->_cart_contents) <= 2) - { - $this->CI->session->unset_userdata('cart_contents'); - - // Nothing more to do... coffee time! - return FALSE; - } - - // If we made it this far it means that our cart has data. - // Let's pass it to the Session class so it can be stored - $this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents)); - - // Woot! - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Cart Total - * - * @return int - */ - public function total() - { - return $this->_cart_contents['cart_total']; - } - - // -------------------------------------------------------------------- - - /** - * Remove Item - * - * Removes an item from the cart - * - * @param int - * @return bool - */ - public function remove($rowid) - { - // unset & save - unset($this->_cart_contents[$rowid]); - $this->_save_cart(); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Total Items - * - * Returns the total item count - * - * @return int - */ - public function total_items() - { - return $this->_cart_contents['total_items']; - } - - // -------------------------------------------------------------------- - - /** - * Cart Contents - * - * Returns the entire cart array - * - * @param bool - * @return array - */ - public function contents($newest_first = FALSE) - { - // do we want the newest first? - $cart = ($newest_first) ? array_reverse($this->_cart_contents) : $this->_cart_contents; - - // Remove these so they don't create a problem when showing the cart table - unset($cart['total_items']); - unset($cart['cart_total']); - - return $cart; - } - - // -------------------------------------------------------------------- - - /** - * Get cart item - * - * Returns the details of a specific item in the cart - * - * @param string $row_id - * @return array - */ - public function get_item($row_id) - { - return (in_array($row_id, array('total_items', 'cart_total'), TRUE) OR ! isset($this->_cart_contents[$row_id])) - ? FALSE - : $this->_cart_contents[$row_id]; - } - - // -------------------------------------------------------------------- - - /** - * Has options - * - * Returns TRUE if the rowid passed to this function correlates to an item - * that has options associated with it. - * - * @param string $row_id = '' - * @return bool - */ - public function has_options($row_id = '') - { - return (isset($this->_cart_contents[$row_id]['options']) && count($this->_cart_contents[$row_id]['options']) !== 0); - } - - // -------------------------------------------------------------------- - - /** - * Product options - * - * Returns the an array of options, for a particular product row ID - * - * @param string $row_id = '' - * @return array - */ - public function product_options($row_id = '') - { - return isset($this->_cart_contents[$row_id]['options']) ? $this->_cart_contents[$row_id]['options'] : array(); - } - - // -------------------------------------------------------------------- - - /** - * Format Number - * - * Returns the supplied number with commas and a decimal point. - * - * @param float - * @return string - */ - public function format_number($n = '') - { - return ($n === '') ? '' : number_format( (float) $n, 2, '.', ','); - } - - // -------------------------------------------------------------------- - - /** - * Destroy the cart - * - * Empties the cart and kills the session - * - * @return void - */ - public function destroy() - { - $this->_cart_contents = array('cart_total' => 0, 'total_items' => 0); - $this->CI->session->unset_userdata('cart_contents'); - } - -} diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php deleted file mode 100755 index 7dd66ec9..00000000 --- a/system/libraries/Driver.php +++ /dev/null @@ -1,342 +0,0 @@ -load_driver($child); - } - - /** - * Load driver - * - * Separate load_driver call to support explicit driver load by library or user - * - * @param string Driver name (w/o parent prefix) - * @return object Child class - */ - public function load_driver($child) - { - // Get CodeIgniter instance and subclass prefix - $prefix = config_item('subclass_prefix'); - - if ( ! isset($this->lib_name)) - { - // Get library name without any prefix - $this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this)); - } - - // The child will be prefixed with the parent lib - $child_name = $this->lib_name.'_'.$child; - - // See if requested child is a valid driver - if ( ! in_array($child, $this->valid_drivers)) - { - // The requested driver isn't valid! - $msg = 'Invalid driver requested: '.$child_name; - log_message('error', $msg); - show_error($msg); - } - - // Get package paths and filename case variations to search - $CI = get_instance(); - $paths = $CI->load->get_package_paths(TRUE); - - // Is there an extension? - $class_name = $prefix.$child_name; - $found = class_exists($class_name, FALSE); - if ( ! $found) - { - // Check for subclass file - foreach ($paths as $path) - { - // Does the file exist? - $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php'; - if (file_exists($file)) - { - // Yes - require base class from BASEPATH - $basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; - if ( ! file_exists($basepath)) - { - $msg = 'Unable to load the requested class: CI_'.$child_name; - log_message('error', $msg); - show_error($msg); - } - - // Include both sources and mark found - include_once($basepath); - include_once($file); - $found = TRUE; - break; - } - } - } - - // Do we need to search for the class? - if ( ! $found) - { - // Use standard class name - $class_name = 'CI_'.$child_name; - if ( ! class_exists($class_name, FALSE)) - { - // Check package paths - foreach ($paths as $path) - { - // Does the file exist? - $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; - if (file_exists($file)) - { - // Include source - include_once($file); - break; - } - } - } - } - - // Did we finally find the class? - if ( ! class_exists($class_name, FALSE)) - { - if (class_exists($child_name, FALSE)) - { - $class_name = $child_name; - } - else - { - $msg = 'Unable to load the requested driver: '.$class_name; - log_message('error', $msg); - show_error($msg); - } - } - - // Instantiate, decorate and add child - $obj = new $class_name(); - $obj->decorate($this); - $this->$child = $obj; - return $this->$child; - } - -} - -// -------------------------------------------------------------------------- - -/** - * CodeIgniter Driver Class - * - * This class enables you to create drivers for a Library based on the Driver Library. - * It handles the drivers' access to the parent library - * - * @package CodeIgniter - * @subpackage Libraries - * @category Libraries - * @author EllisLab Dev Team - * @link - */ -class CI_Driver { - - /** - * Instance of the parent class - * - * @var object - */ - protected $_parent; - - /** - * List of methods in the parent class - * - * @var array - */ - protected $_methods = array(); - - /** - * List of properties in the parent class - * - * @var array - */ - protected $_properties = array(); - - /** - * Array of methods and properties for the parent class(es) - * - * @static - * @var array - */ - protected static $_reflections = array(); - - /** - * Decorate - * - * Decorates the child with the parent driver lib's methods and properties - * - * @param object - * @return void - */ - public function decorate($parent) - { - $this->_parent = $parent; - - // Lock down attributes to what is defined in the class - // and speed up references in magic methods - - $class_name = get_class($parent); - - if ( ! isset(self::$_reflections[$class_name])) - { - $r = new ReflectionObject($parent); - - foreach ($r->getMethods() as $method) - { - if ($method->isPublic()) - { - $this->_methods[] = $method->getName(); - } - } - - foreach ($r->getProperties() as $prop) - { - if ($prop->isPublic()) - { - $this->_properties[] = $prop->getName(); - } - } - - self::$_reflections[$class_name] = array($this->_methods, $this->_properties); - } - else - { - list($this->_methods, $this->_properties) = self::$_reflections[$class_name]; - } - } - - // -------------------------------------------------------------------- - - /** - * __call magic method - * - * Handles access to the parent driver library's methods - * - * @param string - * @param array - * @return mixed - */ - public function __call($method, $args = array()) - { - if (in_array($method, $this->_methods)) - { - return call_user_func_array(array($this->_parent, $method), $args); - } - - throw new BadMethodCallException('No such method: '.$method.'()'); - } - - // -------------------------------------------------------------------- - - /** - * __get magic method - * - * Handles reading of the parent driver library's properties - * - * @param string - * @return mixed - */ - public function __get($var) - { - if (in_array($var, $this->_properties)) - { - return $this->_parent->$var; - } - } - - // -------------------------------------------------------------------- - - /** - * __set magic method - * - * Handles writing to the parent driver library's properties - * - * @param string - * @param array - * @return mixed - */ - public function __set($var, $val) - { - if (in_array($var, $this->_properties)) - { - $this->_parent->$var = $val; - } - } - -} diff --git a/system/libraries/Email.php b/system/libraries/Email.php deleted file mode 100755 index cd74d6da..00000000 --- a/system/libraries/Email.php +++ /dev/null @@ -1,2490 +0,0 @@ - '1 (Highest)', - 2 => '2 (High)', - 3 => '3 (Normal)', - 4 => '4 (Low)', - 5 => '5 (Lowest)' - ); - - /** - * mbstring.func_overload flag - * - * @var bool - */ - protected static $func_overload; - - // -------------------------------------------------------------------- - - /** - * Constructor - Sets Email Preferences - * - * The constructor can be passed an array of config values - * - * @param array $config = array() - * @return void - */ - public function __construct(array $config = array()) - { - $this->charset = config_item('charset'); - $this->initialize($config); - $this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode')); - - isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload')); - - log_message('info', 'Email Class Initialized'); - } - - // -------------------------------------------------------------------- - - /** - * Initialize preferences - * - * @param array $config - * @return CI_Email - */ - public function initialize(array $config = array()) - { - $this->clear(); - - foreach ($config as $key => $val) - { - if (isset($this->$key)) - { - $method = 'set_'.$key; - - if (method_exists($this, $method)) - { - $this->$method($val); - } - else - { - $this->$key = $val; - } - } - } - - $this->charset = strtoupper($this->charset); - $this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Initialize the Email Data - * - * @param bool - * @return CI_Email - */ - public function clear($clear_attachments = FALSE) - { - $this->_subject = ''; - $this->_body = ''; - $this->_finalbody = ''; - $this->_header_str = ''; - $this->_replyto_flag = FALSE; - $this->_recipients = array(); - $this->_cc_array = array(); - $this->_bcc_array = array(); - $this->_headers = array(); - $this->_debug_msg = array(); - - $this->set_header('Date', $this->_set_date()); - - if ($clear_attachments !== FALSE) - { - $this->_attachments = array(); - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set FROM - * - * @param string $from - * @param string $name - * @param string $return_path = NULL Return-Path - * @return CI_Email - */ - public function from($from, $name = '', $return_path = NULL) - { - if (preg_match('/\<(.*)\>/', $from, $match)) - { - $from = $match[1]; - } - - if ($this->validate) - { - $this->validate_email($this->_str_to_array($from)); - if ($return_path) - { - $this->validate_email($this->_str_to_array($return_path)); - } - } - - // prepare the display name - if ($name !== '') - { - // only use Q encoding if there are characters that would require it - if ( ! preg_match('/[\200-\377]/', $name)) - { - // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes - $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"'; - } - else - { - $name = $this->_prep_q_encoding($name); - } - } - - $this->set_header('From', $name.' <'.$from.'>'); - - isset($return_path) OR $return_path = $from; - $this->set_header('Return-Path', '<'.$return_path.'>'); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Reply-to - * - * @param string - * @param string - * @return CI_Email - */ - public function reply_to($replyto, $name = '') - { - if (preg_match('/\<(.*)\>/', $replyto, $match)) - { - $replyto = $match[1]; - } - - if ($this->validate) - { - $this->validate_email($this->_str_to_array($replyto)); - } - - if ($name !== '') - { - // only use Q encoding if there are characters that would require it - if ( ! preg_match('/[\200-\377]/', $name)) - { - // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes - $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"'; - } - else - { - $name = $this->_prep_q_encoding($name); - } - } - - $this->set_header('Reply-To', $name.' <'.$replyto.'>'); - $this->_replyto_flag = TRUE; - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Recipients - * - * @param string - * @return CI_Email - */ - public function to($to) - { - $to = $this->_str_to_array($to); - $to = $this->clean_email($to); - - if ($this->validate) - { - $this->validate_email($to); - } - - if ($this->_get_protocol() !== 'mail') - { - $this->set_header('To', implode(', ', $to)); - } - - $this->_recipients = $to; - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set CC - * - * @param string - * @return CI_Email - */ - public function cc($cc) - { - $cc = $this->clean_email($this->_str_to_array($cc)); - - if ($this->validate) - { - $this->validate_email($cc); - } - - $this->set_header('Cc', implode(', ', $cc)); - - if ($this->_get_protocol() === 'smtp') - { - $this->_cc_array = $cc; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set BCC - * - * @param string - * @param string - * @return CI_Email - */ - public function bcc($bcc, $limit = '') - { - if ($limit !== '' && is_numeric($limit)) - { - $this->bcc_batch_mode = TRUE; - $this->bcc_batch_size = $limit; - } - - $bcc = $this->clean_email($this->_str_to_array($bcc)); - - if ($this->validate) - { - $this->validate_email($bcc); - } - - if ($this->_get_protocol() === 'smtp' OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size)) - { - $this->_bcc_array = $bcc; - } - else - { - $this->set_header('Bcc', implode(', ', $bcc)); - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Email Subject - * - * @param string - * @return CI_Email - */ - public function subject($subject) - { - $subject = $this->_prep_q_encoding($subject); - $this->set_header('Subject', $subject); - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Body - * - * @param string - * @return CI_Email - */ - public function message($body) - { - $this->_body = rtrim(str_replace("\r", '', $body)); - - /* strip slashes only if magic quotes is ON - if we do it with magic quotes OFF, it strips real, user-inputted chars. - - NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and - it will probably not exist in future versions at all. - */ - if ( ! is_php('5.4') && get_magic_quotes_gpc()) - { - $this->_body = stripslashes($this->_body); - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Assign file attachments - * - * @param string $file Can be local path, URL or buffered content - * @param string $disposition = 'attachment' - * @param string $newname = NULL - * @param string $mime = '' - * @return CI_Email - */ - public function attach($file, $disposition = '', $newname = NULL, $mime = '') - { - if ($mime === '') - { - if (strpos($file, '://') === FALSE && ! file_exists($file)) - { - $this->_set_error_message('lang:email_attachment_missing', $file); - return FALSE; - } - - if ( ! $fp = @fopen($file, 'rb')) - { - $this->_set_error_message('lang:email_attachment_unreadable', $file); - return FALSE; - } - - $file_content = stream_get_contents($fp); - $mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION)); - fclose($fp); - } - else - { - $file_content =& $file; // buffered file - } - - $this->_attachments[] = array( - 'name' => array($file, $newname), - 'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters - 'type' => $mime, - 'content' => chunk_split(base64_encode($file_content)), - 'multipart' => 'mixed' - ); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set and return attachment Content-ID - * - * Useful for attached inline pictures - * - * @param string $filename - * @return string - */ - public function attachment_cid($filename) - { - for ($i = 0, $c = count($this->_attachments); $i < $c; $i++) - { - if ($this->_attachments[$i]['name'][0] === $filename) - { - $this->_attachments[$i]['multipart'] = 'related'; - $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@'); - return $this->_attachments[$i]['cid']; - } - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Add a Header Item - * - * @param string - * @param string - * @return CI_Email - */ - public function set_header($header, $value) - { - $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value); - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Convert a String to an Array - * - * @param string - * @return array - */ - protected function _str_to_array($email) - { - if ( ! is_array($email)) - { - return (strpos($email, ',') !== FALSE) - ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY) - : (array) trim($email); - } - - return $email; - } - - // -------------------------------------------------------------------- - - /** - * Set Multipart Value - * - * @param string - * @return CI_Email - */ - public function set_alt_message($str) - { - $this->alt_message = (string) $str; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Mailtype - * - * @param string - * @return CI_Email - */ - public function set_mailtype($type = 'text') - { - $this->mailtype = ($type === 'html') ? 'html' : 'text'; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Wordwrap - * - * @param bool - * @return CI_Email - */ - public function set_wordwrap($wordwrap = TRUE) - { - $this->wordwrap = (bool) $wordwrap; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Protocol - * - * @param string - * @return CI_Email - */ - public function set_protocol($protocol = 'mail') - { - $this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail'; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Priority - * - * @param int - * @return CI_Email - */ - public function set_priority($n = 3) - { - $this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Newline Character - * - * @param string - * @return CI_Email - */ - public function set_newline($newline = "\n") - { - $this->newline = in_array($newline, array("\n", "\r\n", "\r")) ? $newline : "\n"; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set CRLF - * - * @param string - * @return CI_Email - */ - public function set_crlf($crlf = "\n") - { - $this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Get the Message ID - * - * @return string - */ - protected function _get_message_id() - { - $from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']); - return '<'.uniqid('').strstr($from, '@').'>'; - } - - // -------------------------------------------------------------------- - - /** - * Get Mail Protocol - * - * @return mixed - */ - protected function _get_protocol() - { - $this->protocol = strtolower($this->protocol); - in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail'; - return $this->protocol; - } - - // -------------------------------------------------------------------- - - /** - * Get Mail Encoding - * - * @return string - */ - protected function _get_encoding() - { - in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit'; - - foreach ($this->_base_charsets as $charset) - { - if (strpos($this->charset, $charset) === 0) - { - $this->_encoding = '7bit'; - } - } - - return $this->_encoding; - } - - // -------------------------------------------------------------------- - - /** - * Get content type (text/html/attachment) - * - * @return string - */ - protected function _get_content_type() - { - if ($this->mailtype === 'html') - { - return empty($this->_attachments) ? 'html' : 'html-attach'; - } - elseif ($this->mailtype === 'text' && ! empty($this->_attachments)) - { - return 'plain-attach'; - } - - return 'plain'; - } - - // -------------------------------------------------------------------- - - /** - * Set RFC 822 Date - * - * @return string - */ - protected function _set_date() - { - $timezone = date('Z'); - $operator = ($timezone[0] === '-') ? '-' : '+'; - $timezone = abs($timezone); - $timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60; - - return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone); - } - - // -------------------------------------------------------------------- - - /** - * Mime message - * - * @return string - */ - protected function _get_mime_message() - { - return 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.'; - } - - // -------------------------------------------------------------------- - - /** - * Validate Email Address - * - * @param string - * @return bool - */ - public function validate_email($email) - { - if ( ! is_array($email)) - { - $this->_set_error_message('lang:email_must_be_array'); - return FALSE; - } - - foreach ($email as $val) - { - if ( ! $this->valid_email($val)) - { - $this->_set_error_message('lang:email_invalid_address', $val); - return FALSE; - } - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Email Validation - * - * @param string - * @return bool - */ - public function valid_email($email) - { - if (function_exists('idn_to_ascii') && strpos($email, '@')) - { - list($account, $domain) = explode('@', $email, 2); - $domain = defined('INTL_IDNA_VARIANT_UTS46') - ? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) - : idn_to_ascii($domain); - - if ($domain !== FALSE) - { - $email = $account.'@'.$domain; - } - } - - return (bool) filter_var($email, FILTER_VALIDATE_EMAIL); - } - - // -------------------------------------------------------------------- - - /** - * Clean Extended Email Address: Joe Smith - * - * @param string - * @return string - */ - public function clean_email($email) - { - if ( ! is_array($email)) - { - return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email; - } - - $clean_email = array(); - - foreach ($email as $addy) - { - $clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy; - } - - return $clean_email; - } - - // -------------------------------------------------------------------- - - /** - * Build alternative plain text message - * - * Provides the raw message for use in plain-text headers of - * HTML-formatted emails. - * If the user hasn't specified his own alternative message - * it creates one by stripping the HTML - * - * @return string - */ - protected function _get_alt_message() - { - if ( ! empty($this->alt_message)) - { - return ($this->wordwrap) - ? $this->word_wrap($this->alt_message, 76) - : $this->alt_message; - } - - $body = preg_match('/\(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body; - $body = str_replace("\t", '', preg_replace('#\n"; - } - - return $r; - } - - // -------------------------------------------------------------------- - - /** - * Executes the Method - * - * @param object - * @return mixed - */ - protected function _execute($m) - { - $methName = $m->method_name; - - // Check to see if it is a system call - $system_call = (strpos($methName, 'system') === 0); - - if ($this->xss_clean === FALSE) - { - $m->xss_clean = FALSE; - } - - //------------------------------------- - // Valid Method - //------------------------------------- - - if ( ! isset($this->methods[$methName]['function'])) - { - return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); - } - - //------------------------------------- - // Check for Method (and Object) - //------------------------------------- - - $method_parts = explode('.', $this->methods[$methName]['function']); - $objectCall = ! empty($method_parts[1]); - - if ($system_call === TRUE) - { - if ( ! is_callable(array($this, $method_parts[1]))) - { - return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); - } - } - elseif (($objectCall && ! is_callable(array($method_parts[0], $method_parts[1]))) - OR ( ! $objectCall && ! is_callable($this->methods[$methName]['function'])) - ) - { - return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); - } - - //------------------------------------- - // Checking Methods Signature - //------------------------------------- - - if (isset($this->methods[$methName]['signature'])) - { - $sig = $this->methods[$methName]['signature']; - for ($i = 0, $c = count($sig); $i < $c; $i++) - { - $current_sig = $sig[$i]; - - if (count($current_sig) === count($m->params)+1) - { - for ($n = 0, $mc = count($m->params); $n < $mc; $n++) - { - $p = $m->params[$n]; - $pt = ($p->kindOf() === 'scalar') ? $p->scalarval() : $p->kindOf(); - - if ($pt !== $current_sig[$n+1]) - { - $pno = $n+1; - $wanted = $current_sig[$n+1]; - - return new XML_RPC_Response(0, - $this->xmlrpcerr['incorrect_params'], - $this->xmlrpcstr['incorrect_params'] . - ': Wanted '.$wanted.', got '.$pt.' at param '.$pno.')'); - } - } - } - } - } - - //------------------------------------- - // Calls the Function - //------------------------------------- - - if ($objectCall === TRUE) - { - if ($method_parts[0] === 'this' && $system_call === TRUE) - { - return call_user_func(array($this, $method_parts[1]), $m); - } - elseif ($this->object === FALSE) - { - return get_instance()->{$method_parts[1]}($m); - } - - return $this->object->{$method_parts[1]}($m); - } - - return call_user_func($this->methods[$methName]['function'], $m); - } - - // -------------------------------------------------------------------- - - /** - * Server Function: List Methods - * - * @param mixed - * @return object - */ - public function listMethods($m) - { - $v = new XML_RPC_Values(); - $output = array(); - - foreach ($this->methods as $key => $value) - { - $output[] = new XML_RPC_Values($key, 'string'); - } - - foreach ($this->system_methods as $key => $value) - { - $output[] = new XML_RPC_Values($key, 'string'); - } - - $v->addArray($output); - return new XML_RPC_Response($v); - } - - // -------------------------------------------------------------------- - - /** - * Server Function: Return Signature for Method - * - * @param mixed - * @return object - */ - public function methodSignature($m) - { - $parameters = $m->output_parameters(); - $method_name = $parameters[0]; - - if (isset($this->methods[$method_name])) - { - if ($this->methods[$method_name]['signature']) - { - $sigs = array(); - $signature = $this->methods[$method_name]['signature']; - - for ($i = 0, $c = count($signature); $i < $c; $i++) - { - $cursig = array(); - $inSig = $signature[$i]; - for ($j = 0, $jc = count($inSig); $j < $jc; $j++) - { - $cursig[]= new XML_RPC_Values($inSig[$j], 'string'); - } - $sigs[] = new XML_RPC_Values($cursig, 'array'); - } - - return new XML_RPC_Response(new XML_RPC_Values($sigs, 'array')); - } - - return new XML_RPC_Response(new XML_RPC_Values('undef', 'string')); - } - - return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); - } - - // -------------------------------------------------------------------- - - /** - * Server Function: Doc String for Method - * - * @param mixed - * @return object - */ - public function methodHelp($m) - { - $parameters = $m->output_parameters(); - $method_name = $parameters[0]; - - if (isset($this->methods[$method_name])) - { - $docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : ''; - - return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string')); - } - - return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); - } - - // -------------------------------------------------------------------- - - /** - * Server Function: Multi-call - * - * @param mixed - * @return object - */ - public function multicall($m) - { - // Disabled - return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); - - $parameters = $m->output_parameters(); - $calls = $parameters[0]; - - $result = array(); - - foreach ($calls as $value) - { - $m = new XML_RPC_Message($value[0]); - $plist = ''; - - for ($i = 0, $c = count($value[1]); $i < $c; $i++) - { - $m->addParam(new XML_RPC_Values($value[1][$i], 'string')); - } - - $attempt = $this->_execute($m); - - if ($attempt->faultCode() !== 0) - { - return $attempt; - } - - $result[] = new XML_RPC_Values(array($attempt->value()), 'array'); - } - - return new XML_RPC_Response(new XML_RPC_Values($result, 'array')); - } - - // -------------------------------------------------------------------- - - /** - * Multi-call Function: Error Handling - * - * @param mixed - * @return object - */ - public function multicall_error($err) - { - $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString(); - $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode(); - - $struct['faultCode'] = new XML_RPC_Values($code, 'int'); - $struct['faultString'] = new XML_RPC_Values($str, 'string'); - - return new XML_RPC_Values($struct, 'struct'); - } - - // -------------------------------------------------------------------- - - /** - * Multi-call Function: Processes method - * - * @param mixed - * @return object - */ - public function do_multicall($call) - { - if ($call->kindOf() !== 'struct') - { - return $this->multicall_error('notstruct'); - } - elseif ( ! $methName = $call->me['struct']['methodName']) - { - return $this->multicall_error('nomethod'); - } - - list($scalar_value, $scalar_type) = array(reset($methName->me), key($methName->me)); - $scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type; - - if ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string') - { - return $this->multicall_error('notstring'); - } - elseif ($scalar_value === 'system.multicall') - { - return $this->multicall_error('recursion'); - } - elseif ( ! $params = $call->me['struct']['params']) - { - return $this->multicall_error('noparams'); - } - elseif ($params->kindOf() !== 'array') - { - return $this->multicall_error('notarray'); - } - - list($b, $a) = array(reset($params->me), key($params->me)); - - $msg = new XML_RPC_Message($scalar_value); - for ($i = 0, $numParams = count($b); $i < $numParams; $i++) - { - $msg->params[] = $params->me['array'][$i]; - } - - $result = $this->_execute($msg); - - if ($result->faultCode() !== 0) - { - return $this->multicall_error($result); - } - - return new XML_RPC_Values(array($result->value()), 'array'); - } - -} diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php deleted file mode 100755 index f8f95fe1..00000000 --- a/system/libraries/Zip.php +++ /dev/null @@ -1,532 +0,0 @@ -now = time(); - log_message('info', 'Zip Compression Class Initialized'); - } - - // -------------------------------------------------------------------- - - /** - * Add Directory - * - * Lets you add a virtual directory into which you can place files. - * - * @param mixed $directory the directory name. Can be string or array - * @return void - */ - public function add_dir($directory) - { - foreach ((array) $directory as $dir) - { - if ( ! preg_match('|.+/$|', $dir)) - { - $dir .= '/'; - } - - $dir_time = $this->_get_mod_time($dir); - $this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']); - } - } - - // -------------------------------------------------------------------- - - /** - * Get file/directory modification time - * - * If this is a newly created file/dir, we will set the time to 'now' - * - * @param string $dir path to file - * @return array filemtime/filemdate - */ - protected function _get_mod_time($dir) - { - // filemtime() may return false, but raises an error for non-existing files - $date = file_exists($dir) ? getdate(filemtime($dir)) : getdate($this->now); - - return array( - 'file_mtime' => ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2, - 'file_mdate' => (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'] - ); - } - - // -------------------------------------------------------------------- - - /** - * Add Directory - * - * @param string $dir the directory name - * @param int $file_mtime - * @param int $file_mdate - * @return void - */ - protected function _add_dir($dir, $file_mtime, $file_mdate) - { - $dir = str_replace('\\', '/', $dir); - - $this->zipdata .= - "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00" - .pack('v', $file_mtime) - .pack('v', $file_mdate) - .pack('V', 0) // crc32 - .pack('V', 0) // compressed filesize - .pack('V', 0) // uncompressed filesize - .pack('v', self::strlen($dir)) // length of pathname - .pack('v', 0) // extra field length - .$dir - // below is "data descriptor" segment - .pack('V', 0) // crc32 - .pack('V', 0) // compressed filesize - .pack('V', 0); // uncompressed filesize - - $this->directory .= - "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00" - .pack('v', $file_mtime) - .pack('v', $file_mdate) - .pack('V',0) // crc32 - .pack('V',0) // compressed filesize - .pack('V',0) // uncompressed filesize - .pack('v', self::strlen($dir)) // length of pathname - .pack('v', 0) // extra field length - .pack('v', 0) // file comment length - .pack('v', 0) // disk number start - .pack('v', 0) // internal file attributes - .pack('V', 16) // external file attributes - 'directory' bit set - .pack('V', $this->offset) // relative offset of local header - .$dir; - - $this->offset = self::strlen($this->zipdata); - $this->entries++; - } - - // -------------------------------------------------------------------- - - /** - * Add Data to Zip - * - * Lets you add files to the archive. If the path is included - * in the filename it will be placed within a directory. Make - * sure you use add_dir() first to create the folder. - * - * @param mixed $filepath A single filepath or an array of file => data pairs - * @param string $data Single file contents - * @return void - */ - public function add_data($filepath, $data = NULL) - { - if (is_array($filepath)) - { - foreach ($filepath as $path => $data) - { - $file_data = $this->_get_mod_time($path); - $this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']); - } - } - else - { - $file_data = $this->_get_mod_time($filepath); - $this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']); - } - } - - // -------------------------------------------------------------------- - - /** - * Add Data to Zip - * - * @param string $filepath the file name/path - * @param string $data the data to be encoded - * @param int $file_mtime - * @param int $file_mdate - * @return void - */ - protected function _add_data($filepath, $data, $file_mtime, $file_mdate) - { - $filepath = str_replace('\\', '/', $filepath); - - $uncompressed_size = self::strlen($data); - $crc32 = crc32($data); - $gzdata = self::substr(gzcompress($data, $this->compression_level), 2, -4); - $compressed_size = self::strlen($gzdata); - - $this->zipdata .= - "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00" - .pack('v', $file_mtime) - .pack('v', $file_mdate) - .pack('V', $crc32) - .pack('V', $compressed_size) - .pack('V', $uncompressed_size) - .pack('v', self::strlen($filepath)) // length of filename - .pack('v', 0) // extra field length - .$filepath - .$gzdata; // "file data" segment - - $this->directory .= - "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00" - .pack('v', $file_mtime) - .pack('v', $file_mdate) - .pack('V', $crc32) - .pack('V', $compressed_size) - .pack('V', $uncompressed_size) - .pack('v', self::strlen($filepath)) // length of filename - .pack('v', 0) // extra field length - .pack('v', 0) // file comment length - .pack('v', 0) // disk number start - .pack('v', 0) // internal file attributes - .pack('V', 32) // external file attributes - 'archive' bit set - .pack('V', $this->offset) // relative offset of local header - .$filepath; - - $this->offset = self::strlen($this->zipdata); - $this->entries++; - $this->file_num++; - } - - // -------------------------------------------------------------------- - - /** - * Read the contents of a file and add it to the zip - * - * @param string $path - * @param bool $archive_filepath - * @return bool - */ - public function read_file($path, $archive_filepath = FALSE) - { - if (file_exists($path) && FALSE !== ($data = file_get_contents($path))) - { - if (is_string($archive_filepath)) - { - $name = str_replace('\\', '/', $archive_filepath); - } - else - { - $name = str_replace('\\', '/', $path); - - if ($archive_filepath === FALSE) - { - $name = preg_replace('|.*/(.+)|', '\\1', $name); - } - } - - $this->add_data($name, $data); - return TRUE; - } - - return FALSE; - } - - // ------------------------------------------------------------------------ - - /** - * Read a directory and add it to the zip. - * - * This function recursively reads a folder and everything it contains (including - * sub-folders) and creates a zip based on it. Whatever directory structure - * is in the original file path will be recreated in the zip file. - * - * @param string $path path to source directory - * @param bool $preserve_filepath - * @param string $root_path - * @return bool - */ - public function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL) - { - $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR; - if ( ! $fp = @opendir($path)) - { - return FALSE; - } - - // Set the original directory root for child dir's to use as relative - if ($root_path === NULL) - { - $root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR; - } - - while (FALSE !== ($file = readdir($fp))) - { - if ($file[0] === '.') - { - continue; - } - - if (is_dir($path.$file)) - { - $this->read_dir($path.$file.DIRECTORY_SEPARATOR, $preserve_filepath, $root_path); - } - elseif (FALSE !== ($data = file_get_contents($path.$file))) - { - $name = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path); - if ($preserve_filepath === FALSE) - { - $name = str_replace($root_path, '', $name); - } - - $this->add_data($name.$file, $data); - } - } - - closedir($fp); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Get the Zip file - * - * @return string (binary encoded) - */ - public function get_zip() - { - // Is there any data to return? - if ($this->entries === 0) - { - return FALSE; - } - - return $this->zipdata - .$this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00" - .pack('v', $this->entries) // total # of entries "on this disk" - .pack('v', $this->entries) // total # of entries overall - .pack('V', self::strlen($this->directory)) // size of central dir - .pack('V', self::strlen($this->zipdata)) // offset to start of central dir - ."\x00\x00"; // .zip file comment length - } - - // -------------------------------------------------------------------- - - /** - * Write File to the specified directory - * - * Lets you write a file - * - * @param string $filepath the file name - * @return bool - */ - public function archive($filepath) - { - if ( ! ($fp = @fopen($filepath, 'w+b'))) - { - return FALSE; - } - - flock($fp, LOCK_EX); - - for ($result = $written = 0, $data = $this->get_zip(), $length = self::strlen($data); $written < $length; $written += $result) - { - if (($result = fwrite($fp, self::substr($data, $written))) === FALSE) - { - break; - } - } - - flock($fp, LOCK_UN); - fclose($fp); - - return is_int($result); - } - - // -------------------------------------------------------------------- - - /** - * Download - * - * @param string $filename the file name - * @return void - */ - public function download($filename = 'backup.zip') - { - if ( ! preg_match('|.+?\.zip$|', $filename)) - { - $filename .= '.zip'; - } - - get_instance()->load->helper('download'); - $get_zip = $this->get_zip(); - $zip_content =& $get_zip; - - force_download($filename, $zip_content); - } - - // -------------------------------------------------------------------- - - /** - * Initialize Data - * - * Lets you clear current zip data. Useful if you need to create - * multiple zips with different data. - * - * @return CI_Zip - */ - public function clear_data() - { - $this->zipdata = ''; - $this->directory = ''; - $this->entries = 0; - $this->file_num = 0; - $this->offset = 0; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Byte-safe strlen() - * - * @param string $str - * @return int - */ - protected static function strlen($str) - { - return (self::$func_overload) - ? mb_strlen($str, '8bit') - : strlen($str); - } - - // -------------------------------------------------------------------- - - /** - * Byte-safe substr() - * - * @param string $str - * @param int $start - * @param int $length - * @return string - */ - protected static function substr($str, $start, $length = NULL) - { - if (self::$func_overload) - { - // mb_substr($str, $start, null, '8bit') returns an empty - // string on PHP 5.3 - isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start); - return mb_substr($str, $start, $length, '8bit'); - } - - return isset($length) - ? substr($str, $start, $length) - : substr($str, $start); - } -} diff --git a/system/libraries/index.html b/system/libraries/index.html deleted file mode 100755 index b702fbc3..00000000 --- a/system/libraries/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - diff --git a/tests/migration.php b/tests/migration.php new file mode 100644 index 00000000..aff1477f --- /dev/null +++ b/tests/migration.php @@ -0,0 +1,117 @@ +systemDirectory . '/Boot.php'; +Boot::bootConsole($paths); + +function check(bool $condition, string $message): void +{ + if (! $condition) { + throw new RuntimeException($message); + } +} + +$database = config('Database'); +$database->default = array_replace($database->tests, ['DBPrefix' => '']); +$database->defaultGroup = 'default'; +$db = db_connect(); + +foreach (glob(APPPATH . 'Models/*.php') as $file) { + $class = 'App\\Models\\' . basename($file, '.php'); + check(class_exists($class), 'Model cannot be loaded: ' . $class); + model($class); +} + +check(Login::name() === 'login', 'Login table name changed.'); +check(LoginRole::name() === 'login_role', 'Role-link table name changed.'); +check(Transaction::name() === 'transaction', 'Transaction table name changed.'); + +$db->query('CREATE TABLE login (login_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password TEXT)'); +$db->query('CREATE TABLE user (login_id INTEGER PRIMARY KEY, first_name TEXT, last_name TEXT, email TEXT)'); +$db->query('CREATE TABLE role (role_id INTEGER PRIMARY KEY AUTOINCREMENT, role_name TEXT UNIQUE)'); +$db->query('CREATE TABLE login_role (login_id INTEGER, role_id INTEGER)'); +$db->query('CREATE TABLE consumption (login_id INTEGER PRIMARY KEY, amount INTEGER DEFAULT 0)'); +$db->query('CREATE TABLE "transaction" (transaction_id INTEGER PRIMARY KEY AUTOINCREMENT, subject_id INTEGER, author_id INTEGER, amount INTEGER, delta INTEGER, type TEXT, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP)'); + +check(model(Login::class)->add('migration-user', 'migration-password'), 'Login creation failed.'); +$loginId = (int) model(Login::class)->getLoginIdFromUsername('migration-user'); +check(model(Login::class)->checkUsernamePasswordCredentials('migration-user', 'migration-password'), 'Correct password rejected.'); +check(! model(Login::class)->checkUsernamePasswordCredentials('migration-user', 'wrong-password'), 'Wrong password accepted.'); +check(! model(Login::class)->checkUserIdPasswordCredentials(-1, 'password'), 'Missing login accepted.'); +check(model(User::class)->add($loginId, 'Migration', 'User', 'migration@example.com'), 'User creation failed.'); +check(model(Login_User::class)->exists($loginId), 'Login/user join failed.'); +check(model(Role::class)->add(Role::ROLE_USER), 'Role creation failed.'); +$roleId = model(Role::class)->getRoleIdFromRoleName(Role::ROLE_USER); +check(model(LoginRole::class)->add($loginId, $roleId), 'Role assignment failed.'); +check(model(Role_LoginRole::class)->userHasRole($loginId, Role::ROLE_USER), 'Role check failed.'); +check(! model(Role_LoginRole::class)->userHasRole($loginId, Role::ROLE_ADMIN), 'Unexpected admin permission.'); +check(count(model(User_LoginRole::class)->getUsersWithRole($roleId)) === 1, 'Users-by-role query failed.'); +check((int) model(Consumption::class)->get($loginId) === 0, 'New balance was not initialized.'); +check(model(Consumption::class)->change($loginId, $loginId, 5, Transaction::TYPE_UPGRADE), 'Credit change failed.'); +check(model(Consumption::class)->change($loginId, $loginId, -1, Transaction::TYPE_CONSUME), 'Consumption failed.'); +check((int) model(Consumption::class)->get($loginId) === 4, 'Balance is incorrect.'); +check(count(model(Transaction::class)->getAllFromSubject($loginId)) === 2, 'Transaction logging failed.'); +check(model(Transaction::class)->getConsumeCountForSubject($loginId) === 1, 'Consumption count is incorrect.'); +check(model(Transaction::class)->getSumDeltaSubjectIdWithinTop($loginId, 10), 'Leaderboard membership query failed.'); +check(count(model(\App\Models\User_Transaction::class)->getSumDeltaForAllSubjectId(false, 10)) === 1, 'Leaderboard user join failed.'); +check(count(model(\App\Models\Login_User_LoginRole_Role::class)->getAllUserData()) === 1, 'Admin user overview query failed.'); + +$db->query('CREATE TABLE login_reset (login_id INTEGER PRIMARY KEY, reset_key TEXT UNIQUE, date TIMESTAMP DEFAULT CURRENT_TIMESTAMP)'); +$key = model(\App\Models\LoginReset::class)->add($loginId); +check(is_string($key), 'Password reset key was not created.'); +check(model(\App\Models\LoginReset_User::class)->updatePassword($key, 'new-password'), 'Password reset failed.'); +check(! model(\App\Models\LoginReset::class)->exists($key), 'Password reset key can be reused.'); +check(model(Login::class)->checkUsernamePasswordCredentials('migration-user', 'new-password'), 'Password reset did not update credentials.'); + +service('language')->setLocale('en'); +check(lang('Lisa.login_username') === 'Username', 'English translations failed.'); +service('language')->setLocale('nl'); +check(lang('Lisa.login_username') !== 'Lisa.login_username', 'Dutch translations failed.'); + +$modelVersions = (new ReflectionClass(\App\Controllers\Install::class))->getMethod('getModelVersion'); +$db->query('CREATE TABLE model_versions (model_name TEXT, table_name TEXT, version INTEGER)'); +$db->table('model_versions')->insert(['model_name' => 'Login', 'table_name' => 'login', 'version' => 2]); +$installer = new \App\Controllers\Install(); +(new ReflectionProperty($installer, 'db'))->setValue($installer, $db); +check((int) $modelVersions->invoke($installer, Login::class) === 2, 'Historical model version keys changed.'); + +check((new \App\Libraries\Graph())->getGraphForTransactions([]) === '', 'Empty transaction history generated a graph.'); +\Config\Services::injectMock('request', \Config\Services::incomingrequest(null, false)); +$bufferLevel = ob_get_level(); +$javascript = (new \App\Libraries\Transactions())->getJavascript('default'); +check(str_contains($javascript, 'purchase = function'), 'Consumption JavaScript rendering failed.'); +check(ob_get_level() === $bufferLevel, 'Consumption JavaScript leaked an output buffer.'); +$consumeHtml = view('page/default/consume-header', [ + 'myId' => $loginId, + 'fields' => ['login_id' => 'login_id', 'first_name' => 'first_name', 'last_name' => 'last_name', 'amount' => 'amount'], + 'tabs' => ['test' => ['title' => 'Test', 'icon' => '', 'users' => [ + ['login_id' => $loginId, 'first_name' => 'Migration', 'last_name' => 'User', 'amount' => 4], + ]]], + 'winners' => [], + 'transactionsUi' => new \App\Libraries\Transactions(), +]); +check(str_contains($consumeHtml, 'id="snackbar"'), 'Consumption view failed to render its transaction controls.'); +$leaderboardHtml = view('page/default/leaderboard-header', [ + 'fields' => ['login_id' => 'login_id', 'first_name' => 'first_name', 'last_name' => 'last_name', 'sum' => 'sum'], + 'entries' => [['login_id' => $loginId, 'first_name' => 'Migration', 'last_name' => 'User', 'sum' => -1]], + 'streakLength' => [], + 'transactionsLibrary' => new \App\Libraries\Transactions(), +]); +check(str_contains($leaderboardHtml, 'id="snackbar"'), 'Leaderboard view failed to render its transaction controls.'); + +echo "CI4 migration regression checks passed on PHP " . PHP_VERSION . ".\n"; \ No newline at end of file diff --git a/application/.htaccess b/writable/.htaccess old mode 100755 new mode 100644 similarity index 57% rename from application/.htaccess rename to writable/.htaccess index 6c63ed4c..3462048a --- a/application/.htaccess +++ b/writable/.htaccess @@ -1,6 +1,6 @@ - Require all denied + Require all denied - Deny from all - \ No newline at end of file + Deny from all + diff --git a/application/helpers/index.html b/writable/cache/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/helpers/index.html rename to writable/cache/index.html diff --git a/application/hooks/index.html b/writable/debugbar/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/hooks/index.html rename to writable/debugbar/index.html diff --git a/application/index.html b/writable/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/index.html rename to writable/index.html diff --git a/application/language/dutch/admin/index.html b/writable/logs/index.html similarity index 100% rename from application/language/dutch/admin/index.html rename to writable/logs/index.html diff --git a/application/language/dutch/default/index.html b/writable/session/index.html similarity index 100% rename from application/language/dutch/default/index.html rename to writable/session/index.html diff --git a/application/language/dutch/index.html b/writable/uploads/index.html old mode 100755 new mode 100644 similarity index 100% rename from application/language/dutch/index.html rename to writable/uploads/index.html